Skip to content
Closed
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Agentics Foundation Website
url: https://agentics.org
about: Learn more about the Agentics Foundation and membership
94 changes: 94 additions & 0 deletions .github/ISSUE_TEMPLATE/project-submission.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Open Source Project Submission
description: Submit an open source project for review by the Agentics Foundation Open Source Committee
title: "[Project Submission] "
labels: ["status:pending-review"]
body:
- type: markdown
attributes:
value: |
## Agentics Foundation — Open Source Project Submission

Only registered members of the Agentics Foundation may submit projects.
Submissions from non-members will not be reviewed.

Please complete all required fields below.

- type: input
id: full-name
attributes:
label: Full Name
placeholder: Jane Doe
validations:
required: true

- type: input
id: email
attributes:
label: Email Address
placeholder: jane@example.com
validations:
required: true

- type: input
id: linkedin
attributes:
label: LinkedIn Profile URL
placeholder: https://linkedin.com/in/janedoe
validations:
required: true

- type: input
id: github-profile
attributes:
label: GitHub Profile URL
placeholder: https://github.com/janedoe
validations:
required: true

- type: input
id: repo-url
attributes:
label: Repository URL
description: Must be a public repository with a declared license.
placeholder: https://github.com/janedoe/my-project
validations:
required: true

- type: dropdown
id: category
attributes:
label: Submission Category
options:
- Project Donation
- Website Listing
- Co-Founder Search
- Problem Support
- Contributor Engagement
validations:
required: true

- type: textarea
id: description
attributes:
label: Project Description & Request
description: Describe the project and your specific request (max 500 characters).
placeholder: |
Brief description of what the project does and what you're asking the Foundation to help with.
validations:
required: true

- type: checkboxes
id: acknowledgments
attributes:
label: Acknowledgments
options:
- label: I am a registered member of the Agentics Foundation in good standing
required: true
- label: The repository is public and has a clearly stated open source license
required: true
- label: The project is aligned with the Foundation's mission, values, and code of conduct
required: true
- label: There are no known IP infringements associated with this project
required: true
- label: I understand that submission does not guarantee approval
required: true
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
156 changes: 156 additions & 0 deletions .github/workflows/approve-project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
name: Approve Project

on:
issues:
types: [labeled]

permissions:
contents: write
issues: write

jobs:
register-project:
runs-on: ubuntu-latest
if: github.event.label.name == 'status:approved'
concurrency:
group: registry-update
cancel-in-progress: false
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Extract project data and update registry
id: extract
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
script: |
const fs = require('fs');
const body = context.payload.issue.body || '';
const issueNumber = context.payload.issue.number;

// SEC-007: Sanitize user input before embedding in registry
function sanitize(input, maxLen = 500) {
return input
.replace(/[[\](){}|`*_~#>!\\]/g, '')
.substring(0, maxLen)
.trim();
}

// Parse fields from issue form
function extract(label) {
const re = new RegExp(`### ${label}\\s*\\n\\s*(.+)`, 'i');
const m = body.match(re);
return m ? m[1].trim() : '';
}

const name = extract('Full Name');
const repoUrl = extract('Repository URL');
const description = body.match(/### Project Description & Request\s*\n\s*([\s\S]*?)(?=\n###|\n---|\Z)/i);
const descText = description ? description[1].trim().split('\n')[0] : '';

// SEC-007: Validate repo URL format
const repoUrlPattern = /^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/?$/;
if (repoUrl && !repoUrlPattern.test(repoUrl)) {
core.warning(`Invalid repo URL format: ${repoUrl}`);
}

// Extract category
const categoryMap = {
'Project Donation': 'donation',
'Website Listing': 'website-listing',
'Co-Founder Search': 'cofounder',
'Problem Support': 'support',
'Contributor Engagement': 'contributors'
};
let category = 'unknown';
for (const [text, slug] of Object.entries(categoryMap)) {
if (body.includes(text)) { category = slug; break; }
}

// Calculate total score from score comments
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber
});

let totalScore = 0;
let scoreCount = 0;
for (const c of comments) {
const match = c.body.match(/\*\*Total\*\*\s*\|\s*\*\*(\d+)\/25\*\*/);
if (match) {
totalScore += parseInt(match[1]);
scoreCount++;
}
}
const avgScore = scoreCount > 0 ? Math.round(totalScore / scoreCount) : 0;

// SEC-009: Validate registry schema
const registryPath = 'data/approved-projects.json';
let registry;
try {
registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
if (!Array.isArray(registry)) {
throw new Error('Registry must be an array');
}
} catch (e) {
core.setFailed(`Registry validation failed: ${e.message}`);
return;
}

// Generate ID from issue number (avoids race condition with registry.length)
const id = `proj-${String(issueNumber).padStart(3, '0')}`;

// Add entry
registry.push({
id,
name: sanitize(descText, 80) || `Submission #${issueNumber}`,
repo_url: repoUrl,
category,
approved_date: new Date().toISOString().split('T')[0],
submitter: context.payload.issue.user.login,
description: sanitize(descText, 500),
total_score: avgScore,
issue_number: issueNumber,
status: 'active'
});
Comment on lines +101 to +116

fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2) + '\n');

// Set output for commit step
core.setOutput('project_id', id);
core.setOutput('issue_number', String(issueNumber));

- name: Commit and push
env:
PROJECT_ID: ${{ steps.extract.outputs.project_id }}
ISSUE_NUMBER: ${{ steps.extract.outputs.issue_number }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add data/approved-projects.json
git commit -m "Add approved project ${PROJECT_ID} from issue #${ISSUE_NUMBER}" || exit 0
git pull --rebase origin main
git push

- name: Post confirmation
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
env:
PROJECT_ID: ${{ steps.extract.outputs.project_id }}
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: [
'### Project Registered',
'',
`This project has been added to the [approved projects registry](../blob/main/data/approved-projects.json) as \`${process.env.PROJECT_ID}\`.`,
'',
'The submitter will be contacted with next steps.',
'',
'---',
'*Automated registration.*'
].join('\n')
});
Loading
Loading