devflow-kit 1.8.2 → 2.0.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 +191 -6
- package/LICENSE +1 -1
- package/README.md +139 -219
- package/dist/cli/agents-view/index.js +9 -0
- package/dist/cli/agents-view/render.js +310 -0
- package/dist/cli/agents-view/state.js +423 -0
- package/dist/cli/agents-view/terminal.js +179 -0
- package/dist/cli/commands/agents.js +585 -0
- package/dist/cli/commands/ambient.js +231 -0
- package/dist/cli/commands/capture.js +133 -0
- package/dist/cli/commands/compliance-prompts.js +174 -0
- package/dist/cli/commands/compliance.js +337 -0
- package/dist/cli/commands/context.js +66 -0
- package/dist/cli/commands/debug.js +122 -0
- package/dist/cli/commands/flags.js +119 -0
- package/dist/cli/commands/hud.js +229 -0
- package/dist/cli/commands/init-seed.js +226 -0
- package/dist/cli/commands/init.js +1786 -0
- package/dist/cli/commands/knowledge/index.js +25 -0
- package/dist/cli/commands/knowledge/list.js +115 -0
- package/dist/cli/commands/knowledge/toggle.js +84 -0
- package/dist/cli/commands/learning.js +311 -0
- package/dist/cli/commands/legacy-hooks.js +40 -0
- package/dist/cli/commands/memory.js +349 -0
- package/dist/cli/commands/proxy.js +1435 -0
- package/dist/cli/commands/rules.js +334 -0
- package/dist/cli/commands/safe-delete.js +140 -0
- package/dist/cli/commands/security.js +179 -0
- package/dist/cli/commands/skills.js +140 -0
- package/dist/cli/commands/uninstall.js +1214 -0
- package/dist/cli.js +35 -10
- package/dist/commands/bug-analysis.md +400 -0
- package/dist/commands/code-review.md +403 -0
- package/dist/commands/debug.md +282 -0
- package/dist/commands/dynamic-build.md +972 -0
- package/dist/commands/dynamic-plan.md +383 -0
- package/dist/commands/dynamic-profile.md +206 -0
- package/dist/commands/dynamic-tickets.md +583 -0
- package/dist/commands/explore.md +208 -0
- package/dist/commands/implement.md +590 -0
- package/dist/commands/plan.md +570 -0
- package/dist/commands/release.md +206 -0
- package/dist/commands/research.md +212 -0
- package/dist/commands/resolve.md +731 -0
- package/dist/commands/self-review.md +251 -0
- package/dist/core/agent-frontmatter.js +209 -0
- package/dist/core/agent-models.js +556 -0
- package/dist/core/agent-state.js +44 -0
- package/dist/core/assets.js +39 -0
- package/dist/core/cache.js +205 -0
- package/dist/core/codex-auth-inspect.js +128 -0
- package/dist/core/compliance-compose.js +458 -0
- package/dist/core/compliance.js +165 -0
- package/dist/core/external-models.js +76 -0
- package/dist/core/feature-config.js +125 -0
- package/dist/core/flags.js +309 -0
- package/dist/core/fs-atomic.js +74 -0
- package/dist/core/git.js +61 -0
- package/dist/core/learning-queue-cleanup.js +92 -0
- package/dist/core/learning-tuning-config.js +78 -0
- package/dist/core/manifest.js +171 -0
- package/dist/core/migrations.js +330 -0
- package/dist/core/model-discovery.js +655 -0
- package/dist/core/observation-io.js +50 -0
- package/dist/core/observations.js +109 -0
- package/dist/core/orphan-sweep.js +92 -0
- package/dist/core/paths.js +48 -0
- package/dist/core/plugins.js +525 -0
- package/dist/core/project-paths.js +159 -0
- package/dist/core/proxy-log.js +165 -0
- package/dist/core/proxy-state.js +305 -0
- package/dist/core/safe-delete-install.js +191 -0
- package/dist/core/teammate-mode-cleanup.js +33 -0
- package/dist/hud/colors.js +13 -2
- package/dist/hud/components/config-counts.js +2 -2
- package/dist/hud/components/context-usage.js +2 -2
- package/dist/hud/components/learning-counts.js +92 -0
- package/dist/hud/components/session-cost.js +10 -2
- package/dist/hud/components/usage-quota.js +43 -8
- package/dist/hud/components/version-badge.js +17 -7
- package/dist/hud/config.js +4 -2
- package/dist/hud/cost-history.js +281 -0
- package/dist/hud/git.js +112 -71
- package/dist/hud/index.js +47 -4
- package/dist/hud/render.js +8 -20
- package/dist/hud/transcript.js +5 -6
- package/dist/targets/claude-code/claude-paths.js +106 -0
- package/dist/targets/claude-code/compliance-install.js +305 -0
- package/dist/targets/claude-code/hooks.js +9 -0
- package/dist/targets/claude-code/installer.js +458 -0
- package/dist/targets/claude-code/legacy.js +239 -0
- package/dist/targets/claude-code/post-install.js +971 -0
- package/package.json +23 -17
- package/src/assets/agents/code.md +244 -0
- package/src/assets/agents/design.md +113 -0
- package/src/assets/agents/diagnose.md +210 -0
- package/src/assets/agents/evaluate.md +117 -0
- package/src/assets/agents/git.md +938 -0
- package/src/assets/agents/knowledge.md +89 -0
- package/src/assets/agents/learning.md +203 -0
- package/src/assets/agents/research.md +134 -0
- package/src/assets/agents/review.md +220 -0
- package/src/assets/agents/scrutinize.md +95 -0
- package/src/assets/agents/simplify.md +108 -0
- package/src/assets/agents/skim.md +136 -0
- package/src/assets/agents/synthesize.md +402 -0
- package/src/assets/agents/test.md +155 -0
- package/src/assets/agents/triage.md +141 -0
- package/src/assets/agents/validate.md +103 -0
- package/src/assets/commands/_partials/_compliance.mds +5 -0
- package/src/assets/commands/_partials/_decisions.mds +20 -0
- package/src/assets/commands/_partials/_engine.mds +244 -0
- package/src/assets/commands/_partials/_factory.mds +216 -0
- package/src/assets/commands/_partials/_knowledge.mds +95 -0
- package/src/assets/commands/_partials/_plan_contract.mds +51 -0
- package/src/assets/commands/_partials/_preamble.mds +82 -0
- package/src/assets/commands/_partials/_publication.mds +7 -0
- package/src/assets/commands/_partials/_roster.mds +31 -0
- package/src/assets/commands/_partials/_ticket_template.mds +59 -0
- package/src/assets/commands/_partials/_wave.mds +123 -0
- package/src/assets/commands/bug-analysis.mds +353 -0
- package/src/assets/commands/code-review.mds +355 -0
- package/src/assets/commands/debug.mds +224 -0
- package/src/assets/commands/dynamic-build.mds +534 -0
- package/src/assets/commands/dynamic-plan.mds +244 -0
- package/src/assets/commands/dynamic-profile.mds +132 -0
- package/src/assets/commands/dynamic-tickets.mds +226 -0
- package/src/assets/commands/explore.mds +150 -0
- package/src/assets/commands/implement.mds +496 -0
- package/src/assets/commands/plan.mds +523 -0
- package/src/assets/commands/release.md +206 -0
- package/src/assets/commands/research.mds +164 -0
- package/src/assets/commands/resolve.mds +636 -0
- package/src/assets/commands/self-review.mds +156 -0
- package/src/assets/rules/accessibility.md +11 -0
- package/src/assets/rules/compliance.md +15 -0
- package/src/assets/rules/engineering.md +13 -0
- package/src/assets/rules/go.md +12 -0
- package/src/assets/rules/java.md +12 -0
- package/src/assets/rules/python.md +12 -0
- package/src/assets/rules/quality.md +14 -0
- package/src/assets/rules/react.md +11 -0
- package/src/assets/rules/reliability.md +12 -0
- package/src/assets/rules/rust.md +12 -0
- package/src/assets/rules/security.md +12 -0
- package/src/assets/rules/typescript.md +12 -0
- package/src/assets/rules/ui-design.md +11 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +21 -0
- package/src/assets/scripts/hooks/background-memory-update +460 -0
- package/src/assets/scripts/hooks/capture-prompt +87 -0
- package/src/assets/scripts/hooks/capture-question +153 -0
- package/src/assets/scripts/hooks/capture-turn +105 -0
- package/src/assets/scripts/hooks/debug-trace +75 -0
- package/src/assets/scripts/hooks/decisions-usage-scan.cjs +131 -0
- package/src/assets/scripts/hooks/ensure-devflow-init +39 -0
- package/src/assets/scripts/hooks/ensure-proxy +294 -0
- package/src/assets/scripts/hooks/ensure-root-gitignore +116 -0
- package/src/assets/scripts/hooks/get-mtime +23 -0
- package/src/assets/scripts/hooks/git-marker +16 -0
- package/src/assets/scripts/hooks/hook-bootstrap +25 -0
- package/src/assets/scripts/hooks/hook-log-init +41 -0
- package/src/assets/scripts/hooks/json-helper.cjs +725 -0
- package/src/assets/scripts/hooks/json-parse +243 -0
- package/src/assets/scripts/hooks/learning-lock +44 -0
- package/src/assets/scripts/hooks/lib/decisions-format.cjs +301 -0
- package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +106 -0
- package/src/assets/scripts/hooks/lib/project-paths.cjs +230 -0
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +398 -0
- package/src/assets/scripts/hooks/lib/safe-path.cjs +29 -0
- package/src/assets/scripts/hooks/log-paths +24 -0
- package/src/assets/scripts/hooks/memory-worker +101 -0
- package/src/assets/scripts/hooks/pre-compact-memory +126 -0
- package/src/assets/scripts/hooks/preamble +77 -0
- package/src/assets/scripts/hooks/queue-append +120 -0
- package/src/assets/scripts/hooks/resolve-project-root +37 -0
- package/src/assets/scripts/hooks/run-hook +31 -0
- package/src/assets/scripts/hooks/session-start-context +180 -0
- package/src/assets/scripts/hooks/session-start-memory +318 -0
- package/src/assets/scripts/hooks/session-start-orchestrator +54 -0
- package/src/assets/scripts/hud.sh +5 -0
- package/src/assets/scripts/redact-secrets.cjs +410 -0
- package/src/assets/skills/accessibility/SKILL.md +145 -0
- package/src/assets/skills/accessibility/references/patterns.md +398 -0
- package/src/assets/skills/accessibility/references/sources.md +26 -0
- package/src/assets/skills/accessibility/references/violations.md +358 -0
- package/src/assets/skills/apply-decisions/SKILL.md +100 -0
- package/src/assets/skills/apply-feature-knowledge/SKILL.md +69 -0
- package/src/assets/skills/architecture/SKILL.md +138 -0
- package/src/assets/skills/architecture/references/patterns.md +378 -0
- package/src/assets/skills/architecture/references/sources.md +41 -0
- package/src/assets/skills/architecture/references/violations.md +314 -0
- package/src/assets/skills/boundary-validation/SKILL.md +119 -0
- package/src/assets/skills/boundary-validation/references/detection.md +283 -0
- package/src/assets/skills/boundary-validation/references/patterns.md +283 -0
- package/src/assets/skills/boundary-validation/references/sources.md +41 -0
- package/src/assets/skills/boundary-validation/references/violations.md +211 -0
- package/src/assets/skills/complexity/SKILL.md +150 -0
- package/src/assets/skills/compliance/SKILL.md +121 -0
- package/src/assets/skills/compliance/frameworks/gdpr/fragment.md +11 -0
- package/src/assets/skills/compliance/frameworks/gdpr/reference.md +50 -0
- package/src/assets/skills/compliance/frameworks/hipaa/fragment.md +11 -0
- package/src/assets/skills/compliance/frameworks/hipaa/reference.md +44 -0
- package/src/assets/skills/compliance/frameworks/iso-27001/fragment.md +10 -0
- package/src/assets/skills/compliance/frameworks/iso-27001/reference.md +45 -0
- package/src/assets/skills/compliance/frameworks/pci-dss/fragment.md +11 -0
- package/src/assets/skills/compliance/frameworks/pci-dss/reference.md +55 -0
- package/src/assets/skills/compliance/frameworks/soc2/fragment.md +10 -0
- package/src/assets/skills/compliance/frameworks/soc2/reference.md +53 -0
- package/src/assets/skills/compliance/frameworks/sox/fragment.md +10 -0
- package/src/assets/skills/compliance/frameworks/sox/reference.md +67 -0
- package/src/assets/skills/compliance/references/detection.md +108 -0
- package/src/assets/skills/compliance/references/sources.md +28 -0
- package/src/assets/skills/consistency/SKILL.md +140 -0
- package/src/assets/skills/database/SKILL.md +134 -0
- package/src/assets/skills/dependencies/SKILL.md +141 -0
- package/src/assets/skills/dependency-research/SKILL.md +133 -0
- package/src/assets/skills/dependency-research/references/evaluation-criteria.md +101 -0
- package/src/assets/skills/design-review/SKILL.md +155 -0
- package/src/assets/skills/design-review/references/anti-patterns.md +207 -0
- package/src/assets/skills/docs-framework/SKILL.md +190 -0
- package/src/assets/skills/docs-framework/references/patterns.md +381 -0
- package/src/assets/skills/docs-framework/references/violations.md +221 -0
- package/src/assets/skills/documentation/SKILL.md +126 -0
- package/src/assets/skills/feature-knowledge/SKILL.md +341 -0
- package/src/assets/skills/gap-analysis/SKILL.md +151 -0
- package/src/assets/skills/gap-analysis/references/check-layers.md +125 -0
- package/src/assets/skills/git/SKILL.md +287 -0
- package/src/assets/skills/git/references/github-api.md +666 -0
- package/src/assets/skills/git/references/patterns.md +324 -0
- package/src/assets/skills/git/references/sources.md +16 -0
- package/src/assets/skills/git/references/violations.md +128 -0
- package/src/assets/skills/go/SKILL.md +147 -0
- package/src/assets/skills/go/references/concurrency.md +335 -0
- package/src/assets/skills/go/references/patterns.md +278 -0
- package/src/assets/skills/go/references/sources.md +41 -0
- package/src/assets/skills/go/references/violations.md +262 -0
- package/src/assets/skills/java/SKILL.md +148 -0
- package/src/assets/skills/java/references/modern-java.md +335 -0
- package/src/assets/skills/java/references/patterns.md +311 -0
- package/src/assets/skills/java/references/sources.md +44 -0
- package/src/assets/skills/java/references/violations.md +257 -0
- package/src/assets/skills/patterns/SKILL.md +162 -0
- package/src/assets/skills/performance/SKILL.md +135 -0
- package/src/assets/skills/performance/references/patterns.md +516 -0
- package/src/assets/skills/performance/references/sources.md +41 -0
- package/src/assets/skills/performance/references/violations.md +417 -0
- package/src/assets/skills/python/SKILL.md +156 -0
- package/src/assets/skills/python/references/async.md +255 -0
- package/src/assets/skills/python/references/patterns.md +280 -0
- package/src/assets/skills/python/references/sources.md +41 -0
- package/src/assets/skills/python/references/violations.md +272 -0
- package/src/assets/skills/qa/SKILL.md +136 -0
- package/src/assets/skills/qa/references/browser-testing.md +97 -0
- package/src/assets/skills/qa/references/patterns.md +118 -0
- package/src/assets/skills/qa/references/scenario-templates.md +102 -0
- package/src/assets/skills/qa/references/sources.md +30 -0
- package/src/assets/skills/qa/references/violations.md +98 -0
- package/src/assets/skills/quality-gates/SKILL.md +152 -0
- package/src/assets/skills/quality-gates/references/stub-detection.md +135 -0
- package/src/assets/skills/react/SKILL.md +143 -0
- package/src/assets/skills/react/references/error-handling.md +179 -0
- package/src/assets/skills/react/references/forms.md +214 -0
- package/src/assets/skills/react/references/hooks.md +193 -0
- package/src/assets/skills/react/references/patterns.md +1336 -0
- package/src/assets/skills/react/references/sources.md +35 -0
- package/src/assets/skills/react/references/violations.md +570 -0
- package/src/assets/skills/regression/SKILL.md +146 -0
- package/src/assets/skills/reliability/SKILL.md +153 -0
- package/src/assets/skills/reliability/references/detection.md +66 -0
- package/src/assets/skills/reliability/references/patterns.md +151 -0
- package/src/assets/skills/reliability/references/sources.md +27 -0
- package/src/assets/skills/reliability/references/violations.md +125 -0
- package/src/assets/skills/research-codebase/SKILL.md +135 -0
- package/src/assets/skills/research-competitor/SKILL.md +145 -0
- package/src/assets/skills/research-external/SKILL.md +141 -0
- package/src/assets/skills/research-market/SKILL.md +145 -0
- package/src/assets/skills/research-technology/SKILL.md +180 -0
- package/src/assets/skills/review-methodology/SKILL.md +121 -0
- package/src/assets/skills/review-methodology/references/patterns.md +186 -0
- package/src/assets/skills/review-methodology/references/report-template.md +142 -0
- package/src/assets/skills/review-methodology/references/violations.md +125 -0
- package/src/assets/skills/rust/SKILL.md +148 -0
- package/src/assets/skills/rust/references/ownership.md +278 -0
- package/src/assets/skills/rust/references/patterns.md +247 -0
- package/src/assets/skills/rust/references/sources.md +46 -0
- package/src/assets/skills/rust/references/violations.md +252 -0
- package/src/assets/skills/security/SKILL.md +121 -0
- package/src/assets/skills/security/references/patterns.md +271 -0
- package/src/assets/skills/security/references/sources.md +41 -0
- package/src/assets/skills/security/references/violations.md +241 -0
- package/src/assets/skills/software-design/SKILL.md +143 -0
- package/src/assets/skills/software-design/references/checklist.md +259 -0
- package/src/assets/skills/software-design/references/code-smell-violations.md +189 -0
- package/src/assets/skills/software-design/references/patterns.md +359 -0
- package/src/assets/skills/software-design/references/sources.md +41 -0
- package/src/assets/skills/software-design/references/violations.md +341 -0
- package/src/assets/skills/test-driven-development/SKILL.md +197 -0
- package/src/assets/skills/testing/SKILL.md +139 -0
- package/src/assets/skills/testing/references/patterns.md +346 -0
- package/src/assets/skills/testing/references/sources.md +53 -0
- package/src/assets/skills/testing/references/violations.md +322 -0
- package/src/assets/skills/typescript/SKILL.md +150 -0
- package/src/assets/skills/typescript/references/async.md +333 -0
- package/src/assets/skills/typescript/references/patterns.md +440 -0
- package/src/assets/skills/typescript/references/sources.md +41 -0
- package/src/assets/skills/typescript/references/type-guards.md +333 -0
- package/src/assets/skills/typescript/references/utility-types.md +255 -0
- package/src/assets/skills/typescript/references/violations.md +285 -0
- package/src/assets/skills/ui-design/SKILL.md +144 -0
- package/src/assets/skills/ui-design/references/patterns.md +341 -0
- package/src/assets/skills/ui-design/references/sources.md +41 -0
- package/src/assets/skills/ui-design/references/violations.md +312 -0
- package/src/assets/skills/worktree-support/SKILL.md +92 -0
- package/src/targets/claude-code/templates/claudeignore.template +188 -0
- package/src/targets/claude-code/templates/settings.json +86 -0
- package/dist/cli.d.ts +0 -3
- package/dist/commands/ambient.d.ts +0 -18
- package/dist/commands/ambient.js +0 -137
- package/dist/commands/hud.d.ts +0 -22
- package/dist/commands/hud.js +0 -180
- package/dist/commands/init.d.ts +0 -16
- package/dist/commands/init.js +0 -738
- package/dist/commands/list.d.ts +0 -24
- package/dist/commands/list.js +0 -89
- package/dist/commands/memory.d.ts +0 -22
- package/dist/commands/memory.js +0 -175
- package/dist/commands/skills.d.ts +0 -11
- package/dist/commands/skills.js +0 -116
- package/dist/commands/uninstall.d.ts +0 -22
- package/dist/commands/uninstall.js +0 -558
- package/dist/hud/cache.d.ts +0 -14
- package/dist/hud/cache.js +0 -54
- package/dist/hud/colors.d.ts +0 -23
- package/dist/hud/components/config-counts.d.ts +0 -8
- package/dist/hud/components/context-usage.d.ts +0 -8
- package/dist/hud/components/diff-stats.d.ts +0 -3
- package/dist/hud/components/directory.d.ts +0 -3
- package/dist/hud/components/git-ahead-behind.d.ts +0 -3
- package/dist/hud/components/git-branch.d.ts +0 -3
- package/dist/hud/components/model.d.ts +0 -3
- package/dist/hud/components/release-info.d.ts +0 -3
- package/dist/hud/components/session-cost.d.ts +0 -3
- package/dist/hud/components/session-duration.d.ts +0 -3
- package/dist/hud/components/todo-progress.d.ts +0 -3
- package/dist/hud/components/usage-quota.d.ts +0 -3
- package/dist/hud/components/version-badge.d.ts +0 -3
- package/dist/hud/components/worktree-count.d.ts +0 -3
- package/dist/hud/config.d.ts +0 -10
- package/dist/hud/credentials.d.ts +0 -17
- package/dist/hud/credentials.js +0 -99
- package/dist/hud/git.d.ts +0 -7
- package/dist/hud/index.d.ts +0 -2
- package/dist/hud/render.d.ts +0 -7
- package/dist/hud/stdin.d.ts +0 -6
- package/dist/hud/transcript.d.ts +0 -7
- package/dist/hud/types.d.ts +0 -116
- package/dist/hud/usage-api.d.ts +0 -7
- package/dist/hud/usage-api.js +0 -84
- package/dist/plugins.d.ts +0 -46
- package/dist/plugins.js +0 -233
- package/dist/utils/cli.d.ts +0 -5
- package/dist/utils/cli.js +0 -14
- package/dist/utils/git.d.ts +0 -11
- package/dist/utils/git.js +0 -36
- package/dist/utils/installer.d.ts +0 -41
- package/dist/utils/installer.js +0 -195
- package/dist/utils/manifest.d.ts +0 -46
- package/dist/utils/manifest.js +0 -100
- package/dist/utils/paths.d.ts +0 -42
- package/dist/utils/paths.js +0 -106
- package/dist/utils/post-install.d.ts +0 -87
- package/dist/utils/post-install.js +0 -546
- package/dist/utils/safe-delete-install.d.ts +0 -29
- package/dist/utils/safe-delete-install.js +0 -191
- package/dist/utils/safe-delete.d.ts +0 -12
- package/plugins/devflow-accessibility/.claude-plugin/plugin.json +0 -21
- package/plugins/devflow-accessibility/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-accessibility/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-accessibility/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-ambient/.claude-plugin/plugin.json +0 -33
- package/plugins/devflow-ambient/README.md +0 -68
- package/plugins/devflow-ambient/agents/coder.md +0 -135
- package/plugins/devflow-ambient/agents/reviewer.md +0 -165
- package/plugins/devflow-ambient/agents/scrutinizer.md +0 -82
- package/plugins/devflow-ambient/agents/shepherd.md +0 -108
- package/plugins/devflow-ambient/agents/simplifier.md +0 -102
- package/plugins/devflow-ambient/agents/skimmer.md +0 -143
- package/plugins/devflow-ambient/agents/validator.md +0 -86
- package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +0 -145
- package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +0 -74
- package/plugins/devflow-ambient/skills/debug-orchestration/SKILL.md +0 -69
- package/plugins/devflow-ambient/skills/implementation-orchestration/SKILL.md +0 -92
- package/plugins/devflow-ambient/skills/plan-orchestration/SKILL.md +0 -71
- package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +0 -19
- package/plugins/devflow-audit-claude/README.md +0 -46
- package/plugins/devflow-audit-claude/agents/claude-md-auditor.md +0 -134
- package/plugins/devflow-audit-claude/commands/audit-claude.md +0 -89
- package/plugins/devflow-code-review/.claude-plugin/plugin.json +0 -38
- package/plugins/devflow-code-review/README.md +0 -73
- package/plugins/devflow-code-review/agents/git.md +0 -277
- package/plugins/devflow-code-review/agents/reviewer.md +0 -165
- package/plugins/devflow-code-review/agents/synthesizer.md +0 -211
- package/plugins/devflow-code-review/commands/code-review-teams.md +0 -285
- package/plugins/devflow-code-review/commands/code-review.md +0 -163
- package/plugins/devflow-code-review/skills/agent-teams/SKILL.md +0 -124
- package/plugins/devflow-code-review/skills/agent-teams/references/cleanup.md +0 -104
- package/plugins/devflow-code-review/skills/agent-teams/references/communication.md +0 -122
- package/plugins/devflow-code-review/skills/agent-teams/references/team-patterns.md +0 -217
- package/plugins/devflow-code-review/skills/architecture-patterns/SKILL.md +0 -153
- package/plugins/devflow-code-review/skills/architecture-patterns/references/patterns.md +0 -873
- package/plugins/devflow-code-review/skills/architecture-patterns/references/violations.md +0 -575
- package/plugins/devflow-code-review/skills/complexity-patterns/SKILL.md +0 -143
- package/plugins/devflow-code-review/skills/consistency-patterns/SKILL.md +0 -140
- package/plugins/devflow-code-review/skills/database-patterns/SKILL.md +0 -134
- package/plugins/devflow-code-review/skills/dependencies-patterns/SKILL.md +0 -141
- package/plugins/devflow-code-review/skills/documentation-patterns/SKILL.md +0 -125
- package/plugins/devflow-code-review/skills/knowledge-persistence/SKILL.md +0 -128
- package/plugins/devflow-code-review/skills/knowledge-persistence/references/examples.md +0 -44
- package/plugins/devflow-code-review/skills/performance-patterns/SKILL.md +0 -154
- package/plugins/devflow-code-review/skills/performance-patterns/references/patterns.md +0 -503
- package/plugins/devflow-code-review/skills/performance-patterns/references/violations.md +0 -354
- package/plugins/devflow-code-review/skills/regression-patterns/SKILL.md +0 -146
- package/plugins/devflow-code-review/skills/review-methodology/SKILL.md +0 -119
- package/plugins/devflow-code-review/skills/review-methodology/references/patterns.md +0 -186
- package/plugins/devflow-code-review/skills/review-methodology/references/report-template.md +0 -142
- package/plugins/devflow-code-review/skills/review-methodology/references/violations.md +0 -125
- package/plugins/devflow-code-review/skills/security-patterns/SKILL.md +0 -156
- package/plugins/devflow-code-review/skills/security-patterns/references/patterns.md +0 -507
- package/plugins/devflow-code-review/skills/security-patterns/references/violations.md +0 -237
- package/plugins/devflow-code-review/skills/test-patterns/SKILL.md +0 -183
- package/plugins/devflow-code-review/skills/test-patterns/references/patterns.md +0 -220
- package/plugins/devflow-code-review/skills/test-patterns/references/violations.md +0 -221
- package/plugins/devflow-core-skills/.claude-plugin/plugin.json +0 -31
- package/plugins/devflow-core-skills/README.md +0 -50
- package/plugins/devflow-core-skills/skills/core-patterns/SKILL.md +0 -162
- package/plugins/devflow-core-skills/skills/core-patterns/references/checklist.md +0 -276
- package/plugins/devflow-core-skills/skills/core-patterns/references/code-smell-violations.md +0 -144
- package/plugins/devflow-core-skills/skills/core-patterns/references/patterns.md +0 -576
- package/plugins/devflow-core-skills/skills/core-patterns/references/violations.md +0 -369
- package/plugins/devflow-core-skills/skills/docs-framework/SKILL.md +0 -143
- package/plugins/devflow-core-skills/skills/docs-framework/references/patterns.md +0 -346
- package/plugins/devflow-core-skills/skills/docs-framework/references/violations.md +0 -221
- package/plugins/devflow-core-skills/skills/git-safety/SKILL.md +0 -122
- package/plugins/devflow-core-skills/skills/git-safety/references/patterns.md +0 -289
- package/plugins/devflow-core-skills/skills/git-safety/references/violations.md +0 -18
- package/plugins/devflow-core-skills/skills/git-workflow/SKILL.md +0 -158
- package/plugins/devflow-core-skills/skills/git-workflow/references/commit-patterns.md +0 -115
- package/plugins/devflow-core-skills/skills/git-workflow/references/commit-violations.md +0 -77
- package/plugins/devflow-core-skills/skills/git-workflow/references/pr-patterns.md +0 -127
- package/plugins/devflow-core-skills/skills/git-workflow/references/pr-violations.md +0 -96
- package/plugins/devflow-core-skills/skills/github-patterns/SKILL.md +0 -153
- package/plugins/devflow-core-skills/skills/github-patterns/references/patterns.md +0 -572
- package/plugins/devflow-core-skills/skills/github-patterns/references/violations.md +0 -298
- package/plugins/devflow-core-skills/skills/input-validation/SKILL.md +0 -148
- package/plugins/devflow-core-skills/skills/input-validation/references/detection.md +0 -283
- package/plugins/devflow-core-skills/skills/input-validation/references/patterns.md +0 -361
- package/plugins/devflow-core-skills/skills/input-validation/references/violations.md +0 -224
- package/plugins/devflow-core-skills/skills/search-first/SKILL.md +0 -133
- package/plugins/devflow-core-skills/skills/search-first/references/evaluation-criteria.md +0 -101
- package/plugins/devflow-core-skills/skills/test-driven-development/SKILL.md +0 -137
- package/plugins/devflow-core-skills/skills/test-patterns/SKILL.md +0 -183
- package/plugins/devflow-core-skills/skills/test-patterns/references/detection.md +0 -149
- package/plugins/devflow-core-skills/skills/test-patterns/references/patterns.md +0 -220
- package/plugins/devflow-core-skills/skills/test-patterns/references/report-template.md +0 -108
- package/plugins/devflow-core-skills/skills/test-patterns/references/violations.md +0 -221
- package/plugins/devflow-debug/.claude-plugin/plugin.json +0 -26
- package/plugins/devflow-debug/README.md +0 -65
- package/plugins/devflow-debug/agents/git.md +0 -277
- package/plugins/devflow-debug/agents/synthesizer.md +0 -211
- package/plugins/devflow-debug/commands/debug-teams.md +0 -245
- package/plugins/devflow-debug/commands/debug.md +0 -174
- package/plugins/devflow-debug/skills/agent-teams/SKILL.md +0 -124
- package/plugins/devflow-debug/skills/agent-teams/references/cleanup.md +0 -104
- package/plugins/devflow-debug/skills/agent-teams/references/communication.md +0 -122
- package/plugins/devflow-debug/skills/agent-teams/references/team-patterns.md +0 -217
- package/plugins/devflow-debug/skills/git-safety/SKILL.md +0 -122
- package/plugins/devflow-debug/skills/git-safety/references/detection.md +0 -290
- package/plugins/devflow-debug/skills/git-safety/references/patterns.md +0 -289
- package/plugins/devflow-debug/skills/git-safety/references/violations.md +0 -18
- package/plugins/devflow-debug/skills/knowledge-persistence/SKILL.md +0 -128
- package/plugins/devflow-debug/skills/knowledge-persistence/references/examples.md +0 -44
- package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +0 -21
- package/plugins/devflow-frontend-design/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-frontend-design/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-frontend-design/skills/frontend-design/references/violations.md +0 -453
- package/plugins/devflow-go/.claude-plugin/plugin.json +0 -21
- package/plugins/devflow-go/skills/go/SKILL.md +0 -187
- package/plugins/devflow-go/skills/go/references/concurrency.md +0 -312
- package/plugins/devflow-go/skills/go/references/patterns.md +0 -232
- package/plugins/devflow-go/skills/go/references/violations.md +0 -205
- package/plugins/devflow-implement/.claude-plugin/plugin.json +0 -35
- package/plugins/devflow-implement/README.md +0 -71
- package/plugins/devflow-implement/agents/coder.md +0 -135
- package/plugins/devflow-implement/agents/git.md +0 -277
- package/plugins/devflow-implement/agents/scrutinizer.md +0 -82
- package/plugins/devflow-implement/agents/shepherd.md +0 -108
- package/plugins/devflow-implement/agents/simplifier.md +0 -102
- package/plugins/devflow-implement/agents/skimmer.md +0 -143
- package/plugins/devflow-implement/agents/synthesizer.md +0 -211
- package/plugins/devflow-implement/agents/validator.md +0 -86
- package/plugins/devflow-implement/commands/implement-teams.md +0 -620
- package/plugins/devflow-implement/commands/implement.md +0 -430
- package/plugins/devflow-implement/skills/agent-teams/SKILL.md +0 -124
- package/plugins/devflow-implement/skills/agent-teams/references/cleanup.md +0 -104
- package/plugins/devflow-implement/skills/agent-teams/references/communication.md +0 -122
- package/plugins/devflow-implement/skills/agent-teams/references/team-patterns.md +0 -217
- package/plugins/devflow-implement/skills/implementation-patterns/SKILL.md +0 -162
- package/plugins/devflow-implement/skills/knowledge-persistence/SKILL.md +0 -128
- package/plugins/devflow-implement/skills/knowledge-persistence/references/examples.md +0 -44
- package/plugins/devflow-implement/skills/self-review/SKILL.md +0 -149
- package/plugins/devflow-implement/skills/self-review/references/stub-detection.md +0 -135
- package/plugins/devflow-java/.claude-plugin/plugin.json +0 -21
- package/plugins/devflow-java/skills/java/SKILL.md +0 -183
- package/plugins/devflow-java/skills/java/references/modern-java.md +0 -270
- package/plugins/devflow-java/skills/java/references/patterns.md +0 -235
- package/plugins/devflow-java/skills/java/references/violations.md +0 -213
- package/plugins/devflow-python/.claude-plugin/plugin.json +0 -21
- package/plugins/devflow-python/skills/python/SKILL.md +0 -188
- package/plugins/devflow-python/skills/python/references/async.md +0 -220
- package/plugins/devflow-python/skills/python/references/patterns.md +0 -226
- package/plugins/devflow-python/skills/python/references/violations.md +0 -204
- package/plugins/devflow-react/.claude-plugin/plugin.json +0 -21
- package/plugins/devflow-react/skills/react/SKILL.md +0 -276
- package/plugins/devflow-react/skills/react/references/patterns.md +0 -1331
- package/plugins/devflow-react/skills/react/references/violations.md +0 -565
- package/plugins/devflow-resolve/.claude-plugin/plugin.json +0 -29
- package/plugins/devflow-resolve/README.md +0 -65
- package/plugins/devflow-resolve/agents/git.md +0 -277
- package/plugins/devflow-resolve/agents/resolver.md +0 -131
- package/plugins/devflow-resolve/agents/simplifier.md +0 -102
- package/plugins/devflow-resolve/commands/resolve-teams.md +0 -307
- package/plugins/devflow-resolve/commands/resolve.md +0 -246
- package/plugins/devflow-resolve/skills/agent-teams/SKILL.md +0 -124
- package/plugins/devflow-resolve/skills/agent-teams/references/cleanup.md +0 -104
- package/plugins/devflow-resolve/skills/agent-teams/references/communication.md +0 -122
- package/plugins/devflow-resolve/skills/agent-teams/references/team-patterns.md +0 -217
- package/plugins/devflow-resolve/skills/implementation-patterns/SKILL.md +0 -162
- package/plugins/devflow-resolve/skills/implementation-patterns/references/patterns.md +0 -1063
- package/plugins/devflow-resolve/skills/implementation-patterns/references/violations.md +0 -483
- package/plugins/devflow-resolve/skills/knowledge-persistence/SKILL.md +0 -128
- package/plugins/devflow-resolve/skills/knowledge-persistence/references/examples.md +0 -44
- package/plugins/devflow-resolve/skills/security-patterns/SKILL.md +0 -156
- package/plugins/devflow-resolve/skills/security-patterns/references/detection.md +0 -287
- package/plugins/devflow-resolve/skills/security-patterns/references/patterns.md +0 -507
- package/plugins/devflow-resolve/skills/security-patterns/references/violations.md +0 -237
- package/plugins/devflow-rust/.claude-plugin/plugin.json +0 -21
- package/plugins/devflow-rust/skills/rust/SKILL.md +0 -193
- package/plugins/devflow-rust/skills/rust/references/ownership.md +0 -242
- package/plugins/devflow-rust/skills/rust/references/patterns.md +0 -210
- package/plugins/devflow-rust/skills/rust/references/violations.md +0 -191
- package/plugins/devflow-self-review/.claude-plugin/plugin.json +0 -26
- package/plugins/devflow-self-review/README.md +0 -38
- package/plugins/devflow-self-review/agents/scrutinizer.md +0 -82
- package/plugins/devflow-self-review/agents/simplifier.md +0 -102
- package/plugins/devflow-self-review/agents/validator.md +0 -86
- package/plugins/devflow-self-review/commands/self-review.md +0 -132
- package/plugins/devflow-self-review/skills/core-patterns/SKILL.md +0 -162
- package/plugins/devflow-self-review/skills/core-patterns/references/checklist.md +0 -276
- package/plugins/devflow-self-review/skills/core-patterns/references/code-smell-violations.md +0 -144
- package/plugins/devflow-self-review/skills/core-patterns/references/detection.md +0 -303
- package/plugins/devflow-self-review/skills/core-patterns/references/patterns.md +0 -576
- package/plugins/devflow-self-review/skills/core-patterns/references/violations.md +0 -369
- package/plugins/devflow-self-review/skills/self-review/SKILL.md +0 -149
- package/plugins/devflow-self-review/skills/self-review/references/patterns.md +0 -405
- package/plugins/devflow-self-review/skills/self-review/references/report-template.md +0 -253
- package/plugins/devflow-self-review/skills/self-review/references/stub-detection.md +0 -135
- package/plugins/devflow-self-review/skills/self-review/references/violations.md +0 -308
- package/plugins/devflow-specify/.claude-plugin/plugin.json +0 -25
- package/plugins/devflow-specify/README.md +0 -46
- package/plugins/devflow-specify/agents/skimmer.md +0 -143
- package/plugins/devflow-specify/agents/synthesizer.md +0 -211
- package/plugins/devflow-specify/commands/specify-teams.md +0 -321
- package/plugins/devflow-specify/commands/specify.md +0 -186
- package/plugins/devflow-specify/skills/agent-teams/SKILL.md +0 -124
- package/plugins/devflow-specify/skills/agent-teams/references/cleanup.md +0 -104
- package/plugins/devflow-specify/skills/agent-teams/references/communication.md +0 -122
- package/plugins/devflow-specify/skills/agent-teams/references/team-patterns.md +0 -217
- package/plugins/devflow-typescript/.claude-plugin/plugin.json +0 -21
- package/plugins/devflow-typescript/skills/typescript/SKILL.md +0 -176
- package/plugins/devflow-typescript/skills/typescript/references/patterns.md +0 -1105
- package/plugins/devflow-typescript/skills/typescript/references/violations.md +0 -433
- package/scripts/hooks/ambient-prompt +0 -50
- package/scripts/hooks/background-memory-update +0 -257
- package/scripts/hooks/ensure-memory-gitignore +0 -17
- package/scripts/hooks/pre-compact-memory +0 -87
- package/scripts/hooks/run-hook +0 -23
- package/scripts/hooks/session-start-memory +0 -165
- package/scripts/hooks/stop-update-memory +0 -85
- package/scripts/hud/cache.d.ts +0 -14
- package/scripts/hud/cache.d.ts.map +0 -1
- package/scripts/hud/cache.js +0 -54
- package/scripts/hud/cache.js.map +0 -1
- package/scripts/hud/colors.d.ts +0 -23
- package/scripts/hud/colors.d.ts.map +0 -1
- package/scripts/hud/colors.js +0 -62
- package/scripts/hud/colors.js.map +0 -1
- package/scripts/hud/components/config-counts.d.ts +0 -8
- package/scripts/hud/components/config-counts.d.ts.map +0 -1
- package/scripts/hud/components/config-counts.js +0 -91
- package/scripts/hud/components/config-counts.js.map +0 -1
- package/scripts/hud/components/context-usage.d.ts +0 -8
- package/scripts/hud/components/context-usage.d.ts.map +0 -1
- package/scripts/hud/components/context-usage.js +0 -49
- package/scripts/hud/components/context-usage.js.map +0 -1
- package/scripts/hud/components/diff-stats.d.ts +0 -3
- package/scripts/hud/components/diff-stats.d.ts.map +0 -1
- package/scripts/hud/components/diff-stats.js +0 -36
- package/scripts/hud/components/diff-stats.js.map +0 -1
- package/scripts/hud/components/directory.d.ts +0 -3
- package/scripts/hud/components/directory.d.ts.map +0 -1
- package/scripts/hud/components/directory.js +0 -10
- package/scripts/hud/components/directory.js.map +0 -1
- package/scripts/hud/components/git-ahead-behind.d.ts +0 -3
- package/scripts/hud/components/git-ahead-behind.d.ts.map +0 -1
- package/scripts/hud/components/git-ahead-behind.js +0 -16
- package/scripts/hud/components/git-ahead-behind.js.map +0 -1
- package/scripts/hud/components/git-branch.d.ts +0 -3
- package/scripts/hud/components/git-branch.d.ts.map +0 -1
- package/scripts/hud/components/git-branch.js +0 -14
- package/scripts/hud/components/git-branch.js.map +0 -1
- package/scripts/hud/components/model.d.ts +0 -3
- package/scripts/hud/components/model.d.ts.map +0 -1
- package/scripts/hud/components/model.js +0 -21
- package/scripts/hud/components/model.js.map +0 -1
- package/scripts/hud/components/release-info.d.ts +0 -3
- package/scripts/hud/components/release-info.d.ts.map +0 -1
- package/scripts/hud/components/release-info.js +0 -9
- package/scripts/hud/components/release-info.js.map +0 -1
- package/scripts/hud/components/session-cost.d.ts +0 -3
- package/scripts/hud/components/session-cost.d.ts.map +0 -1
- package/scripts/hud/components/session-cost.js +0 -9
- package/scripts/hud/components/session-cost.js.map +0 -1
- package/scripts/hud/components/session-duration.d.ts +0 -3
- package/scripts/hud/components/session-duration.d.ts.map +0 -1
- package/scripts/hud/components/session-duration.js +0 -18
- package/scripts/hud/components/session-duration.js.map +0 -1
- package/scripts/hud/components/todo-progress.d.ts +0 -3
- package/scripts/hud/components/todo-progress.d.ts.map +0 -1
- package/scripts/hud/components/todo-progress.js +0 -11
- package/scripts/hud/components/todo-progress.js.map +0 -1
- package/scripts/hud/components/usage-quota.d.ts +0 -3
- package/scripts/hud/components/usage-quota.d.ts.map +0 -1
- package/scripts/hud/components/usage-quota.js +0 -45
- package/scripts/hud/components/usage-quota.js.map +0 -1
- package/scripts/hud/components/version-badge.d.ts +0 -3
- package/scripts/hud/components/version-badge.d.ts.map +0 -1
- package/scripts/hud/components/version-badge.js +0 -80
- package/scripts/hud/components/version-badge.js.map +0 -1
- package/scripts/hud/components/worktree-count.d.ts +0 -3
- package/scripts/hud/components/worktree-count.d.ts.map +0 -1
- package/scripts/hud/components/worktree-count.js +0 -8
- package/scripts/hud/components/worktree-count.js.map +0 -1
- package/scripts/hud/config.d.ts +0 -10
- package/scripts/hud/config.d.ts.map +0 -1
- package/scripts/hud/config.js +0 -55
- package/scripts/hud/config.js.map +0 -1
- package/scripts/hud/credentials.d.ts +0 -17
- package/scripts/hud/credentials.d.ts.map +0 -1
- package/scripts/hud/credentials.js +0 -99
- package/scripts/hud/credentials.js.map +0 -1
- package/scripts/hud/git.d.ts +0 -7
- package/scripts/hud/git.d.ts.map +0 -1
- package/scripts/hud/git.js +0 -153
- package/scripts/hud/git.js.map +0 -1
- package/scripts/hud/index.d.ts +0 -2
- package/scripts/hud/index.d.ts.map +0 -1
- package/scripts/hud/index.js +0 -81
- package/scripts/hud/index.js.map +0 -1
- package/scripts/hud/render.d.ts +0 -7
- package/scripts/hud/render.d.ts.map +0 -1
- package/scripts/hud/render.js +0 -111
- package/scripts/hud/render.js.map +0 -1
- package/scripts/hud/stdin.d.ts +0 -6
- package/scripts/hud/stdin.d.ts.map +0 -1
- package/scripts/hud/stdin.js +0 -25
- package/scripts/hud/stdin.js.map +0 -1
- package/scripts/hud/transcript.d.ts +0 -7
- package/scripts/hud/transcript.d.ts.map +0 -1
- package/scripts/hud/transcript.js +0 -130
- package/scripts/hud/transcript.js.map +0 -1
- package/scripts/hud/types.d.ts +0 -116
- package/scripts/hud/types.d.ts.map +0 -1
- package/scripts/hud/types.js +0 -2
- package/scripts/hud/types.js.map +0 -1
- package/scripts/hud/usage-api.d.ts +0 -7
- package/scripts/hud/usage-api.d.ts.map +0 -1
- package/scripts/hud/usage-api.js +0 -84
- package/scripts/hud/usage-api.js.map +0 -1
- package/scripts/hud.sh +0 -5
- package/shared/agents/coder.md +0 -135
- package/shared/agents/git.md +0 -277
- package/shared/agents/resolver.md +0 -131
- package/shared/agents/reviewer.md +0 -165
- package/shared/agents/scrutinizer.md +0 -82
- package/shared/agents/shepherd.md +0 -108
- package/shared/agents/simplifier.md +0 -102
- package/shared/agents/skimmer.md +0 -143
- package/shared/agents/synthesizer.md +0 -211
- package/shared/agents/validator.md +0 -86
- package/shared/skills/accessibility/SKILL.md +0 -229
- package/shared/skills/accessibility/references/detection.md +0 -171
- package/shared/skills/accessibility/references/patterns.md +0 -670
- package/shared/skills/accessibility/references/violations.md +0 -419
- package/shared/skills/agent-teams/SKILL.md +0 -124
- package/shared/skills/agent-teams/references/cleanup.md +0 -104
- package/shared/skills/agent-teams/references/communication.md +0 -122
- package/shared/skills/agent-teams/references/team-patterns.md +0 -217
- package/shared/skills/ambient-router/SKILL.md +0 -145
- package/shared/skills/ambient-router/references/skill-catalog.md +0 -74
- package/shared/skills/architecture-patterns/SKILL.md +0 -153
- package/shared/skills/architecture-patterns/references/detection.md +0 -337
- package/shared/skills/architecture-patterns/references/patterns.md +0 -873
- package/shared/skills/architecture-patterns/references/violations.md +0 -575
- package/shared/skills/complexity-patterns/SKILL.md +0 -143
- package/shared/skills/complexity-patterns/references/detection.md +0 -264
- package/shared/skills/complexity-patterns/references/patterns.md +0 -487
- package/shared/skills/complexity-patterns/references/violations.md +0 -361
- package/shared/skills/consistency-patterns/SKILL.md +0 -140
- package/shared/skills/consistency-patterns/references/detection.md +0 -207
- package/shared/skills/consistency-patterns/references/patterns.md +0 -202
- package/shared/skills/consistency-patterns/references/violations.md +0 -213
- package/shared/skills/core-patterns/SKILL.md +0 -162
- package/shared/skills/core-patterns/references/checklist.md +0 -276
- package/shared/skills/core-patterns/references/code-smell-violations.md +0 -144
- package/shared/skills/core-patterns/references/detection.md +0 -303
- package/shared/skills/core-patterns/references/patterns.md +0 -576
- package/shared/skills/core-patterns/references/violations.md +0 -369
- package/shared/skills/database-patterns/SKILL.md +0 -134
- package/shared/skills/database-patterns/references/detection.md +0 -208
- package/shared/skills/database-patterns/references/patterns.md +0 -394
- package/shared/skills/database-patterns/references/violations.md +0 -332
- package/shared/skills/debug-orchestration/SKILL.md +0 -69
- package/shared/skills/dependencies-patterns/SKILL.md +0 -141
- package/shared/skills/dependencies-patterns/references/detection.md +0 -181
- package/shared/skills/dependencies-patterns/references/patterns.md +0 -225
- package/shared/skills/dependencies-patterns/references/violations.md +0 -247
- package/shared/skills/docs-framework/SKILL.md +0 -143
- package/shared/skills/docs-framework/references/patterns.md +0 -346
- package/shared/skills/docs-framework/references/violations.md +0 -221
- package/shared/skills/documentation-patterns/SKILL.md +0 -125
- package/shared/skills/documentation-patterns/references/detection.md +0 -190
- package/shared/skills/documentation-patterns/references/patterns.md +0 -189
- package/shared/skills/documentation-patterns/references/violations.md +0 -163
- package/shared/skills/frontend-design/SKILL.md +0 -254
- package/shared/skills/frontend-design/references/detection.md +0 -184
- package/shared/skills/frontend-design/references/patterns.md +0 -511
- package/shared/skills/frontend-design/references/violations.md +0 -453
- package/shared/skills/git-safety/SKILL.md +0 -122
- package/shared/skills/git-safety/references/detection.md +0 -290
- package/shared/skills/git-safety/references/patterns.md +0 -289
- package/shared/skills/git-safety/references/violations.md +0 -18
- package/shared/skills/git-workflow/SKILL.md +0 -158
- package/shared/skills/git-workflow/references/commit-patterns.md +0 -115
- package/shared/skills/git-workflow/references/commit-violations.md +0 -77
- package/shared/skills/git-workflow/references/pr-patterns.md +0 -127
- package/shared/skills/git-workflow/references/pr-violations.md +0 -96
- package/shared/skills/github-patterns/SKILL.md +0 -153
- package/shared/skills/github-patterns/references/patterns.md +0 -572
- package/shared/skills/github-patterns/references/violations.md +0 -298
- package/shared/skills/go/SKILL.md +0 -187
- package/shared/skills/go/references/concurrency.md +0 -312
- package/shared/skills/go/references/detection.md +0 -129
- package/shared/skills/go/references/patterns.md +0 -232
- package/shared/skills/go/references/violations.md +0 -205
- package/shared/skills/implementation-orchestration/SKILL.md +0 -92
- package/shared/skills/implementation-patterns/SKILL.md +0 -162
- package/shared/skills/implementation-patterns/references/patterns.md +0 -1063
- package/shared/skills/implementation-patterns/references/violations.md +0 -483
- package/shared/skills/input-validation/SKILL.md +0 -148
- package/shared/skills/input-validation/references/detection.md +0 -283
- package/shared/skills/input-validation/references/patterns.md +0 -361
- package/shared/skills/input-validation/references/violations.md +0 -224
- package/shared/skills/java/SKILL.md +0 -183
- package/shared/skills/java/references/detection.md +0 -120
- package/shared/skills/java/references/modern-java.md +0 -270
- package/shared/skills/java/references/patterns.md +0 -235
- package/shared/skills/java/references/violations.md +0 -213
- package/shared/skills/knowledge-persistence/SKILL.md +0 -128
- package/shared/skills/knowledge-persistence/references/examples.md +0 -44
- package/shared/skills/performance-patterns/SKILL.md +0 -154
- package/shared/skills/performance-patterns/references/detection.md +0 -351
- package/shared/skills/performance-patterns/references/patterns.md +0 -503
- package/shared/skills/performance-patterns/references/violations.md +0 -354
- package/shared/skills/plan-orchestration/SKILL.md +0 -71
- package/shared/skills/python/SKILL.md +0 -188
- package/shared/skills/python/references/async.md +0 -220
- package/shared/skills/python/references/detection.md +0 -128
- package/shared/skills/python/references/patterns.md +0 -226
- package/shared/skills/python/references/violations.md +0 -204
- package/shared/skills/react/SKILL.md +0 -276
- package/shared/skills/react/references/patterns.md +0 -1331
- package/shared/skills/react/references/violations.md +0 -565
- package/shared/skills/regression-patterns/SKILL.md +0 -146
- package/shared/skills/regression-patterns/references/detection.md +0 -237
- package/shared/skills/regression-patterns/references/patterns.md +0 -226
- package/shared/skills/regression-patterns/references/violations.md +0 -225
- package/shared/skills/review-methodology/SKILL.md +0 -119
- package/shared/skills/review-methodology/references/patterns.md +0 -186
- package/shared/skills/review-methodology/references/report-template.md +0 -142
- package/shared/skills/review-methodology/references/violations.md +0 -125
- package/shared/skills/rust/SKILL.md +0 -193
- package/shared/skills/rust/references/detection.md +0 -131
- package/shared/skills/rust/references/ownership.md +0 -242
- package/shared/skills/rust/references/patterns.md +0 -210
- package/shared/skills/rust/references/violations.md +0 -191
- package/shared/skills/search-first/SKILL.md +0 -133
- package/shared/skills/search-first/references/evaluation-criteria.md +0 -101
- package/shared/skills/security-patterns/SKILL.md +0 -156
- package/shared/skills/security-patterns/references/detection.md +0 -287
- package/shared/skills/security-patterns/references/patterns.md +0 -507
- package/shared/skills/security-patterns/references/violations.md +0 -237
- package/shared/skills/self-review/SKILL.md +0 -149
- package/shared/skills/self-review/references/patterns.md +0 -405
- package/shared/skills/self-review/references/report-template.md +0 -253
- package/shared/skills/self-review/references/stub-detection.md +0 -135
- package/shared/skills/self-review/references/violations.md +0 -308
- package/shared/skills/test-driven-development/SKILL.md +0 -137
- package/shared/skills/test-driven-development/references/rationalization-prevention.md +0 -111
- package/shared/skills/test-patterns/SKILL.md +0 -183
- package/shared/skills/test-patterns/references/detection.md +0 -149
- package/shared/skills/test-patterns/references/patterns.md +0 -220
- package/shared/skills/test-patterns/references/report-template.md +0 -108
- package/shared/skills/test-patterns/references/violations.md +0 -221
- package/shared/skills/typescript/SKILL.md +0 -176
- package/shared/skills/typescript/references/patterns.md +0 -1105
- package/shared/skills/typescript/references/violations.md +0 -433
- package/src/templates/claudeignore.template +0 -188
- package/src/templates/settings.json +0 -59
- /package/dist/{utils → core}/safe-delete.js +0 -0
- /package/{plugins/devflow-accessibility → src/assets}/skills/accessibility/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/architecture-patterns → src/assets/skills/architecture}/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/complexity-patterns → src/assets/skills/complexity}/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/complexity-patterns → src/assets/skills/complexity}/references/patterns.md +0 -0
- /package/{plugins/devflow-code-review/skills/complexity-patterns → src/assets/skills/complexity}/references/violations.md +0 -0
- /package/{plugins/devflow-code-review/skills/consistency-patterns → src/assets/skills/consistency}/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/consistency-patterns → src/assets/skills/consistency}/references/patterns.md +0 -0
- /package/{plugins/devflow-code-review/skills/consistency-patterns → src/assets/skills/consistency}/references/violations.md +0 -0
- /package/{plugins/devflow-code-review/skills/database-patterns → src/assets/skills/database}/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/database-patterns → src/assets/skills/database}/references/patterns.md +0 -0
- /package/{plugins/devflow-code-review/skills/database-patterns → src/assets/skills/database}/references/violations.md +0 -0
- /package/{plugins/devflow-code-review/skills/dependencies-patterns → src/assets/skills/dependencies}/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/dependencies-patterns → src/assets/skills/dependencies}/references/patterns.md +0 -0
- /package/{plugins/devflow-code-review/skills/dependencies-patterns → src/assets/skills/dependencies}/references/violations.md +0 -0
- /package/{plugins/devflow-code-review/skills/documentation-patterns → src/assets/skills/documentation}/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/documentation-patterns → src/assets/skills/documentation}/references/patterns.md +0 -0
- /package/{plugins/devflow-code-review/skills/documentation-patterns → src/assets/skills/documentation}/references/violations.md +0 -0
- /package/{plugins/devflow-core-skills/skills/git-safety → src/assets/skills/git}/references/detection.md +0 -0
- /package/{plugins/devflow-go → src/assets}/skills/go/references/detection.md +0 -0
- /package/{plugins/devflow-java → src/assets}/skills/java/references/detection.md +0 -0
- /package/{plugins/devflow-implement/skills/implementation-patterns → src/assets/skills/patterns}/references/patterns.md +0 -0
- /package/{plugins/devflow-implement/skills/implementation-patterns → src/assets/skills/patterns}/references/violations.md +0 -0
- /package/{plugins/devflow-code-review/skills/performance-patterns → src/assets/skills/performance}/references/detection.md +0 -0
- /package/{plugins/devflow-python → src/assets}/skills/python/references/detection.md +0 -0
- /package/{plugins/devflow-implement/skills/self-review → src/assets/skills/quality-gates}/references/patterns.md +0 -0
- /package/{plugins/devflow-implement/skills/self-review → src/assets/skills/quality-gates}/references/report-template.md +0 -0
- /package/{plugins/devflow-implement/skills/self-review → src/assets/skills/quality-gates}/references/violations.md +0 -0
- /package/{plugins/devflow-code-review/skills/regression-patterns → src/assets/skills/regression}/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/regression-patterns → src/assets/skills/regression}/references/patterns.md +0 -0
- /package/{plugins/devflow-code-review/skills/regression-patterns → src/assets/skills/regression}/references/violations.md +0 -0
- /package/{plugins/devflow-rust → src/assets}/skills/rust/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/security-patterns → src/assets/skills/security}/references/detection.md +0 -0
- /package/{plugins/devflow-core-skills/skills/core-patterns → src/assets/skills/software-design}/references/detection.md +0 -0
- /package/{plugins/devflow-core-skills → src/assets}/skills/test-driven-development/references/rationalization-prevention.md +0 -0
- /package/{plugins/devflow-code-review/skills/test-patterns → src/assets/skills/testing}/references/detection.md +0 -0
- /package/{plugins/devflow-code-review/skills/test-patterns → src/assets/skills/testing}/references/report-template.md +0 -0
- /package/{plugins/devflow-frontend-design/skills/frontend-design → src/assets/skills/ui-design}/references/detection.md +0 -0
- /package/src/{templates → targets/claude-code/templates}/managed-settings.json +0 -0
|
@@ -0,0 +1,1786 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import * as p from '@clack/prompts';
|
|
6
|
+
import color from 'picocolors';
|
|
7
|
+
import { getInstallationPaths } from '../../targets/claude-code/claude-paths.js';
|
|
8
|
+
import { getGitRoot } from '../../core/git.js';
|
|
9
|
+
import { installViaFileCopy, composeScripts } from '../../targets/claude-code/installer.js';
|
|
10
|
+
import { installSettings, installManagedSettings, installClaudeignore, discoverProjectGitRoots, updateGitignore, ensureDevflowGitignore, createDocsStructure, applyUserSecurityDenyList, detectDenyState, resolveSecurityAction, assertHistoricalDenySuperset, loadTemplateDenyEntries, stripUserSecurityDenyList, } from '../../targets/claude-code/post-install.js';
|
|
11
|
+
import { DEVFLOW_PLUGINS, LEGACY_COMMAND_NAMES, LEGACY_RULE_NAMES, buildAssetMaps, buildFullSkillsMap, buildRulesMap, partitionSelectablePlugins, WORKFLOW_ORDER, parsePluginSelection, resolveFeatureRedirect } from '../../core/plugins.js';
|
|
12
|
+
import { LEGACY_SKILL_NAMES } from '../../targets/claude-code/legacy.js';
|
|
13
|
+
import { detectPlatform, detectShell, getProfilePath, getSafeDeleteInfo, hasSafeDelete } from '../../core/safe-delete.js';
|
|
14
|
+
import { generateSafeDeleteBlock, installToProfile, removeFromProfile, getInstalledVersion, SAFE_DELETE_BLOCK_VERSION } from '../../core/safe-delete-install.js';
|
|
15
|
+
import { addAmbientHook, removeAmbientHook } from './ambient.js';
|
|
16
|
+
import { addMemoryHooks, removeMemoryHooks } from './memory.js';
|
|
17
|
+
import { addCaptureHooks, removeCaptureHooks } from './capture.js';
|
|
18
|
+
import { removeDreamHook } from './legacy-hooks.js';
|
|
19
|
+
import { addProxyHooks, removeProxyHooks, applyProxyEnv, stripProxyEnv, runProxyPreflight, buildRealPreflightDeps } from './proxy.js';
|
|
20
|
+
import { reapplyAgentMapping, readAgentMapping } from '../../core/agent-models.js';
|
|
21
|
+
import { readProxyState, writeProxyState, buildProxyState, buildRoutingConfigJson, DEFAULT_PROXY_PORT } from '../../core/proxy-state.js';
|
|
22
|
+
import { stripDevflowTeammateModeFromJson } from '../../core/teammate-mode-cleanup.js';
|
|
23
|
+
// Settings/HookMatcher types used by hook utilities — each in their own module
|
|
24
|
+
import { addHudStatusLine, removeHudStatusLine } from './hud.js';
|
|
25
|
+
import { loadConfig as loadHudConfig, saveConfig as saveHudConfig } from '../../hud/config.js';
|
|
26
|
+
import { readManifest, writeManifest, resolvePluginList, detectUpgrade } from '../../core/manifest.js';
|
|
27
|
+
import { applyFlags, stripFlags, applyViewMode, stripViewMode, FLAG_REGISTRY, resolveExistingViewMode, resolveFinalViewMode } from '../../core/flags.js';
|
|
28
|
+
import { addContextHook, removeContextHook, hasContextHook } from './context.js';
|
|
29
|
+
import { writeConfig, readConfigIfPresent } from '../../core/feature-config.js';
|
|
30
|
+
import { resolveInitSeed, applyCliToggles, resolveResetGatedInputs } from './init-seed.js';
|
|
31
|
+
import { parseFrameworkList, normalizeFrameworks } from '../../core/compliance.js';
|
|
32
|
+
import { formatComplianceSummary, shouldRunComplianceStep, runComplianceStep, buildClackCompliancePrompts, } from './compliance-prompts.js';
|
|
33
|
+
import { convergeFromManifest } from '../../targets/claude-code/compliance-install.js';
|
|
34
|
+
import { getPendingTurnsPath, getPendingTurnsProcessingPath } from '../../core/project-paths.js';
|
|
35
|
+
import * as os from 'os';
|
|
36
|
+
// Re-export pure functions for tests (canonical source is post-install.ts)
|
|
37
|
+
export { substituteSettingsTemplate, computeGitignoreAppend, mergeDenyList, discoverProjectGitRoots } from '../../targets/claude-code/post-install.js';
|
|
38
|
+
export { addAmbientHook, removeAmbientHook, hasAmbientHook } from './ambient.js';
|
|
39
|
+
export { addMemoryHooks, removeMemoryHooks, hasMemoryHooks } from './memory.js';
|
|
40
|
+
export { addCaptureHooks, removeCaptureHooks, hasCaptureHooks } from './capture.js';
|
|
41
|
+
export { removeDreamHook, hasDreamHook } from './legacy-hooks.js';
|
|
42
|
+
export { addHudStatusLine, removeHudStatusLine, hasHudStatusLine } from './hud.js';
|
|
43
|
+
import { reportMigrationResult } from '../../core/migrations.js';
|
|
44
|
+
import { getPackageRoot } from '../../core/paths.js';
|
|
45
|
+
/**
|
|
46
|
+
* D32/D35: Orchestrates the init-level migration-runner seam.
|
|
47
|
+
*
|
|
48
|
+
* Computes the project list with the D37 fallback rule:
|
|
49
|
+
* 1. Use discoveredProjects when non-empty.
|
|
50
|
+
* 2. Fall back to [gitRoot] when discoveredProjects is empty and gitRoot is set.
|
|
51
|
+
* 3. Run with no per-project targets when both are absent (global-only; per-project
|
|
52
|
+
* migrations are vacuously applied per D37 semantics).
|
|
53
|
+
*
|
|
54
|
+
* Migrations are a one-time cleanup pass over ~/.devflow runtime data
|
|
55
|
+
* (memory, learning, knowledge). They never touch the installer's
|
|
56
|
+
* copy targets (skills, agents, rules, commands, scripts), so ordering
|
|
57
|
+
* relative to installViaFileCopy carries no data dependency.
|
|
58
|
+
*
|
|
59
|
+
* The `runner` parameter accepts the runMigrations function — injected to make
|
|
60
|
+
* this helper testable without real filesystem migration state.
|
|
61
|
+
*/
|
|
62
|
+
export async function runMigrationsWithFallback(discoveredProjects, gitRoot, devflowDir, logger, verbose, runner) {
|
|
63
|
+
const projectsForMigration = discoveredProjects.length > 0 ? discoveredProjects : (gitRoot ? [gitRoot] : []);
|
|
64
|
+
const migrationResult = await runner({ devflowDir }, projectsForMigration);
|
|
65
|
+
reportMigrationResult(migrationResult, logger, verbose);
|
|
66
|
+
return migrationResult;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Turn the orphan-sweep half of an InstallReport into summary lines.
|
|
70
|
+
*
|
|
71
|
+
* The sweeps delete files from `~/.claude/{agents,commands,skills}/` behind the
|
|
72
|
+
* user's back; a removal the user never hears about is indistinguishable from an
|
|
73
|
+
* asset that was never installed, and a removal that FAILED leaves a retired agent
|
|
74
|
+
* or command still loading in Claude Code with no diagnostic at all. Both outcomes
|
|
75
|
+
* have to reach the summary.
|
|
76
|
+
*
|
|
77
|
+
* Pure function — returns lines, logs nothing (applies ADR-013).
|
|
78
|
+
*/
|
|
79
|
+
export function formatSweepSummary(report) {
|
|
80
|
+
const lines = [];
|
|
81
|
+
if (report.sweptOrphans.length > 0) {
|
|
82
|
+
// Format each orphan as "{kind} {name}" for disambiguation — the same registry
|
|
83
|
+
// name can appear in both the agent and command namespaces. (F15)
|
|
84
|
+
const labels = report.sweptOrphans.map(o => `${o.kind} ${o.name}`).join(', ');
|
|
85
|
+
lines.push({
|
|
86
|
+
level: 'info',
|
|
87
|
+
message: `Removed ${report.sweptOrphans.length} orphaned asset(s) no longer in the registry: ` +
|
|
88
|
+
`${labels}`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
for (const failure of report.sweepFailures) {
|
|
92
|
+
const reason = failure.error instanceof Error ? failure.error.message : String(failure.error);
|
|
93
|
+
lines.push({
|
|
94
|
+
level: 'warn',
|
|
95
|
+
message: `Could not remove orphaned ${failure.kind} "${failure.name}" (${reason}) — ` +
|
|
96
|
+
`it will keep loading in Claude Code until deleted manually`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return lines;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Classify the safe-delete installation state based on the installed version
|
|
103
|
+
* in the user's shell profile.
|
|
104
|
+
*/
|
|
105
|
+
export function classifySafeDeleteState(installedVersion, currentVersion) {
|
|
106
|
+
if (installedVersion === currentVersion)
|
|
107
|
+
return 'current';
|
|
108
|
+
if (installedVersion > 0)
|
|
109
|
+
return 'outdated';
|
|
110
|
+
return 'missing';
|
|
111
|
+
}
|
|
112
|
+
export { addContextHook, removeContextHook, hasContextHook };
|
|
113
|
+
/**
|
|
114
|
+
* Combine workflow and language selections into a single plugin list.
|
|
115
|
+
* Returns the merged array and whether a valid (non-empty) selection was made.
|
|
116
|
+
*
|
|
117
|
+
* Pure function — no I/O, no side effects; extracted for testability.
|
|
118
|
+
*/
|
|
119
|
+
export function combineSelection(workflowSelected, languageSelected) {
|
|
120
|
+
const plugins = [...workflowSelected, ...languageSelected];
|
|
121
|
+
return { plugins, accepted: plugins.length > 0 };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Returns true when the selection loop should retry: selection was empty and
|
|
125
|
+
* the attempt ceiling has not been reached. Returns false when accepted or
|
|
126
|
+
* when attempts are exhausted (caller should exit).
|
|
127
|
+
*
|
|
128
|
+
* Pure function — no I/O, no side effects; extracted for testability.
|
|
129
|
+
*/
|
|
130
|
+
export function shouldRetry(attempt, maxAttempts, accepted) {
|
|
131
|
+
if (accepted)
|
|
132
|
+
return false;
|
|
133
|
+
return attempt < maxAttempts;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Parse the --compliance / --no-compliance CLI option into a compliance override.
|
|
137
|
+
*
|
|
138
|
+
* Pure function — no I/O, no side effects; extracted for testability.
|
|
139
|
+
*
|
|
140
|
+
* Returns:
|
|
141
|
+
* {ok: true, value} — override state derived from the option
|
|
142
|
+
* {ok: false, error} — invalid framework IDs (caller handles exit)
|
|
143
|
+
* undefined — option was not supplied; no override
|
|
144
|
+
*
|
|
145
|
+
* Respects the disable-keeps-frameworks contract: --no-compliance preserves
|
|
146
|
+
* the seed's framework list so re-enable can restore them.
|
|
147
|
+
*/
|
|
148
|
+
export function resolveComplianceInitState(complianceOption, seedFrameworks) {
|
|
149
|
+
if (typeof complianceOption === 'string') {
|
|
150
|
+
const parsed = parseFrameworkList(complianceOption);
|
|
151
|
+
if (!parsed.ok) {
|
|
152
|
+
return { ok: false, error: parsed.error };
|
|
153
|
+
}
|
|
154
|
+
return { ok: true, value: { enabled: true, frameworks: parsed.value } };
|
|
155
|
+
}
|
|
156
|
+
if (complianceOption === false) {
|
|
157
|
+
return { ok: true, value: { enabled: false, frameworks: seedFrameworks } };
|
|
158
|
+
}
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
// Re-export formatComplianceSummary from compliance-prompts.ts so existing test imports
|
|
162
|
+
// (tests/init-logic.test.ts:1615 — imports from '../src/cli/commands/init.js') keep resolving.
|
|
163
|
+
export { formatComplianceSummary } from './compliance-prompts.js';
|
|
164
|
+
export const initCommand = new Command('init')
|
|
165
|
+
.description('Initialize Devflow for Claude Code')
|
|
166
|
+
.option('--scope <type>', 'Installation scope: user or local (project-only)', /^(user|local)$/i)
|
|
167
|
+
.option('--verbose', 'Show detailed installation output')
|
|
168
|
+
.option('--plugin <names>', 'Install specific plugin(s), comma-separated (e.g., implement,code-review)')
|
|
169
|
+
.option('--ambient', 'Enable ambient mode (orchestrator charter + plan handoff)')
|
|
170
|
+
.option('--no-ambient', 'Disable ambient mode')
|
|
171
|
+
.option('--memory', 'Enable working memory (session context preservation)')
|
|
172
|
+
.option('--no-memory', 'Disable working memory hooks')
|
|
173
|
+
.option('--hud', 'Enable HUD (git info, context usage, session stats)')
|
|
174
|
+
.option('--no-hud', 'Disable HUD status line')
|
|
175
|
+
.option('--knowledge', 'Enable feature knowledge bases')
|
|
176
|
+
.option('--no-knowledge', 'Disable feature knowledge bases')
|
|
177
|
+
.option('--learning', 'Enable learning (decision/pitfall tracking)')
|
|
178
|
+
.option('--no-learning', 'Disable learning (decision/pitfall tracking)')
|
|
179
|
+
.option('--rules', 'Enable rules (always-on engineering principles)')
|
|
180
|
+
.option('--no-rules', 'Disable rules')
|
|
181
|
+
.option('--proxy', 'Enable external model routing (GPT models via your OpenAI/Codex subscription)')
|
|
182
|
+
.option('--no-proxy', 'Disable external model routing')
|
|
183
|
+
.option('--compliance <list>', 'Enable compliance with comma-separated framework IDs (e.g., gdpr,hipaa)')
|
|
184
|
+
.option('--no-compliance', 'Disable compliance (artifacts removed; frameworks remembered for re-enable)')
|
|
185
|
+
.option('--security <mode>', 'Security deny list location: user, managed, or none', /^(user|managed|none)$/i)
|
|
186
|
+
.option('--hud-only', 'Install only the HUD (no plugins, hooks, or extras)')
|
|
187
|
+
.option('--recommended', 'Apply recommended defaults after plugin selection (skip advanced prompts)')
|
|
188
|
+
.option('--advanced', 'Show all configuration prompts')
|
|
189
|
+
.option('--reset', 'Factory reset — restore all defaults, ignoring prior installation state')
|
|
190
|
+
.action(async (options) => {
|
|
191
|
+
// Get package version
|
|
192
|
+
const packageJsonPath = path.join(getPackageRoot(), 'package.json');
|
|
193
|
+
let version = '';
|
|
194
|
+
try {
|
|
195
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
196
|
+
version = packageJson.version;
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
version = 'unknown';
|
|
200
|
+
}
|
|
201
|
+
const verbose = options.verbose ?? false;
|
|
202
|
+
// Start the CLI flow
|
|
203
|
+
p.intro(color.bgCyan(color.black(` Devflow v${version} `)));
|
|
204
|
+
// --reset + --plugin are mutually exclusive: --reset clears prior state while --plugin
|
|
205
|
+
// does a partial install that relies on the existing manifest — contradictory intents.
|
|
206
|
+
if (options.reset && options.plugin) {
|
|
207
|
+
p.log.error('--reset and --plugin are mutually exclusive. Use --reset alone to restore defaults, or --plugin to update a specific plugin.');
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
// Determine installation scope
|
|
211
|
+
let scope = 'user';
|
|
212
|
+
if (options.hudOnly) {
|
|
213
|
+
// --hud-only: skip scope prompt, always user scope
|
|
214
|
+
scope = 'user';
|
|
215
|
+
}
|
|
216
|
+
else if (options.scope) {
|
|
217
|
+
const normalizedScope = options.scope.toLowerCase();
|
|
218
|
+
if (normalizedScope !== 'user' && normalizedScope !== 'local') {
|
|
219
|
+
p.log.error('Invalid scope. Use "user" or "local"');
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
scope = normalizedScope;
|
|
223
|
+
}
|
|
224
|
+
else if (!process.stdin.isTTY) {
|
|
225
|
+
p.log.info('Non-interactive mode detected, using scope: user');
|
|
226
|
+
scope = 'user';
|
|
227
|
+
}
|
|
228
|
+
// --hud-only: install only HUD (skip plugins, hooks, extras)
|
|
229
|
+
if (options.hudOnly) {
|
|
230
|
+
// Resolve paths
|
|
231
|
+
const paths = await getInstallationPaths(scope);
|
|
232
|
+
const claudeDir = paths.claudeDir;
|
|
233
|
+
const devflowDir = paths.devflowDir;
|
|
234
|
+
// Save HUD config
|
|
235
|
+
const existingHud = loadHudConfig();
|
|
236
|
+
saveHudConfig({ enabled: true, detail: existingHud.detail });
|
|
237
|
+
// Update statusLine in settings.json
|
|
238
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
239
|
+
try {
|
|
240
|
+
let content;
|
|
241
|
+
try {
|
|
242
|
+
content = await fs.readFile(settingsPath, 'utf-8');
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
content = '{}';
|
|
246
|
+
}
|
|
247
|
+
const updated = addHudStatusLine(content, devflowDir);
|
|
248
|
+
await fs.writeFile(settingsPath, updated, 'utf-8');
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
p.log.error(`Failed to update settings: ${error instanceof Error ? error.message : error}`);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
// Install HUD scripts to devflow dir
|
|
255
|
+
const scriptsTarget = path.join(devflowDir, 'scripts');
|
|
256
|
+
try {
|
|
257
|
+
await composeScripts(scriptsTarget);
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
p.log.error(`Failed to install HUD scripts: ${error instanceof Error ? error.message : error}`);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
// Read existing manifest to preserve user-set compliance state (disable-keeps-frameworks
|
|
264
|
+
// contract: the hud-only path must not erase frameworks the user previously selected).
|
|
265
|
+
let existingHudManifest = null;
|
|
266
|
+
try {
|
|
267
|
+
existingHudManifest = await readManifest(devflowDir);
|
|
268
|
+
}
|
|
269
|
+
catch { /* absent on fresh install — existingHudManifest stays null */ }
|
|
270
|
+
// Write minimal manifest
|
|
271
|
+
const now = new Date().toISOString();
|
|
272
|
+
try {
|
|
273
|
+
await writeManifest(devflowDir, {
|
|
274
|
+
version,
|
|
275
|
+
plugins: [],
|
|
276
|
+
scope,
|
|
277
|
+
features: {
|
|
278
|
+
ambient: false, memory: false, hud: true, knowledge: false,
|
|
279
|
+
learning: false, rules: false, flags: [], proxy: false,
|
|
280
|
+
compliance: existingHudManifest?.features.compliance ?? { enabled: false, frameworks: [] },
|
|
281
|
+
},
|
|
282
|
+
installedAt: now,
|
|
283
|
+
updatedAt: now,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
catch { /* non-fatal */ }
|
|
287
|
+
p.log.success('HUD installed');
|
|
288
|
+
p.log.info(`Configure later: ${color.cyan('devflow hud --status')}`);
|
|
289
|
+
p.outro(color.green('HUD-only install complete.'));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
// ── Hoist reads: resolve paths early to compute InitSeed for pre-seeded prompts (Phase 4) ──
|
|
293
|
+
// Best-effort: if path resolution fails here, seed falls back to fresh-install defaults.
|
|
294
|
+
// The authoritative error gate for failed path resolution remains at the install-begins
|
|
295
|
+
// spinner (see "Resolving paths" below). Hoisted above multiselect so Phase 4 can
|
|
296
|
+
// pre-seed plugin/flag/feature prompts.
|
|
297
|
+
let existingManifest = null;
|
|
298
|
+
let earlyProjectConfig = null;
|
|
299
|
+
let earlySettingsJson = null;
|
|
300
|
+
let earlyGitRoot = null;
|
|
301
|
+
try {
|
|
302
|
+
const earlyPaths = await getInstallationPaths(scope);
|
|
303
|
+
existingManifest = await readManifest(earlyPaths.devflowDir);
|
|
304
|
+
earlyGitRoot = earlyPaths.gitRoot ?? await getGitRoot();
|
|
305
|
+
if (earlyGitRoot) {
|
|
306
|
+
earlyProjectConfig = await readConfigIfPresent(earlyGitRoot);
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
earlySettingsJson = await fs.readFile(path.join(earlyPaths.claudeDir, 'settings.json'), 'utf-8');
|
|
310
|
+
}
|
|
311
|
+
catch { /* settings.json absent — treated as empty */ }
|
|
312
|
+
}
|
|
313
|
+
catch { /* path resolution deferred to install-begins gate */ }
|
|
314
|
+
// --reset: factory reset — treat as a fresh install for all seeding and routing decisions.
|
|
315
|
+
// The REAL existingManifest / earlySettingsJson are still used below for installedAt
|
|
316
|
+
// preservation, upgrade messaging, and security deny-state detection. resolveResetGatedInputs
|
|
317
|
+
// discards the manifest, config, AND settings snapshot under --reset so the seed collapses to
|
|
318
|
+
// registry defaults — including viewMode 'default' (an externally-set /focus in settings.json
|
|
319
|
+
// must not survive a factory reset).
|
|
320
|
+
const { seedManifest, seedConfig, seedSettings } = resolveResetGatedInputs(!!options.reset, existingManifest, earlyProjectConfig, earlySettingsJson ?? '');
|
|
321
|
+
const seed = resolveInitSeed(seedManifest, seedConfig, seedSettings, DEVFLOW_PLUGINS);
|
|
322
|
+
// Early validation: parse --compliance <list> at the boundary before any prompts (PF-parse-at-boundary).
|
|
323
|
+
// options.compliance: string → --compliance <list>; false → --no-compliance; undefined → not passed
|
|
324
|
+
let cliComplianceOverride;
|
|
325
|
+
{
|
|
326
|
+
const complianceStateResult = resolveComplianceInitState(options.compliance, seed.features.compliance.frameworks);
|
|
327
|
+
if (complianceStateResult !== undefined) {
|
|
328
|
+
if (!complianceStateResult.ok) {
|
|
329
|
+
p.log.error(complianceStateResult.error);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
cliComplianceOverride = complianceStateResult.value;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Select plugins to install
|
|
336
|
+
let selectedPlugins = [];
|
|
337
|
+
if (options.plugin) {
|
|
338
|
+
// Friendly redirect for retired feature plugins (e.g. devflow-compliance → `devflow compliance`).
|
|
339
|
+
// Strip retired names, emit the notice, and continue with the remaining plugins so that a
|
|
340
|
+
// mixed list like `devflow-implement,devflow-compliance` still installs devflow-implement.
|
|
341
|
+
// Exit 0 only when nothing non-retired remains (compliance-only invocation).
|
|
342
|
+
const rawNames = options.plugin.split(',').map((s) => s.trim());
|
|
343
|
+
const redirect = resolveFeatureRedirect(rawNames);
|
|
344
|
+
if (redirect.notice) {
|
|
345
|
+
p.log.info(redirect.notice);
|
|
346
|
+
}
|
|
347
|
+
if (redirect.remaining.length === 0) {
|
|
348
|
+
process.exit(0);
|
|
349
|
+
}
|
|
350
|
+
const { selected, invalid } = parsePluginSelection(redirect.remaining.join(','), DEVFLOW_PLUGINS);
|
|
351
|
+
selectedPlugins = selected;
|
|
352
|
+
if (invalid.length > 0) {
|
|
353
|
+
p.log.error(`Unknown plugin(s): ${invalid.join(', ')}`);
|
|
354
|
+
p.log.info(`Valid plugins: ${DEVFLOW_PLUGINS.map(pl => pl.name).join(', ')}`);
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
else if (process.stdin.isTTY) {
|
|
359
|
+
// Short hints to prevent overflow in multiselect — full descriptions live in plugins.ts
|
|
360
|
+
const pluginHints = {
|
|
361
|
+
'devflow-plan': 'gap analysis, design review',
|
|
362
|
+
'devflow-implement': 'code, validate, self-review, PR',
|
|
363
|
+
'devflow-code-review': 'parallel specialized Review agents',
|
|
364
|
+
'devflow-resolve': 'fix review issues by risk',
|
|
365
|
+
'devflow-debug': 'competing hypotheses',
|
|
366
|
+
'devflow-explore': 'codebase exploration + knowledge bases',
|
|
367
|
+
'devflow-research': 'multi-type research with synthesis',
|
|
368
|
+
'devflow-release': 'adaptive release with learned config',
|
|
369
|
+
'devflow-self-review': 'Simplify agent + Scrutinize agent',
|
|
370
|
+
'devflow-bug-analysis': 'proactive bug finding, post-pipeline',
|
|
371
|
+
'devflow-typescript': 'TypeScript patterns',
|
|
372
|
+
'devflow-react': 'React patterns',
|
|
373
|
+
'devflow-accessibility': 'WCAG compliance',
|
|
374
|
+
'devflow-ui-design': 'typography, color, spacing',
|
|
375
|
+
'devflow-go': 'Go patterns',
|
|
376
|
+
'devflow-java': 'Java patterns',
|
|
377
|
+
'devflow-python': 'Python patterns',
|
|
378
|
+
'devflow-rust': 'Rust patterns',
|
|
379
|
+
};
|
|
380
|
+
const { workflow, language } = partitionSelectablePlugins(DEVFLOW_PLUGINS);
|
|
381
|
+
const toChoice = (pl) => ({
|
|
382
|
+
value: pl.name,
|
|
383
|
+
label: pl.name.replace('devflow-', ''),
|
|
384
|
+
hint: pluginHints[pl.name] ?? pl.description,
|
|
385
|
+
});
|
|
386
|
+
const workflowChoices = workflow.map(toChoice);
|
|
387
|
+
const languageChoices = language.map(toChoice);
|
|
388
|
+
// Pre-seed from prior state: if this is a re-init, seed.workflowPlugins carries the prior selection;
|
|
389
|
+
// on fresh installs it defaults to the non-optional workflow plugins.
|
|
390
|
+
const workflowInitialValues = seed.workflowPlugins;
|
|
391
|
+
// Bounded selection loop — max 3 attempts (reliability rule: no unbounded loops)
|
|
392
|
+
const MAX_ATTEMPTS = 3;
|
|
393
|
+
let attempts = 0;
|
|
394
|
+
while (attempts < MAX_ATTEMPTS) {
|
|
395
|
+
attempts++;
|
|
396
|
+
// Step 1 — Workflow plugins (skip if empty bucket)
|
|
397
|
+
let workflowSelected = [];
|
|
398
|
+
if (workflowChoices.length > 0) {
|
|
399
|
+
const step1 = await p.multiselect({
|
|
400
|
+
message: 'Step 1 — Workflow plugins',
|
|
401
|
+
options: workflowChoices,
|
|
402
|
+
initialValues: workflowInitialValues,
|
|
403
|
+
required: false,
|
|
404
|
+
});
|
|
405
|
+
if (p.isCancel(step1)) {
|
|
406
|
+
p.cancel('Installation cancelled.');
|
|
407
|
+
process.exit(0);
|
|
408
|
+
}
|
|
409
|
+
workflowSelected = step1;
|
|
410
|
+
}
|
|
411
|
+
// Step 2 — Language plugins (skip if empty bucket)
|
|
412
|
+
let languageSelected = [];
|
|
413
|
+
if (languageChoices.length > 0) {
|
|
414
|
+
const step2 = await p.multiselect({
|
|
415
|
+
message: 'Step 2 — Language & ecosystem plugins',
|
|
416
|
+
options: languageChoices,
|
|
417
|
+
// Pre-seed from prior state (empty array on fresh installs)
|
|
418
|
+
initialValues: seed.languagePlugins,
|
|
419
|
+
required: false,
|
|
420
|
+
});
|
|
421
|
+
if (p.isCancel(step2)) {
|
|
422
|
+
p.cancel('Installation cancelled.');
|
|
423
|
+
process.exit(0);
|
|
424
|
+
}
|
|
425
|
+
languageSelected = step2;
|
|
426
|
+
}
|
|
427
|
+
const { plugins: combined, accepted } = combineSelection(workflowSelected, languageSelected);
|
|
428
|
+
if (accepted) {
|
|
429
|
+
selectedPlugins = combined;
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
if (!shouldRetry(attempts, MAX_ATTEMPTS, accepted)) {
|
|
433
|
+
p.cancel('Installation cancelled — no plugins selected.');
|
|
434
|
+
process.exit(0);
|
|
435
|
+
}
|
|
436
|
+
p.log.warn('Select at least one plugin.');
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
// Non-interactive re-init: preserve prior plugin selection.
|
|
440
|
+
// When no --plugin flag is given and a manifest exists, the seed carries the prior
|
|
441
|
+
// selection (existing plugins ∪ new non-optional plugins not yet in knownPlugins).
|
|
442
|
+
// Fresh non-interactive installs (no manifest) fall through to the default path
|
|
443
|
+
// in pluginsToInstall which installs all non-optional plugins.
|
|
444
|
+
if (!options.plugin && !process.stdin.isTTY && seedManifest !== null) {
|
|
445
|
+
selectedPlugins = [...seed.workflowPlugins, ...seed.languagePlugins];
|
|
446
|
+
}
|
|
447
|
+
// ╭──────────────────────────────────────────────────────────╮
|
|
448
|
+
// │ Setup mode: Recommended vs Advanced │
|
|
449
|
+
// ╰──────────────────────────────────────────────────────────╯
|
|
450
|
+
// Determine setup mode: --recommended, --advanced, interactive prompt, or non-TTY default
|
|
451
|
+
if (options.recommended && options.advanced) {
|
|
452
|
+
p.log.error('Cannot use both --recommended and --advanced. Pick one.');
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
// modePromptShown: true only when the Setup-mode p.select actually ran (i.e. the user
|
|
456
|
+
// was shown a live interactive prompt and made an active choice). Used by
|
|
457
|
+
// shouldRunComplianceStep to preserve the promptless contracts of --recommended and !isTTY
|
|
458
|
+
// without re-checking the mode name (per PF-029).
|
|
459
|
+
let modePromptShown = false;
|
|
460
|
+
let useRecommended;
|
|
461
|
+
if (options.recommended) {
|
|
462
|
+
useRecommended = true;
|
|
463
|
+
}
|
|
464
|
+
else if (options.advanced) {
|
|
465
|
+
useRecommended = false;
|
|
466
|
+
}
|
|
467
|
+
else if (!process.stdin.isTTY) {
|
|
468
|
+
useRecommended = true;
|
|
469
|
+
}
|
|
470
|
+
else if (seedManifest !== null) {
|
|
471
|
+
// Re-init: skip the Recommended/Advanced mode prompt entirely and go straight to
|
|
472
|
+
// advanced-style prompts pre-seeded from prior state. Enter-through keeps everything.
|
|
473
|
+
// (seedManifest is null under --reset, so that path shows the mode prompt again.)
|
|
474
|
+
p.log.info('Existing installation detected — press Enter through prompts to keep current settings.');
|
|
475
|
+
useRecommended = false;
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
const modeChoice = await p.select({
|
|
479
|
+
message: 'Setup mode',
|
|
480
|
+
options: [
|
|
481
|
+
{ value: 'recommended', label: 'Recommended', hint: 'sensible defaults, quick setup' },
|
|
482
|
+
{ value: 'advanced', label: 'Advanced', hint: 'configure each option individually' },
|
|
483
|
+
],
|
|
484
|
+
});
|
|
485
|
+
if (p.isCancel(modeChoice)) {
|
|
486
|
+
p.cancel('Installation cancelled.');
|
|
487
|
+
process.exit(0);
|
|
488
|
+
}
|
|
489
|
+
// The mode prompt ran and completed — record that the user made an active choice.
|
|
490
|
+
modePromptShown = true;
|
|
491
|
+
useRecommended = modeChoice === 'recommended';
|
|
492
|
+
}
|
|
493
|
+
// Feature toggles — seeded from prior state (fresh installs use registry defaults via seed)
|
|
494
|
+
let ambientEnabled = seed.features.ambient;
|
|
495
|
+
let memoryEnabled = seed.features.memory;
|
|
496
|
+
let hudEnabled = seed.features.hud;
|
|
497
|
+
let knowledgeEnabled = seed.features.knowledge;
|
|
498
|
+
let learningEnabled = seed.features.learning;
|
|
499
|
+
let rulesEnabled = seed.features.rules;
|
|
500
|
+
// proxy: Advanced-only; Recommended path carries seed value unchanged.
|
|
501
|
+
// Fresh installs → false (FEATURE_DEFAULTS.proxy). Re-inits → prior manifest value.
|
|
502
|
+
// --reset → false (resolveResetGatedInputs null-seeds the manifest).
|
|
503
|
+
let proxyEnabled = seed.features.proxy;
|
|
504
|
+
// compliance: manifest-group (like proxy); seed from prior manifest value.
|
|
505
|
+
// CLI override applied below in both Recommended and Advanced paths.
|
|
506
|
+
let complianceEnabled = seed.features.compliance.enabled;
|
|
507
|
+
let complianceFrameworks = seed.features.compliance.frameworks;
|
|
508
|
+
let enabledFlags = seed.flags;
|
|
509
|
+
let viewMode = seed.viewMode;
|
|
510
|
+
// viewModeExplicit: true when the user made an explicit interactive selection or --reset was passed.
|
|
511
|
+
// Used by resolveFinalViewMode to decide whether to clobber an externally-set /focus value.
|
|
512
|
+
// --reset forces viewMode back to 'default': resolveResetGatedInputs empties the settings snapshot
|
|
513
|
+
// so seed.viewMode collapses to 'default', and explicit=true makes that 'default' win at write time.
|
|
514
|
+
let viewModeExplicit = !!options.reset;
|
|
515
|
+
let claudeignoreEnabled = !!earlyGitRoot;
|
|
516
|
+
let discoveredProjects = [];
|
|
517
|
+
let safeDeleteAction = 'skip';
|
|
518
|
+
let safeDeleteBlock = null;
|
|
519
|
+
// Security mode is resolved from flag + manifest + detected reality via resolveSecurityAction.
|
|
520
|
+
// The final value is written to the manifest and consumed by the dedicated security step.
|
|
521
|
+
let securityMode = 'user'; // placeholder; overwritten below by resolve
|
|
522
|
+
let managedSettingsConfirmed = false;
|
|
523
|
+
// Safe-delete detection (both paths need this)
|
|
524
|
+
const platform = detectPlatform();
|
|
525
|
+
const shell = detectShell();
|
|
526
|
+
const safeDeleteInfo = getSafeDeleteInfo(platform);
|
|
527
|
+
const safeDeleteAvailable = hasSafeDelete(platform);
|
|
528
|
+
const profilePath = getProfilePath(shell);
|
|
529
|
+
if (useRecommended) {
|
|
530
|
+
// ── Recommended path ──
|
|
531
|
+
// B4: compliance wizard step — runs only when the Setup-mode prompt actually ran
|
|
532
|
+
// (modePromptShown=true), preserving the promptless contracts of --recommended and !isTTY.
|
|
533
|
+
// shouldRunComplianceStep gates on modePromptShown rather than the mode name (PF-029).
|
|
534
|
+
// Re-init never routes here (seedManifest !== null → banner path → Advanced), so this path
|
|
535
|
+
// always sees fresh-install defaults.
|
|
536
|
+
let wizardCompliance;
|
|
537
|
+
if (shouldRunComplianceStep({
|
|
538
|
+
mode: 'recommended',
|
|
539
|
+
modePromptShown,
|
|
540
|
+
isTTY: process.stdin.isTTY,
|
|
541
|
+
hasCliOverride: cliComplianceOverride !== undefined,
|
|
542
|
+
})) {
|
|
543
|
+
const complianceStep = await runComplianceStep({
|
|
544
|
+
seed: seed.features.compliance,
|
|
545
|
+
prompts: buildClackCompliancePrompts(),
|
|
546
|
+
});
|
|
547
|
+
if (complianceStep.kind === 'cancelled') {
|
|
548
|
+
p.cancel('Installation cancelled.');
|
|
549
|
+
process.exit(0);
|
|
550
|
+
}
|
|
551
|
+
wizardCompliance = complianceStep.state;
|
|
552
|
+
// Step messages not emitted here — the Recommended summary note (below) already
|
|
553
|
+
// prints the Compliance line from complianceSummary via formatComplianceSummary.
|
|
554
|
+
}
|
|
555
|
+
// Apply explicit CLI toggles on top of the seed.
|
|
556
|
+
// Precedence: explicit CLI flag > wizard result > seed value (prior state > registry default).
|
|
557
|
+
// proxy is included: --proxy/--no-proxy CLI flags override the seed in non-interactive mode.
|
|
558
|
+
const effectiveFeatures = applyCliToggles(seed.features, {
|
|
559
|
+
ambient: options.ambient,
|
|
560
|
+
memory: options.memory,
|
|
561
|
+
hud: options.hud,
|
|
562
|
+
knowledge: options.knowledge,
|
|
563
|
+
learning: options.learning,
|
|
564
|
+
rules: options.rules,
|
|
565
|
+
proxy: options.proxy,
|
|
566
|
+
compliance: cliComplianceOverride ?? wizardCompliance,
|
|
567
|
+
});
|
|
568
|
+
ambientEnabled = effectiveFeatures.ambient;
|
|
569
|
+
memoryEnabled = effectiveFeatures.memory;
|
|
570
|
+
hudEnabled = effectiveFeatures.hud;
|
|
571
|
+
knowledgeEnabled = effectiveFeatures.knowledge;
|
|
572
|
+
learningEnabled = effectiveFeatures.learning;
|
|
573
|
+
rulesEnabled = effectiveFeatures.rules;
|
|
574
|
+
proxyEnabled = effectiveFeatures.proxy;
|
|
575
|
+
complianceEnabled = effectiveFeatures.compliance.enabled;
|
|
576
|
+
complianceFrameworks = effectiveFeatures.compliance.frameworks;
|
|
577
|
+
// enabledFlags and viewMode are already initialised to seed values above.
|
|
578
|
+
// Compute safe-delete block synchronously so we know whether to fetch installed version
|
|
579
|
+
if (profilePath && safeDeleteAvailable) {
|
|
580
|
+
const trashCmd = safeDeleteInfo.command;
|
|
581
|
+
safeDeleteBlock = generateSafeDeleteBlock(shell, process.platform, trashCmd);
|
|
582
|
+
}
|
|
583
|
+
// Run independent I/O in parallel: project discovery + safe-delete version check
|
|
584
|
+
const needsDiscovery = earlyGitRoot && scope === 'user';
|
|
585
|
+
const needsVersionCheck = safeDeleteBlock && profilePath;
|
|
586
|
+
const [discoveredResult, installedVersionResult] = await Promise.all([
|
|
587
|
+
needsDiscovery ? discoverProjectGitRoots() : Promise.resolve([]),
|
|
588
|
+
needsVersionCheck ? getInstalledVersion(profilePath) : Promise.resolve(0),
|
|
589
|
+
]);
|
|
590
|
+
discoveredProjects = discoveredResult;
|
|
591
|
+
if (needsVersionCheck) {
|
|
592
|
+
const state = classifySafeDeleteState(installedVersionResult, SAFE_DELETE_BLOCK_VERSION);
|
|
593
|
+
if (state === 'current')
|
|
594
|
+
safeDeleteAction = 'skip';
|
|
595
|
+
else if (state === 'outdated')
|
|
596
|
+
safeDeleteAction = 'upgrade';
|
|
597
|
+
else
|
|
598
|
+
safeDeleteAction = 'install';
|
|
599
|
+
}
|
|
600
|
+
// Print summary
|
|
601
|
+
const defaultFlagCount = enabledFlags.length;
|
|
602
|
+
const complianceSummary = formatComplianceSummary(complianceEnabled, complianceFrameworks);
|
|
603
|
+
const summaryLines = [
|
|
604
|
+
`Ambient mode: ${ambientEnabled ? 'enabled' : 'disabled'}`,
|
|
605
|
+
`Working memory: ${memoryEnabled ? 'enabled' : 'disabled'}`,
|
|
606
|
+
`Learning: ${learningEnabled ? 'enabled' : 'disabled'}`,
|
|
607
|
+
`Rules: ${rulesEnabled ? 'enabled' : 'disabled'}`,
|
|
608
|
+
`HUD: ${hudEnabled ? 'enabled' : 'disabled'}`,
|
|
609
|
+
`Knowledge bases: ${knowledgeEnabled ? 'enabled' : 'disabled'}`,
|
|
610
|
+
`Ext model routing: ${proxyEnabled ? 'enabled' : 'disabled'}`,
|
|
611
|
+
`Compliance: ${complianceSummary}`,
|
|
612
|
+
`View mode: ${viewMode}`,
|
|
613
|
+
`Claude Code flags: ${defaultFlagCount} enabled`,
|
|
614
|
+
`${claudeignoreEnabled ? '.claudeignore: created' : ''}`,
|
|
615
|
+
`${safeDeleteAction !== 'skip' ? 'Safe delete: installed' : ''}`,
|
|
616
|
+
].filter(l => l.trim()).join('\n');
|
|
617
|
+
p.note(summaryLines + `\n\nCustomize later: ${color.cyan('devflow init --advanced')}`, 'Recommended settings applied');
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
// ── Advanced path: full interactive flow ──
|
|
621
|
+
// Advanced mode requires a TTY for interactive prompts. In non-TTY
|
|
622
|
+
// environments, fall back to --recommended or pass explicit flags.
|
|
623
|
+
if (!process.stdin.isTTY) {
|
|
624
|
+
p.log.error('--advanced requires an interactive terminal. Use --recommended or pass explicit flags (e.g., --no-ambient).');
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
if (options.ambient !== undefined) {
|
|
628
|
+
ambientEnabled = options.ambient;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
p.note('Puts every session (git repos only) in orchestrator posture:\n' +
|
|
632
|
+
'a ~535-token charter at session start plus a per-prompt reminder\n' +
|
|
633
|
+
'steer the main model to delegate work to agents and devflow workflows\n' +
|
|
634
|
+
'instead of doing it mainline. Plan-mode handoffs auto-run devflow:implement.', 'Ambient Mode');
|
|
635
|
+
const ambientChoice = await p.select({
|
|
636
|
+
message: 'Enable ambient mode?',
|
|
637
|
+
options: [
|
|
638
|
+
{ value: true, label: 'Yes', hint: 'Recommended' },
|
|
639
|
+
{ value: false, label: 'No', hint: 'Plain sessions — no charter, no reminder' },
|
|
640
|
+
],
|
|
641
|
+
initialValue: seed.features.ambient,
|
|
642
|
+
});
|
|
643
|
+
if (p.isCancel(ambientChoice)) {
|
|
644
|
+
p.cancel('Installation cancelled.');
|
|
645
|
+
process.exit(0);
|
|
646
|
+
}
|
|
647
|
+
ambientEnabled = ambientChoice;
|
|
648
|
+
}
|
|
649
|
+
if (options.memory !== undefined) {
|
|
650
|
+
memoryEnabled = options.memory;
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
p.note('Preserves session context across /clear, restarts, and context\n' +
|
|
654
|
+
'compaction. Clear your session at any point and resume right\n' +
|
|
655
|
+
'where you left off.\n\n' +
|
|
656
|
+
'Runs a background agent on session stop that consumes additional\n' +
|
|
657
|
+
'tokens. Consider skipping if token usage is a concern.', 'Working Memory');
|
|
658
|
+
const memoryChoice = await p.confirm({
|
|
659
|
+
message: 'Enable working memory? (Recommended)',
|
|
660
|
+
initialValue: seed.features.memory,
|
|
661
|
+
});
|
|
662
|
+
if (p.isCancel(memoryChoice)) {
|
|
663
|
+
p.cancel('Installation cancelled.');
|
|
664
|
+
process.exit(0);
|
|
665
|
+
}
|
|
666
|
+
memoryEnabled = memoryChoice;
|
|
667
|
+
}
|
|
668
|
+
if (options.hud !== undefined) {
|
|
669
|
+
hudEnabled = options.hud;
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
p.note('The HUD displays git branch, context usage, and session stats\n' +
|
|
673
|
+
'in the Claude Code status bar. Configurable via devflow hud.', 'HUD');
|
|
674
|
+
const hudChoice = await p.confirm({
|
|
675
|
+
message: 'Enable HUD? (Recommended)',
|
|
676
|
+
initialValue: seed.features.hud,
|
|
677
|
+
});
|
|
678
|
+
if (p.isCancel(hudChoice)) {
|
|
679
|
+
p.cancel('Installation cancelled.');
|
|
680
|
+
process.exit(0);
|
|
681
|
+
}
|
|
682
|
+
hudEnabled = hudChoice;
|
|
683
|
+
}
|
|
684
|
+
if (options.knowledge !== undefined) {
|
|
685
|
+
knowledgeEnabled = options.knowledge;
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
p.note('Per-feature knowledge bases capture cross-cutting patterns,\n' +
|
|
689
|
+
'conventions, and gotchas. Created and updated automatically\n' +
|
|
690
|
+
'when workflows touch a documented area (write-through model).', 'Feature Knowledge Bases');
|
|
691
|
+
const knowledgeChoice = await p.confirm({
|
|
692
|
+
message: 'Enable feature knowledge bases? (Recommended)',
|
|
693
|
+
initialValue: seed.features.knowledge,
|
|
694
|
+
});
|
|
695
|
+
if (p.isCancel(knowledgeChoice)) {
|
|
696
|
+
p.cancel('Installation cancelled.');
|
|
697
|
+
process.exit(0);
|
|
698
|
+
}
|
|
699
|
+
knowledgeEnabled = knowledgeChoice;
|
|
700
|
+
}
|
|
701
|
+
if (options.learning !== undefined) {
|
|
702
|
+
learningEnabled = options.learning;
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
p.note('Detects architectural decisions and pitfalls from your session\n' +
|
|
706
|
+
'dialogs. Runs a background agent on session stop that consumes\n' +
|
|
707
|
+
'additional tokens.', 'Learning (Decision/Pitfall Tracking)');
|
|
708
|
+
const learningChoice = await p.confirm({
|
|
709
|
+
message: 'Enable learning? (Recommended)',
|
|
710
|
+
initialValue: seed.features.learning,
|
|
711
|
+
});
|
|
712
|
+
if (p.isCancel(learningChoice)) {
|
|
713
|
+
p.cancel('Installation cancelled.');
|
|
714
|
+
process.exit(0);
|
|
715
|
+
}
|
|
716
|
+
learningEnabled = learningChoice;
|
|
717
|
+
}
|
|
718
|
+
if (options.rules !== undefined) {
|
|
719
|
+
rulesEnabled = options.rules;
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
p.note('Rules are ultra-condensed engineering principles (~10-15 lines each).\n' +
|
|
723
|
+
'Language rules only load for matching files (e.g., TypeScript rules\n' +
|
|
724
|
+
'activate for .ts files) — minimal token cost. The compliance rule is\n' +
|
|
725
|
+
'always available via `devflow compliance`.', 'Rules');
|
|
726
|
+
const rulesChoice = await p.confirm({
|
|
727
|
+
message: 'Enable rules? (Recommended)',
|
|
728
|
+
initialValue: seed.features.rules,
|
|
729
|
+
});
|
|
730
|
+
if (p.isCancel(rulesChoice)) {
|
|
731
|
+
p.cancel('Installation cancelled.');
|
|
732
|
+
process.exit(0);
|
|
733
|
+
}
|
|
734
|
+
rulesEnabled = rulesChoice;
|
|
735
|
+
}
|
|
736
|
+
// External model routing (Advanced-only; default OFF; never part of Recommended)
|
|
737
|
+
if (options.proxy !== undefined) {
|
|
738
|
+
proxyEnabled = options.proxy;
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
p.note('Routes compatible agents through a local relay that forwards requests to\n' +
|
|
742
|
+
'GPT models via your OpenAI/Codex subscription.\n\n' +
|
|
743
|
+
'Requires the Codex CLI signed in (`codex login`). Takes effect in new\n' +
|
|
744
|
+
'Claude Code sessions. Disable leaves a running relay alone until reboot.\n\n' +
|
|
745
|
+
'GPT model assignments are preserved (dormant) while routing is off and\n' +
|
|
746
|
+
're-activate when you re-enable routing. Use `devflow agents` to configure.', 'External Model Routing');
|
|
747
|
+
const proxyChoice = await p.confirm({
|
|
748
|
+
message: 'Enable external model routing (GPT models via your OpenAI/Codex subscription)?',
|
|
749
|
+
initialValue: seed.features.proxy,
|
|
750
|
+
});
|
|
751
|
+
if (p.isCancel(proxyChoice)) {
|
|
752
|
+
p.cancel('Installation cancelled.');
|
|
753
|
+
process.exit(0);
|
|
754
|
+
}
|
|
755
|
+
proxyEnabled = proxyChoice;
|
|
756
|
+
}
|
|
757
|
+
// Compliance feature (after proxy, before flags — runs in both Advanced and re-init paths).
|
|
758
|
+
// Gated by the same shouldRunComplianceStep predicate as the Recommended path so the
|
|
759
|
+
// documented gate table is the single authority for both — the two paths cannot drift.
|
|
760
|
+
// Here isTTY is guaranteed true (the non-TTY guard above exit-1'd), so the predicate
|
|
761
|
+
// reduces to "no --compliance/--no-compliance override".
|
|
762
|
+
if (shouldRunComplianceStep({
|
|
763
|
+
mode: 'advanced',
|
|
764
|
+
modePromptShown,
|
|
765
|
+
isTTY: process.stdin.isTTY,
|
|
766
|
+
hasCliOverride: cliComplianceOverride !== undefined,
|
|
767
|
+
})) {
|
|
768
|
+
// runComplianceStep: note with "Current setting:" header (legible on re-init per PF-029),
|
|
769
|
+
// labeled Yes/No select (immune to Enter-through muscle memory), and framework multiselect.
|
|
770
|
+
// Returns {kind:'cancelled'} on Escape — caller owns the cancel idiom (PF-014).
|
|
771
|
+
const complianceStep = await runComplianceStep({
|
|
772
|
+
seed: { enabled: complianceEnabled, frameworks: complianceFrameworks },
|
|
773
|
+
prompts: buildClackCompliancePrompts(),
|
|
774
|
+
});
|
|
775
|
+
if (complianceStep.kind === 'cancelled') {
|
|
776
|
+
p.cancel('Installation cancelled.');
|
|
777
|
+
process.exit(0);
|
|
778
|
+
}
|
|
779
|
+
complianceEnabled = complianceStep.state.enabled;
|
|
780
|
+
complianceFrameworks = complianceStep.state.frameworks;
|
|
781
|
+
// Advanced path has no end-of-wizard summary recap (unlike Recommended) — emit outcome line.
|
|
782
|
+
for (const msg of complianceStep.messages) {
|
|
783
|
+
if (msg.level === 'success')
|
|
784
|
+
p.log.success(msg.text);
|
|
785
|
+
else
|
|
786
|
+
p.log.info(msg.text);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
else if (cliComplianceOverride !== undefined) {
|
|
790
|
+
// --compliance or --no-compliance passed explicitly — honour without prompting.
|
|
791
|
+
complianceEnabled = cliComplianceOverride.enabled;
|
|
792
|
+
complianceFrameworks = cliComplianceOverride.frameworks;
|
|
793
|
+
}
|
|
794
|
+
// No third case in practice: on this path the predicate only returns false for a
|
|
795
|
+
// CLI override (isTTY is guaranteed true by the non-TTY guard above). If it ever
|
|
796
|
+
// did, the seed values assigned at declaration stand — which is the right default.
|
|
797
|
+
// Claude Code flags multiselect (advanced only)
|
|
798
|
+
const recommended = FLAG_REGISTRY.filter(f => f.defaultEnabled);
|
|
799
|
+
const optional = FLAG_REGISTRY.filter(f => !f.defaultEnabled);
|
|
800
|
+
const flagChoices = [
|
|
801
|
+
...recommended.map(f => ({
|
|
802
|
+
value: f.id,
|
|
803
|
+
label: f.label,
|
|
804
|
+
hint: `${f.hint} · recommended`,
|
|
805
|
+
})),
|
|
806
|
+
{ value: '_separator', label: color.dim('── Optional (skip if unsure) ──'), hint: '' },
|
|
807
|
+
...optional.map(f => ({
|
|
808
|
+
value: f.id,
|
|
809
|
+
label: f.label,
|
|
810
|
+
hint: f.hint,
|
|
811
|
+
})),
|
|
812
|
+
];
|
|
813
|
+
p.note('Recommended flags are pre-selected. Optional flags are for\n' +
|
|
814
|
+
'advanced users — if you don\'t recognize one, skip it.', 'Claude Code Flags');
|
|
815
|
+
const flagSelection = await p.multiselect({
|
|
816
|
+
message: 'Claude Code flags',
|
|
817
|
+
options: flagChoices,
|
|
818
|
+
// Pre-seeded from prior state; fresh installs start with all default-ON flags.
|
|
819
|
+
initialValues: seed.flags,
|
|
820
|
+
required: false,
|
|
821
|
+
});
|
|
822
|
+
if (p.isCancel(flagSelection)) {
|
|
823
|
+
p.cancel('Installation cancelled.');
|
|
824
|
+
process.exit(0);
|
|
825
|
+
}
|
|
826
|
+
enabledFlags = flagSelection.filter(id => id !== '_separator');
|
|
827
|
+
// View mode selector (advanced only)
|
|
828
|
+
p.note('Controls how much detail Claude Code shows in the transcript.\n' +
|
|
829
|
+
'• default — normal display with expandable tool output\n' +
|
|
830
|
+
'• verbose — shows everything including thinking blocks\n' +
|
|
831
|
+
'• focus — minimal: prompt, one-line tool summaries, final response', 'View Mode');
|
|
832
|
+
const viewModeChoice = await p.select({
|
|
833
|
+
message: 'View mode',
|
|
834
|
+
options: [
|
|
835
|
+
{ value: 'default', label: 'Default', hint: 'expandable tool output · recommended' },
|
|
836
|
+
{ value: 'verbose', label: 'Verbose', hint: 'shows everything including thinking' },
|
|
837
|
+
{ value: 'focus', label: 'Focus', hint: 'minimal output, one-line summaries' },
|
|
838
|
+
],
|
|
839
|
+
// Pre-seeded from prior state (fresh installs default to 'default').
|
|
840
|
+
initialValue: seed.viewMode,
|
|
841
|
+
});
|
|
842
|
+
if (p.isCancel(viewModeChoice)) {
|
|
843
|
+
p.cancel('Installation cancelled.');
|
|
844
|
+
process.exit(0);
|
|
845
|
+
}
|
|
846
|
+
viewMode = viewModeChoice;
|
|
847
|
+
// Mark as explicit: user actively selected this mode, so resolveFinalViewMode will
|
|
848
|
+
// let it win over an externally-set /focus value.
|
|
849
|
+
viewModeExplicit = true;
|
|
850
|
+
// .claudeignore prompt
|
|
851
|
+
if (earlyGitRoot) {
|
|
852
|
+
if (scope === 'user') {
|
|
853
|
+
discoveredProjects = await discoverProjectGitRoots();
|
|
854
|
+
p.note('Scans all projects Claude has worked on and creates a\n' +
|
|
855
|
+
'.claudeignore in each git repository. Excludes secrets,\n' +
|
|
856
|
+
'API keys, dependencies, and build artifacts from context.', '.claudeignore');
|
|
857
|
+
if (discoveredProjects.length > 0) {
|
|
858
|
+
const maxShow = 5;
|
|
859
|
+
const projectLines = discoveredProjects.slice(0, maxShow).join('\n');
|
|
860
|
+
const overflow = discoveredProjects.length > maxShow
|
|
861
|
+
? `\n... (${discoveredProjects.length - maxShow} more)`
|
|
862
|
+
: '';
|
|
863
|
+
p.note(projectLines + overflow, `Discovered ${discoveredProjects.length} projects`);
|
|
864
|
+
const claudeignoreChoice = await p.confirm({
|
|
865
|
+
message: `Install .claudeignore to ${discoveredProjects.length} projects? (Recommended)`,
|
|
866
|
+
initialValue: true,
|
|
867
|
+
});
|
|
868
|
+
if (p.isCancel(claudeignoreChoice)) {
|
|
869
|
+
p.cancel('Installation cancelled.');
|
|
870
|
+
process.exit(0);
|
|
871
|
+
}
|
|
872
|
+
claudeignoreEnabled = claudeignoreChoice;
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
const claudeignoreChoice = await p.confirm({
|
|
876
|
+
message: 'Create .claudeignore? (Recommended)',
|
|
877
|
+
initialValue: true,
|
|
878
|
+
});
|
|
879
|
+
if (p.isCancel(claudeignoreChoice)) {
|
|
880
|
+
p.cancel('Installation cancelled.');
|
|
881
|
+
process.exit(0);
|
|
882
|
+
}
|
|
883
|
+
claudeignoreEnabled = claudeignoreChoice;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
p.note('Creates a .claudeignore in this project that excludes\n' +
|
|
888
|
+
'secrets, API keys, dependencies, and build artifacts from\n' +
|
|
889
|
+
'Claude\'s context window.', '.claudeignore');
|
|
890
|
+
const claudeignoreChoice = await p.confirm({
|
|
891
|
+
message: 'Create .claudeignore? (Recommended)',
|
|
892
|
+
initialValue: true,
|
|
893
|
+
});
|
|
894
|
+
if (p.isCancel(claudeignoreChoice)) {
|
|
895
|
+
p.cancel('Installation cancelled.');
|
|
896
|
+
process.exit(0);
|
|
897
|
+
}
|
|
898
|
+
claudeignoreEnabled = claudeignoreChoice;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
claudeignoreEnabled = false;
|
|
903
|
+
}
|
|
904
|
+
// Safe-delete detection + prompt (advanced only)
|
|
905
|
+
if (process.stdin.isTTY && profilePath && safeDeleteAvailable) {
|
|
906
|
+
const trashCmd = safeDeleteInfo.command;
|
|
907
|
+
safeDeleteBlock = generateSafeDeleteBlock(shell, process.platform, trashCmd);
|
|
908
|
+
if (safeDeleteBlock) {
|
|
909
|
+
const installedVersion = await getInstalledVersion(profilePath);
|
|
910
|
+
const state = classifySafeDeleteState(installedVersion, SAFE_DELETE_BLOCK_VERSION);
|
|
911
|
+
if (state === 'current') {
|
|
912
|
+
safeDeleteAction = 'skip';
|
|
913
|
+
}
|
|
914
|
+
else if (state === 'outdated') {
|
|
915
|
+
safeDeleteAction = 'upgrade';
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
p.note('Overrides rm to use your system trash CLI instead of permanent\n' +
|
|
919
|
+
'deletion. Prevents accidental data loss from rm -rf.', 'Safe Delete');
|
|
920
|
+
const safeDeleteConfirm = await p.confirm({
|
|
921
|
+
message: `Install safe-delete to ${profilePath}? (uses ${trashCmd ?? 'recycle bin'})`,
|
|
922
|
+
initialValue: true,
|
|
923
|
+
});
|
|
924
|
+
if (!p.isCancel(safeDeleteConfirm) && safeDeleteConfirm) {
|
|
925
|
+
safeDeleteAction = 'install';
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
// Security deny list placement (user scope + TTY only)
|
|
931
|
+
if (scope === 'user' && process.stdin.isTTY) {
|
|
932
|
+
p.note('Devflow includes a security deny list that blocks dangerous\n' +
|
|
933
|
+
'commands (rm -rf, sudo, eval, etc). It can be installed as a\n' +
|
|
934
|
+
'read-only system file or in your editable settings.json.', 'Security Deny List');
|
|
935
|
+
const securityChoice = await p.select({
|
|
936
|
+
message: 'How should Devflow install the deny list?',
|
|
937
|
+
options: [
|
|
938
|
+
{ value: 'managed', label: 'Managed settings', hint: 'Recommended — read-only, cannot be overridden' },
|
|
939
|
+
{ value: 'user', label: 'User settings', hint: 'Editable in settings.json' },
|
|
940
|
+
],
|
|
941
|
+
});
|
|
942
|
+
if (p.isCancel(securityChoice)) {
|
|
943
|
+
p.cancel('Installation cancelled.');
|
|
944
|
+
process.exit(0);
|
|
945
|
+
}
|
|
946
|
+
securityMode = securityChoice;
|
|
947
|
+
}
|
|
948
|
+
// Managed settings sudo confirmation (last interactive step)
|
|
949
|
+
if (securityMode === 'managed') {
|
|
950
|
+
p.note('This writes a read-only security deny list to a system directory\n' +
|
|
951
|
+
'and may prompt for your password (sudo).\n\n' +
|
|
952
|
+
'Not sure about this? Paste this into another Claude Code session:\n\n' +
|
|
953
|
+
' "I\'m installing Devflow and it wants to write a\n' +
|
|
954
|
+
' managed-settings.json file using sudo. Review the source\n' +
|
|
955
|
+
' at https://github.com/dean0x/devflow and tell me if\n' +
|
|
956
|
+
' it\'s safe."', 'Managed Settings');
|
|
957
|
+
const sudoChoice = await p.select({
|
|
958
|
+
message: 'Continue with managed settings?',
|
|
959
|
+
options: [
|
|
960
|
+
{ value: 'yes', label: 'Yes, continue', hint: 'May prompt for your password' },
|
|
961
|
+
{ value: 'no', label: 'No, fall back to settings.json', hint: 'Editable user settings instead' },
|
|
962
|
+
],
|
|
963
|
+
});
|
|
964
|
+
if (p.isCancel(sudoChoice)) {
|
|
965
|
+
p.cancel('Installation cancelled.');
|
|
966
|
+
process.exit(0);
|
|
967
|
+
}
|
|
968
|
+
managedSettingsConfirmed = sudoChoice === 'yes';
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
// ╭──────────────────────────────────────────────────────────╮
|
|
972
|
+
// │ All prompts collected — installation begins │
|
|
973
|
+
// ╰──────────────────────────────────────────────────────────╯
|
|
974
|
+
const s = p.spinner();
|
|
975
|
+
s.start('Resolving paths');
|
|
976
|
+
// Get installation paths
|
|
977
|
+
let claudeDir;
|
|
978
|
+
let devflowDir;
|
|
979
|
+
let gitRoot = null;
|
|
980
|
+
try {
|
|
981
|
+
const paths = await getInstallationPaths(scope);
|
|
982
|
+
claudeDir = paths.claudeDir;
|
|
983
|
+
devflowDir = paths.devflowDir;
|
|
984
|
+
gitRoot = paths.gitRoot ?? earlyGitRoot;
|
|
985
|
+
}
|
|
986
|
+
catch (error) {
|
|
987
|
+
s.stop('Path resolution failed');
|
|
988
|
+
p.log.error(`Path configuration error: ${error instanceof Error ? error.message : error}`);
|
|
989
|
+
process.exit(1);
|
|
990
|
+
}
|
|
991
|
+
// existingManifest was read early above (hoisted for seed computation); use it here for upgrade detection
|
|
992
|
+
if (existingManifest) {
|
|
993
|
+
const upgrade = detectUpgrade(version, existingManifest.version);
|
|
994
|
+
if (upgrade.isUpgrade) {
|
|
995
|
+
s.message(`Upgrading from v${upgrade.previousVersion} to v${version}`);
|
|
996
|
+
}
|
|
997
|
+
else if (upgrade.isSameVersion) {
|
|
998
|
+
s.message('Reinstalling same version');
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
// Detect current deny list state in user settings (read-only; write happens in security step)
|
|
1002
|
+
{
|
|
1003
|
+
const userSettingsJson = earlySettingsJson;
|
|
1004
|
+
let managedExists = false;
|
|
1005
|
+
let managedContentJson = null;
|
|
1006
|
+
try {
|
|
1007
|
+
const { getManagedSettingsPath: getMgdPath } = await import('../../targets/claude-code/claude-paths.js');
|
|
1008
|
+
const mgdPath = getMgdPath();
|
|
1009
|
+
managedContentJson = await fs.readFile(mgdPath, 'utf-8');
|
|
1010
|
+
managedExists = true;
|
|
1011
|
+
}
|
|
1012
|
+
catch { /* absent or unsupported platform */ }
|
|
1013
|
+
const detected = detectDenyState(userSettingsJson, managedExists, managedContentJson);
|
|
1014
|
+
const flagValue = options.security;
|
|
1015
|
+
const manifestMode = existingManifest?.features.security;
|
|
1016
|
+
const resolution = resolveSecurityAction(flagValue, manifestMode, detected, process.stdin.isTTY);
|
|
1017
|
+
if (resolution.warn) {
|
|
1018
|
+
p.log.warn(resolution.warn);
|
|
1019
|
+
}
|
|
1020
|
+
// In TTY + CONFLICT, prompt the user (the pure fn returned prompt descriptor)
|
|
1021
|
+
if (resolution.prompt && process.stdin.isTTY) {
|
|
1022
|
+
// Default: keep detected reality (the safe choice — don't remove protection silently)
|
|
1023
|
+
const keep = await p.confirm({ message: resolution.prompt, initialValue: true });
|
|
1024
|
+
if (p.isCancel(keep)) {
|
|
1025
|
+
p.cancel('Installation cancelled.');
|
|
1026
|
+
process.exit(0);
|
|
1027
|
+
}
|
|
1028
|
+
// If user declines to keep, switch to the manifest mode
|
|
1029
|
+
if (!keep && manifestMode !== undefined) {
|
|
1030
|
+
securityMode = manifestMode === 'none' ? 'none' : manifestMode;
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
securityMode = resolution.target === 'none' ? 'none' : resolution.target;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
else {
|
|
1037
|
+
securityMode = resolution.target === 'none' ? 'none' : resolution.target;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
// Validate target directory
|
|
1041
|
+
s.message('Validating target directory');
|
|
1042
|
+
if (scope === 'local') {
|
|
1043
|
+
try {
|
|
1044
|
+
await fs.mkdir(claudeDir, { recursive: true });
|
|
1045
|
+
}
|
|
1046
|
+
catch (error) {
|
|
1047
|
+
s.stop('Installation failed');
|
|
1048
|
+
p.log.error(`Failed to create ${claudeDir}: ${error}`);
|
|
1049
|
+
process.exit(1);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
try {
|
|
1054
|
+
await fs.access(claudeDir);
|
|
1055
|
+
}
|
|
1056
|
+
catch {
|
|
1057
|
+
s.stop('Installation failed');
|
|
1058
|
+
p.log.error(`Claude Code not detected at ${claudeDir}`);
|
|
1059
|
+
p.log.info('Install from: https://claude.ai/download');
|
|
1060
|
+
process.exit(1);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
// Resolve plugins and deduplication maps
|
|
1064
|
+
s.message('Installing components');
|
|
1065
|
+
const rootDir = getPackageRoot();
|
|
1066
|
+
let pluginsToInstall = selectedPlugins.length > 0
|
|
1067
|
+
? DEVFLOW_PLUGINS.filter(p => selectedPlugins.includes(p.name))
|
|
1068
|
+
: DEVFLOW_PLUGINS.filter(p => !p.optional);
|
|
1069
|
+
const coreSkillsPlugin = DEVFLOW_PLUGINS.find(p => p.name === 'devflow-core-skills');
|
|
1070
|
+
if (pluginsToInstall.length > 0 && coreSkillsPlugin && !pluginsToInstall.includes(coreSkillsPlugin)) {
|
|
1071
|
+
pluginsToInstall = [coreSkillsPlugin, ...pluginsToInstall];
|
|
1072
|
+
}
|
|
1073
|
+
const ambientPlugin = DEVFLOW_PLUGINS.find(p => p.name === 'devflow-ambient');
|
|
1074
|
+
if (ambientEnabled && ambientPlugin && !pluginsToInstall.includes(ambientPlugin)) {
|
|
1075
|
+
pluginsToInstall.push(ambientPlugin);
|
|
1076
|
+
}
|
|
1077
|
+
// Skills: install ALL from ALL plugins (skills are tiny markdown files;
|
|
1078
|
+
// commands need skills from other plugins to function)
|
|
1079
|
+
const skillsMap = buildFullSkillsMap();
|
|
1080
|
+
// Agents: install only from selected plugins
|
|
1081
|
+
const { agentsMap } = buildAssetMaps(pluginsToInstall);
|
|
1082
|
+
// Rules: install only from selected plugins (plugin-scoped, not universal)
|
|
1083
|
+
const rulesMap = rulesEnabled ? buildRulesMap(pluginsToInstall) : new Map();
|
|
1084
|
+
// D32/D35: Apply one-time migrations (global + per-project) tracked at ~/.devflow/migrations.json.
|
|
1085
|
+
// Migrations clean up ~/.devflow runtime data and never touch the installer's copy
|
|
1086
|
+
// targets, so their position relative to installViaFileCopy carries no dependency.
|
|
1087
|
+
// Migrations are always-run-unapplied: helpers short-circuit when the target data is
|
|
1088
|
+
// absent, so fresh installs are safe no-ops. State lives at the home-dir ~/.devflow
|
|
1089
|
+
// location regardless of install scope (D30).
|
|
1090
|
+
{
|
|
1091
|
+
const { runMigrations } = await import('../../core/migrations.js');
|
|
1092
|
+
const userDevflowDir = path.join(os.homedir(), '.devflow');
|
|
1093
|
+
await runMigrationsWithFallback(discoveredProjects, gitRoot, userDevflowDir, { warn: p.log.warn, info: p.log.info, success: p.log.success }, verbose, runMigrations);
|
|
1094
|
+
}
|
|
1095
|
+
// devflow-compliance was a selectable plugin in earlier releases; it is now a built-in
|
|
1096
|
+
// feature (devflow compliance --enable/--disable). If the prior manifest still lists it
|
|
1097
|
+
// as a plugin, emit a one-line notice so the user understands the sweep report and
|
|
1098
|
+
// knows the correct CLI going forward.
|
|
1099
|
+
if (existingManifest?.plugins?.includes('devflow-compliance')) {
|
|
1100
|
+
p.log.info('Compliance has moved from a plugin to a built-in feature — ' +
|
|
1101
|
+
'use `devflow compliance --enable/--disable` to manage it.');
|
|
1102
|
+
}
|
|
1103
|
+
// I41: probe the compliance rule target BEFORE installViaFileCopy wipes the rules dir.
|
|
1104
|
+
// On a full (non-partial) install, installViaFileCopy removes rules/devflow/ before
|
|
1105
|
+
// reinstalling, so by the time convergeComplianceArtifacts runs the rule file is already
|
|
1106
|
+
// gone. Probing here captures the pre-install state so the legacy-upgrade notice can fire
|
|
1107
|
+
// even when the only surviving artifact was the rule file.
|
|
1108
|
+
let hadComplianceRule = false;
|
|
1109
|
+
try {
|
|
1110
|
+
await fs.access(path.join(claudeDir, 'rules', 'devflow', 'compliance.md'));
|
|
1111
|
+
hadComplianceRule = true;
|
|
1112
|
+
}
|
|
1113
|
+
catch { /* absent — no legacy artifact */ }
|
|
1114
|
+
// Install via file copy
|
|
1115
|
+
let installReport;
|
|
1116
|
+
try {
|
|
1117
|
+
installReport = await installViaFileCopy({
|
|
1118
|
+
plugins: pluginsToInstall,
|
|
1119
|
+
claudeDir,
|
|
1120
|
+
devflowDir,
|
|
1121
|
+
skillsMap,
|
|
1122
|
+
agentsMap,
|
|
1123
|
+
rulesMap,
|
|
1124
|
+
isPartialInstall: !!options.plugin,
|
|
1125
|
+
spinner: s,
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
catch (error) {
|
|
1129
|
+
s.stop('Installation failed');
|
|
1130
|
+
p.log.error(`${error}`);
|
|
1131
|
+
process.exit(1);
|
|
1132
|
+
}
|
|
1133
|
+
// Converge compliance artifacts (PF-015: always converge, never short-circuit).
|
|
1134
|
+
// Called unconditionally so that enabling/disabling compliance during init
|
|
1135
|
+
// is reflected in the installed artifacts without a separate devflow compliance run.
|
|
1136
|
+
// Wrapped in its own try/catch (PF-009: warn-not-abort).
|
|
1137
|
+
let convergeResult = null;
|
|
1138
|
+
try {
|
|
1139
|
+
convergeResult = await convergeFromManifest({
|
|
1140
|
+
claudeDir,
|
|
1141
|
+
devflowDir,
|
|
1142
|
+
manifest: { features: { compliance: { enabled: complianceEnabled, frameworks: complianceFrameworks }, rules: rulesEnabled } },
|
|
1143
|
+
warn: (msg) => p.log.warn(msg),
|
|
1144
|
+
});
|
|
1145
|
+
// I41: emit legacy-upgrade notice when compliance is disabled AND pre-existing artifacts
|
|
1146
|
+
// were found. After I09, the skill dir survives the orphan sweep (knownNames now unions
|
|
1147
|
+
// FEATURE_OWNED_SKILLS), so convergeResult.removedPreexisting correctly fires for the
|
|
1148
|
+
// skill path. hadComplianceRule covers the rule path (wiped by installViaFileCopy before
|
|
1149
|
+
// converge probes on full installs).
|
|
1150
|
+
if (!complianceEnabled && (convergeResult.removedPreexisting || hadComplianceRule)) {
|
|
1151
|
+
p.log.info('Compliance artifacts removed — if you previously had devflow-compliance installed, ' +
|
|
1152
|
+
'run `devflow compliance --enable` to re-enable with your framework selection.');
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
catch (err) {
|
|
1156
|
+
p.log.warn(`Compliance artifact convergence failed — install succeeded but compliance artifacts may be stale: ` +
|
|
1157
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
1158
|
+
}
|
|
1159
|
+
// Clean up stale skills from previous installations
|
|
1160
|
+
s.message('Cleaning up');
|
|
1161
|
+
const skillsDir = path.join(claudeDir, 'skills');
|
|
1162
|
+
const skillRemoveResults = await Promise.allSettled(LEGACY_SKILL_NAMES.map(legacy => fs.rm(path.join(skillsDir, legacy), { recursive: true })));
|
|
1163
|
+
const staleRemoved = skillRemoveResults.filter(r => r.status === 'fulfilled').length;
|
|
1164
|
+
if (staleRemoved > 0 && verbose) {
|
|
1165
|
+
p.log.info(`Cleaned up ${staleRemoved} legacy skill(s)`);
|
|
1166
|
+
}
|
|
1167
|
+
// Clean up stale commands from previous installations (e.g., /review → /code-review)
|
|
1168
|
+
const commandsDir = path.join(claudeDir, 'commands', 'devflow');
|
|
1169
|
+
let staleCommandsRemoved = 0;
|
|
1170
|
+
for (const legacy of LEGACY_COMMAND_NAMES) {
|
|
1171
|
+
for (const suffix of ['.md']) {
|
|
1172
|
+
const legacyPath = path.join(commandsDir, `${legacy}${suffix}`);
|
|
1173
|
+
try {
|
|
1174
|
+
await fs.rm(legacyPath);
|
|
1175
|
+
staleCommandsRemoved++;
|
|
1176
|
+
}
|
|
1177
|
+
catch {
|
|
1178
|
+
// Doesn't exist — expected for most entries
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
// Sweep orphaned *-teams.md workflow command variants left by the Agent Teams
|
|
1183
|
+
// refactor. None are ever re-installed, so a blanket sweep is safe on any
|
|
1184
|
+
// install type (the full-install dir wipe only covers full installs). (PF-009)
|
|
1185
|
+
try {
|
|
1186
|
+
for (const f of await fs.readdir(commandsDir)) {
|
|
1187
|
+
if (f.endsWith('-teams.md')) {
|
|
1188
|
+
try {
|
|
1189
|
+
await fs.rm(path.join(commandsDir, f));
|
|
1190
|
+
staleCommandsRemoved++;
|
|
1191
|
+
}
|
|
1192
|
+
catch { /* already gone */ }
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
catch { /* commands dir absent — nothing to sweep */ }
|
|
1197
|
+
if (staleCommandsRemoved > 0 && verbose) {
|
|
1198
|
+
p.log.info(`Cleaned up ${staleCommandsRemoved} legacy command(s)`);
|
|
1199
|
+
}
|
|
1200
|
+
// Clean up stale rules from previous installations
|
|
1201
|
+
const rulesDir = path.join(claudeDir, 'rules', 'devflow');
|
|
1202
|
+
let staleRulesRemoved = 0;
|
|
1203
|
+
for (const legacy of LEGACY_RULE_NAMES) {
|
|
1204
|
+
const legacyPath = path.join(rulesDir, `${legacy}.md`);
|
|
1205
|
+
try {
|
|
1206
|
+
await fs.rm(legacyPath);
|
|
1207
|
+
staleRulesRemoved++;
|
|
1208
|
+
}
|
|
1209
|
+
catch {
|
|
1210
|
+
// Doesn't exist — expected for most entries
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
if (staleRulesRemoved > 0 && verbose) {
|
|
1214
|
+
p.log.info(`Cleaned up ${staleRulesRemoved} legacy rule(s)`);
|
|
1215
|
+
}
|
|
1216
|
+
// Disable rules directory if rules not enabled
|
|
1217
|
+
if (!rulesEnabled) {
|
|
1218
|
+
try {
|
|
1219
|
+
await fs.rm(path.join(claudeDir, 'rules', 'devflow'), { recursive: true, force: true });
|
|
1220
|
+
}
|
|
1221
|
+
catch { /* ignore */ }
|
|
1222
|
+
}
|
|
1223
|
+
// Clean up legacy hook scripts and lib files left by prior installs
|
|
1224
|
+
// (paths relative to hooksDir; copyDirectory is additive, so stale files
|
|
1225
|
+
// must be actively swept on init or they linger after upgrade)
|
|
1226
|
+
const LEGACY_HOOK_FILES = [
|
|
1227
|
+
'ambient-prompt',
|
|
1228
|
+
'session-start-classification',
|
|
1229
|
+
'session-end-kb-refresh',
|
|
1230
|
+
'background-kb-refresh',
|
|
1231
|
+
'lib/feature-kb.cjs',
|
|
1232
|
+
'background-learning',
|
|
1233
|
+
'prompt-capture-memory',
|
|
1234
|
+
'stop-update-memory',
|
|
1235
|
+
'stop-update-learning',
|
|
1236
|
+
'session-end-learning',
|
|
1237
|
+
'session-end-decisions',
|
|
1238
|
+
'session-end-knowledge-refresh',
|
|
1239
|
+
'background-knowledge-refresh',
|
|
1240
|
+
'eval-learning',
|
|
1241
|
+
'eval-reinforce',
|
|
1242
|
+
'dream-capture',
|
|
1243
|
+
'dream-dispatch',
|
|
1244
|
+
'dream-evaluate',
|
|
1245
|
+
'eval-helpers',
|
|
1246
|
+
'eval-decisions',
|
|
1247
|
+
'eval-curation',
|
|
1248
|
+
'dream-collect-tasks',
|
|
1249
|
+
'dream-recover',
|
|
1250
|
+
'lib/transcript-filter.cjs',
|
|
1251
|
+
'lib/dream-ops.cjs',
|
|
1252
|
+
'spawn-dream-worker',
|
|
1253
|
+
'background-dream-update',
|
|
1254
|
+
'dream-procedure.md',
|
|
1255
|
+
'lib/staleness.cjs',
|
|
1256
|
+
'dream-lock',
|
|
1257
|
+
];
|
|
1258
|
+
const hooksDir = path.join(devflowDir, 'scripts', 'hooks');
|
|
1259
|
+
for (const legacy of LEGACY_HOOK_FILES) {
|
|
1260
|
+
const legacyPath = path.join(hooksDir, legacy);
|
|
1261
|
+
try {
|
|
1262
|
+
await fs.rm(legacyPath);
|
|
1263
|
+
}
|
|
1264
|
+
catch { /* doesn't exist */ }
|
|
1265
|
+
}
|
|
1266
|
+
// === Settings & hooks (all automatic based on collected choices) ===
|
|
1267
|
+
s.message('Configuring settings');
|
|
1268
|
+
await installSettings(claudeDir, rootDir, devflowDir, verbose);
|
|
1269
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
1270
|
+
// === Proxy preflight (when enabled) ===
|
|
1271
|
+
// Runs before the settings mutation pass so that proxyEnabled reflects reality
|
|
1272
|
+
// (preflight failure forces it off without aborting init — avoids PF-009).
|
|
1273
|
+
//
|
|
1274
|
+
// Read existing proxy state once to recover the remembered port.
|
|
1275
|
+
// Init has no --port option; the remembered port from proxy.json always
|
|
1276
|
+
// wins over DEFAULT_PROXY_PORT (matching proxy.ts enable semantics).
|
|
1277
|
+
const priorProxyStateResult = await readProxyState(devflowDir);
|
|
1278
|
+
const effectivePort = priorProxyStateResult.ok
|
|
1279
|
+
? priorProxyStateResult.value.port
|
|
1280
|
+
: DEFAULT_PROXY_PORT;
|
|
1281
|
+
if (proxyEnabled) {
|
|
1282
|
+
const configPath = path.join(devflowDir, 'proxy-routing.json');
|
|
1283
|
+
const logPath = path.join(devflowDir, 'logs', 'proxy.log');
|
|
1284
|
+
const codexAuthPath = path.join(os.homedir(), '.codex', 'auth.json');
|
|
1285
|
+
// Write routing config (create logs dir non-fatally).
|
|
1286
|
+
// Read existing config first so user-added anthropic/limits/logLevel/providers
|
|
1287
|
+
// blocks are preserved; a missing file falls back cleanly inside buildRoutingConfigJson.
|
|
1288
|
+
let routingConfigWritten = false;
|
|
1289
|
+
try {
|
|
1290
|
+
// SEC-2: mode 0o700 for the logs directory (applies to new dirs only).
|
|
1291
|
+
await fs.mkdir(path.join(devflowDir, 'logs'), { recursive: true, mode: 0o700 });
|
|
1292
|
+
let existingRoutingContent;
|
|
1293
|
+
try {
|
|
1294
|
+
existingRoutingContent = await fs.readFile(configPath, 'utf-8');
|
|
1295
|
+
}
|
|
1296
|
+
catch { /* absent — fine */ }
|
|
1297
|
+
await fs.writeFile(configPath, buildRoutingConfigJson(effectivePort, existingRoutingContent), 'utf-8');
|
|
1298
|
+
routingConfigWritten = true;
|
|
1299
|
+
}
|
|
1300
|
+
catch (err) {
|
|
1301
|
+
p.log.warn(`External model routing: could not write routing config: ` +
|
|
1302
|
+
`${err instanceof Error ? err.message : err}. Routing disabled for this init.`);
|
|
1303
|
+
proxyEnabled = false;
|
|
1304
|
+
}
|
|
1305
|
+
if (routingConfigWritten) {
|
|
1306
|
+
// Deliberate difference from runEnable: init swallows settings.json read errors
|
|
1307
|
+
// (swallowSettingsReadError: true) because init creates settings.json itself
|
|
1308
|
+
// and must tolerate an absent file; runEnable propagates read errors to the user.
|
|
1309
|
+
const preflightResult = await runProxyPreflight(effectivePort, codexAuthPath, configPath, logPath, buildRealPreflightDeps({
|
|
1310
|
+
settingsPath,
|
|
1311
|
+
onWarn: (msg) => p.log.warn(msg),
|
|
1312
|
+
swallowSettingsReadError: true,
|
|
1313
|
+
}));
|
|
1314
|
+
if (!preflightResult.ok) {
|
|
1315
|
+
p.log.warn(`External model routing preflight failed: ${preflightResult.error}. ` +
|
|
1316
|
+
'Routing disabled for this init — run `devflow proxy --enable` after signing in.');
|
|
1317
|
+
proxyEnabled = false;
|
|
1318
|
+
// Write proxy.json disabled so runtime authority converges with the
|
|
1319
|
+
// failed preflight outcome (avoids PF-015). Without this write,
|
|
1320
|
+
// proxy.json stays enabled:true from the prior run, causing
|
|
1321
|
+
// reapplyAgentMapping to write GPT model IDs into agent frontmatter
|
|
1322
|
+
// with no relay — a dormancy inversion.
|
|
1323
|
+
if (priorProxyStateResult.ok && priorProxyStateResult.value.enabled) {
|
|
1324
|
+
const disableResult = await writeProxyState(devflowDir, buildProxyState({
|
|
1325
|
+
enabled: false,
|
|
1326
|
+
port: priorProxyStateResult.value.port,
|
|
1327
|
+
binPath: priorProxyStateResult.value.binPath,
|
|
1328
|
+
configPath: priorProxyStateResult.value.configPath,
|
|
1329
|
+
devflowVersion: priorProxyStateResult.value.devflowVersion,
|
|
1330
|
+
}));
|
|
1331
|
+
if (!disableResult.ok) {
|
|
1332
|
+
p.log.warn(`Could not persist proxy disabled state: ${disableResult.error}.`);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
else {
|
|
1337
|
+
// Write proxy.json enabled:true with freshly resolved binPath (heal path for upgrades)
|
|
1338
|
+
const writeResult = await writeProxyState(devflowDir, buildProxyState({
|
|
1339
|
+
enabled: true,
|
|
1340
|
+
port: effectivePort,
|
|
1341
|
+
binPath: preflightResult.value.binPath,
|
|
1342
|
+
configPath,
|
|
1343
|
+
devflowVersion: version,
|
|
1344
|
+
}));
|
|
1345
|
+
if (!writeResult.ok) {
|
|
1346
|
+
p.log.warn(`Could not persist proxy state: ${writeResult.error}. Routing disabled for this init.`);
|
|
1347
|
+
proxyEnabled = false;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
else {
|
|
1353
|
+
// Proxy disabled at entry: if proxy.json exists and is enabled, mark it disabled.
|
|
1354
|
+
// avoids PF-015: runtime authority (proxy.json) must converge with proxyEnabled=false.
|
|
1355
|
+
// Check the Result — write errors are surfaced rather than silently ignored
|
|
1356
|
+
// (consistent with the enable branch above).
|
|
1357
|
+
if (priorProxyStateResult.ok && priorProxyStateResult.value.enabled) {
|
|
1358
|
+
const disableResult = await writeProxyState(devflowDir, buildProxyState({
|
|
1359
|
+
enabled: false,
|
|
1360
|
+
port: priorProxyStateResult.value.port,
|
|
1361
|
+
binPath: priorProxyStateResult.value.binPath,
|
|
1362
|
+
configPath: priorProxyStateResult.value.configPath,
|
|
1363
|
+
devflowVersion: priorProxyStateResult.value.devflowVersion,
|
|
1364
|
+
}));
|
|
1365
|
+
if (!disableResult.ok) {
|
|
1366
|
+
p.log.warn(`Could not persist proxy disabled state: ${disableResult.error}.`);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
// Reapply agent model mapping after fresh file copy — installViaFileCopy writes shipped
|
|
1371
|
+
// defaults; this converges them back to the user's saved model/effort assignments.
|
|
1372
|
+
// MUST run AFTER the proxy preflight block above: preflight can force proxyEnabled=false
|
|
1373
|
+
// on failure, and reapply's dormancy (GPT models materialize only while proxy enabled)
|
|
1374
|
+
// depends on the FINAL proxyEnabled value — running earlier would leave GPT model lines
|
|
1375
|
+
// in agent frontmatter after a preflight failure. Per-item failures are non-fatal (avoids PF-009).
|
|
1376
|
+
//
|
|
1377
|
+
// Init-only optimization: skip reapply when mapping is empty AND proxy is off.
|
|
1378
|
+
// An empty mapping means every agent uses its shipped default; the file copy already
|
|
1379
|
+
// wrote those defaults, so reapply would read ~34 files and write zero. The
|
|
1380
|
+
// disable/revert paths call reapplyAgentMapping directly and always need the full walk.
|
|
1381
|
+
{
|
|
1382
|
+
const agentInstallDir = path.join(claudeDir, 'agents', 'devflow');
|
|
1383
|
+
const preCheckMapping = await readAgentMapping(devflowDir);
|
|
1384
|
+
const hasMappingEntries = preCheckMapping.ok && Object.keys(preCheckMapping.value.agents).length > 0;
|
|
1385
|
+
if (hasMappingEntries || proxyEnabled) {
|
|
1386
|
+
const reapplyResult = await reapplyAgentMapping({
|
|
1387
|
+
proxyEnabled,
|
|
1388
|
+
installDir: agentInstallDir,
|
|
1389
|
+
devflowDir,
|
|
1390
|
+
onWarning: (msg) => { if (verbose)
|
|
1391
|
+
p.log.warn(msg); },
|
|
1392
|
+
});
|
|
1393
|
+
if (reapplyResult.updated.length > 0) {
|
|
1394
|
+
if (verbose) {
|
|
1395
|
+
p.log.info(`Agent model mapping reapplied: ${reapplyResult.updated.length} agent(s) updated`);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
// Configure ambient hook, memory hooks, and HUD statusLine in a single read-modify-write pass
|
|
1401
|
+
try {
|
|
1402
|
+
let content = await fs.readFile(settingsPath, 'utf-8');
|
|
1403
|
+
const original = content;
|
|
1404
|
+
// Ambient hook — always remove-then-add to upgrade from legacy ambient-prompt → preamble
|
|
1405
|
+
const cleanedForAmbient = await removeAmbientHook(content);
|
|
1406
|
+
content = ambientEnabled ? await addAmbientHook(cleanedForAmbient, devflowDir) : cleanedForAmbient;
|
|
1407
|
+
// Capture hooks — always-on (like the context hook below), remove-then-add for
|
|
1408
|
+
// upgrade safety. Queue-append only (capture-prompt/capture-turn/capture-question);
|
|
1409
|
+
// each script gates its own per-queue write internally via feature config, so there
|
|
1410
|
+
// is no CLI-level enable/disable toggle here. MUST run before addMemoryHooks below
|
|
1411
|
+
// so capture-turn lands before memory-worker in the Stop array (AC-C2 ordering:
|
|
1412
|
+
// append-before-spawn).
|
|
1413
|
+
const cleanedForCapture = removeCaptureHooks(content);
|
|
1414
|
+
content = addCaptureHooks(cleanedForCapture, devflowDir);
|
|
1415
|
+
// Memory hooks — always remove-then-add to upgrade hook format (e.g., .sh → run-hook).
|
|
1416
|
+
// Three hooks: Stop (memory-worker), SessionStart (session-start-memory), PreCompact.
|
|
1417
|
+
// Learning agent (spawned via session-start-context directive) handles decision/pitfall
|
|
1418
|
+
// detection. Knowledge is handled in-command via write-through (knowledge_writeback MDS partial).
|
|
1419
|
+
const cleaned = removeMemoryHooks(content);
|
|
1420
|
+
content = memoryEnabled ? addMemoryHooks(cleaned, devflowDir) : cleaned;
|
|
1421
|
+
// HUD statusLine
|
|
1422
|
+
content = hudEnabled
|
|
1423
|
+
? addHudStatusLine(content, devflowDir)
|
|
1424
|
+
: removeHudStatusLine(content);
|
|
1425
|
+
// Context hook — always-on, remove-then-add for upgrade safety
|
|
1426
|
+
const cleanedForContext = removeContextHook(content);
|
|
1427
|
+
content = addContextHook(cleanedForContext, devflowDir);
|
|
1428
|
+
// Legacy dream-worker hook cleanup — strip any stale spawn-dream-worker entry
|
|
1429
|
+
// left in settings.json by a prior install (session-start-context now spawns
|
|
1430
|
+
// the Learning agent via directive).
|
|
1431
|
+
content = removeDreamHook(content);
|
|
1432
|
+
// Strip Devflow-managed teammateMode ("auto"). User-set values (e.g. "tmux") are preserved.
|
|
1433
|
+
content = stripDevflowTeammateModeFromJson(content);
|
|
1434
|
+
// Claude Code flags — strip all managed keys, then re-apply selected flags
|
|
1435
|
+
content = stripFlags(content);
|
|
1436
|
+
content = applyFlags(content, enabledFlags);
|
|
1437
|
+
// Resolve the final viewMode to write.
|
|
1438
|
+
// - explicit=true (interactive selection or --reset): selected value always wins
|
|
1439
|
+
// - explicit=false (recommended/non-TTY): preserve an externally-set /focus value;
|
|
1440
|
+
// otherwise use the seeded viewMode (which already reflects the prior manifest value)
|
|
1441
|
+
viewMode = resolveFinalViewMode(resolveExistingViewMode(content), viewMode, viewModeExplicit);
|
|
1442
|
+
// View mode — strip then apply for upgrade safety
|
|
1443
|
+
content = stripViewMode(content);
|
|
1444
|
+
content = applyViewMode(content, viewMode);
|
|
1445
|
+
// Proxy hooks (SessionStart + UserPromptSubmit) — strip-then-add, idempotent.
|
|
1446
|
+
// Parse Settings once for the hook mutation; env mutation stays in string space.
|
|
1447
|
+
{
|
|
1448
|
+
const parsedSettings = JSON.parse(content);
|
|
1449
|
+
removeProxyHooks(parsedSettings);
|
|
1450
|
+
if (proxyEnabled)
|
|
1451
|
+
addProxyHooks(parsedSettings, devflowDir);
|
|
1452
|
+
content = JSON.stringify(parsedSettings, null, 2) + '\n';
|
|
1453
|
+
}
|
|
1454
|
+
// Proxy env: ANTHROPIC_BASE_URL strip-then-add, scoped to managed port.
|
|
1455
|
+
// Read proxy.json to learn which port we own — only that URL is stripped.
|
|
1456
|
+
// A user's own localhost gateway on any other port is preserved.
|
|
1457
|
+
// Invariant: proxy.json always reflects the final settled state after the
|
|
1458
|
+
// preflight block above — all paths that force proxyEnabled=false also write
|
|
1459
|
+
// proxy.json enabled:false (avoids PF-015), so managedPort == effectivePort.
|
|
1460
|
+
const proxyStateForStrip = await readProxyState(devflowDir);
|
|
1461
|
+
const managedPort = proxyStateForStrip.ok ? proxyStateForStrip.value.port : DEFAULT_PROXY_PORT;
|
|
1462
|
+
content = stripProxyEnv(content, managedPort);
|
|
1463
|
+
if (proxyEnabled)
|
|
1464
|
+
content = applyProxyEnv(content, effectivePort);
|
|
1465
|
+
if (content !== original) {
|
|
1466
|
+
await fs.writeFile(settingsPath, content, 'utf-8');
|
|
1467
|
+
if (verbose) {
|
|
1468
|
+
if (ambientEnabled)
|
|
1469
|
+
p.log.success('Ambient mode hook installed');
|
|
1470
|
+
p.log.info(`Working memory ${memoryEnabled ? 'enabled' : 'disabled'}`);
|
|
1471
|
+
p.log.info(`HUD ${hudEnabled ? 'enabled' : 'disabled'}`);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
catch (err) {
|
|
1476
|
+
// settings.json write failed — warn but do not abort. The manifest records the
|
|
1477
|
+
// intended state; the user can re-run devflow init to retry the settings write.
|
|
1478
|
+
p.log.warn(`Could not configure settings.json: ${err instanceof Error ? err.message : err}. ` +
|
|
1479
|
+
'Manifest records intended state; run devflow init again to retry.');
|
|
1480
|
+
}
|
|
1481
|
+
// Write .devflow/config.json to manage per-feature enable/disable at runtime.
|
|
1482
|
+
// Uses writeConfig (full atomic write) rather than three updateFeature calls because
|
|
1483
|
+
// init always sets all three features at once and is never concurrent with toggle
|
|
1484
|
+
// commands — it is a one-time setup action. See D1 in feature-config.ts for the
|
|
1485
|
+
// concurrency assumption shared by both write strategies.
|
|
1486
|
+
if (gitRoot) {
|
|
1487
|
+
await writeConfig(gitRoot, {
|
|
1488
|
+
memory: memoryEnabled,
|
|
1489
|
+
learning: learningEnabled,
|
|
1490
|
+
knowledge: knowledgeEnabled,
|
|
1491
|
+
// reviewPublication has no prompt, so it is carried over from the
|
|
1492
|
+
// reset-gated snapshot rather than re-read from disk: seedConfig is null
|
|
1493
|
+
// under --reset, which is what collapses the field back to 'auto' with
|
|
1494
|
+
// every other feature (PF-015 — read the post-gate binding, not the file).
|
|
1495
|
+
reviewPublication: seedConfig?.reviewPublication ?? 'auto',
|
|
1496
|
+
});
|
|
1497
|
+
// Drain orphaned queue files when memory is disabled so stale turns
|
|
1498
|
+
// don't process on a future re-enable. Mirrors memory.ts --disable drain.
|
|
1499
|
+
if (!memoryEnabled) {
|
|
1500
|
+
await Promise.all([
|
|
1501
|
+
fs.unlink(getPendingTurnsPath(gitRoot)).catch((e) => { if (e.code !== 'ENOENT')
|
|
1502
|
+
throw e; }),
|
|
1503
|
+
fs.unlink(getPendingTurnsProcessingPath(gitRoot)).catch((e) => { if (e.code !== 'ENOENT')
|
|
1504
|
+
throw e; }),
|
|
1505
|
+
]);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
// Configure HUD
|
|
1509
|
+
const existingHud = loadHudConfig();
|
|
1510
|
+
saveHudConfig({ enabled: hudEnabled, detail: existingHud.detail });
|
|
1511
|
+
// File extras
|
|
1512
|
+
if (claudeignoreEnabled) {
|
|
1513
|
+
if (scope === 'user' && discoveredProjects.length > 0) {
|
|
1514
|
+
const results = await Promise.all(discoveredProjects.map(root => installClaudeignore(root, rootDir, verbose)));
|
|
1515
|
+
const created = results.filter(Boolean).length;
|
|
1516
|
+
if (created > 0) {
|
|
1517
|
+
p.log.success(`.claudeignore created in ${created} project(s)`);
|
|
1518
|
+
}
|
|
1519
|
+
else {
|
|
1520
|
+
p.log.info(`.claudeignore already exists in all ${discoveredProjects.length} project(s)`);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
else if (gitRoot) {
|
|
1524
|
+
await installClaudeignore(gitRoot, rootDir, verbose);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
// Deterministically ensure .devflow/ is gitignored at the repo root — independent
|
|
1528
|
+
// of install scope and every feature toggle. The always-on ensure-root-gitignore
|
|
1529
|
+
// hook covers projects that never re-run init; this covers the init-time path so a
|
|
1530
|
+
// fresh install never tracks .devflow/. Decoupled from memory (avoids PF-014).
|
|
1531
|
+
if (gitRoot) {
|
|
1532
|
+
await ensureDevflowGitignore(gitRoot, verbose);
|
|
1533
|
+
}
|
|
1534
|
+
if (scope === 'local' && gitRoot) {
|
|
1535
|
+
await updateGitignore(gitRoot, verbose);
|
|
1536
|
+
}
|
|
1537
|
+
if (scope === 'local') {
|
|
1538
|
+
await createDocsStructure(verbose);
|
|
1539
|
+
}
|
|
1540
|
+
// Safe-delete execution (decision was captured during prompt phase)
|
|
1541
|
+
if (safeDeleteAction === 'install' && safeDeleteBlock && profilePath) {
|
|
1542
|
+
await installToProfile(profilePath, safeDeleteBlock);
|
|
1543
|
+
}
|
|
1544
|
+
else if (safeDeleteAction === 'upgrade' && safeDeleteBlock && profilePath) {
|
|
1545
|
+
await removeFromProfile(profilePath);
|
|
1546
|
+
await installToProfile(profilePath, safeDeleteBlock);
|
|
1547
|
+
}
|
|
1548
|
+
// ── Dedicated security step (always targets ~/.claude/settings.json for user mode) ──
|
|
1549
|
+
// Runs AFTER managed-install so effective mode is known.
|
|
1550
|
+
// Reads the current template deny list, asserts historical superset at install time.
|
|
1551
|
+
{
|
|
1552
|
+
const userSettingsPath = path.join(claudeDir, 'settings.json');
|
|
1553
|
+
// Use canonical loadTemplateDenyEntries (avoids duplicating parse logic here).
|
|
1554
|
+
const templateDeny = await loadTemplateDenyEntries(rootDir);
|
|
1555
|
+
if (templateDeny.length > 0) {
|
|
1556
|
+
// Catch any drift where a new template entry was not added to DEVFLOW_HISTORICAL_DENY
|
|
1557
|
+
try {
|
|
1558
|
+
assertHistoricalDenySuperset(templateDeny);
|
|
1559
|
+
}
|
|
1560
|
+
catch (e) {
|
|
1561
|
+
p.log.warn(`Security template drift: ${e instanceof Error ? e.message : e}`);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
else if (verbose) {
|
|
1565
|
+
p.log.warn('Could not read managed-settings template; deny list unchanged');
|
|
1566
|
+
}
|
|
1567
|
+
if (securityMode === 'managed') {
|
|
1568
|
+
if (managedSettingsConfirmed) {
|
|
1569
|
+
// Managed path: attempt sudo write, fall back to user on failure
|
|
1570
|
+
s.stop('Configuring managed settings (may prompt for sudo password)...');
|
|
1571
|
+
const managed = await installManagedSettings(rootDir, verbose);
|
|
1572
|
+
if (!managed) {
|
|
1573
|
+
// Real fallback: actually write to user settings (not just a warning)
|
|
1574
|
+
p.log.warn('Managed settings write failed — deny list written to user settings instead');
|
|
1575
|
+
try {
|
|
1576
|
+
await applyUserSecurityDenyList(userSettingsPath, templateDeny);
|
|
1577
|
+
securityMode = 'user'; // update so manifest reflects reality
|
|
1578
|
+
if (verbose)
|
|
1579
|
+
p.log.success('Security deny list written to ~/.claude/settings.json (fallback)');
|
|
1580
|
+
}
|
|
1581
|
+
catch (e) {
|
|
1582
|
+
p.log.warn(`Could not write deny list to user settings either: ${e instanceof Error ? e.message : e}`);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
// Managed write succeeded — strip from user settings to avoid duplication.
|
|
1587
|
+
// Uses the canonical helper (atomic temp+rename; ENOENT-safe; only-write-if-changed).
|
|
1588
|
+
const stripResult = await stripUserSecurityDenyList(userSettingsPath);
|
|
1589
|
+
if (stripResult && verbose)
|
|
1590
|
+
p.log.info('Removed deny list from user settings (now in managed settings)');
|
|
1591
|
+
}
|
|
1592
|
+
s.start('Finalizing installation...');
|
|
1593
|
+
}
|
|
1594
|
+
else {
|
|
1595
|
+
// applies ADR-010: user declined sudo and chose the settings.json fallback.
|
|
1596
|
+
// securityMode is 'managed' (from resolveSecurityAction or interactive choice) but
|
|
1597
|
+
// managedSettingsConfirmed is false — honor the "fall back to settings.json" label
|
|
1598
|
+
// by writing to user settings. Manifest will record 'user' to match reality.
|
|
1599
|
+
if (templateDeny.length > 0) {
|
|
1600
|
+
try {
|
|
1601
|
+
await applyUserSecurityDenyList(userSettingsPath, templateDeny);
|
|
1602
|
+
securityMode = 'user'; // manifest must reflect where the deny list actually landed
|
|
1603
|
+
if (verbose)
|
|
1604
|
+
p.log.success('Security deny list written to ~/.claude/settings.json (declined managed)');
|
|
1605
|
+
}
|
|
1606
|
+
catch (e) {
|
|
1607
|
+
p.log.warn(`Could not write deny list to user settings: ${e instanceof Error ? e.message : e}`);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
else if (securityMode === 'user') {
|
|
1613
|
+
// User mode (default): merge deny list into ~/.claude/settings.json
|
|
1614
|
+
if (templateDeny.length > 0) {
|
|
1615
|
+
try {
|
|
1616
|
+
await applyUserSecurityDenyList(userSettingsPath, templateDeny);
|
|
1617
|
+
if (verbose)
|
|
1618
|
+
p.log.success('Security deny list applied to ~/.claude/settings.json');
|
|
1619
|
+
}
|
|
1620
|
+
catch (e) {
|
|
1621
|
+
if (verbose)
|
|
1622
|
+
p.log.warn(`Could not apply security deny list: ${e instanceof Error ? e.message : e}`);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
else if (securityMode === 'none') {
|
|
1627
|
+
// None: strip Devflow deny entries from user settings.
|
|
1628
|
+
// Uses the canonical helper (atomic temp+rename; ENOENT-safe; only-write-if-changed).
|
|
1629
|
+
const stripResult = await stripUserSecurityDenyList(userSettingsPath);
|
|
1630
|
+
if (stripResult && verbose)
|
|
1631
|
+
p.log.info(`Security deny list removed (${stripResult.removed.length} entries stripped)`);
|
|
1632
|
+
}
|
|
1633
|
+
else {
|
|
1634
|
+
// Exhaustive guard — if TypeScript reaches here, a new SecurityMode variant was added
|
|
1635
|
+
// without a matching branch. avoids PF-009 (stale references after rename/refactor).
|
|
1636
|
+
const _exhaustive = securityMode;
|
|
1637
|
+
void _exhaustive;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
s.stop('Installation complete');
|
|
1641
|
+
// Check for jq (hooks degrade gracefully without it, but features are reduced)
|
|
1642
|
+
try {
|
|
1643
|
+
execSync('command -v jq', { stdio: 'ignore' });
|
|
1644
|
+
}
|
|
1645
|
+
catch {
|
|
1646
|
+
p.log.warn('jq not found — some hook features will have reduced functionality');
|
|
1647
|
+
p.log.info(`Install: ${color.cyan('brew install jq')}`);
|
|
1648
|
+
}
|
|
1649
|
+
// === Summary ===
|
|
1650
|
+
// Shadow override reporting
|
|
1651
|
+
const totalShadowed = installReport.shadowedSkills.length + installReport.shadowedRules.length;
|
|
1652
|
+
if (totalShadowed > 0) {
|
|
1653
|
+
const parts = [
|
|
1654
|
+
...installReport.shadowedSkills.map(s => `skill:${s}`),
|
|
1655
|
+
...installReport.shadowedRules.map(r => `rule:${r}`),
|
|
1656
|
+
];
|
|
1657
|
+
p.log.info(`Applied ${totalShadowed} shadow override(s): ${parts.join(', ')}`);
|
|
1658
|
+
}
|
|
1659
|
+
for (const skip of installReport.skippedShadows) {
|
|
1660
|
+
let reasonMsg;
|
|
1661
|
+
switch (skip.reason) {
|
|
1662
|
+
case 'missing-skill-md':
|
|
1663
|
+
reasonMsg = 'shadow directory has no valid SKILL.md';
|
|
1664
|
+
break;
|
|
1665
|
+
case 'empty-shadow-file':
|
|
1666
|
+
reasonMsg = 'shadow file is empty';
|
|
1667
|
+
break;
|
|
1668
|
+
case 'not-a-file':
|
|
1669
|
+
reasonMsg = 'shadow path is not a file';
|
|
1670
|
+
break;
|
|
1671
|
+
default: {
|
|
1672
|
+
const _exhaustive = skip.reason;
|
|
1673
|
+
void _exhaustive;
|
|
1674
|
+
reasonMsg = 'unknown skip reason';
|
|
1675
|
+
break;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
p.log.warn(`Shadow for ${skip.kind}:${skip.name} skipped (${reasonMsg}) — Devflow's version was installed`);
|
|
1679
|
+
}
|
|
1680
|
+
// Orphan-sweep reporting: removals are silent deletions from ~/.claude/, and a
|
|
1681
|
+
// failed removal leaves a retired asset live. Both must surface.
|
|
1682
|
+
// After I09, the installer's knownNames set unions FEATURE_OWNED_SKILLS, so
|
|
1683
|
+
// devflow:compliance is never swept here — no suppression predicate is needed.
|
|
1684
|
+
for (const line of formatSweepSummary(installReport)) {
|
|
1685
|
+
if (line.level === 'warn')
|
|
1686
|
+
p.log.warn(line.message);
|
|
1687
|
+
else
|
|
1688
|
+
p.log.info(line.message);
|
|
1689
|
+
}
|
|
1690
|
+
const installedSet = new Set(pluginsToInstall.flatMap(p => p.commands).filter(c => c.length > 0));
|
|
1691
|
+
const orderedCommands = WORKFLOW_ORDER.filter(cmd => installedSet.has(cmd));
|
|
1692
|
+
if (orderedCommands.length > 0) {
|
|
1693
|
+
const commandsNote = orderedCommands
|
|
1694
|
+
.map(cmd => color.cyan(cmd))
|
|
1695
|
+
.join('\n');
|
|
1696
|
+
p.note(commandsNote, 'Available commands');
|
|
1697
|
+
}
|
|
1698
|
+
// Safe-delete status messages (after spinner)
|
|
1699
|
+
if (process.stdin.isTTY && profilePath) {
|
|
1700
|
+
if (safeDeleteAction === 'install') {
|
|
1701
|
+
p.log.success(`Safe-delete installed to ${color.dim(profilePath)}`);
|
|
1702
|
+
p.log.info('Restart your shell or run: ' + color.cyan(`source ${profilePath}`));
|
|
1703
|
+
}
|
|
1704
|
+
else if (safeDeleteAction === 'upgrade') {
|
|
1705
|
+
p.log.success(`Safe-delete upgraded in ${color.dim(profilePath)}`);
|
|
1706
|
+
p.log.info('Restart your shell or run: ' + color.cyan(`source ${profilePath}`));
|
|
1707
|
+
}
|
|
1708
|
+
else if (safeDeleteAvailable && safeDeleteBlock) {
|
|
1709
|
+
const installedVersion = await getInstalledVersion(profilePath);
|
|
1710
|
+
if (classifySafeDeleteState(installedVersion, SAFE_DELETE_BLOCK_VERSION) === 'current') {
|
|
1711
|
+
p.log.info(`Safe-delete already configured in ${color.dim(profilePath)}`);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
else if (!safeDeleteAvailable && safeDeleteInfo.installHint) {
|
|
1715
|
+
p.log.info(`Install ${color.cyan(safeDeleteInfo.command ?? 'trash')} first: ${color.dim(safeDeleteInfo.installHint)}`);
|
|
1716
|
+
p.log.info(`Then re-run ${color.cyan('devflow init')} to auto-configure safe-delete.`);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
else if (!process.stdin.isTTY) {
|
|
1720
|
+
if (safeDeleteAvailable && safeDeleteInfo.command) {
|
|
1721
|
+
p.log.info(`Safe-delete available (${safeDeleteInfo.command}). Run interactively to auto-install.`);
|
|
1722
|
+
}
|
|
1723
|
+
else if (safeDeleteInfo.installHint) {
|
|
1724
|
+
p.log.info(`Protect against accidental ${color.red('rm -rf')}: ${color.cyan(safeDeleteInfo.installHint)}`);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
// Verbose mode: show details
|
|
1728
|
+
if (verbose) {
|
|
1729
|
+
const pluginsList = pluginsToInstall
|
|
1730
|
+
.map(plugin => `${color.yellow(plugin.name.padEnd(24))}${color.dim(plugin.description)}`)
|
|
1731
|
+
.join('\n');
|
|
1732
|
+
p.note(pluginsList, 'Installed plugins');
|
|
1733
|
+
p.log.info(`Scope: ${scope}`);
|
|
1734
|
+
p.log.info(`Claude dir: ${claudeDir}`);
|
|
1735
|
+
p.log.info(`Devflow dir: ${devflowDir}`);
|
|
1736
|
+
const totalSkillDeclarations = pluginsToInstall.reduce((sum, p) => sum + p.skills.length, 0);
|
|
1737
|
+
const totalAgentDeclarations = pluginsToInstall.reduce((sum, p) => sum + p.agents.length, 0);
|
|
1738
|
+
p.log.info(`Deduplication: ${skillsMap.size} unique skills (from ${totalSkillDeclarations} declarations)`);
|
|
1739
|
+
p.log.info(`Deduplication: ${agentsMap.size} unique agents (from ${totalAgentDeclarations} declarations)`);
|
|
1740
|
+
}
|
|
1741
|
+
// Write installation manifest for upgrade tracking (non-fatal — install already succeeded)
|
|
1742
|
+
const installedPluginNames = pluginsToInstall.map(pl => pl.name);
|
|
1743
|
+
const now = new Date().toISOString();
|
|
1744
|
+
const manifestData = {
|
|
1745
|
+
version,
|
|
1746
|
+
plugins: resolvePluginList(installedPluginNames, existingManifest, !!options.plugin),
|
|
1747
|
+
scope,
|
|
1748
|
+
// Snapshot of known plugin names at this install — used by resolveSeedPlugins on next init
|
|
1749
|
+
// to detect new non-optional plugins and auto-adopt them.
|
|
1750
|
+
knownPlugins: DEVFLOW_PLUGINS.map(p => p.name),
|
|
1751
|
+
features: {
|
|
1752
|
+
ambient: ambientEnabled,
|
|
1753
|
+
memory: memoryEnabled,
|
|
1754
|
+
hud: hudEnabled,
|
|
1755
|
+
knowledge: knowledgeEnabled,
|
|
1756
|
+
learning: learningEnabled,
|
|
1757
|
+
rules: rulesEnabled,
|
|
1758
|
+
flags: enabledFlags,
|
|
1759
|
+
// Snapshot of known flag ids at this install — used by resolveSeedFlags on next init
|
|
1760
|
+
// to detect new default-ON flags and auto-adopt them.
|
|
1761
|
+
knownFlags: FLAG_REGISTRY.map(f => f.id),
|
|
1762
|
+
viewMode,
|
|
1763
|
+
security: securityMode,
|
|
1764
|
+
// Final resolved value — may be forced off by preflight failure.
|
|
1765
|
+
proxy: proxyEnabled,
|
|
1766
|
+
// Resolved compliance state — seeded from prior manifest, overridden by CLI flags
|
|
1767
|
+
// and Advanced wizard selection. convergeComplianceArtifacts was called above.
|
|
1768
|
+
// normalizeFrameworks: dedup + filter unknowns before persisting.
|
|
1769
|
+
compliance: { enabled: complianceEnabled, frameworks: normalizeFrameworks(complianceFrameworks) },
|
|
1770
|
+
},
|
|
1771
|
+
installedAt: existingManifest?.installedAt ?? now,
|
|
1772
|
+
updatedAt: now,
|
|
1773
|
+
};
|
|
1774
|
+
try {
|
|
1775
|
+
await writeManifest(devflowDir, manifestData);
|
|
1776
|
+
}
|
|
1777
|
+
catch (error) {
|
|
1778
|
+
p.log.warn(`Failed to write installation manifest (install succeeded): ${error instanceof Error ? error.message : error}`);
|
|
1779
|
+
}
|
|
1780
|
+
// External model routing status line (Advanced path / explicit --proxy flag only)
|
|
1781
|
+
if (proxyEnabled) {
|
|
1782
|
+
p.log.info(`External model routing: ${color.green('enabled')} — takes effect in new Claude Code sessions`);
|
|
1783
|
+
}
|
|
1784
|
+
p.outro(color.green('Ready! Run any command in Claude Code to get started.'));
|
|
1785
|
+
});
|
|
1786
|
+
//# sourceMappingURL=init.js.map
|