1313# @all-contributors please add @jane-doe for documentation
1414# @all-contributors @user123 fixed typos in the book
1515# @all-contributors @dev42 implemented feature and wrote tests in tinytorch
16- #
17- # Contribution types: bug, code, doc, design, ideas, review, test, tool
18- # Projects: book (default), tinytorch, kits, labs
1916# =============================================================================
2017
2118name : ' 🤖 All Contributors Add'
2421 issue_comment :
2522 types : [created, edited]
2623
24+ # =============================================================================
25+ # CONFIGURATION - Edit these values to customize the workflow
26+ # =============================================================================
27+ env :
28+ # LLM Configuration
29+ LLM_MODEL : ' llama3.1:8b'
30+
31+ # Git Configuration
32+ TARGET_BRANCH : ' dev'
33+
34+ # Valid contribution types (comma-separated)
35+ CONTRIBUTION_TYPES : ' bug,code,doc,design,ideas,review,test,tool'
36+
37+ # Valid projects (comma-separated, first one is default)
38+ PROJECTS : ' book,tinytorch,kits,labs'
39+ DEFAULT_PROJECT : ' book'
40+
41+ # Project aliases (format: alias1:project1,alias2:project2)
42+ PROJECT_ALIASES : ' tito:tinytorch'
43+
2744jobs :
2845 add-contributor :
2946 name : Add Contributor
7491 uses : ai-action/ollama-action@v2
7592 id : llm
7693 with :
77- model : llama3.1:8b
94+ model : ${{ env.LLM_MODEL }}
7895 prompt : |
7996 Parse this contributor recognition comment and extract the required information.
8097
@@ -96,16 +113,36 @@ jobs:
96113 - tool: Built tools, scripts, automation, CLI utilities
97114
98115 PROJECT OPTIONS: book, tinytorch, kits, labs
99- - Default to "book" unless the comment or issue context mentions another project
116+ - IMPORTANT: Look for project names in the comment! Phrases like "in tinytorch", "in Tinytorch", "for tinytorch", "tinytorch contributor" indicate the project
117+ - Also check issue context (title/labels) for project hints
118+ - Default to "book" ONLY if no other project is mentioned anywhere
119+
120+ OUTPUT FORMAT - Return ONLY a JSON object with exactly these 3 fields:
121+ {
122+ "username": "<github-username-without-@>",
123+ "types": ["<contribution-type>"],
124+ "project": "<project-name>"
125+ }
126+
127+ FIELD REQUIREMENTS:
128+ - username (string, required): GitHub username WITHOUT the @ symbol
129+ - types (array of strings, required): One or more of: bug, code, doc, design, ideas, review, test, tool
130+ - project (string, required): One of: book, tinytorch, kits, labs
100131
101132 EXAMPLES:
102- - "@all-contributors @jane-doe fixed typos" -> username: "jane-doe", types: ["doc"]
103- - "@all-contributors please add @john_smith for documentation in book" -> username: "john_smith", types: ["doc"], project: "book"
104- - "@all-contributors @user123 helped verify the fix worked" -> username: "user123", types: ["test"]
105- - "@all-contributors @dev42 implemented the new feature and wrote tests" -> username: "dev42", types: ["code", "test"]
133+ Input: "@all-contributors @jane-doe fixed typos"
134+ Output: {"username": "jane-doe", "types": ["doc"], "project": "book"}
106135
107- Return ONLY a JSON object (no other text):
108- {"username": "the-username", "types": ["doc"], "project": "book"}
136+ Input: "@all-contributors please add @ngbolin for Doc in Tinytorch"
137+ Output: {"username": "ngbolin", "types": ["doc"], "project": "tinytorch"}
138+
139+ Input: "@all-contributors @user123 helped verify the fix worked in tinytorch"
140+ Output: {"username": "user123", "types": ["test"], "project": "tinytorch"}
141+
142+ Input: "@all-contributors @dev42 implemented the new feature and wrote tests"
143+ Output: {"username": "dev42", "types": ["code", "test"], "project": "book"}
144+
145+ Return ONLY the JSON object, no explanation or other text.
109146
110147 - name : Parse LLM response
111148 if : steps.extract.outputs.should_run == 'true'
@@ -115,19 +152,34 @@ jobs:
115152 LLM_RESPONSE : ${{ steps.llm.outputs.response }}
116153 TRIGGER_LINE : ${{ steps.extract.outputs.trigger_line }}
117154 ISSUE_CONTEXT : ${{ steps.extract.outputs.issue_context }}
155+ CONTRIBUTION_TYPES : ${{ env.CONTRIBUTION_TYPES }}
156+ PROJECTS : ${{ env.PROJECTS }}
157+ DEFAULT_PROJECT : ${{ env.DEFAULT_PROJECT }}
158+ PROJECT_ALIASES : ${{ env.PROJECT_ALIASES }}
118159 with :
119160 script : |
120161 const response = process.env.LLM_RESPONSE || '';
121162 const triggerLine = process.env.TRIGGER_LINE || '';
122163 const issueContext = process.env.ISSUE_CONTEXT || '';
123164 console.log('LLM response:', response);
124165
125- const validTypes = ['bug', 'code', 'doc', 'design', 'ideas', 'review', 'test', 'tool'];
126- const validProjects = ['book', 'tinytorch', 'kits', 'labs'];
166+ // Load configuration from environment
167+ const validTypes = process.env.CONTRIBUTION_TYPES.split(',');
168+ const validProjects = process.env.PROJECTS.split(',');
169+ const defaultProject = process.env.DEFAULT_PROJECT;
170+
171+ // Parse project aliases (format: alias1:project1,alias2:project2)
172+ const projectAliases = {};
173+ if (process.env.PROJECT_ALIASES) {
174+ process.env.PROJECT_ALIASES.split(',').forEach(pair => {
175+ const [alias, project] = pair.split(':');
176+ if (alias && project) projectAliases[alias] = project;
177+ });
178+ }
127179
128180 let username = null;
129181 let types = [];
130- let project = 'book' ;
182+ let project = defaultProject ;
131183
132184 try {
133185 // Find JSON in response (LLM might add extra text)
@@ -163,16 +215,28 @@ jobs:
163215 }
164216 }
165217
166- // Fallback: detect project from issue context if not in LLM response
167- if (project === 'book') {
218+ // Fallback: detect project from trigger line or issue context if not in LLM response
219+ // Priority: trigger line first (explicit user request), then issue context
220+ if (project === defaultProject) {
221+ const triggerLower = triggerLine.toLowerCase();
168222 const contextLower = issueContext.toLowerCase();
169- if (contextLower.includes('tinytorch') || contextLower.includes('tito')) {
170- project = 'tinytorch';
171- } else if (contextLower.includes('kits')) {
172- project = 'kits';
173- } else if (contextLower.includes('labs')) {
174- project = 'labs';
175- }
223+
224+ // Helper function to detect project in text
225+ const detectProject = (text) => {
226+ // Check for direct project names (except default)
227+ for (const p of validProjects) {
228+ if (p !== defaultProject && text.includes(p)) return p;
229+ }
230+ // Check for aliases
231+ for (const [alias, targetProject] of Object.entries(projectAliases)) {
232+ if (text.includes(alias)) return targetProject;
233+ }
234+ return null;
235+ };
236+
237+ // Check trigger line first, then issue context
238+ const detected = detectProject(triggerLower) || detectProject(contextLower);
239+ if (detected) project = detected;
176240 }
177241
178242 console.log('Username:', username);
@@ -202,7 +266,7 @@ jobs:
202266 if : steps.extract.outputs.should_run == 'true' && steps.parse.outputs.success == 'true'
203267 uses : actions/checkout@v4
204268 with :
205- ref : dev
269+ ref : ${{ env.TARGET_BRANCH }}
206270 fetch-depth : 0
207271
208272 - name : Setup Python
@@ -238,6 +302,8 @@ jobs:
238302
239303 - name : Update contributor config
240304 if : steps.extract.outputs.should_run == 'true' && steps.parse.outputs.success == 'true'
305+ env :
306+ PROJECTS : ${{ env.PROJECTS }}
241307 run : |
242308 python3 << 'EOF'
243309 import json
@@ -250,13 +316,9 @@ jobs:
250316 avatar_url = "${{ steps.userinfo.outputs.avatar_url }}"
251317 profile = "${{ steps.userinfo.outputs.profile }}"
252318
253- # Map project to config path
254- config_paths = {
255- 'book': 'book/.all-contributorsrc',
256- 'tinytorch': 'tinytorch/.all-contributorsrc',
257- 'kits': 'kits/.all-contributorsrc',
258- 'labs': 'labs/.all-contributorsrc',
259- }
319+ # Build config paths from PROJECTS env var
320+ projects = os.environ.get('PROJECTS', 'book').split(',')
321+ config_paths = {p: f'{p}/.all-contributorsrc' for p in projects}
260322
261323 config_path = config_paths[project]
262324
@@ -335,7 +397,7 @@ jobs:
335397 echo "No changes to commit"
336398 else
337399 git commit -m "docs: add @${USERNAME} as ${PROJECT} contributor for ${TYPES}"
338- git push origin dev
400+ git push origin ${{ env.TARGET_BRANCH }}
339401 echo "Changes committed and pushed!"
340402 fi
341403
@@ -382,10 +444,15 @@ jobs:
382444 - name : Handle parsing failure
383445 if : steps.extract.outputs.should_run == 'true' && steps.parse.outputs.success == 'false'
384446 uses : actions/github-script@v7
447+ env :
448+ PROJECTS : ${{ env.PROJECTS }}
449+ DEFAULT_PROJECT : ${{ env.DEFAULT_PROJECT }}
385450 with :
386451 script : |
387452 const error = '${{ steps.parse.outputs.error }}';
388453 const triggerLine = `${{ steps.extract.outputs.trigger_line }}`;
454+ const projects = process.env.PROJECTS.split(',');
455+ const defaultProject = process.env.DEFAULT_PROJECT;
389456
390457 let errorMsg = "I couldn't parse that comment.";
391458 if (error === 'no_username') {
@@ -417,7 +484,8 @@ jobs:
417484 "- test (testing, verification)",
418485 "- tool (built tools, automation)",
419486 "",
420- "**Project** is auto-detected from issue context, or you can mention it (e.g., 'in tinytorch')."
487+ `**Projects:** ${projects.join(', ')} (default: ${defaultProject})`,
488+ "Auto-detected from issue context, or mention it explicitly (e.g., 'in tinytorch')."
421489 ].join('\n');
422490
423491 await github.rest.issues.createComment({
0 commit comments