@c0x12c/ai-toolkit 1.15.0

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.
Files changed (255) hide show
  1. package/.claude-plugin/marketplace.json +16 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/README.md +439 -0
  4. package/VERSION +1 -0
  5. package/agents/design-critic.md +127 -0
  6. package/agents/idea-killer.md +72 -0
  7. package/agents/infrastructure-expert.md +49 -0
  8. package/agents/micronaut-backend-expert.md +45 -0
  9. package/agents/phase-reviewer.md +150 -0
  10. package/agents/research-planner.md +70 -0
  11. package/agents/solution-architect-cto.md +49 -0
  12. package/agents/sre-architect.md +49 -0
  13. package/agents/team-coordinator.md +111 -0
  14. package/bin/cli.js +780 -0
  15. package/claude-md/00-header.md +39 -0
  16. package/claude-md/01-core.md +105 -0
  17. package/claude-md/05-database.md +20 -0
  18. package/claude-md/11-backend-micronaut.md +19 -0
  19. package/claude-md/20-frontend-react.md +44 -0
  20. package/claude-md/25-ux-design.md +56 -0
  21. package/claude-md/30-infrastructure.md +24 -0
  22. package/claude-md/30-project-mgmt.md +119 -0
  23. package/claude-md/40-product.md +39 -0
  24. package/claude-md/50-ops.md +34 -0
  25. package/claude-md/60-research.md +27 -0
  26. package/claude-md/90-footer.md +21 -0
  27. package/commands/spartan/brainstorm.md +134 -0
  28. package/commands/spartan/brownfield.md +157 -0
  29. package/commands/spartan/build.md +435 -0
  30. package/commands/spartan/careful.md +94 -0
  31. package/commands/spartan/commit-message.md +112 -0
  32. package/commands/spartan/content.md +17 -0
  33. package/commands/spartan/context-save.md +161 -0
  34. package/commands/spartan/contribute.md +140 -0
  35. package/commands/spartan/daily.md +42 -0
  36. package/commands/spartan/debug.md +308 -0
  37. package/commands/spartan/deep-dive.md +55 -0
  38. package/commands/spartan/deploy.md +207 -0
  39. package/commands/spartan/e2e.md +264 -0
  40. package/commands/spartan/env-setup.md +166 -0
  41. package/commands/spartan/epic.md +199 -0
  42. package/commands/spartan/fe-review.md +181 -0
  43. package/commands/spartan/figma-to-code.md +260 -0
  44. package/commands/spartan/forensics.md +46 -0
  45. package/commands/spartan/freeze.md +84 -0
  46. package/commands/spartan/fundraise.md +53 -0
  47. package/commands/spartan/gate-review.md +229 -0
  48. package/commands/spartan/gsd-upgrade.md +376 -0
  49. package/commands/spartan/guard.md +42 -0
  50. package/commands/spartan/init-project.md +178 -0
  51. package/commands/spartan/init-rules.md +298 -0
  52. package/commands/spartan/interview.md +154 -0
  53. package/commands/spartan/kickoff.md +73 -0
  54. package/commands/spartan/kotlin-service.md +109 -0
  55. package/commands/spartan/lean-canvas.md +222 -0
  56. package/commands/spartan/lint-rules.md +122 -0
  57. package/commands/spartan/map-codebase.md +124 -0
  58. package/commands/spartan/migration.md +82 -0
  59. package/commands/spartan/next-app.md +317 -0
  60. package/commands/spartan/next-feature.md +212 -0
  61. package/commands/spartan/onboard.md +326 -0
  62. package/commands/spartan/outreach.md +16 -0
  63. package/commands/spartan/phase.md +142 -0
  64. package/commands/spartan/pitch.md +18 -0
  65. package/commands/spartan/plan.md +210 -0
  66. package/commands/spartan/pr-ready.md +202 -0
  67. package/commands/spartan/project.md +106 -0
  68. package/commands/spartan/qa.md +222 -0
  69. package/commands/spartan/research.md +254 -0
  70. package/commands/spartan/review.md +132 -0
  71. package/commands/spartan/scan-rules.md +173 -0
  72. package/commands/spartan/sessions.md +143 -0
  73. package/commands/spartan/spec.md +131 -0
  74. package/commands/spartan/startup.md +257 -0
  75. package/commands/spartan/team.md +570 -0
  76. package/commands/spartan/teardown.md +161 -0
  77. package/commands/spartan/testcontainer.md +97 -0
  78. package/commands/spartan/tf-cost.md +123 -0
  79. package/commands/spartan/tf-deploy.md +116 -0
  80. package/commands/spartan/tf-drift.md +100 -0
  81. package/commands/spartan/tf-import.md +107 -0
  82. package/commands/spartan/tf-module.md +121 -0
  83. package/commands/spartan/tf-plan.md +100 -0
  84. package/commands/spartan/tf-review.md +106 -0
  85. package/commands/spartan/tf-scaffold.md +109 -0
  86. package/commands/spartan/tf-security.md +147 -0
  87. package/commands/spartan/think.md +221 -0
  88. package/commands/spartan/unfreeze.md +13 -0
  89. package/commands/spartan/update.md +134 -0
  90. package/commands/spartan/ux.md +1233 -0
  91. package/commands/spartan/validate.md +193 -0
  92. package/commands/spartan/web-to-prd.md +706 -0
  93. package/commands/spartan/workstreams.md +109 -0
  94. package/commands/spartan/write.md +16 -0
  95. package/commands/spartan.md +386 -0
  96. package/frameworks/00-framework-comparison-guide.md +317 -0
  97. package/frameworks/01-lean-canvas.md +196 -0
  98. package/frameworks/02-design-sprint.md +304 -0
  99. package/frameworks/03-foundation-sprint.md +337 -0
  100. package/frameworks/04-business-model-canvas.md +391 -0
  101. package/frameworks/05-customer-development.md +426 -0
  102. package/frameworks/06-jobs-to-be-done.md +358 -0
  103. package/frameworks/07-mom-test.md +392 -0
  104. package/frameworks/08-value-proposition-canvas.md +488 -0
  105. package/frameworks/09-javelin-board.md +428 -0
  106. package/frameworks/10-build-measure-learn.md +467 -0
  107. package/frameworks/11-mvp-approaches.md +533 -0
  108. package/frameworks/think-before-build.md +593 -0
  109. package/lib/assembler.js +197 -0
  110. package/lib/assembler.test.js +159 -0
  111. package/lib/detector.js +166 -0
  112. package/lib/detector.test.js +221 -0
  113. package/lib/packs.js +16 -0
  114. package/lib/resolver.js +272 -0
  115. package/lib/resolver.test.js +298 -0
  116. package/lib/worktree.sh +104 -0
  117. package/package.json +50 -0
  118. package/packs/backend-micronaut.yaml +35 -0
  119. package/packs/backend-nodejs.yaml +15 -0
  120. package/packs/backend-python.yaml +15 -0
  121. package/packs/core.yaml +37 -0
  122. package/packs/database.yaml +21 -0
  123. package/packs/frontend-react.yaml +24 -0
  124. package/packs/infrastructure.yaml +40 -0
  125. package/packs/ops.yaml +16 -0
  126. package/packs/packs.compiled.json +371 -0
  127. package/packs/product.yaml +22 -0
  128. package/packs/project-mgmt.yaml +24 -0
  129. package/packs/research.yaml +39 -0
  130. package/packs/shared-backend.yaml +14 -0
  131. package/packs/ux-design.yaml +21 -0
  132. package/rules/backend-micronaut/API_DESIGN.md +313 -0
  133. package/rules/backend-micronaut/BATCH_PROCESSING.md +92 -0
  134. package/rules/backend-micronaut/CONTROLLERS.md +388 -0
  135. package/rules/backend-micronaut/KOTLIN.md +414 -0
  136. package/rules/backend-micronaut/RETROFIT_PLACEMENT.md +290 -0
  137. package/rules/backend-micronaut/SERVICES_AND_BEANS.md +325 -0
  138. package/rules/core/NAMING_CONVENTIONS.md +208 -0
  139. package/rules/core/SKILL_AUTHORING.md +174 -0
  140. package/rules/core/TIMEZONE.md +316 -0
  141. package/rules/database/ORM_AND_REPO.md +289 -0
  142. package/rules/database/SCHEMA.md +146 -0
  143. package/rules/database/TRANSACTIONS.md +311 -0
  144. package/rules/frontend-react/FRONTEND.md +344 -0
  145. package/rules/infrastructure/MODULES.md +260 -0
  146. package/rules/infrastructure/NAMING.md +196 -0
  147. package/rules/infrastructure/PROVIDERS.md +309 -0
  148. package/rules/infrastructure/SECURITY.md +310 -0
  149. package/rules/infrastructure/STATE_AND_BACKEND.md +237 -0
  150. package/rules/infrastructure/STRUCTURE.md +234 -0
  151. package/rules/infrastructure/VARIABLES.md +285 -0
  152. package/rules/shared-backend/ARCHITECTURE.md +46 -0
  153. package/rules/ux-design/DESIGN_PROCESS.md +176 -0
  154. package/skills/api-endpoint-creator/SKILL.md +455 -0
  155. package/skills/api-endpoint-creator/error-handling-guide.md +244 -0
  156. package/skills/api-endpoint-creator/examples.md +522 -0
  157. package/skills/api-endpoint-creator/testing-patterns.md +302 -0
  158. package/skills/article-writing/SKILL.md +109 -0
  159. package/skills/article-writing/examples.md +59 -0
  160. package/skills/backend-api-design/SKILL.md +84 -0
  161. package/skills/backend-api-design/code-patterns.md +138 -0
  162. package/skills/brainstorm/SKILL.md +95 -0
  163. package/skills/browser-qa/SKILL.md +87 -0
  164. package/skills/browser-qa/playwright-snippets.md +110 -0
  165. package/skills/ci-cd-patterns/SKILL.md +108 -0
  166. package/skills/ci-cd-patterns/workflows.md +149 -0
  167. package/skills/competitive-teardown/SKILL.md +93 -0
  168. package/skills/competitive-teardown/example-analysis.md +50 -0
  169. package/skills/content-engine/SKILL.md +131 -0
  170. package/skills/content-engine/examples.md +72 -0
  171. package/skills/database-patterns/SKILL.md +72 -0
  172. package/skills/database-patterns/code-templates.md +114 -0
  173. package/skills/database-table-creator/SKILL.md +141 -0
  174. package/skills/database-table-creator/examples.md +552 -0
  175. package/skills/database-table-creator/kotlin-templates.md +400 -0
  176. package/skills/database-table-creator/migration-template.sql +68 -0
  177. package/skills/database-table-creator/validation-checklist.md +337 -0
  178. package/skills/deep-research/SKILL.md +80 -0
  179. package/skills/design-intelligence/SKILL.md +268 -0
  180. package/skills/design-workflow/SKILL.md +127 -0
  181. package/skills/design-workflow/checklists.md +45 -0
  182. package/skills/idea-validation/SKILL.md +129 -0
  183. package/skills/idea-validation/example-report.md +50 -0
  184. package/skills/investor-materials/SKILL.md +122 -0
  185. package/skills/investor-materials/example-outline.md +70 -0
  186. package/skills/investor-outreach/SKILL.md +112 -0
  187. package/skills/investor-outreach/examples.md +76 -0
  188. package/skills/kotlin-best-practices/SKILL.md +58 -0
  189. package/skills/kotlin-best-practices/code-patterns.md +132 -0
  190. package/skills/market-research/SKILL.md +99 -0
  191. package/skills/security-checklist/SKILL.md +65 -0
  192. package/skills/security-checklist/audit-reference.md +95 -0
  193. package/skills/service-debugging/SKILL.md +116 -0
  194. package/skills/service-debugging/common-issues.md +65 -0
  195. package/skills/startup-pipeline/SKILL.md +152 -0
  196. package/skills/terraform-best-practices/SKILL.md +244 -0
  197. package/skills/terraform-module-creator/SKILL.md +284 -0
  198. package/skills/terraform-review/SKILL.md +222 -0
  199. package/skills/terraform-security-audit/SKILL.md +280 -0
  200. package/skills/terraform-service-scaffold/SKILL.md +574 -0
  201. package/skills/testing-strategies/SKILL.md +116 -0
  202. package/skills/testing-strategies/examples.md +103 -0
  203. package/skills/testing-strategies/integration-test-setup.md +71 -0
  204. package/skills/ui-ux-pro-max/SKILL.md +238 -0
  205. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  206. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  207. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  208. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  209. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  210. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  211. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  212. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  213. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  214. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  215. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  216. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  217. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  218. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  219. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  220. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  221. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  222. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  223. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  224. package/skills/ui-ux-pro-max/data/styles.csv +68 -0
  225. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  226. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  227. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  228. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  229. package/skills/ui-ux-pro-max/python-setup.md +146 -0
  230. package/skills/ui-ux-pro-max/scripts/core.py +253 -0
  231. package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  232. package/skills/ui-ux-pro-max/scripts/search.py +114 -0
  233. package/skills/web-to-prd/SKILL.md +478 -0
  234. package/templates/build-config.yaml +44 -0
  235. package/templates/commands-config.yaml +55 -0
  236. package/templates/competitor-analysis.md +60 -0
  237. package/templates/content/AGENT_TEMPLATE.md +47 -0
  238. package/templates/content/COMMAND_TEMPLATE.md +27 -0
  239. package/templates/content/RULE_TEMPLATE.md +40 -0
  240. package/templates/content/SKILL_TEMPLATE.md +41 -0
  241. package/templates/design-config.md +105 -0
  242. package/templates/design-doc.md +207 -0
  243. package/templates/epic.md +100 -0
  244. package/templates/feature-spec.md +181 -0
  245. package/templates/idea-canvas.md +47 -0
  246. package/templates/implementation-plan.md +159 -0
  247. package/templates/prd-template.md +86 -0
  248. package/templates/preamble.md +89 -0
  249. package/templates/project-readme.md +35 -0
  250. package/templates/quality-gates.md +230 -0
  251. package/templates/spartan-config.yaml +164 -0
  252. package/templates/user-interview.md +69 -0
  253. package/templates/validation-checklist.md +108 -0
  254. package/templates/workflow-backend-micronaut.md +409 -0
  255. package/templates/workflow-frontend-react.md +233 -0
package/bin/cli.js ADDED
@@ -0,0 +1,780 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Spartan AI Toolkit — npx installer
4
+ // Usage:
5
+ // npx @c0x12c/ai-toolkit@latest
6
+ // npx @c0x12c/ai-toolkit@latest --agent=cursor
7
+ // npx @c0x12c/ai-toolkit@latest --packs=backend-micronaut,product
8
+ // npx @c0x12c/ai-toolkit@latest --all
9
+ // npx @c0x12c/ai-toolkit@latest --local
10
+
11
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, cpSync, readdirSync, copyFileSync } from 'node:fs';
12
+ import { join, dirname, resolve as pathResolve } from 'node:path';
13
+ import { createInterface } from 'node:readline';
14
+ import { homedir } from 'node:os';
15
+ import { fileURLToPath } from 'node:url';
16
+
17
+ // ── Resolve package root (works from npx temp dir) ──────────────
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+ const PKG_ROOT = pathResolve(__dirname, '..');
21
+
22
+ // ── Toolkit source paths ────────────────────────────────────────
23
+ const SRC = {
24
+ commands: join(PKG_ROOT, 'commands'),
25
+ commandsSub: join(PKG_ROOT, 'commands', 'spartan'),
26
+ router: join(PKG_ROOT, 'commands', 'spartan.md'),
27
+ rules: join(PKG_ROOT, 'rules'),
28
+ skills: join(PKG_ROOT, 'skills'),
29
+ agents: join(PKG_ROOT, 'agents'),
30
+ profiles: join(PKG_ROOT, 'profiles'),
31
+ claudeMd: join(PKG_ROOT, 'claude-md'),
32
+ version: join(PKG_ROOT, 'VERSION'),
33
+ claudePlugin: join(PKG_ROOT, '.claude-plugin'),
34
+ };
35
+
36
+ // ── Colors ──────────────────────────────────────────────────────
37
+ const C = {
38
+ blue: '\x1b[34m', green: '\x1b[32m', yellow: '\x1b[33m',
39
+ red: '\x1b[31m', cyan: '\x1b[36m', bold: '\x1b[1m',
40
+ dim: '\x1b[2m', reset: '\x1b[0m',
41
+ };
42
+
43
+ function bold(s) { return `${C.bold}${s}${C.reset}`; }
44
+ function green(s) { return `${C.green}${s}${C.reset}`; }
45
+ function yellow(s) { return `${C.yellow}${s}${C.reset}`; }
46
+ function cyan(s) { return `${C.cyan}${s}${C.reset}`; }
47
+ function dim(s) { return `${C.dim}${s}${C.reset}`; }
48
+ function blue(s) { return `${C.blue}${s}${C.reset}`; }
49
+
50
+ // ── Pack definitions (loaded from YAML manifests) ───────────────
51
+ import { PACKS, PACK_ORDER } from '../lib/packs.js';
52
+ import { assembleCLAUDEmd, assembleAGENTSmd } from '../lib/assembler.js';
53
+ import { resolve as resolveDeps, resolveAliases, loadManifests, loadExternalPacks } from '../lib/resolver.js';
54
+ import { detectStacks } from '../lib/detector.js';
55
+
56
+ const manifests = loadManifests(join(PKG_ROOT, 'packs'));
57
+
58
+ // Maps community pack names to their source directory (for file resolution)
59
+ const externalPackSources = {};
60
+
61
+ // ── Parse args ──────────────────────────────────────────────────
62
+ const args = process.argv.slice(2);
63
+
64
+ let agent = 'claude-code';
65
+ let packsArg = '';
66
+ let installAll = false;
67
+ let mode = 'global'; // default for claude-code
68
+ let showHelp = false;
69
+ let format = ''; // '' = default, 'agents-md' = export AGENTS.md
70
+ let autoDetect = false;
71
+ let packDirArg = ''; // external community pack directory
72
+
73
+ for (const arg of args) {
74
+ if (arg === '--help' || arg === '-h') showHelp = true;
75
+ else if (arg.startsWith('--agent=')) agent = arg.split('=')[1];
76
+ else if (arg.startsWith('--packs=')) packsArg = arg.split('=')[1];
77
+ else if (arg.startsWith('--format=')) format = arg.split('=')[1];
78
+ else if (arg.startsWith('--pack-dir=')) packDirArg = arg.split('=')[1];
79
+ else if (arg === '--all') installAll = true;
80
+ else if (arg === '--auto') autoDetect = true;
81
+ else if (arg === '--global') mode = 'global';
82
+ else if (arg === '--local') mode = 'local';
83
+ }
84
+
85
+ if (showHelp) {
86
+ // Group packs by category for help display
87
+ const categories = {};
88
+ for (const name of PACK_ORDER) {
89
+ const p = PACKS[name];
90
+ if (p.hidden) continue;
91
+ const cat = p.category || 'Other';
92
+ if (!categories[cat]) categories[cat] = [];
93
+ const items = [
94
+ p.commands.length ? `${p.commands.length} commands` : '',
95
+ p.rules.length ? `${p.rules.length} rules` : '',
96
+ p.skills.length ? `${p.skills.length} skills` : '',
97
+ p.agents.length ? `${p.agents.length} agents` : '',
98
+ ].filter(Boolean).join(', ');
99
+ const tag = p.comingSoon ? ` ${dim('(coming soon)')}` : '';
100
+ categories[cat].push(` ${bold(name.padEnd(20))} ${p.description}${tag}${items ? dim(` (${items})`) : ''}`);
101
+ }
102
+
103
+ console.log(`
104
+ ${bold('Spartan AI Toolkit')} — installer
105
+
106
+ ${bold('Usage:')}
107
+ npx @c0x12c/ai-toolkit@latest [options]
108
+
109
+ ${bold('Options:')}
110
+ --agent=NAME Agent to set up for (default: claude-code)
111
+ Choices: claude-code, cursor, windsurf, codex, copilot
112
+ --packs=LIST Comma-separated packs (claude-code only)
113
+ Example: --packs=backend-micronaut,product
114
+ --auto Auto-detect tech stack and suggest packs (no menu)
115
+ --pack-dir=DIR Load community packs from an external directory
116
+ --format=NAME Output format: agents-md (exports AGENTS.md for cross-tool use)
117
+ --all Install all packs
118
+ --global Install to home dir (default for claude-code/codex)
119
+ --local Install to current project dir
120
+ --help Show this help
121
+
122
+ ${bold('Packs:')}
123
+ ${Object.entries(categories).map(([cat, lines]) => `\n ${bold(cat + ':')}
124
+ ${lines.join('\n')}`).join('\n')}
125
+
126
+ ${bold('Examples:')}
127
+ ${cyan('npx @c0x12c/ai-toolkit@latest')}
128
+ Interactive — pick agent and packs from menu
129
+
130
+ ${cyan('npx @c0x12c/ai-toolkit@latest --packs=frontend-react,product')}
131
+ Next.js app with product thinking tools
132
+
133
+ ${cyan('npx @c0x12c/ai-toolkit@latest --packs=backend-micronaut,project-mgmt')}
134
+ Kotlin APIs with full project lifecycle
135
+
136
+ ${cyan('npx @c0x12c/ai-toolkit@latest --all')}
137
+ Everything installed
138
+
139
+ ${cyan('npx @c0x12c/ai-toolkit@latest --agent=cursor')}
140
+ Install rules for Cursor (rules + AGENTS.md only)
141
+
142
+ ${cyan('npx @c0x12c/ai-toolkit@latest --auto')}
143
+ Auto-detect your tech stack and install matching packs
144
+
145
+ ${cyan('npx @c0x12c/ai-toolkit@latest --format=agents-md --packs=backend-micronaut')}
146
+ Export AGENTS.md for any AI coding tool
147
+ `);
148
+ process.exit(0);
149
+ }
150
+
151
+ // ── Readline helper ─────────────────────────────────────────────
152
+ let rl;
153
+
154
+ function getRL() {
155
+ if (!rl) {
156
+ rl = createInterface({ input: process.stdin, output: process.stdout });
157
+ }
158
+ return rl;
159
+ }
160
+
161
+ function ask(question) {
162
+ return new Promise(resolve => {
163
+ getRL().question(question, answer => resolve(answer.trim()));
164
+ });
165
+ }
166
+
167
+ function closeRL() {
168
+ if (rl) { rl.close(); rl = null; }
169
+ }
170
+
171
+ // ── Version ─────────────────────────────────────────────────────
172
+ const VERSION = existsSync(SRC.version)
173
+ ? readFileSync(SRC.version, 'utf-8').trim()
174
+ : '4.2.0';
175
+
176
+ // ── Target directories based on agent + mode ────────────────────
177
+ function getTargets() {
178
+ const home = homedir();
179
+ const cwd = process.cwd();
180
+
181
+ if (agent === 'claude-code') {
182
+ const base = mode === 'global' ? join(home, '.claude') : join(cwd, '.claude');
183
+ return {
184
+ base,
185
+ commands: join(base, 'commands', 'spartan'),
186
+ router: join(base, 'commands', 'spartan.md'),
187
+ rules: join(base, 'rules'),
188
+ skills: join(base, 'skills'),
189
+ agents: join(base, 'agents'),
190
+ claudeMd: mode === 'global' ? join(home, '.claude', 'CLAUDE.md') : join(cwd, 'CLAUDE.md'),
191
+ packsFile: join(base, '.spartan-packs'),
192
+ versionFile: join(base, '.spartan-version'),
193
+ };
194
+ }
195
+
196
+ if (agent === 'codex') {
197
+ const base = mode === 'global' ? join(home, '.codex') : join(cwd, '.codex');
198
+ return {
199
+ base,
200
+ commands: join(base, 'commands', 'spartan'),
201
+ router: join(base, 'commands', 'spartan.md'),
202
+ rules: join(base, 'rules'),
203
+ skills: join(base, 'skills'),
204
+ agents: join(base, 'agents'),
205
+ claudeMd: mode === 'global' ? join(home, '.codex', 'CLAUDE.md') : join(cwd, 'CLAUDE.md'),
206
+ packsFile: join(base, '.spartan-packs'),
207
+ versionFile: join(base, '.spartan-version'),
208
+ };
209
+ }
210
+
211
+ // cursor, windsurf, copilot — project-local only
212
+ const rulesDir = {
213
+ cursor: join(cwd, '.cursor', 'rules'),
214
+ windsurf: join(cwd, '.windsurf', 'rules'),
215
+ copilot: join(cwd, '.github', 'instructions'),
216
+ }[agent];
217
+
218
+ return {
219
+ base: cwd,
220
+ rules: rulesDir,
221
+ agentsMd: join(cwd, 'AGENTS.md'),
222
+ packsFile: join(cwd, '.spartan-packs'),
223
+ versionFile: null,
224
+ };
225
+ }
226
+
227
+ // ── Helpers ─────────────────────────────────────────────────────
228
+ function ensureDir(dir) {
229
+ mkdirSync(dir, { recursive: true });
230
+ }
231
+
232
+ function copyFile(src, dest) {
233
+ ensureDir(dirname(dest));
234
+ const content = readFileSync(src);
235
+ writeFileSync(dest, content);
236
+ }
237
+
238
+ function copyDir(src, dest) {
239
+ ensureDir(dest);
240
+ cpSync(src, dest, { recursive: true });
241
+ }
242
+
243
+ /** Get the source root for a pack (built-in uses PKG_ROOT, community uses pack-dir). */
244
+ function getPackSource(packName) {
245
+ return externalPackSources[packName] || PKG_ROOT;
246
+ }
247
+
248
+ /** Get all items for a category across selected packs, deduplicated. */
249
+ function gatherItems(selectedPacks, category) {
250
+ const seen = new Set();
251
+ const result = [];
252
+ for (const pack of selectedPacks) {
253
+ const def = PACKS[pack];
254
+ if (!def) continue;
255
+ for (const item of def[category]) {
256
+ if (!seen.has(item)) {
257
+ seen.add(item);
258
+ result.push(item);
259
+ }
260
+ }
261
+ }
262
+ return result;
263
+ }
264
+
265
+ /** Like gatherItems but includes the source root for each item (for community pack support). */
266
+ function gatherItemsWithSource(selectedPacks, category) {
267
+ const seen = new Set();
268
+ const result = [];
269
+ for (const pack of selectedPacks) {
270
+ const def = PACKS[pack];
271
+ if (!def) continue;
272
+ const srcRoot = getPackSource(pack);
273
+ for (const item of def[category]) {
274
+ if (!seen.has(item)) {
275
+ seen.add(item);
276
+ result.push({ item, srcRoot });
277
+ }
278
+ }
279
+ }
280
+ return result;
281
+ }
282
+
283
+ // ── Pack selection (grouped menu) ───────────────────────────────
284
+ async function selectPacks(targets) {
285
+ // --all flag
286
+ if (installAll) {
287
+ const all = PACK_ORDER.filter(p => !PACKS[p].hidden && !PACKS[p].comingSoon);
288
+ return resolveDeps(all, manifests);
289
+ }
290
+
291
+ // --packs flag
292
+ if (packsArg) {
293
+ let requested = packsArg.split(',').map(s => s.trim()).filter(Boolean);
294
+ const { resolved: aliased, warnings } = resolveAliases(requested);
295
+ for (const w of warnings) console.log(` ${yellow('!')} ${w}`);
296
+ return resolveDeps(aliased, manifests);
297
+ }
298
+
299
+ // --auto flag: detect tech stack
300
+ if (autoDetect) {
301
+ const cwd = process.cwd();
302
+ console.log(`\n ${blue('Scanning')} ${dim(cwd)} ${blue('for tech stack...')}\n`);
303
+ const { detected, comingSoon } = detectStacks(cwd);
304
+
305
+ if (detected.length > 0) {
306
+ console.log(` ${bold('Detected stacks:')}`);
307
+ for (const d of detected) {
308
+ console.log(` ${green('✓')} ${bold(d.pack)} ${dim(`(${d.reason})`)}`);
309
+ }
310
+
311
+ if (comingSoon.length > 0) {
312
+ console.log('');
313
+ for (const d of comingSoon) {
314
+ console.log(` ${yellow('~')} ${d.pack} ${dim(`(${d.reason})`)} ${dim('— coming soon, skipped')}`);
315
+ }
316
+ }
317
+ console.log('');
318
+
319
+ const packNames = detected.map(d => d.pack);
320
+ const confirm = await ask(` Install ${bold(packNames.join(' + '))}? [Y/n]: `);
321
+ if (confirm !== 'n' && confirm !== 'N') {
322
+ return resolveDeps(packNames, manifests);
323
+ }
324
+ // User said no — fall through to interactive menu
325
+ console.log('');
326
+ } else {
327
+ console.log(` ${dim('No stacks detected.')}`);
328
+ if (comingSoon.length > 0) {
329
+ for (const d of comingSoon) {
330
+ console.log(` ${yellow('~')} ${d.pack} ${dim(`(${d.reason})`)} ${dim('— coming soon')}`);
331
+ }
332
+ }
333
+ console.log(` ${dim('Falling back to interactive menu...')}\n`);
334
+ }
335
+ }
336
+
337
+ // Check saved packs
338
+ if (existsSync(targets.packsFile)) {
339
+ let saved = readFileSync(targets.packsFile, 'utf-8').trim().split('\n').filter(Boolean);
340
+ const { resolved: aliased, warnings } = resolveAliases(saved);
341
+ for (const w of warnings) console.log(` ${yellow('!')} ${w}`);
342
+ saved = aliased;
343
+
344
+ if (saved.length > 0) {
345
+ console.log(`\n ${cyan('Previously installed packs:')} ${saved.filter(p => !PACKS[p]?.hidden).join(', ')}`);
346
+ const reuse = await ask(' Re-install same packs? [Y/n]: ');
347
+ if (reuse !== 'n' && reuse !== 'N') {
348
+ return resolveDeps(saved, manifests);
349
+ }
350
+ }
351
+ }
352
+
353
+ // Interactive grouped menu
354
+ console.log(`\n ${bold('Choose your packs:')}\n`);
355
+
356
+ const categories = {};
357
+ const selectablePacks = [];
358
+ for (const name of PACK_ORDER) {
359
+ const p = PACKS[name];
360
+ if (p.hidden) continue;
361
+ const cat = p.category || 'Other';
362
+ if (!categories[cat]) categories[cat] = [];
363
+ categories[cat].push(name);
364
+ }
365
+
366
+ for (const [cat, packs] of Object.entries(categories)) {
367
+ console.log(` ${bold(cat + ':')}`);
368
+ for (const name of packs) {
369
+ const p = PACKS[name];
370
+ const tag = name === 'core' ? ` ${green('(always included)')}`
371
+ : p.comingSoon ? ` ${dim('(coming soon)')}`
372
+ : '';
373
+ console.log(` ${bold(name)} — ${p.description}${tag}`);
374
+ if (!p.comingSoon && name !== 'core') {
375
+ selectablePacks.push(name);
376
+ }
377
+ }
378
+ console.log('');
379
+ }
380
+
381
+ const allChoice = await ask(' Install all packs? [Y/n]: ');
382
+
383
+ if (allChoice !== 'n' && allChoice !== 'N') {
384
+ return resolveDeps(selectablePacks, manifests);
385
+ }
386
+
387
+ // Ask about each optional pack
388
+ const selected = [];
389
+ for (const pack of selectablePacks) {
390
+ const def = PACKS[pack];
391
+ const choice = await ask(` ${bold(pack)} — ${def.description}? [y/N]: `);
392
+ if (choice === 'y' || choice === 'Y') {
393
+ selected.push(pack);
394
+ }
395
+ }
396
+
397
+ return resolveDeps(selected, manifests);
398
+ }
399
+
400
+ // ── Install for claude-code / codex ─────────────────────────────
401
+ async function installFull() {
402
+ const targets = getTargets();
403
+ const selectedPacks = await selectPacks(targets);
404
+
405
+ // Show user-facing packs (hide hidden deps)
406
+ const userPacks = selectedPacks.filter(p => !PACKS[p]?.hidden);
407
+ const hiddenPacks = selectedPacks.filter(p => PACKS[p]?.hidden);
408
+ console.log(`\n ${green('Selected:')} ${bold(userPacks.join(' '))}`);
409
+ if (hiddenPacks.length > 0) {
410
+ console.log(` ${dim('Auto-included:')} ${dim(hiddenPacks.join(' '))}`);
411
+ }
412
+ console.log('');
413
+
414
+ // 1) Assemble & install CLAUDE.md
415
+ console.log(`${blue('[1/6]')} ${bold('Assembling CLAUDE.md...')}`);
416
+
417
+ if (existsSync(targets.claudeMd)) {
418
+ const backupPath = `${targets.claudeMd}.${Date.now()}.bak`;
419
+ copyFile(targets.claudeMd, backupPath);
420
+ console.log(` ${dim('Backed up existing CLAUDE.md')}`);
421
+ }
422
+
423
+ const assembled = assembleCLAUDEmd(SRC.claudeMd, selectedPacks, PACKS);
424
+ ensureDir(dirname(targets.claudeMd));
425
+ writeFileSync(targets.claudeMd, assembled, 'utf-8');
426
+
427
+ const sectionCount = 2 + 1 + gatherItems(selectedPacks, 'claudeSections').length;
428
+ console.log(` ${green('+')} CLAUDE.md assembled (${sectionCount} sections)\n`);
429
+
430
+ // 2) Commands
431
+ console.log(`${blue('[2/6]')} ${bold('Installing commands...')}`);
432
+ ensureDir(targets.commands);
433
+ let cmdCount = 0;
434
+
435
+ // Smart router (always)
436
+ if (existsSync(SRC.router)) {
437
+ copyFile(SRC.router, targets.router);
438
+ console.log(` ${green('+')} /spartan (smart router)`);
439
+ cmdCount++;
440
+ }
441
+
442
+ const selectedCommands = gatherItemsWithSource(selectedPacks, 'commands');
443
+ for (const { item: cmd, srcRoot } of selectedCommands) {
444
+ const src = join(srcRoot, 'commands', 'spartan', `${cmd}.md`);
445
+ if (existsSync(src)) {
446
+ copyFile(src, join(targets.commands, `${cmd}.md`));
447
+ console.log(` ${green('+')} /spartan:${cmd}`);
448
+ cmdCount++;
449
+ } else {
450
+ console.log(` ${yellow('!')} /spartan:${cmd} (not found, skipped)`);
451
+ }
452
+ }
453
+ console.log(` ${bold(cmdCount + ' commands')} installed\n`);
454
+
455
+ // 3) Rules (now with subdirectory structure)
456
+ const rulesWithSource = gatherItemsWithSource(selectedPacks, 'rules');
457
+ if (rulesWithSource.length > 0) {
458
+ console.log(`${blue('[3/6]')} ${bold('Installing rules...')}`);
459
+ let ruleCount = 0;
460
+
461
+ for (const { item: rule, srcRoot } of rulesWithSource) {
462
+ const src = join(srcRoot, 'rules', rule);
463
+ const dest = join(targets.rules, rule);
464
+ if (existsSync(src)) {
465
+ copyFile(src, dest);
466
+ console.log(` ${green('+')} ${rule}`);
467
+ ruleCount++;
468
+ }
469
+ }
470
+ console.log(` ${bold(ruleCount + ' rules')} installed\n`);
471
+ } else {
472
+ console.log(`${blue('[3/6]')} ${bold('Rules')} — ${dim('no rule packs selected, skipping')}\n`);
473
+ }
474
+
475
+ // 4) Skills
476
+ const skillsWithSource = gatherItemsWithSource(selectedPacks, 'skills');
477
+ if (skillsWithSource.length > 0) {
478
+ console.log(`${blue('[4/6]')} ${bold('Installing skills...')}`);
479
+ ensureDir(targets.skills);
480
+ let skillCount = 0;
481
+
482
+ for (const { item: skill, srcRoot } of skillsWithSource) {
483
+ const src = join(srcRoot, 'skills', skill);
484
+ if (existsSync(src)) {
485
+ copyDir(src, join(targets.skills, skill));
486
+ console.log(` ${green('+')} ${skill}`);
487
+ skillCount++;
488
+ }
489
+ }
490
+ console.log(` ${bold(skillCount + ' skills')} installed\n`);
491
+ } else {
492
+ console.log(`${blue('[4/6]')} ${bold('Skills')} — ${dim('no skill packs selected, skipping')}\n`);
493
+ }
494
+
495
+ // 5) Agents
496
+ const agentsWithSource = gatherItemsWithSource(selectedPacks, 'agents');
497
+ if (agentsWithSource.length > 0) {
498
+ console.log(`${blue('[5/6]')} ${bold('Installing agents...')}`);
499
+ ensureDir(targets.agents);
500
+ let agentCount = 0;
501
+
502
+ for (const { item: agentFile, srcRoot } of agentsWithSource) {
503
+ const src = join(srcRoot, 'agents', agentFile);
504
+ if (existsSync(src)) {
505
+ copyFile(src, join(targets.agents, agentFile));
506
+ console.log(` ${green('+')} ${agentFile.replace('.md', '')}`);
507
+ agentCount++;
508
+ }
509
+ }
510
+ console.log(` ${bold(agentCount + ' agents')} installed\n`);
511
+ } else {
512
+ console.log(`${blue('[5/6]')} ${bold('Agents')} — ${dim('no agent packs selected, skipping')}\n`);
513
+ }
514
+
515
+ // 6) Generate .spartan/config.yaml from best matching profile
516
+ const configDir = mode === 'global'
517
+ ? join(homedir(), '.spartan')
518
+ : join(process.cwd(), '.spartan');
519
+ const configPath = join(configDir, 'config.yaml');
520
+
521
+ if (!existsSync(configPath)) {
522
+ const profile = pickProfile(selectedPacks);
523
+ const profileSrc = join(SRC.profiles, `${profile}.yaml`);
524
+
525
+ if (existsSync(profileSrc)) {
526
+ ensureDir(configDir);
527
+ copyFile(profileSrc, configPath);
528
+ console.log(`${blue('[6/6]')} ${bold('Generated .spartan/config.yaml')} from ${cyan(profile)} profile`);
529
+ console.log(` ${dim('Edit this file to customize rules, review stages, and build commands')}\n`);
530
+ }
531
+ } else {
532
+ console.log(`${blue('[6/6]')} ${bold('.spartan/config.yaml')} — ${dim('already exists, kept as-is')}\n`);
533
+ }
534
+
535
+ // Save pack selection + version
536
+ writeFileSync(targets.packsFile, selectedPacks.join('\n') + '\n', 'utf-8');
537
+ if (targets.versionFile) {
538
+ writeFileSync(targets.versionFile, VERSION + '\n', 'utf-8');
539
+ }
540
+
541
+ return selectedPacks;
542
+ }
543
+
544
+ // ── Profile picker ─────────────────────────────────────────────
545
+ // Maps selected packs to the best matching stack profile.
546
+ function pickProfile(selectedPacks) {
547
+ const packSet = new Set(selectedPacks);
548
+
549
+ // Check for specific stack packs first
550
+ if (packSet.has('backend-micronaut') && packSet.has('frontend-react')) {
551
+ return 'kotlin-micronaut'; // full-stack defaults to backend profile
552
+ }
553
+ if (packSet.has('backend-micronaut')) return 'kotlin-micronaut';
554
+ if (packSet.has('frontend-react')) return 'react-nextjs';
555
+ if (packSet.has('backend-nodejs')) return 'typescript-node';
556
+ if (packSet.has('backend-python')) return 'python-fastapi';
557
+
558
+ // No stack-specific pack → use custom
559
+ return 'custom';
560
+ }
561
+
562
+ // ── Install for cursor / windsurf / copilot ─────────────────────
563
+ async function installRulesOnly() {
564
+ const targets = getTargets();
565
+
566
+ console.log(`\n Agent: ${bold(agent)}`);
567
+ console.log(` This installs ${bold('rules + AGENTS.md')} only.`);
568
+ console.log(` Slash commands are Claude Code specific.\n`);
569
+
570
+ // Ask which rule packs
571
+ console.log(` ${bold('Which rule packs do you need?')}\n`);
572
+
573
+ const rulePacks = PACK_ORDER.filter(p => PACKS[p].rules.length > 0 && !PACKS[p].hidden);
574
+ for (const pack of rulePacks) {
575
+ const ruleNames = PACKS[pack].rules.map(r => r.replace(/.*\//, '').replace('.md', '')).join(', ');
576
+ console.log(` ${bold(pack)} — ${dim(ruleNames)}`);
577
+ }
578
+ console.log('');
579
+
580
+ const allChoice = await ask(' Install all rule packs? [Y/n]: ');
581
+ let selectedPacks;
582
+
583
+ if (allChoice !== 'n' && allChoice !== 'N') {
584
+ selectedPacks = resolveDeps(rulePacks, manifests);
585
+ } else {
586
+ const picks = [];
587
+ for (const pack of rulePacks) {
588
+ const choice = await ask(` ${bold(pack)}? [y/N]: `);
589
+ if (choice === 'y' || choice === 'Y') {
590
+ picks.push(pack);
591
+ }
592
+ }
593
+ selectedPacks = resolveDeps(picks, manifests);
594
+ }
595
+
596
+ // Install rules
597
+ const selectedRules = gatherItems(selectedPacks, 'rules');
598
+ if (selectedRules.length > 0) {
599
+ console.log(`\n${blue('[1/2]')} ${bold('Installing rules...')}`);
600
+ ensureDir(targets.rules);
601
+ let count = 0;
602
+ for (const rule of selectedRules) {
603
+ const src = join(SRC.rules, rule);
604
+ const dest = join(targets.rules, rule);
605
+ if (existsSync(src)) {
606
+ copyFile(src, dest);
607
+ console.log(` ${green('+')} ${rule}`);
608
+ count++;
609
+ }
610
+ }
611
+ console.log(` ${bold(count + ' rules')} installed\n`);
612
+ } else {
613
+ console.log(`\n${blue('[1/2]')} ${bold('Rules')} — ${dim('no rule packs selected')}\n`);
614
+ }
615
+
616
+ // Install AGENTS.md — assembled from pack sections + agents
617
+ console.log(`${blue('[2/2]')} ${bold('Installing AGENTS.md...')}`);
618
+
619
+ if (targets.agentsMd) {
620
+ const agentsContent = assembleAGENTSmd(SRC.claudeMd, SRC.agents, selectedPacks, PACKS);
621
+ writeFileSync(targets.agentsMd, agentsContent, 'utf-8');
622
+ console.log(` ${green('+')} AGENTS.md\n`);
623
+ } else {
624
+ console.log(` ${dim('No AGENTS.md target')}\n`);
625
+ }
626
+
627
+ // Save selection
628
+ if (targets.packsFile) {
629
+ writeFileSync(targets.packsFile, selectedPacks.join('\n') + '\n', 'utf-8');
630
+ }
631
+
632
+ return selectedPacks;
633
+ }
634
+
635
+ // ── Main ────────────────────────────────────────────────────────
636
+ async function main() {
637
+ console.log('');
638
+ console.log(`${bold('================================================')}`);
639
+ console.log(`${bold(' Spartan AI Toolkit')} ${dim(`v${VERSION}`)}`);
640
+ console.log(`${bold('================================================')}`);
641
+ console.log('');
642
+ console.log(` Agent: ${bold(agent)} Mode: ${bold(mode)}`);
643
+
644
+ // Check Node version
645
+ const nodeVer = parseInt(process.versions.node.split('.')[0], 10);
646
+ if (nodeVer < 18) {
647
+ console.error(`\n ${C.red}Node.js ${process.versions.node} is too old. Need >= 18.${C.reset}\n`);
648
+ process.exit(1);
649
+ }
650
+
651
+ // Load community packs if --pack-dir is set
652
+ if (packDirArg) {
653
+ const packDirPath = pathResolve(process.cwd(), packDirArg);
654
+ const builtinNames = new Set(manifests.keys());
655
+ console.log(`\n ${blue('Loading community packs from')} ${dim(packDirPath)}`);
656
+ const { loaded, errors } = loadExternalPacks(packDirPath, builtinNames);
657
+
658
+ if (errors.length > 0) {
659
+ for (const err of errors) {
660
+ console.log(` ${yellow('!')} ${err}`);
661
+ }
662
+ }
663
+
664
+ if (loaded.size > 0) {
665
+ for (const [name, manifest] of loaded) {
666
+ manifests.set(name, manifest);
667
+ externalPackSources[name] = packDirPath;
668
+ // Add to PACKS and PACK_ORDER so menus and install work
669
+ PACKS[name] = {
670
+ description: manifest.description,
671
+ category: manifest.category || 'Community',
672
+ priority: manifest.priority ?? 500,
673
+ hidden: manifest.hidden || false,
674
+ comingSoon: manifest['coming-soon'] || false,
675
+ depends: manifest.depends || [],
676
+ commands: manifest.commands || [],
677
+ rules: manifest.rules || [],
678
+ skills: manifest.skills || [],
679
+ agents: manifest.agents || [],
680
+ claudeSections: manifest['claude-sections'] || [],
681
+ };
682
+ PACK_ORDER.push(name);
683
+ }
684
+ // Re-sort PACK_ORDER by priority
685
+ PACK_ORDER.sort((a, b) => (PACKS[a]?.priority ?? 999) - (PACKS[b]?.priority ?? 999));
686
+
687
+ const names = [...loaded.keys()].join(', ');
688
+ console.log(` ${green('+')} Loaded: ${bold(names)}\n`);
689
+ } else {
690
+ console.log(` ${dim('No valid community packs found')}\n`);
691
+ }
692
+ }
693
+
694
+ let selectedPacks;
695
+
696
+ try {
697
+ if (agent === 'claude-code' || agent === 'codex') {
698
+ selectedPacks = await installFull();
699
+ } else if (['cursor', 'windsurf', 'copilot'].includes(agent)) {
700
+ selectedPacks = await installRulesOnly();
701
+ } else {
702
+ console.error(`\n ${C.red}Unknown agent: ${agent}${C.reset}`);
703
+ console.error(` Supported: claude-code, cursor, windsurf, codex, copilot\n`);
704
+ process.exit(1);
705
+ }
706
+ } finally {
707
+ closeRL();
708
+ }
709
+
710
+ // Generate Codex-compatible skills for codex agent
711
+ if (agent === 'codex') {
712
+ console.log(`${blue('[+]')} ${bold('Generating Codex skills...')}`);
713
+ try {
714
+ const { execSync } = await import('child_process');
715
+ const repoRoot = join(PKG_ROOT, '..');
716
+ execSync('node toolkit/scripts/gen-codex-skills.js', { cwd: repoRoot, stdio: 'pipe' });
717
+ const srcAgents = join(repoRoot, '.agents', 'skills');
718
+ const targets = getTargets();
719
+ const destBase = join(targets.base, '..', '.agents', 'skills');
720
+ if (existsSync(srcAgents)) {
721
+ mkdirSync(destBase, { recursive: true });
722
+ const dirs = readdirSync(srcAgents, { withFileTypes: true }).filter(d => d.isDirectory());
723
+ for (const d of dirs) {
724
+ const destAgentsDir = join(destBase, d.name, 'agents');
725
+ mkdirSync(destAgentsDir, { recursive: true });
726
+ const skillSrc = join(srcAgents, d.name, 'SKILL.md');
727
+ const yamlSrc = join(srcAgents, d.name, 'agents', 'openai.yaml');
728
+ if (existsSync(skillSrc)) copyFileSync(skillSrc, join(destBase, d.name, 'SKILL.md'));
729
+ if (existsSync(yamlSrc)) copyFileSync(yamlSrc, join(destAgentsDir, 'openai.yaml'));
730
+ }
731
+ console.log(` ${green('+')} ${dirs.length} Codex skills generated in .agents/skills/\n`);
732
+ }
733
+ } catch (e) {
734
+ console.log(` ${C.yellow}Warning: Codex skill generation failed — ${e.message}${C.reset}\n`);
735
+ }
736
+ }
737
+
738
+ // Export AGENTS.md alongside normal install when --format=agents-md
739
+ if (format === 'agents-md' && (agent === 'claude-code' || agent === 'codex')) {
740
+ const cwd = process.cwd();
741
+ const agentsMdPath = join(cwd, 'AGENTS.md');
742
+ console.log(`${blue('[+]')} ${bold('Exporting AGENTS.md for cross-tool use...')}`);
743
+ const agentsContent = assembleAGENTSmd(SRC.claudeMd, SRC.agents, selectedPacks, PACKS);
744
+ writeFileSync(agentsMdPath, agentsContent, 'utf-8');
745
+ console.log(` ${green('+')} AGENTS.md (works with Cursor, Copilot, Windsurf, Codex, and 20+ tools)\n`);
746
+ }
747
+
748
+ // Success
749
+ const userPacks = selectedPacks.filter(p => !PACKS[p]?.hidden);
750
+ console.log(`${bold(green('================================================'))}`);
751
+ console.log(`${bold(green(' Setup done!'))} ${dim(`v${VERSION}`)}`);
752
+ console.log(`${bold(green('================================================'))}`);
753
+ console.log('');
754
+ console.log(` Installed packs: ${cyan(userPacks.join(', '))}`);
755
+ console.log('');
756
+
757
+ if (agent === 'claude-code' || agent === 'codex') {
758
+ console.log(` ${bold('Next steps:')}`);
759
+ console.log('');
760
+ console.log(` 1. Open any project folder and type:`);
761
+ console.log(` ${cyan('/spartan')}`);
762
+ console.log('');
763
+ console.log(` 2. To change packs later, run again:`);
764
+ console.log(` ${cyan('npx @c0x12c/ai-toolkit@latest')}`);
765
+ console.log('');
766
+ } else {
767
+ console.log(` ${bold('Next steps:')}`);
768
+ console.log('');
769
+ console.log(` 1. Rules are in your ${agent} rules folder.`);
770
+ console.log(` 2. AGENTS.md is in your project root.`);
771
+ console.log(` 3. Slash commands need Claude Code — they don't work in ${agent}.`);
772
+ console.log('');
773
+ }
774
+ }
775
+
776
+ main().catch(err => {
777
+ console.error(`\n ${C.red}Error: ${err.message}${C.reset}\n`);
778
+ closeRL();
779
+ process.exit(1);
780
+ });