@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/VERSION +1 -1
- package/bin/cli.js +155 -31
- package/claude-md/01-core.md +9 -6
- package/claude-md/20-frontend-react.md +1 -0
- package/commands/spartan/contribute.md +140 -0
- package/commands/spartan/kotlin-service.md +4 -4
- package/commands/spartan/migration.md +1 -1
- package/commands/spartan/qa.md +222 -0
- package/commands/spartan/sessions.md +143 -0
- package/commands/spartan/testcontainer.md +1 -1
- package/commands/spartan.md +141 -1
- package/lib/assembler.js +147 -2
- package/lib/assembler.test.js +159 -0
- package/lib/detector.js +166 -0
- package/lib/detector.test.js +221 -0
- package/lib/resolver.js +129 -1
- package/lib/resolver.test.js +159 -1
- package/package.json +2 -2
- package/packs/core.yaml +2 -0
- package/packs/frontend-react.yaml +2 -0
- package/packs/packs.compiled.json +7 -3
- package/skills/browser-qa/SKILL.md +180 -0
- package/skills/database-table-creator/SKILL.md +12 -394
- package/skills/database-table-creator/kotlin-templates.md +400 -0
- package/skills/database-table-creator/migration-template.sql +1 -1
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"plugins": [
|
|
9
9
|
{
|
|
10
10
|
"name": "spartan-ai-toolkit",
|
|
11
|
-
"description": "5 workflows,
|
|
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.
|
|
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.
|
|
4
|
-
"description": "Engineering discipline layer for Claude Code — 5 workflows,
|
|
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.
|
|
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 =
|
|
362
|
-
for (const cmd of selectedCommands) {
|
|
363
|
-
const src = join(
|
|
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
|
|
376
|
-
if (
|
|
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
|
|
381
|
-
|
|
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
|
|
397
|
-
if (
|
|
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
|
|
403
|
-
const src = join(
|
|
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
|
|
417
|
-
if (
|
|
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
|
|
423
|
-
const src = join(
|
|
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
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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
|
|
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('================================================'))}`);
|
package/claude-md/01-core.md
CHANGED
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
## Core Principles (Always Enforce)
|
|
5
5
|
|
|
6
|
-
### 1.
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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/
|
|
11
|
-
- `rules/
|
|
12
|
-
- `rules/
|
|
13
|
-
- `rules/
|
|
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/
|
|
9
|
+
**Before creating, read:** `rules/database/SCHEMA.md`
|
|
10
10
|
|
|
11
11
|
## Hard Rules (from DATABASE_RULES)
|
|
12
12
|
- **TEXT not VARCHAR** — always
|