@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.
- package/CHANGELOG.md +49 -0
- package/README.md +729 -226
- package/docs/design-previews/aurora-command-ui-website.html +884 -0
- package/docs/design-previews/aurora-command-ui.html +682 -0
- package/docs/design-previews/bold-editorial-ui-website.html +658 -0
- package/docs/design-previews/bold-editorial-ui.html +717 -0
- package/docs/design-previews/clean-saas-ui-website.html +1202 -0
- package/docs/design-previews/clean-saas-ui.html +549 -0
- package/docs/design-previews/cognitive-core-ui-website.html +1009 -0
- package/docs/design-previews/cognitive-core-ui.html +463 -0
- package/docs/design-previews/glassmorphism-ui-website.html +572 -0
- package/docs/design-previews/glassmorphism-ui.html +886 -0
- package/docs/design-previews/index.html +699 -0
- package/docs/design-previews/interface-design-website.html +1187 -0
- package/docs/design-previews/interface-design.html +513 -0
- package/docs/design-previews/neo-brutalist-ui-website.html +621 -0
- package/docs/design-previews/neo-brutalist-ui.html +797 -0
- package/docs/design-previews/premium-command-center-ui-website.html +1217 -0
- package/docs/design-previews/premium-command-center-ui.html +552 -0
- package/docs/design-previews/pt.squarespace.com-homepage.html +889 -0
- package/docs/design-previews/warm-craft-ui-website.html +684 -0
- package/docs/design-previews/warm-craft-ui.html +739 -0
- package/docs/en/cli-reference.md +20 -9
- package/docs/integrations/sdlc-genius-boundary.md +76 -0
- package/docs/integrations/sdlc-genius-eval-matrix.md +75 -0
- package/docs/integrations/sdlc-genius-install-checklist.md +93 -0
- package/docs/integrations/sdlc-genius-review-samples.md +86 -0
- package/docs/pt/README.md +10 -0
- package/docs/pt/agent-sharding.md +132 -0
- package/docs/pt/agentes.md +9 -2
- package/docs/pt/busca-de-contexto.md +129 -0
- package/docs/pt/cache-de-contexto.md +156 -0
- package/docs/pt/comandos-cli.md +915 -1
- package/docs/pt/design-hybrid-forge.md +356 -0
- package/docs/pt/devlog-pipeline.md +270 -0
- package/docs/pt/fluxo-artefatos.md +178 -0
- package/docs/pt/hooks-session-guard.md +454 -0
- package/docs/pt/inicio-rapido.md +54 -3
- package/docs/pt/inteligencia-adaptativa.md +324 -0
- package/docs/pt/monitor-de-contexto.md +158 -0
- package/docs/pt/recuperacao-de-sessao.md +125 -0
- package/docs/pt/sandbox.md +125 -0
- package/docs/pt/sdd-automation-scripts.md +557 -0
- package/docs/pt/site-forge.md +309 -0
- package/docs/pt/skills.md +98 -6
- package/docs/pt/spec-learnings-pipeline.md +265 -0
- package/package.json +1 -1
- package/src/a2a/client.js +165 -0
- package/src/a2a/server.js +223 -0
- package/src/agent-loader.js +280 -0
- package/src/cli.js +329 -1
- package/src/commands/agent-audit.js +397 -0
- package/src/commands/agent-export-skill.js +229 -0
- package/src/commands/agent-loader.js +85 -0
- package/src/commands/artifact-validate.js +189 -0
- package/src/commands/brief-gen.js +405 -0
- package/src/commands/brief-validate.js +65 -0
- package/src/commands/classify.js +256 -0
- package/src/commands/context-cache.js +90 -0
- package/src/commands/context-compact.js +49 -0
- package/src/commands/context-health.js +175 -0
- package/src/commands/context-monitor.js +163 -0
- package/src/commands/context-search.js +66 -0
- package/src/commands/context-trim.js +177 -0
- package/src/commands/design-hybrid-options.js +385 -0
- package/src/commands/detect-test-runner.js +55 -0
- package/src/commands/devlog-export-brains.js +27 -0
- package/src/commands/devlog-process.js +292 -0
- package/src/commands/devlog-watch.js +131 -0
- package/src/commands/feature-close.js +165 -0
- package/src/commands/gate-check.js +228 -0
- package/src/commands/health.js +214 -0
- package/src/commands/hooks-emit.js +253 -0
- package/src/commands/hooks-install.js +347 -0
- package/src/commands/init.js +54 -13
- package/src/commands/install.js +52 -13
- package/src/commands/learning-auto-promote.js +195 -0
- package/src/commands/learning-evolve.js +364 -0
- package/src/commands/learning-export.js +103 -0
- package/src/commands/learning-rollback.js +164 -0
- package/src/commands/live.js +59 -1
- package/src/commands/pattern-detect.js +33 -0
- package/src/commands/preflight-context.js +30 -0
- package/src/commands/preflight.js +208 -0
- package/src/commands/pulse-update.js +130 -0
- package/src/commands/recovery.js +43 -0
- package/src/commands/runner-daemon.js +274 -0
- package/src/commands/runner-plan.js +70 -0
- package/src/commands/runner-queue-from-plan.js +166 -0
- package/src/commands/runner-queue.js +189 -0
- package/src/commands/runner-run.js +129 -0
- package/src/commands/runtime.js +47 -1
- package/src/commands/sandbox.js +37 -0
- package/src/commands/self-implement-loop.js +256 -0
- package/src/commands/session-guard.js +218 -0
- package/src/commands/setup-context.js +22 -2
- package/src/commands/setup.js +178 -0
- package/src/commands/sizing.js +165 -0
- package/src/commands/skill.js +144 -32
- package/src/commands/spec-checkpoint.js +177 -0
- package/src/commands/spec-status.js +79 -0
- package/src/commands/spec-sync.js +190 -0
- package/src/commands/spec-tasks.js +288 -0
- package/src/commands/squad-autorun.js +1220 -0
- package/src/commands/squad-bus.js +217 -0
- package/src/commands/squad-card.js +149 -0
- package/src/commands/squad-daemon.js +134 -0
- package/src/commands/squad-dependency-graph.js +164 -0
- package/src/commands/squad-review.js +106 -0
- package/src/commands/squad-scaffold.js +55 -0
- package/src/commands/squad-tool-register.js +157 -0
- package/src/commands/state-save.js +122 -0
- package/src/commands/tool-registry-cmd.js +232 -0
- package/src/commands/update.js +9 -0
- package/src/commands/verify-gate.js +572 -0
- package/src/commands/workflow-execute.js +241 -0
- package/src/constants.js +18 -0
- package/src/context-cache.js +159 -0
- package/src/context-search.js +326 -0
- package/src/design-variation-catalog.js +503 -0
- package/src/i18n/messages/en.js +32 -2
- package/src/i18n/messages/es.js +30 -2
- package/src/i18n/messages/fr.js +30 -2
- package/src/i18n/messages/pt-BR.js +32 -2
- package/src/install-animation.js +260 -0
- package/src/install-profile.js +143 -0
- package/src/install-wizard.js +475 -0
- package/src/installer.js +44 -10
- package/src/lib/health-check.js +158 -0
- package/src/lib/hook-protocol.js +76 -0
- package/src/mcp/apps/squad-dashboard/app.js +163 -0
- package/src/mcp/apps/squad-dashboard/index.html +261 -0
- package/src/mcp/apps/squad-dashboard/mcp-manifest.json +23 -0
- package/src/mcp/resources/squad-state.js +130 -0
- package/src/parser.js +7 -1
- package/src/preflight-engine.js +443 -0
- package/src/recovery-context-session.js +154 -0
- package/src/runner/cascade.js +97 -0
- package/src/runner/cli-launcher.js +109 -0
- package/src/runner/plan-importer.js +63 -0
- package/src/runner/queue-store.js +159 -0
- package/src/runtime-store.js +158 -4
- package/src/sandbox.js +177 -0
- package/src/squad/agent-teams-adapter.js +264 -0
- package/src/squad/brief-validator.js +350 -0
- package/src/squad/bus-bridge.js +140 -0
- package/src/squad/context-compactor.js +265 -0
- package/src/squad/cross-ai-synthesizer.js +250 -0
- package/src/squad/hooks-generator.js +196 -0
- package/src/squad/inter-squad-events.js +175 -0
- package/src/squad/intra-bus.js +345 -0
- package/src/squad/learning-extractor.js +213 -0
- package/src/squad/pattern-detector.js +365 -0
- package/src/squad/preflight-context.js +296 -0
- package/src/squad/recovery-context.js +242 -71
- package/src/squad/reflection.js +365 -0
- package/src/squad/squad-scaffold.js +177 -0
- package/src/squad/state-manager.js +310 -0
- package/src/squad/task-decomposer.js +652 -0
- package/src/squad/verify-gate.js +303 -0
- package/src/tool-executor.js +94 -0
- package/src/updater.js +10 -3
- package/src/worker-runner.js +186 -1
- package/template/.aioson/agents/analyst.md +119 -3
- package/template/.aioson/agents/architect.md +98 -0
- package/template/.aioson/agents/design-hybrid-forge.md +141 -0
- package/template/.aioson/agents/dev.md +335 -14
- package/template/.aioson/agents/deyvin.md +117 -2
- package/template/.aioson/agents/discovery-design-doc.md +44 -0
- package/template/.aioson/agents/genome.md +14 -0
- package/template/.aioson/agents/neo.md +78 -1
- package/template/.aioson/agents/orache.md +50 -4
- package/template/.aioson/agents/orchestrator.md +197 -1
- package/template/.aioson/agents/pm.md +93 -0
- package/template/.aioson/agents/product.md +77 -4
- package/template/.aioson/agents/profiler-enricher.md +14 -0
- package/template/.aioson/agents/profiler-forge.md +14 -0
- package/template/.aioson/agents/profiler-researcher.md +14 -0
- package/template/.aioson/agents/qa.md +249 -19
- package/template/.aioson/agents/setup.md +144 -12
- package/template/.aioson/agents/sheldon.md +237 -11
- package/template/.aioson/agents/site-forge.md +1753 -0
- package/template/.aioson/agents/squad.md +162 -0
- package/template/.aioson/agents/tester.md +209 -0
- package/template/.aioson/agents/ux-ui.md +34 -1
- package/template/.aioson/brains/README.md +128 -0
- package/template/.aioson/brains/_index.json +16 -0
- package/template/.aioson/brains/scripts/query.js +103 -0
- package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
- package/template/.aioson/config.md +158 -13
- package/template/.aioson/constitution.md +33 -0
- package/template/.aioson/context/forensics/.gitkeep +0 -0
- package/template/.aioson/context/project-pulse.md +34 -0
- package/template/.aioson/context/seeds/seed-example.md +27 -0
- package/template/.aioson/context/user-profile.md +42 -0
- package/template/.aioson/docs/LAYERS.md +79 -0
- package/template/.aioson/docs/README.md +76 -0
- package/template/.aioson/docs/example-external-api-context.md +72 -0
- package/template/.aioson/locales/en/agents/architect.md +17 -0
- package/template/.aioson/locales/en/agents/dev.md +79 -13
- package/template/.aioson/locales/en/agents/orache.md +6 -0
- package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
- package/template/.aioson/locales/en/agents/product.md +50 -0
- package/template/.aioson/locales/en/agents/setup.md +33 -1
- package/template/.aioson/locales/en/agents/sheldon.md +115 -0
- package/template/.aioson/locales/en/agents/squad.md +14 -0
- package/template/.aioson/locales/en/agents/tester.md +6 -0
- package/template/.aioson/locales/es/agents/analyst.md +2 -0
- package/template/.aioson/locales/es/agents/architect.md +19 -0
- package/template/.aioson/locales/es/agents/dev.md +64 -4
- package/template/.aioson/locales/es/agents/deyvin.md +2 -0
- package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
- package/template/.aioson/locales/es/agents/genome.md +2 -0
- package/template/.aioson/locales/es/agents/neo.md +2 -0
- package/template/.aioson/locales/es/agents/orache.md +2 -0
- package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
- package/template/.aioson/locales/es/agents/pair.md +2 -0
- package/template/.aioson/locales/es/agents/pm.md +2 -0
- package/template/.aioson/locales/es/agents/product.md +52 -0
- package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
- package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
- package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
- package/template/.aioson/locales/es/agents/qa.md +2 -0
- package/template/.aioson/locales/es/agents/setup.md +35 -1
- package/template/.aioson/locales/es/agents/sheldon.md +117 -0
- package/template/.aioson/locales/es/agents/squad.md +16 -0
- package/template/.aioson/locales/es/agents/tester.md +9 -0
- package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
- package/template/.aioson/locales/fr/agents/analyst.md +2 -0
- package/template/.aioson/locales/fr/agents/architect.md +19 -0
- package/template/.aioson/locales/fr/agents/dev.md +64 -4
- package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
- package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
- package/template/.aioson/locales/fr/agents/genome.md +2 -0
- package/template/.aioson/locales/fr/agents/neo.md +2 -0
- package/template/.aioson/locales/fr/agents/orache.md +2 -0
- package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
- package/template/.aioson/locales/fr/agents/pair.md +2 -0
- package/template/.aioson/locales/fr/agents/pm.md +2 -0
- package/template/.aioson/locales/fr/agents/product.md +52 -0
- package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
- package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
- package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
- package/template/.aioson/locales/fr/agents/qa.md +2 -0
- package/template/.aioson/locales/fr/agents/setup.md +35 -1
- package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
- package/template/.aioson/locales/fr/agents/squad.md +16 -0
- package/template/.aioson/locales/fr/agents/tester.md +9 -0
- package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
- package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
- package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
- package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
- package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
- package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
- package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
- package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
- package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
- package/template/.aioson/locales/pt-BR/agents/setup.md +134 -19
- package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
- package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
- package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
- package/template/.aioson/rules/README.md +69 -0
- package/template/.aioson/rules/data-format-convention.md +136 -0
- package/template/.aioson/rules/example-monetary-values.md +30 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
- package/template/.aioson/skills/design/aurora-command-ui/SKILL.md +243 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/art-direction.md +293 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/components.md +827 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/dashboards.md +250 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/design-tokens.md +585 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/motion.md +365 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/patterns.md +482 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/websites.md +387 -0
- package/template/.aioson/skills/design/glassmorphism-ui/SKILL.md +222 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/art-direction.md +159 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/components.md +498 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/dashboards.md +236 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/design-tokens.md +274 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/motion.md +355 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/patterns.md +198 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/websites.md +307 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/SKILL.md +213 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/art-direction.md +228 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/components.md +855 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/dashboards.md +334 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/design-tokens.md +342 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/motion.md +286 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/patterns.md +458 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/websites.md +723 -0
- package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
- package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
- package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +46 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +109 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/artifact-map.md +44 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +37 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/hardening-lane.md +49 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +101 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/ui-language.md +75 -0
- package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +147 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/crossover-protocol.md +221 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/naming-registry.md +88 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +306 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +149 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +208 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/variation-library.md +125 -0
- package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
- package/template/.aioson/skills/static/context-budget-guide.md +46 -0
- package/template/.aioson/skills/static/harness-sensors.md +74 -0
- package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
- package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
- package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
- package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
- package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
- package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
- package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
- package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
- package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
- package/template/.aioson/skills/static/threejs-patterns.md +929 -0
- package/template/.aioson/skills/static/web-research-cache.md +112 -0
- package/template/.aioson/tasks/implementation-plan.md +21 -1
- package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
- package/template/.claude/commands/aioson/agent/orache.md +5 -0
- package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
- package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
- package/template/AGENTS.md +75 -1
- package/template/CLAUDE.md +31 -0
- package/template/OPENCODE.md +4 -0
- 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
|
-
|
|
125
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
187
|
-
|
|
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,
|