@jaimevalasek/aioson 1.5.1 → 1.7.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 (341) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +729 -226
  3. package/docs/design-previews/aurora-command-ui-website.html +884 -0
  4. package/docs/design-previews/aurora-command-ui.html +682 -0
  5. package/docs/design-previews/bold-editorial-ui-website.html +658 -0
  6. package/docs/design-previews/bold-editorial-ui.html +717 -0
  7. package/docs/design-previews/clean-saas-ui-website.html +1202 -0
  8. package/docs/design-previews/clean-saas-ui.html +549 -0
  9. package/docs/design-previews/cognitive-core-ui-website.html +1009 -0
  10. package/docs/design-previews/cognitive-core-ui.html +463 -0
  11. package/docs/design-previews/glassmorphism-ui-website.html +572 -0
  12. package/docs/design-previews/glassmorphism-ui.html +886 -0
  13. package/docs/design-previews/index.html +699 -0
  14. package/docs/design-previews/interface-design-website.html +1187 -0
  15. package/docs/design-previews/interface-design.html +513 -0
  16. package/docs/design-previews/neo-brutalist-ui-website.html +621 -0
  17. package/docs/design-previews/neo-brutalist-ui.html +797 -0
  18. package/docs/design-previews/premium-command-center-ui-website.html +1217 -0
  19. package/docs/design-previews/premium-command-center-ui.html +552 -0
  20. package/docs/design-previews/pt.squarespace.com-homepage.html +889 -0
  21. package/docs/design-previews/warm-craft-ui-website.html +684 -0
  22. package/docs/design-previews/warm-craft-ui.html +739 -0
  23. package/docs/en/cli-reference.md +20 -9
  24. package/docs/integrations/sdlc-genius-boundary.md +76 -0
  25. package/docs/integrations/sdlc-genius-eval-matrix.md +75 -0
  26. package/docs/integrations/sdlc-genius-install-checklist.md +93 -0
  27. package/docs/integrations/sdlc-genius-review-samples.md +86 -0
  28. package/docs/pt/README.md +10 -0
  29. package/docs/pt/agent-sharding.md +132 -0
  30. package/docs/pt/agentes.md +9 -2
  31. package/docs/pt/busca-de-contexto.md +129 -0
  32. package/docs/pt/cache-de-contexto.md +156 -0
  33. package/docs/pt/comandos-cli.md +915 -1
  34. package/docs/pt/design-hybrid-forge.md +356 -0
  35. package/docs/pt/devlog-pipeline.md +270 -0
  36. package/docs/pt/fluxo-artefatos.md +178 -0
  37. package/docs/pt/hooks-session-guard.md +454 -0
  38. package/docs/pt/inicio-rapido.md +54 -3
  39. package/docs/pt/inteligencia-adaptativa.md +324 -0
  40. package/docs/pt/monitor-de-contexto.md +158 -0
  41. package/docs/pt/recuperacao-de-sessao.md +125 -0
  42. package/docs/pt/sandbox.md +125 -0
  43. package/docs/pt/sdd-automation-scripts.md +557 -0
  44. package/docs/pt/site-forge.md +309 -0
  45. package/docs/pt/skills.md +98 -6
  46. package/docs/pt/spec-learnings-pipeline.md +265 -0
  47. package/package.json +1 -1
  48. package/src/a2a/client.js +165 -0
  49. package/src/a2a/server.js +223 -0
  50. package/src/agent-loader.js +280 -0
  51. package/src/cli.js +329 -1
  52. package/src/commands/agent-audit.js +397 -0
  53. package/src/commands/agent-export-skill.js +229 -0
  54. package/src/commands/agent-loader.js +85 -0
  55. package/src/commands/artifact-validate.js +189 -0
  56. package/src/commands/brief-gen.js +405 -0
  57. package/src/commands/brief-validate.js +65 -0
  58. package/src/commands/classify.js +256 -0
  59. package/src/commands/context-cache.js +90 -0
  60. package/src/commands/context-compact.js +49 -0
  61. package/src/commands/context-health.js +175 -0
  62. package/src/commands/context-monitor.js +163 -0
  63. package/src/commands/context-search.js +66 -0
  64. package/src/commands/context-trim.js +177 -0
  65. package/src/commands/design-hybrid-options.js +385 -0
  66. package/src/commands/detect-test-runner.js +55 -0
  67. package/src/commands/devlog-export-brains.js +27 -0
  68. package/src/commands/devlog-process.js +292 -0
  69. package/src/commands/devlog-watch.js +131 -0
  70. package/src/commands/feature-close.js +165 -0
  71. package/src/commands/gate-check.js +228 -0
  72. package/src/commands/health.js +214 -0
  73. package/src/commands/hooks-emit.js +253 -0
  74. package/src/commands/hooks-install.js +347 -0
  75. package/src/commands/init.js +54 -13
  76. package/src/commands/install.js +52 -13
  77. package/src/commands/learning-auto-promote.js +195 -0
  78. package/src/commands/learning-evolve.js +364 -0
  79. package/src/commands/learning-export.js +103 -0
  80. package/src/commands/learning-rollback.js +164 -0
  81. package/src/commands/live.js +59 -1
  82. package/src/commands/pattern-detect.js +33 -0
  83. package/src/commands/preflight-context.js +30 -0
  84. package/src/commands/preflight.js +208 -0
  85. package/src/commands/pulse-update.js +130 -0
  86. package/src/commands/recovery.js +43 -0
  87. package/src/commands/runner-daemon.js +274 -0
  88. package/src/commands/runner-plan.js +70 -0
  89. package/src/commands/runner-queue-from-plan.js +166 -0
  90. package/src/commands/runner-queue.js +189 -0
  91. package/src/commands/runner-run.js +129 -0
  92. package/src/commands/runtime.js +47 -1
  93. package/src/commands/sandbox.js +37 -0
  94. package/src/commands/self-implement-loop.js +256 -0
  95. package/src/commands/session-guard.js +218 -0
  96. package/src/commands/setup-context.js +22 -2
  97. package/src/commands/setup.js +178 -0
  98. package/src/commands/sizing.js +165 -0
  99. package/src/commands/skill.js +144 -32
  100. package/src/commands/spec-checkpoint.js +177 -0
  101. package/src/commands/spec-status.js +79 -0
  102. package/src/commands/spec-sync.js +190 -0
  103. package/src/commands/spec-tasks.js +288 -0
  104. package/src/commands/squad-autorun.js +1220 -0
  105. package/src/commands/squad-bus.js +217 -0
  106. package/src/commands/squad-card.js +149 -0
  107. package/src/commands/squad-daemon.js +134 -0
  108. package/src/commands/squad-dependency-graph.js +164 -0
  109. package/src/commands/squad-review.js +106 -0
  110. package/src/commands/squad-scaffold.js +55 -0
  111. package/src/commands/squad-tool-register.js +157 -0
  112. package/src/commands/state-save.js +122 -0
  113. package/src/commands/tool-registry-cmd.js +232 -0
  114. package/src/commands/update.js +9 -0
  115. package/src/commands/verify-gate.js +572 -0
  116. package/src/commands/workflow-execute.js +241 -0
  117. package/src/constants.js +18 -0
  118. package/src/context-cache.js +159 -0
  119. package/src/context-search.js +326 -0
  120. package/src/design-variation-catalog.js +503 -0
  121. package/src/i18n/messages/en.js +32 -2
  122. package/src/i18n/messages/es.js +30 -2
  123. package/src/i18n/messages/fr.js +30 -2
  124. package/src/i18n/messages/pt-BR.js +32 -2
  125. package/src/install-animation.js +260 -0
  126. package/src/install-profile.js +143 -0
  127. package/src/install-wizard.js +475 -0
  128. package/src/installer.js +44 -10
  129. package/src/lib/health-check.js +158 -0
  130. package/src/lib/hook-protocol.js +76 -0
  131. package/src/mcp/apps/squad-dashboard/app.js +163 -0
  132. package/src/mcp/apps/squad-dashboard/index.html +261 -0
  133. package/src/mcp/apps/squad-dashboard/mcp-manifest.json +23 -0
  134. package/src/mcp/resources/squad-state.js +130 -0
  135. package/src/parser.js +7 -1
  136. package/src/preflight-engine.js +443 -0
  137. package/src/recovery-context-session.js +154 -0
  138. package/src/runner/cascade.js +97 -0
  139. package/src/runner/cli-launcher.js +109 -0
  140. package/src/runner/plan-importer.js +63 -0
  141. package/src/runner/queue-store.js +159 -0
  142. package/src/runtime-store.js +158 -4
  143. package/src/sandbox.js +177 -0
  144. package/src/squad/agent-teams-adapter.js +264 -0
  145. package/src/squad/brief-validator.js +350 -0
  146. package/src/squad/bus-bridge.js +140 -0
  147. package/src/squad/context-compactor.js +265 -0
  148. package/src/squad/cross-ai-synthesizer.js +250 -0
  149. package/src/squad/hooks-generator.js +196 -0
  150. package/src/squad/inter-squad-events.js +175 -0
  151. package/src/squad/intra-bus.js +345 -0
  152. package/src/squad/learning-extractor.js +213 -0
  153. package/src/squad/pattern-detector.js +365 -0
  154. package/src/squad/preflight-context.js +296 -0
  155. package/src/squad/recovery-context.js +242 -71
  156. package/src/squad/reflection.js +365 -0
  157. package/src/squad/squad-scaffold.js +177 -0
  158. package/src/squad/state-manager.js +310 -0
  159. package/src/squad/task-decomposer.js +652 -0
  160. package/src/squad/verify-gate.js +303 -0
  161. package/src/tool-executor.js +94 -0
  162. package/src/updater.js +10 -3
  163. package/src/worker-runner.js +186 -1
  164. package/template/.aioson/agents/analyst.md +119 -3
  165. package/template/.aioson/agents/architect.md +98 -0
  166. package/template/.aioson/agents/design-hybrid-forge.md +141 -0
  167. package/template/.aioson/agents/dev.md +335 -14
  168. package/template/.aioson/agents/deyvin.md +117 -2
  169. package/template/.aioson/agents/discovery-design-doc.md +44 -0
  170. package/template/.aioson/agents/genome.md +14 -0
  171. package/template/.aioson/agents/neo.md +78 -1
  172. package/template/.aioson/agents/orache.md +50 -4
  173. package/template/.aioson/agents/orchestrator.md +197 -1
  174. package/template/.aioson/agents/pm.md +93 -0
  175. package/template/.aioson/agents/product.md +77 -4
  176. package/template/.aioson/agents/profiler-enricher.md +14 -0
  177. package/template/.aioson/agents/profiler-forge.md +14 -0
  178. package/template/.aioson/agents/profiler-researcher.md +14 -0
  179. package/template/.aioson/agents/qa.md +249 -19
  180. package/template/.aioson/agents/setup.md +144 -12
  181. package/template/.aioson/agents/sheldon.md +237 -11
  182. package/template/.aioson/agents/site-forge.md +1753 -0
  183. package/template/.aioson/agents/squad.md +162 -0
  184. package/template/.aioson/agents/tester.md +209 -0
  185. package/template/.aioson/agents/ux-ui.md +34 -1
  186. package/template/.aioson/brains/README.md +128 -0
  187. package/template/.aioson/brains/_index.json +16 -0
  188. package/template/.aioson/brains/scripts/query.js +103 -0
  189. package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
  190. package/template/.aioson/config.md +158 -13
  191. package/template/.aioson/constitution.md +33 -0
  192. package/template/.aioson/context/forensics/.gitkeep +0 -0
  193. package/template/.aioson/context/project-pulse.md +34 -0
  194. package/template/.aioson/context/seeds/seed-example.md +27 -0
  195. package/template/.aioson/context/user-profile.md +42 -0
  196. package/template/.aioson/docs/LAYERS.md +79 -0
  197. package/template/.aioson/docs/README.md +76 -0
  198. package/template/.aioson/docs/example-external-api-context.md +72 -0
  199. package/template/.aioson/locales/en/agents/architect.md +17 -0
  200. package/template/.aioson/locales/en/agents/dev.md +79 -13
  201. package/template/.aioson/locales/en/agents/orache.md +6 -0
  202. package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
  203. package/template/.aioson/locales/en/agents/product.md +50 -0
  204. package/template/.aioson/locales/en/agents/setup.md +33 -1
  205. package/template/.aioson/locales/en/agents/sheldon.md +115 -0
  206. package/template/.aioson/locales/en/agents/squad.md +14 -0
  207. package/template/.aioson/locales/en/agents/tester.md +6 -0
  208. package/template/.aioson/locales/es/agents/analyst.md +2 -0
  209. package/template/.aioson/locales/es/agents/architect.md +19 -0
  210. package/template/.aioson/locales/es/agents/dev.md +64 -4
  211. package/template/.aioson/locales/es/agents/deyvin.md +2 -0
  212. package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
  213. package/template/.aioson/locales/es/agents/genome.md +2 -0
  214. package/template/.aioson/locales/es/agents/neo.md +2 -0
  215. package/template/.aioson/locales/es/agents/orache.md +2 -0
  216. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  217. package/template/.aioson/locales/es/agents/pair.md +2 -0
  218. package/template/.aioson/locales/es/agents/pm.md +2 -0
  219. package/template/.aioson/locales/es/agents/product.md +52 -0
  220. package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
  221. package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
  222. package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
  223. package/template/.aioson/locales/es/agents/qa.md +2 -0
  224. package/template/.aioson/locales/es/agents/setup.md +35 -1
  225. package/template/.aioson/locales/es/agents/sheldon.md +117 -0
  226. package/template/.aioson/locales/es/agents/squad.md +16 -0
  227. package/template/.aioson/locales/es/agents/tester.md +9 -0
  228. package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
  229. package/template/.aioson/locales/fr/agents/analyst.md +2 -0
  230. package/template/.aioson/locales/fr/agents/architect.md +19 -0
  231. package/template/.aioson/locales/fr/agents/dev.md +64 -4
  232. package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
  233. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
  234. package/template/.aioson/locales/fr/agents/genome.md +2 -0
  235. package/template/.aioson/locales/fr/agents/neo.md +2 -0
  236. package/template/.aioson/locales/fr/agents/orache.md +2 -0
  237. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  238. package/template/.aioson/locales/fr/agents/pair.md +2 -0
  239. package/template/.aioson/locales/fr/agents/pm.md +2 -0
  240. package/template/.aioson/locales/fr/agents/product.md +52 -0
  241. package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
  242. package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
  243. package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
  244. package/template/.aioson/locales/fr/agents/qa.md +2 -0
  245. package/template/.aioson/locales/fr/agents/setup.md +35 -1
  246. package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
  247. package/template/.aioson/locales/fr/agents/squad.md +16 -0
  248. package/template/.aioson/locales/fr/agents/tester.md +9 -0
  249. package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
  250. package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
  251. package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
  252. package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
  253. package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
  254. package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
  255. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
  256. package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
  257. package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
  258. package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
  259. package/template/.aioson/locales/pt-BR/agents/setup.md +134 -19
  260. package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
  261. package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
  262. package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
  263. package/template/.aioson/rules/README.md +69 -0
  264. package/template/.aioson/rules/data-format-convention.md +136 -0
  265. package/template/.aioson/rules/example-monetary-values.md +30 -0
  266. package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
  267. package/template/.aioson/skills/design/aurora-command-ui/SKILL.md +243 -0
  268. package/template/.aioson/skills/design/aurora-command-ui/references/art-direction.md +293 -0
  269. package/template/.aioson/skills/design/aurora-command-ui/references/components.md +827 -0
  270. package/template/.aioson/skills/design/aurora-command-ui/references/dashboards.md +250 -0
  271. package/template/.aioson/skills/design/aurora-command-ui/references/design-tokens.md +585 -0
  272. package/template/.aioson/skills/design/aurora-command-ui/references/motion.md +365 -0
  273. package/template/.aioson/skills/design/aurora-command-ui/references/patterns.md +482 -0
  274. package/template/.aioson/skills/design/aurora-command-ui/references/websites.md +387 -0
  275. package/template/.aioson/skills/design/glassmorphism-ui/SKILL.md +222 -0
  276. package/template/.aioson/skills/design/glassmorphism-ui/references/art-direction.md +159 -0
  277. package/template/.aioson/skills/design/glassmorphism-ui/references/components.md +498 -0
  278. package/template/.aioson/skills/design/glassmorphism-ui/references/dashboards.md +236 -0
  279. package/template/.aioson/skills/design/glassmorphism-ui/references/design-tokens.md +274 -0
  280. package/template/.aioson/skills/design/glassmorphism-ui/references/motion.md +355 -0
  281. package/template/.aioson/skills/design/glassmorphism-ui/references/patterns.md +198 -0
  282. package/template/.aioson/skills/design/glassmorphism-ui/references/websites.md +307 -0
  283. package/template/.aioson/skills/design/neo-brutalist-ui/SKILL.md +213 -0
  284. package/template/.aioson/skills/design/neo-brutalist-ui/references/art-direction.md +228 -0
  285. package/template/.aioson/skills/design/neo-brutalist-ui/references/components.md +855 -0
  286. package/template/.aioson/skills/design/neo-brutalist-ui/references/dashboards.md +334 -0
  287. package/template/.aioson/skills/design/neo-brutalist-ui/references/design-tokens.md +342 -0
  288. package/template/.aioson/skills/design/neo-brutalist-ui/references/motion.md +286 -0
  289. package/template/.aioson/skills/design/neo-brutalist-ui/references/patterns.md +458 -0
  290. package/template/.aioson/skills/design/neo-brutalist-ui/references/websites.md +723 -0
  291. package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
  292. package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
  293. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
  294. package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
  295. package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
  296. package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
  297. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
  298. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +46 -0
  299. package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
  300. package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +109 -0
  301. package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
  302. package/template/.aioson/skills/process/aioson-spec-driven/references/artifact-map.md +44 -0
  303. package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +37 -0
  304. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
  305. package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
  306. package/template/.aioson/skills/process/aioson-spec-driven/references/hardening-lane.md +49 -0
  307. package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +101 -0
  308. package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
  309. package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
  310. package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
  311. package/template/.aioson/skills/process/aioson-spec-driven/references/ui-language.md +75 -0
  312. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +147 -0
  313. package/template/.aioson/skills/process/design-hybrid-forge/references/crossover-protocol.md +221 -0
  314. package/template/.aioson/skills/process/design-hybrid-forge/references/naming-registry.md +88 -0
  315. package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +306 -0
  316. package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +149 -0
  317. package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +208 -0
  318. package/template/.aioson/skills/process/design-hybrid-forge/references/variation-library.md +125 -0
  319. package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
  320. package/template/.aioson/skills/static/context-budget-guide.md +46 -0
  321. package/template/.aioson/skills/static/harness-sensors.md +74 -0
  322. package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
  323. package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
  324. package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
  325. package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
  326. package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
  327. package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
  328. package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
  329. package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
  330. package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
  331. package/template/.aioson/skills/static/threejs-patterns.md +929 -0
  332. package/template/.aioson/skills/static/web-research-cache.md +112 -0
  333. package/template/.aioson/tasks/implementation-plan.md +21 -1
  334. package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
  335. package/template/.claude/commands/aioson/agent/orache.md +5 -0
  336. package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
  337. package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
  338. package/template/AGENTS.md +75 -1
  339. package/template/CLAUDE.md +31 -0
  340. package/template/OPENCODE.md +4 -0
  341. package/template/researchs/.gitkeep +0 -0
@@ -0,0 +1,475 @@
1
+ 'use strict';
2
+
3
+ const readline = require('node:readline');
4
+ const { getCliVersionSync } = require('./version');
5
+
6
+ const TOOLS = [
7
+ { id: 'claude', label: 'Claude Code', desc: 'Slash commands, CLAUDE.md, .claude/' },
8
+ { id: 'codex', label: 'Codex (OpenAI)', desc: 'AGENTS.md protocol' },
9
+ { id: 'gemini', label: 'Gemini CLI', desc: 'GEMINI.md + .gemini/commands/' },
10
+ { id: 'opencode', label: 'OpenCode', desc: 'OPENCODE.md protocol' }
11
+ ];
12
+
13
+ const USES = [
14
+ {
15
+ id: 'development',
16
+ label: 'Development',
17
+ desc: 'Agent workflow: setup → analyst → architect → dev → qa',
18
+ locked: true
19
+ },
20
+ {
21
+ id: 'squads',
22
+ label: 'Squads',
23
+ desc: 'Create and run AI squads (squad, genome, orache, profiler)',
24
+ locked: false
25
+ }
26
+ ];
27
+
28
+ const DESIGNS = [
29
+ { id: 'none', label: 'None', desc: 'No design system installed' },
30
+ { id: 'clean-saas-ui', label: 'Clean SaaS UI', desc: 'Minimal, functional — dashboards & tools' },
31
+ { id: 'aurora-command-ui', label: 'Aurora Command UI', desc: 'Dark, glowing — command centers & apps' },
32
+ { id: 'cognitive-core-ui', label: 'Cognitive Core UI', desc: 'Information-dense — data & analytics' },
33
+ { id: 'bold-editorial-ui', label: 'Bold Editorial UI', desc: 'High contrast typography — content sites' },
34
+ { id: 'warm-craft-ui', label: 'Warm Craft UI', desc: 'Warm tones, organic — consumer & lifestyle' },
35
+ { id: 'glassmorphism-ui', label: 'Glassmorphism UI', desc: 'Translucent layers — immersive interfaces' },
36
+ { id: 'neo-brutalist-ui', label: 'Neo-Brutalist UI', desc: 'Raw, high-contrast — bold statements' },
37
+ { id: 'premium-command-center-ui',label: 'Premium Command Center UI',desc: 'Enterprise-grade — ops & monitoring' },
38
+ { id: 'interface-design', label: 'Interface Design', desc: 'Foundational system — general purpose' }
39
+ ];
40
+
41
+ const LOCALES = [
42
+ { id: 'en', label: 'English', flag: '🇺🇸' },
43
+ { id: 'pt-BR', label: 'Português (Brasil)', flag: '🇧🇷' },
44
+ { id: 'es', label: 'Español', flag: '🇪🇸' },
45
+ { id: 'fr', label: 'Français', flag: '🇫🇷' }
46
+ ];
47
+
48
+ const BANNER_ART = [
49
+ '█████╗ ██╗ ██████╗ ███████╗ ██████╗ ███╗ ██╗',
50
+ '██╔══██╗██║██╔═══██╗██╔════╝██╔═══██╗████╗ ██║',
51
+ '███████║██║██║ ██║███████╗██║ ██║██╔██╗ ██║',
52
+ '██╔══██║██║██║ ██║╚════██║██║ ██║██║╚██╗██║',
53
+ '██║ ██║██║╚██████╔╝███████║╚██████╔╝██║ ╚████║',
54
+ '╚═╝ ╚═╝╚═╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═══╝'
55
+ ];
56
+
57
+ function getBanner(version, stdout) {
58
+ const cols = (stdout && stdout.columns) || 80;
59
+ const noColor = process.env.NO_COLOR !== undefined;
60
+ const dumb = process.env.TERM === 'dumb';
61
+
62
+ if (dumb || cols < 60) {
63
+ return `AIOSON v${version}\n\n`;
64
+ }
65
+
66
+ const cyan = noColor ? '' : '\x1b[1;36m';
67
+ const border = noColor ? '' : '\x1b[36m';
68
+ const dim = noColor ? '' : '\x1b[90m';
69
+ const reset = noColor ? '' : '\x1b[0m';
70
+
71
+ const artWidth = Math.max(...BANNER_ART.map(r => r.length));
72
+ const sidePad = 3;
73
+ const inner = artWidth + sidePad * 2;
74
+ const dashes = '─'.repeat(inner);
75
+
76
+ function centered(content, visibleLen) {
77
+ const left = Math.floor((inner - visibleLen) / 2);
78
+ const right = inner - left - visibleLen;
79
+ return ` ${border}│${reset}${' '.repeat(left)}${content}${' '.repeat(right)}${border}│${reset}`;
80
+ }
81
+
82
+ const emptyRow = ` ${border}│${reset}${' '.repeat(inner)}${border}│${reset}`;
83
+ const tagline = `AI Operating Framework v${version}`;
84
+
85
+ return [
86
+ ` ${border}╭${dashes}╮${reset}`,
87
+ emptyRow,
88
+ ...BANNER_ART.map(row => {
89
+ const left = Math.floor((inner - row.length) / 2);
90
+ const right = inner - left - row.length;
91
+ return ` ${border}│${reset}${' '.repeat(left)}${cyan}${row}${reset}${' '.repeat(right)}${border}│${reset}`;
92
+ }),
93
+ emptyRow,
94
+ centered(`${dim}${tagline}${reset}`, tagline.length),
95
+ emptyRow,
96
+ ` ${border}╰${dashes}╯${reset}`,
97
+ ''
98
+ ].join('\n') + '\n';
99
+ }
100
+
101
+ function header(screen, total, stdout) {
102
+ stdout.write('\x1Bc');
103
+ stdout.write(getBanner(getCliVersionSync(), stdout));
104
+ stdout.write(` AIOSON — Installation Wizard (${screen}/${total})\n\n`);
105
+ }
106
+
107
+ function renderScreen1(cursor, selected, warn, stdout) {
108
+ header(1, 4, stdout);
109
+ stdout.write(' Which AI tools will you use in this project?\n');
110
+ stdout.write(' (↑/↓ to move, space to select, enter to continue)\n\n');
111
+ for (let i = 0; i < TOOLS.length; i++) {
112
+ const tool = TOOLS[i];
113
+ const pointer = i === cursor ? '►' : ' ';
114
+ const check = selected.has(tool.id) ? '✓' : ' ';
115
+ stdout.write(` ${pointer} [${check}] ${tool.label.padEnd(20)} ${tool.desc}\n`);
116
+ }
117
+ if (warn) stdout.write('\n ⚠ Select at least one tool to continue.\n');
118
+ stdout.write('\n');
119
+ }
120
+
121
+ function renderScreen2(cursor, selected, warn, stdout) {
122
+ header(2, 4, stdout);
123
+ stdout.write(' What will you do with AIOSON?\n');
124
+ stdout.write(' (space to select, enter to continue)\n\n');
125
+ for (let i = 0; i < USES.length; i++) {
126
+ const use = USES[i];
127
+ const pointer = i === cursor ? '►' : ' ';
128
+ const check = selected.has(use.id) ? '✓' : ' ';
129
+ const lock = use.locked ? ' (always on)' : '';
130
+ stdout.write(` ${pointer} [${check}] ${use.label}${lock}\n`);
131
+ stdout.write(` ${use.desc}\n`);
132
+ }
133
+ if (warn) stdout.write('\n ⚠ Select at least one use to continue.\n');
134
+ stdout.write('\n');
135
+ }
136
+
137
+ function renderScreen3(cursor, selected, warn, stdout) {
138
+ header(3, 4, stdout);
139
+ stdout.write(' Which design system? (optional — select multiple)\n');
140
+ stdout.write(' (↑/↓ to move, space to toggle, enter to continue)\n\n');
141
+ for (let i = 0; i < DESIGNS.length; i++) {
142
+ const d = DESIGNS[i];
143
+ const pointer = i === cursor ? '►' : ' ';
144
+ const check = selected.has(d.id) ? '✓' : ' ';
145
+ stdout.write(` ${pointer} [${check}] ${d.label.padEnd(28)} ${d.desc}\n`);
146
+ }
147
+ if (warn) stdout.write('\n ⚠ Select "None" or at least one design skill.\n');
148
+ stdout.write('\n');
149
+ }
150
+
151
+ function renderScreen4(cursor, stdout) {
152
+ header(4, 4, stdout);
153
+ stdout.write(' Which language for agents?\n');
154
+ stdout.write(' (↑/↓ to move, enter to select)\n\n');
155
+ for (let i = 0; i < LOCALES.length; i++) {
156
+ const loc = LOCALES[i];
157
+ const pointer = i === cursor ? '►' : ' ';
158
+ const bullet = i === cursor ? '◉' : '○';
159
+ stdout.write(` ${pointer} ${bullet} ${loc.flag} ${loc.label}\n`);
160
+ }
161
+ stdout.write('\n');
162
+ }
163
+
164
+ function renderConfirm(tools, uses, design, locale, existingProfile, t, stdout) {
165
+ const TOOL_NAMES = { claude: 'Claude Code', codex: 'Codex', gemini: 'Gemini CLI', opencode: 'OpenCode' };
166
+ const toolNames = tools.map(id => TOOL_NAMES[id] || id).join(', ');
167
+ const modeLabel = uses.includes('squads') ? 'Development + Squads' : 'Development';
168
+ // design can be string (single/id/all) or string[] (multiple)
169
+ const designList = Array.isArray(design)
170
+ ? design.map(id => DESIGNS.find(d => d.id === id)?.label || id)
171
+ : [DESIGNS.find(d => d.id === design)?.label || design];
172
+ const designLabel = designList.join(', ');
173
+ const localeName = LOCALES.find(l => l.id === locale)?.label || locale;
174
+
175
+ stdout.write('\x1Bc');
176
+ stdout.write(` ${t('install_wizard.ready_to_install')}\n\n`);
177
+ stdout.write(` Tools → ${toolNames}\n`);
178
+ stdout.write(` Mode → ${modeLabel}\n`);
179
+ stdout.write(` Design → ${designLabel}\n`);
180
+ stdout.write(` Locale → ${localeName}\n\n`);
181
+
182
+ // Warn if reconfigure has deselected items (we don't auto-remove files)
183
+ if (existingProfile) {
184
+ const prevTools = new Set(Array.isArray(existingProfile.tools) ? existingProfile.tools : [existingProfile.tools]);
185
+ const prevDesign = new Set(Array.isArray(existingProfile.design) ? existingProfile.design : [existingProfile.design]);
186
+ const currTools = new Set(tools);
187
+ const currDesign = new Set(Array.isArray(design) ? design : [design]);
188
+
189
+ const removedTools = [...prevTools].filter(t => !currTools.has(t) && t !== 'none');
190
+ const removedDesign = [...prevDesign].filter(d => !currDesign.has(d) && d !== 'none' && d !== 'all');
191
+
192
+ if (removedTools.length > 0 || removedDesign.length > 0) {
193
+ stdout.write(` ${t('install_wizard.deselected_warning')}\n`);
194
+ stdout.write(`${t('install_wizard.deselected_hint')}\n\n`);
195
+ }
196
+ }
197
+
198
+ stdout.write(` ${t('install_wizard.press_enter_to_install')}\n\n`);
199
+ }
200
+
201
+ function makeRawSession(io) {
202
+ const stdin = io.stdin || process.stdin;
203
+ const wasRaw = Boolean(stdin.isRaw);
204
+ const wasPaused = typeof stdin.isPaused === 'function' ? stdin.isPaused() : true;
205
+
206
+ readline.emitKeypressEvents(stdin);
207
+ if (typeof stdin.setRawMode === 'function') stdin.setRawMode(true);
208
+ if (typeof stdin.resume === 'function') stdin.resume();
209
+
210
+ function cleanupListeners(onKeypress) {
211
+ stdin.removeListener('keypress', onKeypress);
212
+ if (stdin.listenerCount('keypress') === 0 && stdin.listenerCount('data') > 0) {
213
+ stdin.emit('data', Buffer.alloc(0));
214
+ }
215
+ if (typeof stdin.setRawMode === 'function') stdin.setRawMode(wasRaw);
216
+ if (wasPaused && typeof stdin.pause === 'function') stdin.pause();
217
+ }
218
+
219
+ return { stdin, cleanupListeners };
220
+ }
221
+
222
+ // Generic multi-select prompt (checkbox)
223
+ async function promptCheckbox({ items, defaultSelected, lockFirst, render, io = {} }) {
224
+ const stdout = io.stdout || process.stdout;
225
+ const { stdin, cleanupListeners } = makeRawSession(io);
226
+ let cursor = 0;
227
+ const selected = new Set(defaultSelected);
228
+ let warn = false;
229
+
230
+ render(cursor, selected, warn, stdout);
231
+
232
+ return new Promise((resolve) => {
233
+ let cleanedUp = false;
234
+ function cleanup() {
235
+ if (cleanedUp) return;
236
+ cleanedUp = true;
237
+ cleanupListeners(onKeypress);
238
+ }
239
+ function onKeypress(_str, key) {
240
+ if (!key) return;
241
+ if ((key.ctrl && key.name === 'c') || key.name === 'q') { cleanup(); resolve(null); return; }
242
+ if (key.name === 'up') { cursor = cursor === 0 ? items.length - 1 : cursor - 1; render(cursor, selected, warn, stdout); return; }
243
+ if (key.name === 'down') { cursor = cursor === items.length - 1 ? 0 : cursor + 1; render(cursor, selected, warn, stdout); return; }
244
+ if (key.name === 'space') {
245
+ const item = items[cursor];
246
+ if (lockFirst && item.locked) return;
247
+ if (selected.has(item.id)) selected.delete(item.id);
248
+ else selected.add(item.id);
249
+ warn = false;
250
+ render(cursor, selected, warn, stdout);
251
+ return;
252
+ }
253
+ if (key.name === 'return') {
254
+ if (selected.size === 0) { warn = true; render(cursor, selected, warn, stdout); return; }
255
+ cleanup();
256
+ resolve([...selected]);
257
+ }
258
+ }
259
+ stdin.on('keypress', onKeypress);
260
+ });
261
+ }
262
+
263
+ // Generic single-select prompt (radio)
264
+ async function promptRadio({ items, defaultIndex, render, io = {} }) {
265
+ const stdout = io.stdout || process.stdout;
266
+ const { stdin, cleanupListeners } = makeRawSession(io);
267
+ let cursor = defaultIndex || 0;
268
+
269
+ render(cursor, stdout);
270
+
271
+ return new Promise((resolve) => {
272
+ let cleanedUp = false;
273
+ function cleanup() {
274
+ if (cleanedUp) return;
275
+ cleanedUp = true;
276
+ cleanupListeners(onKeypress);
277
+ }
278
+ function onKeypress(_str, key) {
279
+ if (!key) return;
280
+ if ((key.ctrl && key.name === 'c') || key.name === 'q') { cleanup(); resolve(null); return; }
281
+ if (key.name === 'up') { cursor = cursor === 0 ? items.length - 1 : cursor - 1; render(cursor, stdout); return; }
282
+ if (key.name === 'down') { cursor = cursor === items.length - 1 ? 0 : cursor + 1; render(cursor, stdout); return; }
283
+ if (key.name === 'return') { cleanup(); resolve(items[cursor].id); }
284
+ }
285
+ stdin.on('keypress', onKeypress);
286
+ });
287
+ }
288
+
289
+ // Multi-select with exclusive option: when 'noneId' is selected it clears all others;
290
+ // when any other is selected it clears 'noneId'.
291
+ async function promptDesignCheckbox({ items, noneId, defaultSelected, render, io = {} }) {
292
+ const stdout = io.stdout || process.stdout;
293
+ const { stdin, cleanupListeners } = makeRawSession(io);
294
+ let cursor = 0;
295
+ const selected = new Set(defaultSelected);
296
+ let warn = false;
297
+
298
+ render(cursor, selected, warn, stdout);
299
+
300
+ return new Promise((resolve) => {
301
+ let cleanedUp = false;
302
+ function cleanup() {
303
+ if (cleanedUp) return;
304
+ cleanedUp = true;
305
+ cleanupListeners(onKeypress);
306
+ }
307
+ function onKeypress(_str, key) {
308
+ if (!key) return;
309
+ if ((key.ctrl && key.name === 'c') || key.name === 'q') { cleanup(); resolve(null); return; }
310
+ if (key.name === 'up') { cursor = cursor === 0 ? items.length - 1 : cursor - 1; render(cursor, selected, warn, stdout); return; }
311
+ if (key.name === 'down') { cursor = cursor === items.length - 1 ? 0 : cursor + 1; render(cursor, selected, warn, stdout); return; }
312
+ if (key.name === 'space') {
313
+ const item = items[cursor];
314
+ if (item.id === noneId) {
315
+ // Exclusive: selecting 'none' clears everything else
316
+ selected.clear();
317
+ selected.add(noneId);
318
+ } else {
319
+ // Selecting any other clears 'none'
320
+ selected.delete(noneId);
321
+ if (selected.has(item.id)) selected.delete(item.id);
322
+ else selected.add(item.id);
323
+ }
324
+ warn = false;
325
+ render(cursor, selected, warn, stdout);
326
+ return;
327
+ }
328
+ if (key.name === 'return') {
329
+ if (selected.size === 0) { warn = true; render(cursor, selected, warn, stdout); return; }
330
+ cleanup();
331
+ resolve([...selected]);
332
+ }
333
+ }
334
+ stdin.on('keypress', onKeypress);
335
+ });
336
+ }
337
+
338
+ async function promptConfirmScreen(tools, uses, design, locale, existingProfile, t, io = {}) {
339
+ const stdout = io.stdout || process.stdout;
340
+ const { stdin, cleanupListeners } = makeRawSession(io);
341
+
342
+ renderConfirm(tools, uses, design, locale, existingProfile, t, stdout);
343
+
344
+ return new Promise((resolve) => {
345
+ let cleanedUp = false;
346
+ function cleanup() {
347
+ if (cleanedUp) return;
348
+ cleanedUp = true;
349
+ cleanupListeners(onKeypress);
350
+ }
351
+ function onKeypress(_str, key) {
352
+ if (!key) return;
353
+ if ((key.ctrl && key.name === 'c') || key.name === 'q') { cleanup(); resolve(false); return; }
354
+ if (key.name === 'return') { cleanup(); resolve(true); }
355
+ }
356
+ stdin.on('keypress', onKeypress);
357
+ });
358
+ }
359
+
360
+ /**
361
+ * Runs the interactive install wizard.
362
+ * Returns { tools, uses, design, locale } or null (cancelled / non-TTY / --no-interactive).
363
+ * @param {object} options
364
+ * @param {object} [options.existingProfile] - Pre-existing profile to pre-select in wizard
365
+ * @param {function} [options.t] - Translator function for i18n strings
366
+ */
367
+ async function runInstallWizard(options = {}, io = {}) {
368
+ const stdin = io.stdin || process.stdin;
369
+ const stdout = io.stdout || process.stdout;
370
+ const existingProfile = options.existingProfile || null;
371
+ const t = options.t || ((key) => key);
372
+
373
+ if (!stdin.isTTY || !stdout.isTTY) return null;
374
+ if (options.noInteractive) return null;
375
+
376
+ function finalCleanup() {
377
+ if (stdin === process.stdin) {
378
+ if (typeof stdin.setRawMode === 'function') stdin.setRawMode(false);
379
+ stdin.pause();
380
+ if (typeof stdin.unref === 'function') stdin.unref();
381
+ }
382
+ }
383
+
384
+ // Derive defaults from existing profile (supports both string and array design)
385
+ const defaultTools = existingProfile
386
+ ? (Array.isArray(existingProfile.tools) ? existingProfile.tools : [existingProfile.tools])
387
+ : ['claude'];
388
+ const defaultUses = existingProfile
389
+ ? (Array.isArray(existingProfile.uses) ? existingProfile.uses : [existingProfile.uses])
390
+ : ['development'];
391
+ const defaultDesign = existingProfile
392
+ ? (Array.isArray(existingProfile.design)
393
+ ? existingProfile.design
394
+ : (existingProfile.design === 'none' || existingProfile.design === 'all'
395
+ ? [existingProfile.design]
396
+ : [existingProfile.design])) // single design skill
397
+ : ['none'];
398
+ const defaultLocale = existingProfile
399
+ ? (LOCALES.findIndex(l => l.id === existingProfile.locale) || 0)
400
+ : 0;
401
+
402
+ // Screen 1 — Tools (multi-select)
403
+ const tools = await promptCheckbox({
404
+ items: TOOLS,
405
+ defaultSelected: defaultTools,
406
+ lockFirst: false,
407
+ render: (cursor, selected, warn, out) => renderScreen1(cursor, selected, warn, out),
408
+ io
409
+ });
410
+ if (!tools) { finalCleanup(); return null; }
411
+
412
+ // Screen 2 — Uses (multi-select, development locked)
413
+ const uses = await promptCheckbox({
414
+ items: USES,
415
+ defaultSelected: defaultUses,
416
+ lockFirst: true,
417
+ render: (cursor, selected, warn, out) => renderScreen2(cursor, selected, warn, out),
418
+ io
419
+ });
420
+ if (!uses) { finalCleanup(); return null; }
421
+
422
+ // Screen 3 — Design (multi-select with exclusive 'none')
423
+ const design = await promptDesignCheckbox({
424
+ items: DESIGNS,
425
+ noneId: 'none',
426
+ defaultSelected: defaultDesign,
427
+ render: (cursor, selected, warn, out) => renderScreen3(cursor, selected, warn, out),
428
+ io
429
+ });
430
+ if (design === null) { finalCleanup(); return null; }
431
+
432
+ // Screen 4 — Locale (single-select / radio)
433
+ const locale = await promptRadio({
434
+ items: LOCALES,
435
+ defaultIndex: defaultLocale,
436
+ render: (cursor, out) => renderScreen4(cursor, out),
437
+ io
438
+ });
439
+ if (locale === null) { finalCleanup(); return null; }
440
+
441
+ // Confirm screen
442
+ const confirmed = await promptConfirmScreen(tools, uses, design, locale, existingProfile, t, io);
443
+ if (!confirmed) { finalCleanup(); return null; }
444
+
445
+ stdout.write('\x1Bc');
446
+ finalCleanup();
447
+
448
+ // Normalize design: empty array → 'none', single 'none' → 'none'
449
+ const normalizedDesign = (design.length === 0 || (design.length === 1 && design[0] === 'none'))
450
+ ? 'none'
451
+ : (design.length === 1 && design[0] === 'all')
452
+ ? 'all'
453
+ : design;
454
+
455
+ return { tools, uses, design: normalizedDesign, locale };
456
+ }
457
+
458
+ module.exports = {
459
+ runInstallWizard,
460
+ __test__: {
461
+ renderScreen1,
462
+ renderScreen2,
463
+ renderScreen3,
464
+ renderScreen4,
465
+ renderConfirm,
466
+ getBanner,
467
+ TOOLS,
468
+ USES,
469
+ DESIGNS,
470
+ LOCALES,
471
+ promptCheckbox,
472
+ promptRadio,
473
+ promptDesignCheckbox
474
+ }
475
+ };
package/src/installer.js CHANGED
@@ -6,6 +6,7 @@ const { MANAGED_FILES } = require('./constants');
6
6
  const { getCliVersion } = require('./version');
7
7
  const { exists, ensureDir, copyFileWithDir, nowStamp, toRelativeSafe } = require('./utils');
8
8
  const { ensureProjectRuntime } = require('./execution-gateway');
9
+ const { shouldIncludeForProfile } = require('./install-profile');
9
10
 
10
11
  const ROOT_DIR = path.join(__dirname, '..');
11
12
  const TEMPLATE_DIR = path.join(ROOT_DIR, 'template');
@@ -121,17 +122,24 @@ async function listFilesRecursive(dir) {
121
122
  return out;
122
123
  }
123
124
 
124
- function shouldSkipTemplatePath(rel) {
125
- if (rel.startsWith('.aioson/context/')) return true;
125
+ /**
126
+ * Returns a skip-reason string if the file should be skipped, or false if it should be installed.
127
+ */
128
+ function shouldSkipTemplatePath(rel, profile = null) {
126
129
  if (rel === '.aioson/context/.gitkeep') return false;
130
+ if (rel.startsWith('.aioson/context/')) return 'context-protected';
127
131
  // Never overwrite user-installed skills (only the .gitkeep is created)
128
- if (rel.startsWith('.aioson/installed-skills/') && rel !== '.aioson/installed-skills/.gitkeep') return true;
132
+ if (rel.startsWith('.aioson/installed-skills/') && rel !== '.aioson/installed-skills/.gitkeep') return 'context-protected';
129
133
  // Never overwrite custom agents (only the .gitkeep is created)
130
- if (rel.startsWith('.aioson/my-agents/') && rel !== '.aioson/my-agents/.gitkeep') return true;
134
+ if (rel.startsWith('.aioson/my-agents/') && rel !== '.aioson/my-agents/.gitkeep') return 'context-protected';
135
+
136
+ // Profile-based filtering
137
+ if (profile && !shouldIncludeForProfile(rel, profile)) return 'not-in-profile';
138
+
131
139
  return false;
132
140
  }
133
141
 
134
- async function writeInstallMetadata(targetDir, action, frameworkDetection) {
142
+ async function writeInstallMetadata(targetDir, action, frameworkDetection, installProfile) {
135
143
  const metaPath = path.join(targetDir, '.aioson/install.json');
136
144
  await ensureDir(path.dirname(metaPath));
137
145
  const existing = (await exists(metaPath)) ? JSON.parse(await fs.readFile(metaPath, 'utf8')) : {};
@@ -143,12 +151,24 @@ async function writeInstallMetadata(targetDir, action, frameworkDetection) {
143
151
  template_version: version,
144
152
  last_action: action,
145
153
  last_action_at: new Date().toISOString(),
146
- framework_detected: frameworkDetection || existing.framework_detected || null
154
+ framework_detected: frameworkDetection || existing.framework_detected || null,
155
+ install_profile: installProfile || existing.install_profile || null
147
156
  };
148
157
 
149
158
  await fs.writeFile(metaPath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
150
159
  }
151
160
 
161
+ async function readInstallProfile(targetDir) {
162
+ const metaPath = path.join(targetDir, '.aioson/install.json');
163
+ if (!(await exists(metaPath))) return null;
164
+ try {
165
+ const data = JSON.parse(await fs.readFile(metaPath, 'utf8'));
166
+ return data.install_profile || null;
167
+ } catch {
168
+ return null;
169
+ }
170
+ }
171
+
152
172
  async function backupManagedFile(targetDir, relPath, backupRoot) {
153
173
  const source = path.join(targetDir, relPath);
154
174
  if (!(await exists(source))) return null;
@@ -164,7 +184,10 @@ async function installTemplate(targetDir, options = {}) {
164
184
  dryRun = false,
165
185
  mode = 'install',
166
186
  backupOnOverwrite = mode === 'update',
167
- frameworkDetection = null
187
+ frameworkDetection = null,
188
+ installProfile = null,
189
+ selectiveUpdate = false,
190
+ onProgress = null
168
191
  } = options;
169
192
 
170
193
  await ensureDir(targetDir);
@@ -183,8 +206,9 @@ async function installTemplate(targetDir, options = {}) {
183
206
 
184
207
  for (const absPath of templateFiles) {
185
208
  const rel = toRelativeSafe(TEMPLATE_DIR, absPath);
186
- if (shouldSkipTemplatePath(rel)) {
187
- skipped.push({ path: rel, reason: 'context-protected' });
209
+ const skipReason = shouldSkipTemplatePath(rel, installProfile);
210
+ if (skipReason) {
211
+ skipped.push({ path: rel, reason: skipReason });
188
212
  continue;
189
213
  }
190
214
 
@@ -196,6 +220,11 @@ async function installTemplate(targetDir, options = {}) {
196
220
  continue;
197
221
  }
198
222
 
223
+ if (!destExists && selectiveUpdate) {
224
+ skipped.push({ path: rel, reason: 'not-installed' });
225
+ continue;
226
+ }
227
+
199
228
  if (destExists && !overwrite && mode !== 'update') {
200
229
  skipped.push({ path: rel, reason: 'already-exists' });
201
230
  continue;
@@ -215,6 +244,10 @@ async function installTemplate(targetDir, options = {}) {
215
244
  }
216
245
 
217
246
  copied.push(rel);
247
+
248
+ if (onProgress) {
249
+ onProgress({ copied: copied.length, total: templateFiles.length, file: rel });
250
+ }
218
251
  }
219
252
 
220
253
  if (!dryRun) {
@@ -225,7 +258,7 @@ async function installTemplate(targetDir, options = {}) {
225
258
  await fs.writeFile(gitkeep, '', 'utf8');
226
259
  }
227
260
 
228
- await writeInstallMetadata(targetDir, mode, frameworkDetection);
261
+ await writeInstallMetadata(targetDir, mode, frameworkDetection, installProfile);
229
262
 
230
263
  await ensureProjectGitignorePolicy(targetDir);
231
264
 
@@ -252,6 +285,7 @@ module.exports = {
252
285
  TEMPLATE_DIR,
253
286
  detectExistingInstall,
254
287
  installTemplate,
288
+ readInstallProfile,
255
289
  listFilesRecursive,
256
290
  ensureGitignoreEntry,
257
291
  ensureGitignoreEntries,