@c0x12c/spartan-ai-toolkit 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,9 +8,9 @@
8
8
  "plugins": [
9
9
  {
10
10
  "name": "spartan-ai-toolkit",
11
- "description": "5 workflows, 48 commands, 11 rules, 18 skills, 4 agents — organized in 11 packs with dependencies",
11
+ "description": "5 workflows, 51 commands, 11 rules, 19 skills, 4 agents — organized in 11 packs with dependencies",
12
12
  "source": "./toolkit",
13
- "version": "1.2.0"
13
+ "version": "1.2.1"
14
14
  }
15
15
  ]
16
16
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spartan-ai-toolkit",
3
- "version": "1.2.0",
4
- "description": "Engineering discipline layer for Claude Code — 5 workflows, 48 commands, 11 rules, 18 skills, 4 agents organized in 11 packs",
3
+ "version": "1.2.1",
4
+ "description": "Engineering discipline layer for Claude Code — 5 workflows, 51 commands, 11 rules, 19 skills, 4 agents organized in 11 packs",
5
5
  "author": {
6
6
  "name": "Khoa Tran",
7
7
  "url": "https://github.com/spartan-stratos"
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0
1
+ 1.2.1
package/bin/cli.js CHANGED
@@ -48,11 +48,15 @@ function blue(s) { return `${C.blue}${s}${C.reset}`; }
48
48
 
49
49
  // ── Pack definitions (loaded from YAML manifests) ───────────────
50
50
  import { PACKS, PACK_ORDER } from '../lib/packs.js';
51
- import { assembleCLAUDEmd } from '../lib/assembler.js';
52
- import { resolve as resolveDeps, resolveAliases, loadManifests } from '../lib/resolver.js';
51
+ import { assembleCLAUDEmd, assembleAGENTSmd } from '../lib/assembler.js';
52
+ import { resolve as resolveDeps, resolveAliases, loadManifests, loadExternalPacks } from '../lib/resolver.js';
53
+ import { detectStacks } from '../lib/detector.js';
53
54
 
54
55
  const manifests = loadManifests(join(PKG_ROOT, 'packs'));
55
56
 
57
+ // Maps community pack names to their source directory (for file resolution)
58
+ const externalPackSources = {};
59
+
56
60
  // ── Parse args ──────────────────────────────────────────────────
57
61
  const args = process.argv.slice(2);
58
62
 
@@ -61,12 +65,18 @@ let packsArg = '';
61
65
  let installAll = false;
62
66
  let mode = 'global'; // default for claude-code
63
67
  let showHelp = false;
68
+ let format = ''; // '' = default, 'agents-md' = export AGENTS.md
69
+ let autoDetect = false;
70
+ let packDirArg = ''; // external community pack directory
64
71
 
65
72
  for (const arg of args) {
66
73
  if (arg === '--help' || arg === '-h') showHelp = true;
67
74
  else if (arg.startsWith('--agent=')) agent = arg.split('=')[1];
68
75
  else if (arg.startsWith('--packs=')) packsArg = arg.split('=')[1];
76
+ else if (arg.startsWith('--format=')) format = arg.split('=')[1];
77
+ else if (arg.startsWith('--pack-dir=')) packDirArg = arg.split('=')[1];
69
78
  else if (arg === '--all') installAll = true;
79
+ else if (arg === '--auto') autoDetect = true;
70
80
  else if (arg === '--global') mode = 'global';
71
81
  else if (arg === '--local') mode = 'local';
72
82
  }
@@ -100,6 +110,9 @@ if (showHelp) {
100
110
  Choices: claude-code, cursor, windsurf, codex, copilot
101
111
  --packs=LIST Comma-separated packs (claude-code only)
102
112
  Example: --packs=backend-micronaut,product
113
+ --auto Auto-detect tech stack and suggest packs (no menu)
114
+ --pack-dir=DIR Load community packs from an external directory
115
+ --format=NAME Output format: agents-md (exports AGENTS.md for cross-tool use)
103
116
  --all Install all packs
104
117
  --global Install to home dir (default for claude-code/codex)
105
118
  --local Install to current project dir
@@ -124,6 +137,12 @@ ${lines.join('\n')}`).join('\n')}
124
137
 
125
138
  ${cyan('npx spartan-ai-toolkit@latest --agent=cursor')}
126
139
  Install rules for Cursor (rules + AGENTS.md only)
140
+
141
+ ${cyan('npx spartan-ai-toolkit@latest --auto')}
142
+ Auto-detect your tech stack and install matching packs
143
+
144
+ ${cyan('npx spartan-ai-toolkit@latest --format=agents-md --packs=backend-micronaut')}
145
+ Export AGENTS.md for any AI coding tool
127
146
  `);
128
147
  process.exit(0);
129
148
  }
@@ -220,6 +239,11 @@ function copyDir(src, dest) {
220
239
  cpSync(src, dest, { recursive: true });
221
240
  }
222
241
 
242
+ /** Get the source root for a pack (built-in uses PKG_ROOT, community uses pack-dir). */
243
+ function getPackSource(packName) {
244
+ return externalPackSources[packName] || PKG_ROOT;
245
+ }
246
+
223
247
  /** Get all items for a category across selected packs, deduplicated. */
224
248
  function gatherItems(selectedPacks, category) {
225
249
  const seen = new Set();
@@ -237,6 +261,24 @@ function gatherItems(selectedPacks, category) {
237
261
  return result;
238
262
  }
239
263
 
264
+ /** Like gatherItems but includes the source root for each item (for community pack support). */
265
+ function gatherItemsWithSource(selectedPacks, category) {
266
+ const seen = new Set();
267
+ const result = [];
268
+ for (const pack of selectedPacks) {
269
+ const def = PACKS[pack];
270
+ if (!def) continue;
271
+ const srcRoot = getPackSource(pack);
272
+ for (const item of def[category]) {
273
+ if (!seen.has(item)) {
274
+ seen.add(item);
275
+ result.push({ item, srcRoot });
276
+ }
277
+ }
278
+ }
279
+ return result;
280
+ }
281
+
240
282
  // ── Pack selection (grouped menu) ───────────────────────────────
241
283
  async function selectPacks(targets) {
242
284
  // --all flag
@@ -253,6 +295,44 @@ async function selectPacks(targets) {
253
295
  return resolveDeps(aliased, manifests);
254
296
  }
255
297
 
298
+ // --auto flag: detect tech stack
299
+ if (autoDetect) {
300
+ const cwd = process.cwd();
301
+ console.log(`\n ${blue('Scanning')} ${dim(cwd)} ${blue('for tech stack...')}\n`);
302
+ const { detected, comingSoon } = detectStacks(cwd);
303
+
304
+ if (detected.length > 0) {
305
+ console.log(` ${bold('Detected stacks:')}`);
306
+ for (const d of detected) {
307
+ console.log(` ${green('✓')} ${bold(d.pack)} ${dim(`(${d.reason})`)}`);
308
+ }
309
+
310
+ if (comingSoon.length > 0) {
311
+ console.log('');
312
+ for (const d of comingSoon) {
313
+ console.log(` ${yellow('~')} ${d.pack} ${dim(`(${d.reason})`)} ${dim('— coming soon, skipped')}`);
314
+ }
315
+ }
316
+ console.log('');
317
+
318
+ const packNames = detected.map(d => d.pack);
319
+ const confirm = await ask(` Install ${bold(packNames.join(' + '))}? [Y/n]: `);
320
+ if (confirm !== 'n' && confirm !== 'N') {
321
+ return resolveDeps(packNames, manifests);
322
+ }
323
+ // User said no — fall through to interactive menu
324
+ console.log('');
325
+ } else {
326
+ console.log(` ${dim('No stacks detected.')}`);
327
+ if (comingSoon.length > 0) {
328
+ for (const d of comingSoon) {
329
+ console.log(` ${yellow('~')} ${d.pack} ${dim(`(${d.reason})`)} ${dim('— coming soon')}`);
330
+ }
331
+ }
332
+ console.log(` ${dim('Falling back to interactive menu...')}\n`);
333
+ }
334
+ }
335
+
256
336
  // Check saved packs
257
337
  if (existsSync(targets.packsFile)) {
258
338
  let saved = readFileSync(targets.packsFile, 'utf-8').trim().split('\n').filter(Boolean);
@@ -358,9 +438,9 @@ async function installFull() {
358
438
  cmdCount++;
359
439
  }
360
440
 
361
- const selectedCommands = gatherItems(selectedPacks, 'commands');
362
- for (const cmd of selectedCommands) {
363
- const src = join(SRC.commandsSub, `${cmd}.md`);
441
+ const selectedCommands = gatherItemsWithSource(selectedPacks, 'commands');
442
+ for (const { item: cmd, srcRoot } of selectedCommands) {
443
+ const src = join(srcRoot, 'commands', 'spartan', `${cmd}.md`);
364
444
  if (existsSync(src)) {
365
445
  copyFile(src, join(targets.commands, `${cmd}.md`));
366
446
  console.log(` ${green('+')} /spartan:${cmd}`);
@@ -372,14 +452,13 @@ async function installFull() {
372
452
  console.log(` ${bold(cmdCount + ' commands')} installed\n`);
373
453
 
374
454
  // 3) Rules (now with subdirectory structure)
375
- const selectedRules = gatherItems(selectedPacks, 'rules');
376
- if (selectedRules.length > 0) {
455
+ const rulesWithSource = gatherItemsWithSource(selectedPacks, 'rules');
456
+ if (rulesWithSource.length > 0) {
377
457
  console.log(`${blue('[3/5]')} ${bold('Installing rules...')}`);
378
458
  let ruleCount = 0;
379
459
 
380
- for (const rule of selectedRules) {
381
- // Rules now have subdir paths like "database/SCHEMA.md"
382
- const src = join(SRC.rules, rule);
460
+ for (const { item: rule, srcRoot } of rulesWithSource) {
461
+ const src = join(srcRoot, 'rules', rule);
383
462
  const dest = join(targets.rules, rule);
384
463
  if (existsSync(src)) {
385
464
  copyFile(src, dest);
@@ -393,14 +472,14 @@ async function installFull() {
393
472
  }
394
473
 
395
474
  // 4) Skills
396
- const selectedSkills = gatherItems(selectedPacks, 'skills');
397
- if (selectedSkills.length > 0) {
475
+ const skillsWithSource = gatherItemsWithSource(selectedPacks, 'skills');
476
+ if (skillsWithSource.length > 0) {
398
477
  console.log(`${blue('[4/5]')} ${bold('Installing skills...')}`);
399
478
  ensureDir(targets.skills);
400
479
  let skillCount = 0;
401
480
 
402
- for (const skill of selectedSkills) {
403
- const src = join(SRC.skills, skill);
481
+ for (const { item: skill, srcRoot } of skillsWithSource) {
482
+ const src = join(srcRoot, 'skills', skill);
404
483
  if (existsSync(src)) {
405
484
  copyDir(src, join(targets.skills, skill));
406
485
  console.log(` ${green('+')} ${skill}`);
@@ -413,14 +492,14 @@ async function installFull() {
413
492
  }
414
493
 
415
494
  // 5) Agents
416
- const selectedAgents = gatherItems(selectedPacks, 'agents');
417
- if (selectedAgents.length > 0) {
495
+ const agentsWithSource = gatherItemsWithSource(selectedPacks, 'agents');
496
+ if (agentsWithSource.length > 0) {
418
497
  console.log(`${blue('[5/5]')} ${bold('Installing agents...')}`);
419
498
  ensureDir(targets.agents);
420
499
  let agentCount = 0;
421
500
 
422
- for (const agentFile of selectedAgents) {
423
- const src = join(SRC.agents, agentFile);
501
+ for (const { item: agentFile, srcRoot } of agentsWithSource) {
502
+ const src = join(srcRoot, 'agents', agentFile);
424
503
  if (existsSync(src)) {
425
504
  copyFile(src, join(targets.agents, agentFile));
426
505
  console.log(` ${green('+')} ${agentFile.replace('.md', '')}`);
@@ -495,23 +574,15 @@ async function installRulesOnly() {
495
574
  console.log(`\n${blue('[1/2]')} ${bold('Rules')} — ${dim('no rule packs selected')}\n`);
496
575
  }
497
576
 
498
- // Install AGENTS.md
577
+ // Install AGENTS.md — assembled from pack sections + agents
499
578
  console.log(`${blue('[2/2]')} ${bold('Installing AGENTS.md...')}`);
500
579
 
501
- const allAgents = gatherItems([...PACK_ORDER], 'agents');
502
- if (allAgents.length > 0 && targets.agentsMd) {
503
- let agentsContent = '# Spartan AI Toolkit — Agents\n\n';
504
- agentsContent += 'Expert agents for your AI coding assistant.\n\n---\n\n';
505
- for (const agentFile of allAgents) {
506
- const src = join(SRC.agents, agentFile);
507
- if (existsSync(src)) {
508
- agentsContent += readFileSync(src, 'utf-8') + '\n\n---\n\n';
509
- }
510
- }
511
- writeFileSync(targets.agentsMd, agentsContent.trimEnd() + '\n', 'utf-8');
580
+ if (targets.agentsMd) {
581
+ const agentsContent = assembleAGENTSmd(SRC.claudeMd, SRC.agents, selectedPacks, PACKS);
582
+ writeFileSync(targets.agentsMd, agentsContent, 'utf-8');
512
583
  console.log(` ${green('+')} AGENTS.md\n`);
513
584
  } else {
514
- console.log(` ${dim('No agents to install')}\n`);
585
+ console.log(` ${dim('No AGENTS.md target')}\n`);
515
586
  }
516
587
 
517
588
  // Save selection
@@ -538,6 +609,49 @@ async function main() {
538
609
  process.exit(1);
539
610
  }
540
611
 
612
+ // Load community packs if --pack-dir is set
613
+ if (packDirArg) {
614
+ const packDirPath = pathResolve(process.cwd(), packDirArg);
615
+ const builtinNames = new Set(manifests.keys());
616
+ console.log(`\n ${blue('Loading community packs from')} ${dim(packDirPath)}`);
617
+ const { loaded, errors } = loadExternalPacks(packDirPath, builtinNames);
618
+
619
+ if (errors.length > 0) {
620
+ for (const err of errors) {
621
+ console.log(` ${yellow('!')} ${err}`);
622
+ }
623
+ }
624
+
625
+ if (loaded.size > 0) {
626
+ for (const [name, manifest] of loaded) {
627
+ manifests.set(name, manifest);
628
+ externalPackSources[name] = packDirPath;
629
+ // Add to PACKS and PACK_ORDER so menus and install work
630
+ PACKS[name] = {
631
+ description: manifest.description,
632
+ category: manifest.category || 'Community',
633
+ priority: manifest.priority ?? 500,
634
+ hidden: manifest.hidden || false,
635
+ comingSoon: manifest['coming-soon'] || false,
636
+ depends: manifest.depends || [],
637
+ commands: manifest.commands || [],
638
+ rules: manifest.rules || [],
639
+ skills: manifest.skills || [],
640
+ agents: manifest.agents || [],
641
+ claudeSections: manifest['claude-sections'] || [],
642
+ };
643
+ PACK_ORDER.push(name);
644
+ }
645
+ // Re-sort PACK_ORDER by priority
646
+ PACK_ORDER.sort((a, b) => (PACKS[a]?.priority ?? 999) - (PACKS[b]?.priority ?? 999));
647
+
648
+ const names = [...loaded.keys()].join(', ');
649
+ console.log(` ${green('+')} Loaded: ${bold(names)}\n`);
650
+ } else {
651
+ console.log(` ${dim('No valid community packs found')}\n`);
652
+ }
653
+ }
654
+
541
655
  let selectedPacks;
542
656
 
543
657
  try {
@@ -554,6 +668,16 @@ async function main() {
554
668
  closeRL();
555
669
  }
556
670
 
671
+ // Export AGENTS.md alongside normal install when --format=agents-md
672
+ if (format === 'agents-md' && (agent === 'claude-code' || agent === 'codex')) {
673
+ const cwd = process.cwd();
674
+ const agentsMdPath = join(cwd, 'AGENTS.md');
675
+ console.log(`${blue('[+]')} ${bold('Exporting AGENTS.md for cross-tool use...')}`);
676
+ const agentsContent = assembleAGENTSmd(SRC.claudeMd, SRC.agents, selectedPacks, PACKS);
677
+ writeFileSync(agentsMdPath, agentsContent, 'utf-8');
678
+ console.log(` ${green('+')} AGENTS.md (works with Cursor, Copilot, Windsurf, Codex, and 20+ tools)\n`);
679
+ }
680
+
557
681
  // Success
558
682
  const userPacks = selectedPacks.filter(p => !PACKS[p]?.hidden);
559
683
  console.log(`${bold(green('================================================'))}`);
@@ -3,16 +3,19 @@
3
3
 
4
4
  ## Core Principles (Always Enforce)
5
5
 
6
- ### 1. Spec Before Code
6
+ ### 1. Match the User's Language
7
+ **Detect the language of the user's message and respond entirely in that same language.** This is not optional — it overrides the default English behavior of all commands. If the user writes in Vietnamese, ALL output must be in Vietnamese. If in French, respond in French. If in English, respond in English. This applies to everything: explanations, questions, gate prompts, debug reports, summaries, and PR descriptions. Only code syntax, variable names, file paths, and command names (e.g., `/spartan:fix`) stay in their original form.
8
+
9
+ ### 2. Spec Before Code
7
10
  - Task < 1 day → `/spartan:quickplan` for fast spec + plan
8
11
  - Task > 1 day → `/spartan:project new` or `/spartan:project milestone-new`
9
12
  - Never write production code without a written spec or plan
10
13
 
11
- ### 2. TDD is Non-Negotiable
14
+ ### 3. TDD is Non-Negotiable
12
15
  - Red → Green → Refactor, always
13
16
  - Write tests first, then the code that makes them pass
14
17
 
15
- ### 3. Atomic Commits
18
+ ### 4. Atomic Commits
16
19
  Each commit = one task, tests passing:
17
20
  ```
18
21
  type(scope): what changed
@@ -21,7 +24,7 @@ type(scope): what changed
21
24
  ```
22
25
  Types: `feat` · `fix` · `test` · `refactor` · `chore` · `docs`
23
26
 
24
- ### 4. Context Hygiene (Auto-Managed)
27
+ ### 5. Context Hygiene (Auto-Managed)
25
28
  Claude proactively manages its own context window:
26
29
  - When detecting context pressure (slow responses, forgetting earlier context, long conversation) → auto-run `/compact` to summarize and free space
27
30
  - If compaction isn't enough → auto-save critical state to `.handoff/` and `.memory/`, then tell user to start a fresh session
@@ -35,7 +38,7 @@ Claude proactively manages its own context window:
35
38
  - Response quality dropping → warn user + compact
36
39
  - Multi-step command taking unusually long → consider compacting between steps
37
40
 
38
- ### 5. Auto Mode
41
+ ### 6. Auto Mode
39
42
  When user says **"auto on"** or **"auto mode"**, all Spartan commands skip confirmation prompts and execute straight through. Claude will:
40
43
  - Show the spec/plan/output but NOT pause to ask "does this match?" or "shall I proceed?"
41
44
  - Continue to the next step automatically after each step completes
@@ -46,7 +49,7 @@ Turn off with **"auto off"**. Default is **auto off** (commands ask for confirma
46
49
 
47
50
  Auto mode is ideal for experienced users who trust the workflow and want maximum velocity.
48
51
 
49
- ### 6. Safety Guardrails
52
+ ### 7. Safety Guardrails
50
53
 
51
54
  | Command | What it does |
52
55
  |---|---|
@@ -42,3 +42,4 @@ For small tasks (< 1 day), `/spartan:quickplan` covers spec + plan in one shot.
42
42
  | `/spartan:fe-review` | PR review with Next.js App Router conventions |
43
43
  | `/spartan:figma-to-code [url]` | Convert Figma screen to production code via MCP |
44
44
  | `/spartan:e2e [feature]` | Scaffold Playwright E2E testing |
45
+ | `/spartan:qa [url] [feature]` | Real browser QA — opens Chromium, tests flows, finds bugs |
@@ -0,0 +1,140 @@
1
+ ---
2
+ name: spartan:contribute
3
+ description: Toggle contributor mode — Claude rates its own skill experience (0-10) after each command and files improvement reports to ~/.spartan/contributor-logs/. Opt-in only.
4
+ argument-hint: "[on | off | reports]"
5
+ preamble-tier: 2
6
+ ---
7
+
8
+ # Contributor Mode: {{ args[0] | default: "on" }}
9
+
10
+ You are managing **contributor mode** — a self-improvement loop for Spartan.
11
+
12
+ When active, after every `/spartan:*` command finishes, Claude writes a short field report rating how well the skill worked.
13
+
14
+ ---
15
+
16
+ {% if args[0] == "on" or args[0] == nil %}
17
+ ## Activate Contributor Mode
18
+
19
+ ```bash
20
+ mkdir -p ~/.spartan/contributor-logs
21
+ echo "enabled" > ~/.spartan/contributor-mode
22
+ ```
23
+
24
+ Say: "Contributor mode ON. I'll file a quick report after each command — you won't notice unless something scored low."
25
+
26
+ ### What Happens Now
27
+
28
+ After every `/spartan:*` command completes, **silently** evaluate and write a report:
29
+
30
+ ```bash
31
+ mkdir -p ~/.spartan/contributor-logs
32
+ ```
33
+
34
+ Write a file to `~/.spartan/contributor-logs/YYYY-MM-DD-HH-MM-command-name.md`:
35
+
36
+ ```markdown
37
+ ## Field Report: /spartan:[command]
38
+ Date: [ISO timestamp]
39
+ Score: [0-10]
40
+ Branch: [current branch]
41
+ Directory: [project directory]
42
+
43
+ ### What worked
44
+ - [1-2 bullets on what the skill did well]
45
+
46
+ ### What didn't work
47
+ - [1-2 bullets on friction, confusion, or missing features]
48
+ - [skip this section if score >= 8]
49
+
50
+ ### Suggestion
51
+ [One concrete improvement — a specific change to the command/skill, not vague feedback]
52
+ ```
53
+
54
+ ### Scoring Guide
55
+
56
+ | Score | Meaning |
57
+ |-------|---------|
58
+ | 9-10 | Nailed it. No friction. Would use again without changes. |
59
+ | 7-8 | Good. Minor friction but got the job done. |
60
+ | 5-6 | OK. Needed manual intervention or workarounds. |
61
+ | 3-4 | Poor. Missed the point or gave bad guidance. |
62
+ | 0-2 | Broken. Actively wasted time or caused errors. |
63
+
64
+ ### Rules for Reporting
65
+
66
+ 1. **Be honest.** Don't inflate scores. A 6 is a 6.
67
+ 2. **Be specific.** "The migration template assumed Flyway but we use Liquibase" is useful. "Could be better" is not.
68
+ 3. **One suggestion per report.** The best improvement, not a wish list.
69
+ 4. **Don't slow down the user.** Write the report silently after the command finishes. Never ask the user to rate anything.
70
+ 5. **Score relative to the skill's promise.** If `/spartan:qa` says it'll find bugs but only checked if the page loads, that's a 4.
71
+
72
+ {% elif args[0] == "off" %}
73
+ ## Deactivate Contributor Mode
74
+
75
+ ```bash
76
+ rm -f ~/.spartan/contributor-mode
77
+ ```
78
+
79
+ Say: "Contributor mode OFF. No more field reports. Your existing reports are still in `~/.spartan/contributor-logs/`."
80
+
81
+ {% elif args[0] == "reports" %}
82
+ ## View Reports
83
+
84
+ ```bash
85
+ echo "=== Contributor Reports ==="
86
+ echo ""
87
+
88
+ if [ ! -d ~/.spartan/contributor-logs ] || [ -z "$(ls ~/.spartan/contributor-logs/ 2>/dev/null)" ]; then
89
+ echo "No reports yet. Enable contributor mode with /spartan:contribute"
90
+ exit 0
91
+ fi
92
+
93
+ # Summary stats
94
+ TOTAL=$(ls ~/.spartan/contributor-logs/*.md 2>/dev/null | wc -l | tr -d ' ')
95
+ echo "Total reports: $TOTAL"
96
+ echo ""
97
+
98
+ # Show last 10 reports with scores
99
+ echo "Recent reports:"
100
+ for f in $(ls -t ~/.spartan/contributor-logs/*.md 2>/dev/null | head -10); do
101
+ FNAME=$(basename "$f" .md)
102
+ SCORE=$(grep "^Score:" "$f" 2>/dev/null | head -1 | awk '{print $2}')
103
+ echo " [$SCORE/10] $FNAME"
104
+ done
105
+ ```
106
+
107
+ Show the results in a clean table. Then:
108
+
109
+ - If any score < 5: "Some skills scored low. Want me to read those reports and suggest fixes?"
110
+ - If average > 7: "Skills are working well overall."
111
+ - Always: "Reports are in `~/.spartan/contributor-logs/`. You can share them as GitHub issues to help improve Spartan."
112
+
113
+ ### Aggregate Analysis
114
+
115
+ If the user asks for deeper analysis, group reports by command and show:
116
+
117
+ | Command | Reports | Avg Score | Lowest | Top Issue |
118
+ |---------|---------|-----------|--------|-----------|
119
+ | /spartan:build | N | X.X | X | [most common complaint] |
120
+ | /spartan:fix | N | X.X | X | [most common complaint] |
121
+
122
+ {% else %}
123
+ ## Unknown argument: {{ args[0] }}
124
+
125
+ Available options:
126
+ - `/spartan:contribute` — Turn on contributor mode (default)
127
+ - `/spartan:contribute off` — Turn it off
128
+ - `/spartan:contribute reports` — View and analyze filed reports
129
+ {% endif %}
130
+
131
+ ---
132
+
133
+ ## How to Check if Active
134
+
135
+ Any skill can check:
136
+ ```bash
137
+ [ -f ~/.spartan/contributor-mode ] && echo "CONTRIBUTOR_MODE=on"
138
+ ```
139
+
140
+ If the file exists and contains "enabled", file a report after the skill finishes.
@@ -7,10 +7,10 @@ argument-hint: "[service-name] [brief description]"
7
7
  You are scaffolding a new Kotlin Micronaut microservice for the Spartan platform.
8
8
 
9
9
  **Before scaffolding, read these company rules:**
10
- - `rules/project/ARCHITECTURE_RULES.md` — Layered architecture
11
- - `rules/project/CORE_RULES.md` — Kotlin conventions
12
- - `rules/project/DATABASE_RULES.md` — Schema standards
13
- - `rules/project/API_RULES.md` — API design patterns
10
+ - `rules/shared-backend/ARCHITECTURE.md` — Layered architecture
11
+ - `rules/backend-micronaut/KOTLIN.md` — Kotlin conventions
12
+ - `rules/database/SCHEMA.md` — Schema standards
13
+ - `rules/backend-micronaut/API_DESIGN.md` — API design patterns
14
14
 
15
15
  ## Service: {{ args[0] }}
16
16
  ## Purpose: {{ args[1] }}
@@ -6,7 +6,7 @@ argument-hint: "[description of the migration]"
6
6
 
7
7
  Create a migration for: {{ args[0] }}
8
8
 
9
- **Before creating, read:** `rules/project/DATABASE_RULES.md`
9
+ **Before creating, read:** `rules/database/SCHEMA.md`
10
10
 
11
11
  ## Hard Rules (from DATABASE_RULES)
12
12
  - **TEXT not VARCHAR** — always