@event4u/agent-config 6.0.0 → 7.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/.claude-plugin/marketplace.json +39 -7
- package/AGENTS.md +8 -7
- package/CHANGELOG.md +557 -422
- package/CONTRIBUTING.md +1 -1
- package/README.md +18 -16
- package/dist/agent-src/commands/agent-handoff.md +5 -4
- package/dist/agent-src/commands/agent-status.md +3 -2
- package/dist/agent-src/commands/agents/audit.md +4 -3
- package/dist/agent-src/commands/agents/init.md +4 -1
- package/dist/agent-src/commands/agents/optimize.md +5 -4
- package/dist/agent-src/commands/agents/user/accept.md +1 -0
- package/dist/agent-src/commands/agents/user/init.md +1 -0
- package/dist/agent-src/commands/agents/user/review.md +1 -0
- package/dist/agent-src/commands/agents/user/show.md +1 -0
- package/dist/agent-src/commands/agents/user/update.md +1 -0
- package/dist/agent-src/commands/agents/user.md +1 -0
- package/dist/agent-src/commands/agents.md +1 -0
- package/dist/agent-src/commands/analytics/prune.md +3 -2
- package/dist/agent-src/commands/analytics/show.md +3 -2
- package/dist/agent-src/commands/analytics.md +3 -2
- package/dist/agent-src/commands/analyze/decision.md +108 -0
- package/dist/agent-src/commands/analyze/incident.md +120 -0
- package/dist/agent-src/commands/analyze/near-miss.md +113 -0
- package/dist/agent-src/commands/analyze/postmortem.md +130 -0
- package/dist/agent-src/commands/analyze/premortem.md +104 -0
- package/dist/agent-src/commands/analyze-reference-repo.md +1 -0
- package/dist/agent-src/commands/analyze.md +124 -0
- package/dist/agent-src/commands/brand/identity.md +27 -0
- package/dist/agent-src/commands/brand/review.md +27 -0
- package/dist/agent-src/commands/brand/strategy.md +27 -0
- package/dist/agent-src/commands/brand/tokens.md +28 -0
- package/dist/agent-src/commands/brand/voice.md +27 -0
- package/dist/agent-src/commands/brand.md +58 -0
- package/dist/agent-src/commands/bug-fix.md +1 -0
- package/dist/agent-src/commands/bug-investigate.md +1 -0
- package/dist/agent-src/commands/challenge-me/vision.md +3 -2
- package/dist/agent-src/commands/challenge-me/with-docs.md +3 -2
- package/dist/agent-src/commands/challenge-me.md +3 -2
- package/dist/agent-src/commands/chat-history/import.md +9 -9
- package/dist/agent-src/commands/chat-history.md +32 -30
- package/dist/agent-src/commands/check-current-md.md +4 -3
- package/dist/agent-src/commands/commit/in-chunks.md +1 -0
- package/dist/agent-src/commands/commit.md +1 -0
- package/dist/agent-src/commands/condense.md +3 -2
- package/dist/agent-src/commands/context/create.md +1 -0
- package/dist/agent-src/commands/context/refactor.md +1 -0
- package/dist/agent-src/commands/context.md +1 -0
- package/dist/agent-src/commands/cost-report.md +5 -4
- package/dist/agent-src/commands/council/analysis.md +3 -2
- package/dist/agent-src/commands/council/debate.md +7 -6
- package/dist/agent-src/commands/council/default.md +48 -20
- package/dist/agent-src/commands/council/design.md +3 -2
- package/dist/agent-src/commands/council/optimize.md +3 -2
- package/dist/agent-src/commands/council/pr.md +3 -2
- package/dist/agent-src/commands/council.md +4 -3
- package/dist/agent-src/commands/e2e-heal.md +1 -0
- package/dist/agent-src/commands/e2e-plan.md +1 -0
- package/dist/agent-src/commands/estimate-ticket.md +1 -0
- package/dist/agent-src/commands/feature/dev.md +1 -0
- package/dist/agent-src/commands/feature/explore.md +1 -0
- package/dist/agent-src/commands/feature/plan.md +6 -6
- package/dist/agent-src/commands/feature/refactor.md +1 -0
- package/dist/agent-src/commands/feature/roadmap.md +1 -0
- package/dist/agent-src/commands/feature.md +1 -0
- package/dist/agent-src/commands/fix/ci.md +1 -0
- package/dist/agent-src/commands/fix/portability.md +4 -3
- package/dist/agent-src/commands/fix/pr-comments.md +147 -15
- package/dist/agent-src/commands/fix/refs.md +4 -3
- package/dist/agent-src/commands/fix/seeder.md +1 -0
- package/dist/agent-src/commands/fix.md +8 -8
- package/dist/agent-src/commands/ghostwriter/delete.md +1 -0
- package/dist/agent-src/commands/ghostwriter/fetch.md +1 -0
- package/dist/agent-src/commands/ghostwriter/list.md +1 -0
- package/dist/agent-src/commands/ghostwriter/show.md +1 -0
- package/dist/agent-src/commands/ghostwriter/write.md +1 -0
- package/dist/agent-src/commands/ghostwriter.md +1 -0
- package/dist/agent-src/commands/grill-me.md +3 -2
- package/dist/agent-src/commands/image/analyse.md +1 -0
- package/dist/agent-src/commands/image/create.md +1 -0
- package/dist/agent-src/commands/image/verify.md +1 -0
- package/dist/agent-src/commands/image.md +1 -0
- package/dist/agent-src/commands/implement-ticket.md +37 -6
- package/dist/agent-src/commands/jira-ticket.md +1 -0
- package/dist/agent-src/commands/judge/on-diff.md +1 -0
- package/dist/agent-src/commands/judge/solo.md +1 -0
- package/dist/agent-src/commands/judge/steps.md +1 -0
- package/dist/agent-src/commands/judge.md +1 -0
- package/dist/agent-src/commands/knowledge/cross-repo.md +2 -1
- package/dist/agent-src/commands/knowledge/forget.md +1 -0
- package/dist/agent-src/commands/knowledge/ingest.md +1 -0
- package/dist/agent-src/commands/knowledge/list.md +1 -0
- package/dist/agent-src/commands/knowledge.md +1 -0
- package/dist/agent-src/commands/memory/add.md +9 -7
- package/dist/agent-src/commands/memory/learn-low-impact.md +3 -2
- package/dist/agent-src/commands/memory/load.md +7 -7
- package/dist/agent-src/commands/memory/mine-session.md +39 -12
- package/dist/agent-src/commands/memory/promote.md +3 -2
- package/dist/agent-src/commands/memory/propose.md +7 -6
- package/dist/agent-src/commands/memory.md +3 -2
- package/dist/agent-src/commands/mission/upgrade.md +182 -0
- package/dist/agent-src/commands/mode.md +1 -0
- package/dist/agent-src/commands/module/create.md +1 -0
- package/dist/agent-src/commands/module/explore.md +1 -0
- package/dist/agent-src/commands/module.md +1 -0
- package/dist/agent-src/commands/optimize/agents-dir.md +1 -0
- package/dist/agent-src/commands/optimize/augmentignore.md +1 -0
- package/dist/agent-src/commands/optimize/rtk.md +1 -0
- package/dist/agent-src/commands/optimize/skills.md +3 -2
- package/dist/agent-src/commands/optimize-prompt.md +1 -0
- package/dist/agent-src/commands/optimize.md +1 -0
- package/dist/agent-src/commands/orchestrate.md +2 -1
- package/dist/agent-src/commands/override/create.md +1 -0
- package/dist/agent-src/commands/override/manage.md +1 -0
- package/dist/agent-src/commands/override.md +1 -0
- package/dist/agent-src/commands/package-reset.md +1 -0
- package/dist/agent-src/commands/package-test.md +1 -0
- package/dist/agent-src/commands/post-as/ghostwriter.md +1 -0
- package/dist/agent-src/commands/post-as/me.md +1 -0
- package/dist/agent-src/commands/post-as.md +1 -0
- package/dist/agent-src/commands/pr/create/description-only.md +1 -0
- package/dist/agent-src/commands/pr/create.md +31 -4
- package/dist/agent-src/commands/prediction-pool.md +1 -0
- package/dist/agent-src/commands/prepare-for-review.md +1 -0
- package/dist/agent-src/commands/profile/activate.md +1 -0
- package/dist/agent-src/commands/profile/deactivate.md +1 -0
- package/dist/agent-src/commands/profile/show.md +1 -0
- package/dist/agent-src/commands/profile.md +1 -0
- package/dist/agent-src/commands/project-analyze.md +1 -0
- package/dist/agent-src/commands/project-health.md +1 -0
- package/dist/agent-src/commands/quality-fix.md +1 -0
- package/dist/agent-src/commands/refine-ticket.md +1 -0
- package/dist/agent-src/commands/research/deep.md +1 -0
- package/dist/agent-src/commands/research/report.md +1 -0
- package/dist/agent-src/commands/research.md +1 -0
- package/dist/agent-src/commands/review-changes.md +9 -0
- package/dist/agent-src/commands/review-routing.md +1 -0
- package/dist/agent-src/commands/roadmap/ai-council.md +1 -0
- package/dist/agent-src/commands/roadmap/create.md +1 -0
- package/dist/agent-src/commands/roadmap/materialize.md +73 -0
- package/dist/agent-src/commands/roadmap/process-full.md +1 -0
- package/dist/agent-src/commands/roadmap/process-phase.md +1 -0
- package/dist/agent-src/commands/roadmap/process-step.md +1 -0
- package/dist/agent-src/commands/roadmap.md +1 -0
- package/dist/agent-src/commands/rule-compliance-audit.md +1 -0
- package/dist/agent-src/commands/security-audit-config.md +84 -0
- package/dist/agent-src/commands/set-cost-profile.md +1 -0
- package/dist/agent-src/commands/skill/preview.md +2 -1
- package/dist/agent-src/commands/skill.md +1 -0
- package/dist/agent-src/commands/skills/discover.md +2 -1
- package/dist/agent-src/commands/skills.md +1 -0
- package/dist/agent-src/commands/sync-agent-settings.md +1 -0
- package/dist/agent-src/commands/sync-gitignore/fix.md +1 -0
- package/dist/agent-src/commands/sync-gitignore.md +1 -0
- package/dist/agent-src/commands/tests/create.md +1 -0
- package/dist/agent-src/commands/tests/execute.md +1 -0
- package/dist/agent-src/commands/tests.md +1 -0
- package/dist/agent-src/commands/threat-model.md +5 -4
- package/dist/agent-src/commands/update-form-request-messages.md +1 -0
- package/dist/agent-src/commands/upstream-contribute.md +4 -3
- package/dist/agent-src/commands/video/from-script.md +3 -2
- package/dist/agent-src/commands/video/from-song.md +4 -3
- package/dist/agent-src/commands/video/scene.md +2 -1
- package/dist/agent-src/commands/video/stitch.md +1 -0
- package/dist/agent-src/commands/video/storyboard.md +2 -1
- package/dist/agent-src/commands/video.md +4 -3
- package/dist/agent-src/commands/work.md +1 -0
- package/dist/agent-src/contexts/augment-infrastructure.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +2 -2
- package/dist/agent-src/contexts/communication/rules-auto/source-of-truth-mechanics.md +3 -3
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +6 -6
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +1 -1
- package/dist/agent-src/contexts/contracts/consumer-agents-md-guide.md +2 -2
- package/dist/agent-src/contexts/execution/evidence-discipline.md +153 -0
- package/dist/agent-src/contexts/execution/project-intelligence.md +264 -0
- package/dist/agent-src/contexts/execution/rdp-gate.md +75 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +2 -1
- package/dist/agent-src/contexts/subagent-configuration.md +1 -0
- package/dist/agent-src/personas/advisors/contrarian.md +1 -1
- package/dist/agent-src/personas/advisors/executor.md +1 -1
- package/dist/agent-src/personas/advisors/expansionist.md +1 -1
- package/dist/agent-src/personas/advisors/first-principles.md +1 -1
- package/dist/agent-src/personas/advisors/outsider.md +1 -1
- package/dist/agent-src/personas/ai-video-technical-director.md +1 -1
- package/dist/agent-src/personas/brand-strategist.md +74 -0
- package/dist/agent-src/personas/design-director.md +74 -0
- package/dist/agent-src/rules/autonomous-execution.md +12 -0
- package/dist/agent-src/rules/brand-consistency.md +77 -0
- package/dist/agent-src/rules/brand-source-of-truth.md +57 -0
- package/dist/agent-src/rules/direct-answers.md +2 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +2 -0
- package/dist/agent-src/rules/external-reference-deep-dive.md +1 -1
- package/dist/agent-src/rules/git-history-discipline.md +48 -1
- package/dist/agent-src/rules/icon-consistency.md +53 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +67 -0
- package/dist/agent-src/rules/improve-before-implement.md +12 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +80 -0
- package/dist/agent-src/rules/no-pr-progress-comments.md +3 -4
- package/dist/agent-src/rules/notes-first-reasoning.md +71 -0
- package/dist/agent-src/rules/persona-governance.md +2 -2
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +58 -31
- package/dist/agent-src/rules/security-sensitive-stop.md +22 -3
- package/dist/agent-src/rules/size-enforcement.md +1 -1
- package/dist/agent-src/rules/source-confidentiality.md +97 -0
- package/dist/agent-src/rules/source-discovery-gate.md +98 -0
- package/dist/agent-src/rules/think-before-action.md +10 -1
- package/dist/agent-src/rules/ui-audit-gate.md +2 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +76 -0
- package/dist/agent-src/rules/user-interaction.md +1 -1
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +392 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +824 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +5 -5
- package/dist/agent-src/skills/adversarial-review/SKILL.md +14 -0
- package/dist/agent-src/skills/agent-security-review/SKILL.md +113 -0
- package/dist/agent-src/skills/agent-security-review/evals/triggers.json +52 -0
- package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +1 -1
- package/dist/agent-src/skills/ai-council/SKILL.md +4 -4
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +9 -13
- package/dist/agent-src/skills/async-python-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/blade-ui/SKILL.md +12 -5
- package/dist/agent-src/skills/blameless-post-mortem/SKILL.md +199 -0
- package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +12 -11
- package/dist/agent-src/skills/brand/ATTRIBUTION.md +38 -0
- package/dist/agent-src/skills/brand/SKILL.md +115 -0
- package/dist/agent-src/skills/brand/data/archetypes.csv +13 -0
- package/dist/agent-src/skills/brand/data/color-psychology.csv +14 -0
- package/dist/agent-src/skills/brand/data/logo-style-fit.csv +13 -0
- package/dist/agent-src/skills/brand/data/manifest.json +226 -0
- package/dist/agent-src/skills/brand/data/messaging-frameworks.csv +13 -0
- package/dist/agent-src/skills/brand/data/naming-patterns.csv +13 -0
- package/dist/agent-src/skills/brand/data/typography-principles.csv +13 -0
- package/dist/agent-src/skills/brand/data/voice-tone.csv +13 -0
- package/dist/agent-src/skills/brand/evals/triggers.json +17 -0
- package/dist/agent-src/skills/brand-asset-generation/SKILL.md +89 -0
- package/dist/agent-src/skills/brand-asset-generation/evals/triggers.json +17 -0
- package/dist/agent-src/skills/brand-audit/SKILL.md +67 -0
- package/dist/agent-src/skills/brand-audit/evals/triggers.json +17 -0
- package/dist/agent-src/skills/brand-identity/SKILL.md +101 -0
- package/dist/agent-src/skills/brand-identity/evals/triggers.json +17 -0
- package/dist/agent-src/skills/brand-strategy/SKILL.md +83 -0
- package/dist/agent-src/skills/brand-strategy/evals/triggers.json +17 -0
- package/dist/agent-src/skills/brand-to-tokens/SKILL.md +102 -0
- package/dist/agent-src/skills/brand-to-tokens/evals/triggers.json +17 -0
- package/dist/agent-src/skills/brand-to-tokens/templates/marp-brand-deck.md.example +46 -0
- package/dist/agent-src/skills/brand-to-tokens/templates/reveal-brand-deck.yaml +32 -0
- package/dist/agent-src/skills/canvas-design/evals/triggers.json +1 -0
- package/dist/agent-src/skills/check-refs/SKILL.md +5 -5
- package/dist/agent-src/skills/code-review/SKILL.md +6 -15
- package/dist/agent-src/skills/command-routing/SKILL.md +1 -1
- package/dist/agent-src/skills/command-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/complexity-first-planning/SKILL.md +96 -0
- package/dist/agent-src/skills/complexity-first-planning/evals/triggers.json +17 -0
- package/dist/agent-src/skills/context-authoring/SKILL.md +2 -2
- package/dist/agent-src/skills/context-document/SKILL.md +35 -2
- package/dist/agent-src/skills/copilot-config/SKILL.md +3 -4
- package/dist/agent-src/skills/corpus-grounding/evals/triggers.json +1 -0
- package/dist/agent-src/skills/corpus-grounding/scripts/bm25_search.ts +482 -0
- package/dist/agent-src/skills/corpus-grounding/scripts/decision_engine.ts +803 -0
- package/dist/agent-src/skills/corpus-grounding/scripts/ground.ts +541 -0
- package/dist/agent-src/skills/corpus-grounding/scripts/schema_validator.ts +309 -0
- package/dist/agent-src/skills/database/SKILL.md +26 -4
- package/dist/agent-src/skills/decision-record/SKILL.md +1 -1
- package/dist/agent-src/skills/decision-record/evals/triggers.json +17 -0
- package/dist/agent-src/skills/decision-review/SKILL.md +179 -0
- package/dist/agent-src/skills/defense-in-depth/SKILL.md +1 -1
- package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
- package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
- package/dist/agent-src/skills/design-intelligence/data/manifest.json +23 -6
- package/dist/agent-src/skills/design-intelligence/evals/triggers.json +1 -0
- package/dist/agent-src/skills/design-tokens/evals/triggers.json +1 -0
- package/dist/agent-src/skills/design-tokens/scripts/tokens.ts +888 -0
- package/dist/agent-src/skills/developer-like-execution/SKILL.md +5 -4
- package/dist/agent-src/skills/doc-coauthoring/evals/triggers.json +1 -0
- package/dist/agent-src/skills/eloquent/evals/triggers.json +1 -0
- package/dist/agent-src/skills/emit-tickets/SKILL.md +198 -0
- package/dist/agent-src/skills/error-handling-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/evals/triggers.json +1 -0
- package/dist/agent-src/skills/feature-planning/SKILL.md +2 -2
- package/dist/agent-src/skills/git-workflow/SKILL.md +33 -0
- package/dist/agent-src/skills/guideline-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/iconography/SKILL.md +88 -0
- package/dist/agent-src/skills/iconography/evals/triggers.json +17 -0
- package/dist/agent-src/skills/image-analyser/evals/triggers.json +1 -0
- package/dist/agent-src/skills/image-creator/evals/triggers.json +1 -0
- package/dist/agent-src/skills/image-editing/SKILL.md +100 -0
- package/dist/agent-src/skills/image-editing/evals/triggers.json +17 -0
- package/dist/agent-src/skills/image-generation/SKILL.md +95 -0
- package/dist/agent-src/skills/image-generation/evals/triggers.json +17 -0
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +96 -0
- package/dist/agent-src/skills/image-provider-routing/evals/triggers.json +17 -0
- package/dist/agent-src/skills/launch-readiness/SKILL.md +21 -0
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +12 -8
- package/dist/agent-src/skills/lint-skills/SKILL.md +5 -5
- package/dist/agent-src/skills/logo-generation/SKILL.md +98 -0
- package/dist/agent-src/skills/logo-generation/evals/triggers.json +17 -0
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
- package/dist/agent-src/skills/mcp-builder/SKILL.md +1 -1
- package/dist/agent-src/skills/md-language-check/SKILL.md +1 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +63 -17
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +1 -1
- package/dist/agent-src/skills/php-coder/evals/triggers.json +1 -0
- package/dist/agent-src/skills/prediction-pool-optimizer/evals/triggers.json +1 -0
- package/dist/agent-src/skills/premortem/SKILL.md +137 -0
- package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +115 -0
- package/dist/agent-src/skills/prompt-engineering-image/evals/triggers.json +17 -0
- package/dist/agent-src/skills/prompt-engineering-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/prompt-validator/evals/triggers.json +1 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +12 -5
- package/dist/agent-src/skills/react-shadcn-ui/scripts/shadcn_add.ts +388 -0
- package/dist/agent-src/skills/readme-writing-package/SKILL.md +1 -1
- package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +119 -0
- package/dist/agent-src/skills/reasoning-orchestrator/evals/triggers.json +17 -0
- package/dist/agent-src/skills/receiving-code-review/SKILL.md +6 -6
- package/dist/agent-src/skills/refine-prompt/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/evals/triggers.json +1 -0
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/roadmap-management/SKILL.md +16 -3
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +76 -0
- package/dist/agent-src/skills/root-cause-frameworks/SKILL.md +146 -0
- package/dist/agent-src/skills/rule-refactor/SKILL.md +9 -9
- package/dist/agent-src/skills/rule-writing/SKILL.md +7 -7
- package/dist/agent-src/skills/script-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/secrets-management/SKILL.md +1 -1
- package/dist/agent-src/skills/security-audit/SKILL.md +5 -0
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +19 -3
- package/dist/agent-src/skills/skill-management/SKILL.md +3 -3
- package/dist/agent-src/skills/skill-reviewer/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +5 -5
- package/dist/agent-src/skills/skill-writing/evals/triggers.json +1 -0
- package/dist/agent-src/skills/source-discovery/SKILL.md +182 -0
- package/dist/agent-src/skills/standards-from-config/SKILL.md +93 -0
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +10 -3
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +7 -0
- package/dist/agent-src/skills/tailwind-engineer/scripts/tailwind_config_gen.ts +561 -0
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/testing-anti-patterns/process-anti-patterns.md +1 -1
- package/dist/agent-src/skills/threat-modeling/SKILL.md +1 -0
- package/dist/agent-src/skills/token-optimizer/SKILL.md +1 -1
- package/dist/agent-src/skills/typography-system/SKILL.md +138 -0
- package/dist/agent-src/skills/typography-system/evals/triggers.json +17 -0
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -3
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +209 -0
- package/dist/agent-src/skills/verify-repair-loop/evals/output-schema.yml +20 -0
- package/dist/agent-src/skills/verify-repair-loop/evals/triggers.json +17 -0
- package/dist/agent-src/templates/agent-settings.md +7 -0
- package/dist/agent-src/templates/agents/.gitattributes.fragment +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +4 -4
- package/dist/agent-src/templates/contexts/knowledge-card.md +69 -0
- package/dist/agent-src/templates/contexts/lesson-card.md +73 -0
- package/dist/agent-src/templates/roadmaps.md +29 -1
- package/dist/agent-src/templates/scripts/README.md +6 -6
- package/dist/agent-src/templates/scripts/check_memory.ts +640 -0
- package/dist/agent-src/templates/scripts/check_memory_proposal.ts +351 -0
- package/dist/agent-src/templates/scripts/implement_ticket/__main__.ts +27 -0
- package/dist/agent-src/templates/scripts/memory_hash.ts +333 -0
- package/dist/agent-src/templates/scripts/memory_lookup.ts +1067 -0
- package/dist/agent-src/templates/scripts/memory_report.ts +846 -0
- package/dist/agent-src/templates/scripts/memory_signal.ts +422 -0
- package/dist/agent-src/templates/scripts/memory_status.ts +191 -0
- package/dist/agent-src/templates/scripts/pr_review_routing.ts +523 -0
- package/dist/agent-src/templates/scripts/pr_risk_review.ts +0 -0
- package/dist/agent-src/templates/scripts/telemetry/aggregator.ts +0 -0
- package/dist/agent-src/templates/scripts/telemetry/boundary.ts +164 -0
- package/dist/agent-src/templates/scripts/telemetry/engagement.ts +479 -0
- package/dist/agent-src/templates/scripts/telemetry/report_renderer.ts +394 -0
- package/dist/agent-src/templates/scripts/telemetry/settings.ts +210 -0
- package/dist/agent-src/templates/scripts/telemetry_record.ts +255 -0
- package/dist/agent-src/templates/scripts/telemetry_report.ts +189 -0
- package/dist/agent-src/templates/scripts/telemetry_status.ts +312 -0
- package/dist/agent-src/templates/scripts/tier_usage_report.ts +597 -0
- package/dist/agent-src/templates/scripts/work_engine/__main__.ts +14 -0
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +1118 -0
- package/dist/agent-src/templates/scripts/work_engine/_lib/user_global_paths.ts +329 -0
- package/dist/agent-src/templates/scripts/work_engine/cli.ts +206 -0
- package/dist/agent-src/templates/scripts/work_engine/cli_args.ts +249 -0
- package/dist/agent-src/templates/scripts/work_engine/delivery_state.ts +225 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/analyze.ts +125 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +189 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/index.ts +94 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/memory.ts +193 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/plan.ts +267 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/refine.ts +518 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/report.ts +379 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +268 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +258 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/index.ts +32 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/contract.ts +243 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/index.ts +108 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +259 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/ui.ts +216 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/_passthrough.ts +40 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +241 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +216 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +506 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +325 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/index.ts +102 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +462 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +474 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +352 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.ts +33 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.ts +213 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/index.ts +111 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.ts +126 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/report.ts +112 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/test.ts +164 -0
- package/dist/agent-src/templates/scripts/work_engine/dispatcher.ts +515 -0
- package/dist/agent-src/templates/scripts/work_engine/emitters.ts +119 -0
- package/dist/agent-src/templates/scripts/work_engine/errors.ts +24 -0
- package/dist/agent-src/templates/scripts/work_engine/hook_bootstrap.ts +104 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.ts +176 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.ts +41 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.ts +89 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +193 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +304 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.ts +110 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.ts +118 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/index.ts +17 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +161 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.ts +45 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/trace.ts +134 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/context.ts +94 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/events.ts +58 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/exceptions.ts +85 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/index.ts +27 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/registry.ts +66 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/runner.ts +90 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +260 -0
- package/dist/agent-src/templates/scripts/work_engine/input_builders.ts +260 -0
- package/dist/agent-src/templates/scripts/work_engine/intent/classify.ts +466 -0
- package/dist/agent-src/templates/scripts/work_engine/migration/v0_to_v1.ts +531 -0
- package/dist/agent-src/templates/scripts/work_engine/orchestration.ts +366 -0
- package/dist/agent-src/templates/scripts/work_engine/persona_policy.ts +97 -0
- package/dist/agent-src/templates/scripts/work_engine/resolvers/diff.ts +135 -0
- package/dist/agent-src/templates/scripts/work_engine/resolvers/file.ts +175 -0
- package/dist/agent-src/templates/scripts/work_engine/resolvers/prompt.ts +115 -0
- package/dist/agent-src/templates/scripts/work_engine/scoring/confidence.ts +415 -0
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +466 -0
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_trace.ts +298 -0
- package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +444 -0
- package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +252 -0
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +745 -0
- package/dist/agent-src/templates/scripts/work_engine/state.ts +1151 -0
- package/dist/agent-src/templates/scripts/work_engine/state_io.ts +413 -0
- package/dist/agent-src/templates/tickets.md +120 -0
- package/dist/cli/agent-config.js +31 -300
- package/dist/cli/agent-config.js.map +1 -1
- package/dist/cli/commands/commands.js +11 -6
- package/dist/cli/commands/commands.js.map +1 -1
- package/dist/cli/commands/doctorShell.js +4 -22
- package/dist/cli/commands/doctorShell.js.map +1 -1
- package/dist/cli/commands/packs.js +1 -1
- package/dist/cli/commands/packs.js.map +1 -1
- package/dist/cli/commands/recordTriggerEval.js +179 -0
- package/dist/cli/commands/recordTriggerEval.js.map +1 -0
- package/dist/cli/commands/recordTriggerEval.test.js +113 -0
- package/dist/cli/commands/recordTriggerEval.test.js.map +1 -0
- package/dist/cli/commands/workspaces.js +1 -1
- package/dist/cli/commands/workspaces.js.map +1 -1
- package/dist/cli/discovery/loadManifest.js.map +1 -1
- package/dist/cli/main.js +330 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/python/knowledge_ingest.js +1048 -0
- package/dist/cli/python/knowledge_ingest.js.map +1 -0
- package/dist/cli/python/workspace_analytics.js +1085 -0
- package/dist/cli/python/workspace_analytics.js.map +1 -0
- package/dist/cli/python/workspace_crypto.js +544 -0
- package/dist/cli/python/workspace_crypto.js.map +1 -0
- package/dist/cli/python/workspace_documents.js +1216 -0
- package/dist/cli/python/workspace_documents.js.map +1 -0
- package/dist/cli/python/workspace_drive.js +574 -0
- package/dist/cli/python/workspace_drive.js.map +1 -0
- package/dist/cli/python/workspace_drive_health.js +628 -0
- package/dist/cli/python/workspace_drive_health.js.map +1 -0
- package/dist/cli/python/workspace_explain.js +765 -0
- package/dist/cli/python/workspace_explain.js.map +1 -0
- package/dist/cli/python/workspace_hosts.js +349 -0
- package/dist/cli/python/workspace_hosts.js.map +1 -0
- package/dist/cli/python/workspace_inbox.js +692 -0
- package/dist/cli/python/workspace_inbox.js.map +1 -0
- package/dist/cli/python/workspace_render.js +816 -0
- package/dist/cli/python/workspace_render.js.map +1 -0
- package/dist/cli/python/workspace_roles.js +487 -0
- package/dist/cli/python/workspace_roles.js.map +1 -0
- package/dist/cli/python/workspace_secrets.js +180 -0
- package/dist/cli/python/workspace_secrets.js.map +1 -0
- package/dist/cli/python/workspace_sessions.js +1079 -0
- package/dist/cli/python/workspace_sessions.js.map +1 -0
- package/dist/cli/python/workspace_skills.js +417 -0
- package/dist/cli/python/workspace_skills.js.map +1 -0
- package/dist/cli/registry.js +2 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +1802 -448
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +12 -6
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +303 -43
- package/dist/discovery/trust-report.md +4 -4
- package/dist/discovery/workspaces.json +127 -41
- package/dist/install/install.mjs +13934 -0
- package/dist/mcp/registry-manifest.json +4 -4
- package/dist/router.json +1 -1
- package/dist/server/routes/wizard.js +54 -24
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/server/routes/workspace.js +44 -25
- package/dist/server/routes/workspace.js.map +1 -1
- package/dist/server/schemas/settings.js +33 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/MIGRATION.md +1 -1
- package/docs/SKILL_CENSUS.md +344 -0
- package/docs/adrs/cost/0001-hard-stop-hook.md +5 -5
- package/docs/adrs/memory/0001-consumer-side-snapshot.md +15 -7
- package/docs/adrs/memory/README.md +6 -5
- package/docs/adrs/router/0001-three-tier-routing.md +2 -2
- package/docs/adrs/schema/0001-json-schema-frontmatter.md +2 -2
- package/docs/adrs/smoke/0001-per-tier-smoke-scripts.md +5 -5
- package/docs/adrs/telegraph/0001-default-off-until-bench.md +3 -3
- package/docs/architecture/augment-projection.md +1 -1
- package/docs/architecture/multi-tool-projection.md +3 -3
- package/docs/architecture.md +42 -11
- package/docs/archive/CHANGELOG-pre-2.2.0.md +30 -30
- package/docs/archive/CHANGELOG-pre-2.25.0.md +1 -1
- package/docs/archive/CHANGELOG-pre-4.5.0.md +1 -1
- package/docs/archive/CHANGELOG-pre-6.0.0.md +473 -0
- package/docs/benchmark.md +51 -53
- package/docs/benchmarks.md +2 -2
- package/docs/capability-matrix.md +32 -0
- package/docs/case-studies/frontend-design-positioning.md +86 -0
- package/docs/catalog.md +63 -15
- package/docs/command-flows.md +90 -92
- package/docs/command-naming-audit.md +60 -0
- package/docs/contracts/STABILITY.md +32 -0
- package/docs/contracts/adr-layout.md +2 -3
- package/docs/contracts/adr-level-6-productization.md +1 -1
- package/docs/contracts/agents-md-tech-stack.md +1 -1
- package/docs/contracts/ai-council-config.md +64 -29
- package/docs/contracts/analysis-memory-loop.md +149 -0
- package/docs/contracts/benchmark-ab-contract.md +3 -3
- package/docs/contracts/branch-protection-policy.md +27 -0
- package/docs/contracts/brand-token-consumption.md +69 -0
- package/docs/contracts/command-clusters.md +3 -2
- package/docs/contracts/command-surface-tiers.md +13 -0
- package/docs/contracts/cost-enforcement.md +1 -1
- package/docs/contracts/cost-summary-schema.md +1 -1
- package/docs/contracts/daily-workspace.md +1 -0
- package/docs/contracts/discovery-manifest.schema.json +25 -4
- package/docs/contracts/explain-modes.md +1 -1
- package/docs/contracts/implement-ticket-flow.md +15 -16
- package/docs/contracts/install-layout.md +249 -0
- package/docs/contracts/kernel-membership.md +1 -1
- package/docs/contracts/linear-ai-rules-inclusion.md +2 -2
- package/docs/contracts/linter-structural-model.md +1 -1
- package/docs/contracts/mcp-discovery-phase-notice.md +1 -1
- package/docs/contracts/mcp-tool-inventory.md +10 -10
- package/docs/contracts/measurement-baseline.md +1 -1
- package/docs/contracts/memory-visibility-v1.md +1 -5
- package/docs/contracts/multi-tool-projection-fidelity.md +1 -1
- package/docs/contracts/namespace.md +3 -3
- package/docs/contracts/no-runtime-boundary.md +56 -0
- package/docs/contracts/package-self-orientation.md +24 -0
- package/docs/contracts/persona-schema.md +1 -1
- package/docs/contracts/provider-lifecycle.md +3 -3
- package/docs/contracts/reasoning-discipline-protocol.md +83 -0
- package/docs/contracts/rule-classification.md +3 -3
- package/docs/contracts/rule-interactions.md +1 -1
- package/docs/contracts/skill-domains.md +1 -1
- package/docs/contracts/smoke-contracts.md +2 -2
- package/docs/contracts/surface-tiers.md +81 -0
- package/docs/contracts/ticket-bundle-format.md +228 -0
- package/docs/contracts/universal-skills.md +0 -1
- package/docs/contracts/workspace-boundary.md +84 -0
- package/docs/cookbook.md +152 -0
- package/docs/customization.md +15 -4
- package/docs/decisions/ADR-009-event4u-namespace.md +1 -1
- package/docs/decisions/ADR-013-discovery-frontmatter-contract.md +17 -1
- package/docs/decisions/ADR-026-explain-mode-translation.md +1 -1
- package/docs/decisions/ADR-056-unvalidated-video-adapters-disposition.md +1 -1
- package/docs/decisions/ADR-059-render-resume-filesystem-as-state.md +1 -1
- package/docs/decisions/ADR-060-comfyui-sandbox-model.md +1 -1
- package/docs/decisions/ADR-061-corpus-grounding-layer.md +48 -1
- package/docs/decisions/ADR-088-no-external-runtime-federation.md +26 -27
- package/docs/decisions/ADR-090-visibility-command-frontmatter-field.md +95 -0
- package/docs/decisions/ADR-091-split-meta-capability-packs.md +113 -0
- package/docs/decisions/ADR-092-defer-command-tier-alias-removal.md +93 -0
- package/docs/decisions/ADR-093-ai-council-config-user-global.md +111 -0
- package/docs/decisions/ADR-094-agent-memory-layer-removal.md +94 -0
- package/docs/decisions/ADR-095-workspace-boundary-contract.md +108 -0
- package/docs/decisions/ADR-096-analysis-workbench.md +110 -0
- package/docs/decisions/ADR-097-mission-recipe-privilege-boundary.md +121 -0
- package/docs/decisions/ADR-098-evidence-first-structure-discovery.md +154 -0
- package/docs/decisions/ADR-099-file-first-pattern-library.md +87 -0
- package/docs/decisions/ADR-100-global-knowledge-card-sharing.md +133 -0
- package/docs/decisions/ADR-101-ticket-bundle-emission.md +109 -0
- package/docs/decisions/ADR-102-ticket-handoff-paste-and-mcp.md +72 -0
- package/docs/decisions/ADR-103-global-knowledge-default-off-until-measured.md +92 -0
- package/docs/decisions/ADR-200-python-to-typescript-migration.md +193 -0
- package/docs/decisions/INDEX.md +15 -0
- package/docs/development.md +5 -7
- package/docs/distribution/mcp-submission-checklist.md +3 -3
- package/docs/featured-commands.md +1 -1
- package/docs/featured-skills.md +1 -1
- package/docs/getting-started-by-role.md +2 -0
- package/docs/getting-started.md +4 -4
- package/docs/guidelines/agent-infra/5w2h-analysis.md +1 -1
- package/docs/guidelines/agent-infra/comparison-matrix.md +1 -1
- package/docs/guidelines/agent-infra/corpus-grounding-authoring.md +1 -1
- package/docs/guidelines/agent-infra/critical-thinking.md +1 -1
- package/docs/guidelines/agent-infra/engineering-memory-data-format.md +1 -5
- package/docs/guidelines/agent-infra/failure-signatures.md +35 -0
- package/docs/guidelines/agent-infra/first-principles.md +1 -1
- package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +169 -0
- package/docs/guidelines/agent-infra/inversion-thinking.md +1 -1
- package/docs/guidelines/agent-infra/ios-simulator-guide.md +9 -14
- package/docs/guidelines/agent-infra/mcp-request-signing.md +19 -22
- package/docs/guidelines/agent-infra/memory-access.md +25 -31
- package/docs/guidelines/agent-infra/mental-models.md +1 -1
- package/docs/guidelines/agent-infra/model-recommendation.md +29 -0
- package/docs/guidelines/agent-infra/scqa-framework.md +3 -3
- package/docs/guidelines/agent-infra/security-lint-containment.md +81 -0
- package/docs/guidelines/agent-infra/six-hats.md +1 -1
- package/docs/guidelines/agent-infra/size-and-scope.md +17 -0
- package/docs/guidelines/agent-infra/skill-quality-checklist.md +2 -2
- package/docs/guidelines/agent-infra/systems-thinking.md +1 -1
- package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +72 -0
- package/docs/guides/frontend-design-corpus-refresh.md +83 -0
- package/docs/guides/skill-preview.md +1 -1
- package/docs/hook-payload-capture.md +4 -4
- package/docs/installation.md +1 -1
- package/docs/mcp.md +3 -3
- package/docs/migration/consumer-template-consumption-model.md +145 -0
- package/docs/migration/divergences/README.md +55 -0
- package/docs/migration/divergences/_template.md +50 -0
- package/docs/migration/divergences/bench-stats-float-precision.md +72 -0
- package/docs/migration/divergences/mcp-telemetry-node-sqlite.md +61 -0
- package/docs/migration/divergences/pack-mcp-content-gzip-body.md +53 -0
- package/docs/migration/divergences/src-scripts-build_cloud_bundle.md +63 -0
- package/docs/migration/divergences/src-scripts-check_memory.md +91 -0
- package/docs/migration/divergences/src-scripts-inventory_abstraction_budget.md +65 -0
- package/docs/migration/divergences/src-scripts-lint_marketplace.md +57 -0
- package/docs/migration/divergences/src-scripts-lint_mcp_registry_manifest.md +70 -0
- package/docs/migration/divergences/src-scripts-spotcheck_thin_root.md +60 -0
- package/docs/migration/divergences/src-scripts-validate_agent_settings.md +58 -0
- package/docs/migration/node-floor.md +86 -0
- package/docs/migration/yaml-roundtrip-spike.md +163 -0
- package/docs/parity/bench-external.json +58 -0
- package/docs/parity/external-runtime.md +46 -0
- package/docs/personas.md +6 -1
- package/docs/quality.md +3 -3
- package/docs/role-experiences.md +19 -0
- package/docs/safety.md +3 -3
- package/docs/setup/per-ide/windsurf.md +1 -1
- package/docs/skills-catalog.md +26 -2
- package/docs/threat-model.md +28 -0
- package/llms.txt +25 -1
- package/package.json +10 -15
- package/src/config/agent-settings.template.yml +128 -3
- package/src/config/discovery/packs.yml +60 -0
- package/src/config/discovery/unassigned-artefacts.yml +6 -0
- package/src/config/discovery/workspaces.yml +5 -3
- package/src/config/gitignore-block.txt +13 -0
- package/src/scripts/_cli/cmd_doctor.ts +2306 -0
- package/src/scripts/_cli/cmd_explain.ts +748 -0
- package/src/scripts/_cli/cmd_export.ts +375 -0
- package/src/scripts/_cli/cmd_migrate.ts +951 -0
- package/src/scripts/_cli/cmd_prune.ts +610 -0
- package/src/scripts/_cli/cmd_refresh.ts +530 -0
- package/src/scripts/_cli/cmd_settings_check.ts +407 -0
- package/src/scripts/_cli/cmd_settings_migrate.ts +344 -0
- package/src/scripts/_cli/cmd_sync.ts +381 -0
- package/src/scripts/_cli/cmd_uninstall.ts +833 -0
- package/src/scripts/_cli/cmd_update.ts +585 -0
- package/src/scripts/_cli/cmd_upgrade.ts +390 -0
- package/src/scripts/_cli/cmd_validate.ts +394 -0
- package/src/scripts/_cli/cmd_versions.ts +492 -0
- package/src/scripts/_cli/explain_last/assumptions.ts +114 -0
- package/src/scripts/_cli/explain_last/council.ts +197 -0
- package/src/scripts/_cli/explain_last/halt.ts +73 -0
- package/src/scripts/_cli/explain_last/index.ts +155 -0
- package/src/scripts/_cli/explain_last/inputs.ts +211 -0
- package/src/scripts/_cli/explain_last/memory.ts +231 -0
- package/src/scripts/_cli/explain_last/provider.ts +82 -0
- package/src/scripts/_cli/explain_last/render.ts +54 -0
- package/src/scripts/_cli/explain_last/route.ts +70 -0
- package/src/scripts/_cli/explain_last/scrubber.ts +138 -0
- package/src/scripts/_cli/explain_last/sections/assumptions.ts +51 -0
- package/src/scripts/_cli/explain_last/sections/council.ts +56 -0
- package/src/scripts/_cli/explain_last/sections/halt.ts +60 -0
- package/src/scripts/_cli/explain_last/sections/header.ts +50 -0
- package/src/scripts/_cli/explain_last/sections/index.ts +21 -0
- package/src/scripts/_cli/explain_last/sections/inputs.ts +63 -0
- package/src/scripts/_cli/explain_last/sections/memory.ts +124 -0
- package/src/scripts/_cli/explain_last/sections/pack.ts +42 -0
- package/src/scripts/_cli/explain_last/sections/provider.ts +51 -0
- package/src/scripts/_cli/explain_last/sections/route.ts +48 -0
- package/src/scripts/_cli/explain_last/state_loader.ts +119 -0
- package/src/scripts/_dispatch.bash +179 -163
- package/src/scripts/_lib/agent_settings.ts +1123 -0
- package/src/scripts/_lib/agent_src.ts +654 -0
- package/src/scripts/_lib/agents_overlay.ts +183 -0
- package/src/scripts/_lib/bench_ab_cache.ts +399 -0
- package/src/scripts/_lib/bench_ab_scoring.ts +352 -0
- package/src/scripts/_lib/bench_ab_scoring_v2.ts +751 -0
- package/src/scripts/_lib/bench_cost.ts +396 -0
- package/src/scripts/_lib/bench_quality.ts +237 -0
- package/src/scripts/_lib/bench_report.ts +255 -0
- package/src/scripts/_lib/bench_telegraph.ts +516 -0
- package/src/scripts/_lib/bench_telegraph_report.ts +272 -0
- package/src/scripts/_lib/changelog_eras.ts +398 -0
- package/src/scripts/_lib/claude_desktop_bundler.ts +324 -0
- package/src/scripts/_lib/cli_wrapper.ts +89 -0
- package/src/scripts/_lib/fs_atomic.ts +172 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +639 -0
- package/src/scripts/_lib/install_layout.ts +87 -0
- package/src/scripts/_lib/install_regenerator.ts +157 -0
- package/src/scripts/_lib/installed_lock.ts +451 -0
- package/src/scripts/_lib/installed_tools.ts +518 -0
- package/src/scripts/_lib/json_pointers.ts +388 -0
- package/src/scripts/_lib/knowledge_global.ts +770 -0
- package/src/scripts/_lib/knowledge_global_promote.ts +453 -0
- package/src/scripts/_lib/knowledge_global_redaction.ts +448 -0
- package/src/scripts/_lib/link_crypto.ts +325 -0
- package/src/scripts/_lib/linked_projects.ts +613 -0
- package/src/scripts/_lib/model_tier.ts +65 -0
- package/src/scripts/_lib/module_detection.ts +275 -0
- package/src/scripts/_lib/node_sqlite.d.ts +32 -0
- package/src/scripts/_lib/pin_resolver.ts +264 -0
- package/src/scripts/_lib/py_random.ts +212 -0
- package/src/scripts/_lib/script_output.ts +147 -0
- package/src/scripts/_lib/security_lint.ts +623 -0
- package/src/scripts/_lib/surface_tiers.ts +127 -0
- package/src/scripts/_lib/token_count.ts +126 -0
- package/src/scripts/_lib/update_check.ts +297 -0
- package/src/scripts/_lib/user_global_paths.ts +329 -0
- package/src/scripts/_lib/value_ladder.ts +882 -0
- package/src/scripts/_lib/value_report.ts +617 -0
- package/src/scripts/_lib/zip_min.ts +175 -0
- package/src/scripts/adoption_report.ts +357 -0
- package/src/scripts/adoption_snapshot.ts +392 -0
- package/src/scripts/adoption_status.ts +424 -0
- package/src/scripts/adr/regenerate_index.ts +257 -0
- package/src/scripts/ai-image/adapters/flux.sh +45 -0
- package/src/scripts/ai-image/adapters/gemini-image.sh +45 -0
- package/src/scripts/ai-image/adapters/ideogram.sh +45 -0
- package/src/scripts/ai-image/adapters/recraft.sh +47 -0
- package/src/scripts/ai-video/adapters/comfyui.sh +3 -3
- package/src/scripts/ai-video/adapters/fal.sh +3 -3
- package/src/scripts/ai-video/adapters/gemini-veo.sh +3 -3
- package/src/scripts/ai-video/adapters/higgsfield.sh +3 -3
- package/src/scripts/ai-video/adapters/kling.sh +3 -3
- package/src/scripts/ai-video/adapters/musetalk.sh +2 -2
- package/src/scripts/ai-video/adapters/openai-images.sh +3 -3
- package/src/scripts/ai-video/adapters/replicate.sh +3 -3
- package/src/scripts/ai-video/adapters/sora.sh +3 -3
- package/src/scripts/ai-video/adapters/syncso.sh +3 -3
- package/src/scripts/ai-video/audio-adapters/allin1.sh +2 -2
- package/src/scripts/ai-video/audio-adapters/whisperx.sh +2 -2
- package/src/scripts/ai-video/lib/audio-adapter-contract.md +1 -1
- package/src/scripts/ai-video/lib/embed-provenance.sh +2 -2
- package/src/scripts/ai-video/lib/ingest-song.sh +2 -2
- package/src/scripts/ai-video/lib/parse-blueprint.sh +1 -1
- package/src/scripts/ai-video/lib/resume-scan.sh +2 -2
- package/src/scripts/ai-video/smoke-trace.sh +16 -7
- package/src/scripts/ai-video/stitch.sh +2 -2
- package/src/scripts/ai_council/_default_prices.ts +73 -0
- package/src/scripts/ai_council/advisors.ts +244 -0
- package/src/scripts/ai_council/airgap.ts +249 -0
- package/src/scripts/ai_council/budget_guard.ts +492 -0
- package/src/scripts/ai_council/bundler.ts +376 -0
- package/src/scripts/ai_council/cli_hints.ts +120 -0
- package/src/scripts/ai_council/clients.ts +2214 -0
- package/src/scripts/ai_council/compile_corpus.ts +681 -0
- package/src/scripts/ai_council/confidence_gate.ts +230 -0
- package/src/scripts/ai_council/config.ts +1729 -0
- package/src/scripts/ai_council/consensus.ts +551 -0
- package/src/scripts/ai_council/events_log.ts +327 -0
- package/src/scripts/ai_council/learn_low_impact_preview.ts +317 -0
- package/src/scripts/ai_council/low_impact.ts +1069 -0
- package/src/scripts/ai_council/low_impact_corpus.ts +662 -0
- package/src/scripts/ai_council/low_impact_intake.ts +222 -0
- package/src/scripts/ai_council/modes.ts +169 -0
- package/src/scripts/ai_council/necessity.ts +933 -0
- package/src/scripts/ai_council/orchestrator.ts +1689 -0
- package/src/scripts/ai_council/pricing.ts +267 -0
- package/src/scripts/ai_council/probation_gate.ts +282 -0
- package/src/scripts/ai_council/project_context.ts +308 -0
- package/src/scripts/ai_council/prompts.ts +600 -0
- package/src/scripts/ai_council/redact_low_impact_entry.ts +291 -0
- package/src/scripts/ai_council/replay.ts +314 -0
- package/src/scripts/ai_council/session.ts +558 -0
- package/src/scripts/ai_council/shadow_dispatch.ts +509 -0
- package/src/scripts/ai_council/solo_dispatch.ts +281 -0
- package/src/scripts/analysis_freshness.ts +343 -0
- package/src/scripts/annotate_discovery.ts +288 -0
- package/src/scripts/apply_modules_config.ts +537 -0
- package/src/scripts/audit_adr_coverage.ts +357 -0
- package/src/scripts/audit_auto_rules.ts +415 -0
- package/src/scripts/audit_cloud_compatibility.ts +608 -0
- package/src/scripts/audit_command_surface.ts +1227 -0
- package/src/scripts/audit_initial_context.ts +694 -0
- package/src/scripts/audit_likelihood.ts +434 -0
- package/src/scripts/audit_mcp_tools.ts +252 -0
- package/src/scripts/audit_overlap.ts +421 -0
- package/src/scripts/audit_skill_descriptions.ts +402 -0
- package/src/scripts/audit_skill_overlap.ts +576 -0
- package/src/scripts/audit_user_type_axis.ts +264 -0
- package/src/scripts/backfill_model_tier.ts +349 -0
- package/src/scripts/bench_ab_cache_dispatch.ts +126 -0
- package/src/scripts/bench_ab_clone.ts +610 -0
- package/src/scripts/bench_ab_diff.ts +609 -0
- package/src/scripts/bench_ab_integrity.ts +261 -0
- package/src/scripts/bench_ab_run.ts +417 -0
- package/src/scripts/bench_ab_task_runner.ts +1382 -0
- package/src/scripts/bench_ab_tracka_run.ts +436 -0
- package/src/scripts/bench_ab_v2_run.ts +585 -0
- package/src/scripts/bench_ab_v2_stats.ts +1018 -0
- package/src/scripts/bench_baseline_ready.ts +326 -0
- package/src/scripts/bench_condense_memory.ts +479 -0
- package/src/scripts/bench_drift_check.ts +503 -0
- package/src/scripts/bench_per_tool.ts +591 -0
- package/src/scripts/bench_rtk_savings.ts +710 -0
- package/src/scripts/bench_run.ts +509 -0
- package/src/scripts/bench_runner.ts +519 -0
- package/src/scripts/build_cloud_bundle.ts +692 -0
- package/src/scripts/build_discovery_manifest.ts +1371 -0
- package/src/scripts/build_linear_digest.ts +368 -0
- package/src/scripts/build_mcp_registry_manifest.ts +351 -0
- package/src/scripts/build_rule_trigger_matrix.ts +469 -0
- package/src/scripts/capture_showcase_session.ts +735 -0
- package/src/scripts/chat_history.ts +2301 -0
- package/src/scripts/check_always_budget.ts +694 -0
- package/src/scripts/check_artefact_checksums.ts +281 -0
- package/src/scripts/check_augment_description_cap.ts +133 -0
- package/src/scripts/check_augmentignore.ts +108 -0
- package/src/scripts/check_beta_review_markers.ts +234 -0
- package/src/scripts/check_bite_sized_granularity.ts +116 -0
- package/src/scripts/check_cluster_patterns.ts +285 -0
- package/src/scripts/check_command_count_messaging.ts +224 -0
- package/src/scripts/check_condensation.ts +900 -0
- package/src/scripts/check_condensed_paths.ts +414 -0
- package/src/scripts/check_context_paths.ts +388 -0
- package/src/scripts/check_council_config_location.ts +260 -0
- package/src/scripts/check_council_layout.ts +180 -0
- package/src/scripts/check_council_references.ts +345 -0
- package/src/scripts/check_discovery_determinism.ts +124 -0
- package/src/scripts/check_gate_paths.ts +230 -0
- package/src/scripts/check_iron_law_prominence.ts +298 -0
- package/src/scripts/check_kernel_rule_bundle.ts +242 -0
- package/src/scripts/check_knowledge_cards.ts +759 -0
- package/src/scripts/check_md_language.ts +291 -0
- package/src/scripts/check_memory.ts +845 -0
- package/src/scripts/check_memory_proposal.ts +351 -0
- package/src/scripts/check_module_management_neutral.ts +238 -0
- package/src/scripts/check_no_conflict_markers.ts +298 -0
- package/src/scripts/check_no_conflict_markers_allowlist.json +4 -0
- package/src/scripts/check_no_external_sources.ts +351 -0
- package/src/scripts/check_no_local_settings_committed.ts +69 -0
- package/src/scripts/check_no_new_legacy_path.ts +188 -0
- package/src/scripts/check_no_roadmap_refs.ts +304 -0
- package/src/scripts/check_one_off_location.ts +165 -0
- package/src/scripts/check_overlay_cascade_subdirs.ts +188 -0
- package/src/scripts/check_portability.ts +860 -0
- package/src/scripts/check_proposal.ts +0 -0
- package/src/scripts/check_public_catalog_links.ts +204 -0
- package/src/scripts/check_public_links.ts +357 -0
- package/src/scripts/check_references.ts +963 -0
- package/src/scripts/check_release_includes_discovery.ts +94 -0
- package/src/scripts/check_release_pr_shape.ts +222 -0
- package/src/scripts/check_release_published.ts +235 -0
- package/src/scripts/check_release_trunk_sync.ts +203 -0
- package/src/scripts/check_reply_consistency.ts +359 -0
- package/src/scripts/check_roadmap_trackable.ts +268 -0
- package/src/scripts/check_role_doc_links.ts +187 -0
- package/src/scripts/check_safety_floor_untouched.ts +160 -0
- package/src/scripts/check_skill_requires.ts +205 -0
- package/src/scripts/check_structural_breaking.ts +170 -0
- package/src/scripts/check_surface_tiers.ts +567 -0
- package/src/scripts/check_template_pin_drift.ts +222 -0
- package/src/scripts/check_test_coverage_diff.ts +235 -0
- package/src/scripts/check_token_optimizer_freshness.ts +183 -0
- package/src/scripts/check_trigger_evals.ts +375 -0
- package/src/scripts/check_update_banner.ts +143 -0
- package/src/scripts/ci_status.ts +0 -0
- package/src/scripts/ci_summary.ts +235 -0
- package/src/scripts/ci_time_ratio.ts +526 -0
- package/src/scripts/command_suggester/cooldown.ts +176 -0
- package/src/scripts/command_suggester/index.ts +41 -0
- package/src/scripts/command_suggester/loader.ts +205 -0
- package/src/scripts/command_suggester/match.ts +294 -0
- package/src/scripts/command_suggester/rank.ts +201 -0
- package/src/scripts/command_suggester/render.ts +122 -0
- package/src/scripts/command_suggester/sanitize.ts +114 -0
- package/src/scripts/command_suggester/settings.ts +186 -0
- package/src/scripts/command_suggester/types.ts +0 -0
- package/src/scripts/compile_router.ts +297 -0
- package/src/scripts/condense.sh +7 -1
- package/src/scripts/condense.ts +2035 -0
- package/src/scripts/condense_memory.ts +334 -0
- package/src/scripts/config/index.ts +15 -0
- package/src/scripts/config/packs.ts +310 -0
- package/src/scripts/config/presets.ts +369 -0
- package/src/scripts/config/profile_explain.ts +114 -0
- package/src/scripts/config/profiles.ts +277 -0
- package/src/scripts/config/session_profiles.ts +1064 -0
- package/src/scripts/context_hygiene_hook.ts +272 -0
- package/src/scripts/cost_by_conversation.ts +444 -0
- package/src/scripts/cost_summary.ts +407 -0
- package/src/scripts/council_cli.ts +2827 -0
- package/src/scripts/council_prune.ts +153 -0
- package/src/scripts/cross_repo_retrieve.ts +694 -0
- package/src/scripts/discovery_stats.ts +218 -0
- package/src/scripts/evidence_report.ts +580 -0
- package/src/scripts/external_sources_denylist.json +92 -0
- package/src/scripts/extract_audit_patterns.ts +394 -0
- package/src/scripts/first_run_gate_hook.ts +246 -0
- package/src/scripts/gen_discovery_baseline.ts +297 -0
- package/src/scripts/generate_capabilities_index.ts +496 -0
- package/src/scripts/generate_capability_matrix.ts +430 -0
- package/src/scripts/generate_catalog.ts +178 -0
- package/src/scripts/generate_command_flows.ts +316 -0
- package/src/scripts/generate_cookbook.ts +302 -0
- package/src/scripts/generate_index.ts +500 -0
- package/src/scripts/generate_ownership_matrix.ts +646 -0
- package/src/scripts/generate_pack_manifests.ts +1025 -0
- package/src/scripts/generate_role_experiences_catalog.ts +265 -0
- package/src/scripts/hermetic-install.sh +22 -11
- package/src/scripts/hook_manifest.yaml +37 -15
- package/src/scripts/hooks/augment-chat-history.sh +3 -10
- package/src/scripts/hooks/augment-context-hygiene.sh +3 -10
- package/src/scripts/hooks/augment-dispatcher.sh +3 -10
- package/src/scripts/hooks/augment-onboarding-gate.sh +3 -10
- package/src/scripts/hooks/augment-roadmap-progress.sh +3 -10
- package/src/scripts/hooks/block_no_verify.ts +413 -0
- package/src/scripts/hooks/cline-dispatcher.sh +3 -10
- package/src/scripts/hooks/cowork-dispatcher.sh +3 -14
- package/src/scripts/hooks/cursor-dispatcher.sh +3 -10
- package/src/scripts/hooks/dispatch_hook.ts +851 -0
- package/src/scripts/hooks/dispatch_issues.ts +226 -0
- package/src/scripts/hooks/envelope.ts +140 -0
- package/src/scripts/hooks/gemini-dispatcher.sh +3 -8
- package/src/scripts/hooks/replay_hook.ts +364 -0
- package/src/scripts/hooks/state_io.ts +293 -0
- package/src/scripts/hooks/windsurf-dispatcher.sh +3 -9
- package/src/scripts/hooks_doctor.ts +418 -0
- package/src/scripts/hooks_status.ts +292 -0
- package/src/scripts/injection_scan_hook.ts +285 -0
- package/src/scripts/install +36 -22
- package/src/scripts/install-hooks.sh +29 -12
- package/src/scripts/install.sh +38 -14
- package/src/scripts/install.ts +4515 -0
- package/src/scripts/inventory_abstraction_budget.ts +1104 -0
- package/src/scripts/inventory_frontmatter.ts +320 -0
- package/src/scripts/inventory_meta_layers.ts +516 -0
- package/src/scripts/iron_law_sha.ts +233 -0
- package/src/scripts/knowledge_global_cli.ts +1105 -0
- package/src/scripts/linked_projects_list.ts +310 -0
- package/src/scripts/lint_agent_security.ts +224 -0
- package/src/scripts/lint_agent_skill_names.ts +241 -0
- package/src/scripts/lint_agents_layout.ts +205 -0
- package/src/scripts/lint_agents_md.ts +294 -0
- package/src/scripts/lint_archived_skills.ts +309 -0
- package/src/scripts/lint_artefact_frontmatter.ts +359 -0
- package/src/scripts/lint_bench_ab.ts +319 -0
- package/src/scripts/lint_bench_corpus.ts +421 -0
- package/src/scripts/lint_command_flow_coverage.ts +231 -0
- package/src/scripts/lint_command_routing.ts +377 -0
- package/src/scripts/lint_command_tiers.ts +345 -0
- package/src/scripts/lint_command_verbs.ts +379 -0
- package/src/scripts/lint_commit_subjects.ts +243 -0
- package/src/scripts/lint_context_spine_usage.ts +198 -0
- package/src/scripts/lint_discovery_manifest.ts +540 -0
- package/src/scripts/lint_discovery_vocabulary.ts +393 -0
- package/src/scripts/lint_empty_roadmaps.ts +147 -0
- package/src/scripts/lint_eval_freshness.ts +335 -0
- package/src/scripts/lint_examples.ts +183 -0
- package/src/scripts/lint_explain_trace.ts +381 -0
- package/src/scripts/lint_featured_skills.ts +0 -0
- package/src/scripts/lint_flows.ts +701 -0
- package/src/scripts/lint_framework_leakage.ts +497 -0
- package/src/scripts/lint_framework_leakage_allowlist.json +8 -1
- package/src/scripts/lint_frontmatter_boilerplate.ts +356 -0
- package/src/scripts/lint_ghostwriter_source.ts +389 -0
- package/src/scripts/lint_global_paths.ts +420 -0
- package/src/scripts/lint_handoffs.ts +362 -0
- package/src/scripts/lint_hidden_unicode.ts +350 -0
- package/src/scripts/lint_hook_concern_budget.ts +319 -0
- package/src/scripts/lint_hook_manifest.ts +354 -0
- package/src/scripts/lint_instruction_smuggling.ts +173 -0
- package/src/scripts/lint_load_context.ts +371 -0
- package/src/scripts/lint_marketplace.ts +286 -0
- package/src/scripts/lint_marketplace_install_completeness.ts +309 -0
- package/src/scripts/lint_mcp_config_security.ts +225 -0
- package/src/scripts/lint_mcp_registry_manifest.ts +350 -0
- package/src/scripts/lint_media_policy_linkage.ts +224 -0
- package/src/scripts/lint_missions.ts +774 -0
- package/src/scripts/lint_model_tier_coverage.ts +151 -0
- package/src/scripts/lint_namespace.ts +295 -0
- package/src/scripts/lint_namespace_collisions.ts +203 -0
- package/src/scripts/lint_new_skill_gate.ts +462 -0
- package/src/scripts/lint_no_new_atomic_commands.ts +342 -0
- package/src/scripts/lint_one_off_age.ts +348 -0
- package/src/scripts/lint_orchestration_dsl.ts +377 -0
- package/src/scripts/lint_orchestrator_auto_detect.ts +177 -0
- package/src/scripts/lint_pack_boundaries.ts +366 -0
- package/src/scripts/lint_pack_dependencies.ts +541 -0
- package/src/scripts/lint_pack_first_win.ts +202 -0
- package/src/scripts/lint_persona_governance.ts +292 -0
- package/src/scripts/lint_positioning.ts +257 -0
- package/src/scripts/lint_profile_overlay_set_only.ts +324 -0
- package/src/scripts/lint_readme_jargon.ts +189 -0
- package/src/scripts/lint_readme_size.ts +73 -0
- package/src/scripts/lint_regression.ts +497 -0
- package/src/scripts/lint_roadmap_ci_steps.ts +252 -0
- package/src/scripts/lint_roadmap_complexity.ts +295 -0
- package/src/scripts/lint_roadmap_later_disposition.ts +357 -0
- package/src/scripts/lint_role_experiences.ts +410 -0
- package/src/scripts/lint_rule_interactions.ts +281 -0
- package/src/scripts/lint_rule_tiers.ts +169 -0
- package/src/scripts/lint_showcase_sessions.ts +254 -0
- package/src/scripts/lint_skill_frontmatter_safety.ts +279 -0
- package/src/scripts/lint_skill_originality.ts +586 -0
- package/src/scripts/lint_skill_originality_allowlist.json +20 -0
- package/src/scripts/lint_skill_tools.ts +320 -0
- package/src/scripts/lint_ticket_buildable.ts +1027 -0
- package/src/scripts/lint_topics_yaml.ts +203 -0
- package/src/scripts/lint_trust_coherence.ts +377 -0
- package/src/scripts/lint_value_dashboard.ts +314 -0
- package/src/scripts/lint_workflow_security.ts +637 -0
- package/src/scripts/lint_workflow_security_allowlist.json +20 -0
- package/src/scripts/lint_workspace_boundary.ts +248 -0
- package/src/scripts/mcp_parity_smoke.ts +638 -0
- package/src/scripts/mcp_render.ts +346 -0
- package/src/scripts/mcp_server/__main__.ts +28 -0
- package/src/scripts/mcp_server/catalog.ts +154 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +2 -3
- package/src/scripts/mcp_server/index.ts +24 -0
- package/src/scripts/mcp_server/metadata.ts +83 -0
- package/src/scripts/mcp_server/prompts.ts +711 -0
- package/src/scripts/mcp_server/resources.ts +343 -0
- package/src/scripts/mcp_server/server.ts +439 -0
- package/src/scripts/mcp_server/telemetry.ts +154 -0
- package/src/scripts/mcp_server/tools.ts +1031 -0
- package/src/scripts/mcp_setup.sh +25 -52
- package/src/scripts/mcp_telemetry_health.ts +362 -0
- package/src/scripts/mcp_telemetry_query.ts +371 -0
- package/src/scripts/mcp_telemetry_store.ts +422 -0
- package/src/scripts/measure_augment_budget.ts +453 -0
- package/src/scripts/measure_density.ts +618 -0
- package/src/scripts/measure_frugality_savings.ts +353 -0
- package/src/scripts/measure_markitdown_lift.ts +299 -0
- package/src/scripts/measure_patterns.ts +682 -0
- package/src/scripts/measure_projection_bytes.ts +425 -0
- package/src/scripts/measure_rule_budget.ts +627 -0
- package/src/scripts/measure_skill_reduction.ts +442 -0
- package/src/scripts/media/lib/adapter-common.sh +247 -0
- package/src/scripts/media/lib/adapter-contract.md +329 -0
- package/src/scripts/media/lib/fixtures/comfyui/result.json +1 -0
- package/src/scripts/media/lib/fixtures/fal/result.json +1 -0
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/flux/result.json +1 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/result.json +1 -0
- package/src/scripts/media/lib/fixtures/gemini-veo/result.json +1 -0
- package/src/scripts/media/lib/fixtures/higgsfield/result.json +1 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/result.json +1 -0
- package/src/scripts/media/lib/fixtures/kling/result.json +1 -0
- package/src/scripts/media/lib/fixtures/musetalk/result.json +1 -0
- package/src/scripts/media/lib/fixtures/openai-images/result.json +1 -0
- package/src/scripts/media/lib/fixtures/recraft/asset-0001.svg +1 -0
- package/src/scripts/media/lib/fixtures/recraft/result.json +1 -0
- package/src/scripts/media/lib/fixtures/replicate/result.json +1 -0
- package/src/scripts/media/lib/fixtures/sora/result.json +1 -0
- package/src/scripts/media/lib/fixtures/syncso/result.json +1 -0
- package/src/scripts/media/lib/load-config.sh +180 -0
- package/src/scripts/media/lib/redact.sh +85 -0
- package/src/scripts/memory_hash.ts +331 -0
- package/src/scripts/memory_lookup.ts +1278 -0
- package/src/scripts/memory_report.ts +845 -0
- package/src/scripts/memory_signal.ts +417 -0
- package/src/scripts/memory_status.ts +189 -0
- package/src/scripts/migrate_command_suggestions.ts +341 -0
- package/src/scripts/migrate_frontmatter_defaults.ts +539 -0
- package/src/scripts/migration_status.ts +301 -0
- package/src/scripts/mine_session.ts +645 -0
- package/src/scripts/minimal_safe_diff_hook.ts +355 -0
- package/src/scripts/move_artefact.ts +869 -0
- package/src/scripts/new_skill.ts +404 -0
- package/src/scripts/onboarding_gate_hook.ts +224 -0
- package/src/scripts/pack_dependency_allowlist.json +2 -2
- package/src/scripts/pack_mcp_content.ts +552 -0
- package/src/scripts/parity/README.md +140 -0
- package/src/scripts/parity/compare.ts +189 -0
- package/src/scripts/parity/coverage_diff.ts +199 -0
- package/src/scripts/parity/phase-manifest.json +93 -0
- package/src/scripts/parity/phase_gate.ts +270 -0
- package/src/scripts/parity/replay.ts +320 -0
- package/src/scripts/pattern_share.ts +363 -0
- package/src/scripts/plan_physical_move.ts +605 -0
- package/src/scripts/prediction-pool/poisson_sim.ts +537 -0
- package/src/scripts/prediction-pool/pool_winsim.ts +677 -0
- package/src/scripts/prediction-pool/score_ev.ts +546 -0
- package/src/scripts/print_required_checks.ts +249 -0
- package/src/scripts/probe_projection_fidelity.ts +468 -0
- package/src/scripts/probe_skill_registration.ts +787 -0
- package/src/scripts/profile_staleness_hook.ts +169 -0
- package/src/scripts/profile_use.ts +227 -0
- package/src/scripts/project_thin_rules.ts +387 -0
- package/src/scripts/propose_modules_config.ts +311 -0
- package/src/scripts/prototype_lint_contradictions.ts +414 -0
- package/src/scripts/prove_pack_extractable.ts +388 -0
- package/src/scripts/readme_linter.ts +913 -0
- package/src/scripts/redact_hook_capture.ts +325 -0
- package/src/scripts/refine_ticket_detect.ts +703 -0
- package/src/scripts/release.ts +1697 -0
- package/src/scripts/render_benchmark_md.ts +664 -0
- package/src/scripts/render_value_md.ts +506 -0
- package/src/scripts/repro/repro_marketplace_install_gap.sh +1 -1
- package/src/scripts/roadmap_progress_hook.ts +410 -0
- package/src/scripts/router_telemetry.ts +972 -0
- package/src/scripts/run.ts +98 -0
- package/src/scripts/run_skill_evals.ts +477 -0
- package/src/scripts/runtime_dispatcher.ts +586 -0
- package/src/scripts/runtime_handler.ts +231 -0
- package/src/scripts/runtime_registry.ts +394 -0
- package/src/scripts/schemas/command.schema.json +7 -1
- package/src/scripts/schemas/mission-catalog.schema.json +112 -0
- package/src/scripts/schemas/mission.schema.json +87 -0
- package/src/scripts/schemas/pack.schema.json +6 -0
- package/src/scripts/schemas/rule.schema.json +1 -0
- package/src/scripts/schemas/skill.schema.json +1 -0
- package/src/scripts/schemas/ticket-manifest.schema.json +35 -0
- package/src/scripts/schemas/ticket.schema.json +60 -0
- package/src/scripts/score_skill_selection.ts +570 -0
- package/src/scripts/security_audit_config.ts +423 -0
- package/src/scripts/skill_collision_clusters.ts +448 -0
- package/src/scripts/skill_discovery.ts +690 -0
- package/src/scripts/skill_linter.ts +4276 -0
- package/src/scripts/skill_overlap.ts +414 -0
- package/src/scripts/skill_preview.ts +548 -0
- package/src/scripts/skill_tools/audit_persona_coverage.ts +427 -0
- package/src/scripts/skill_tools/audit_user_type_coverage.ts +507 -0
- package/src/scripts/skill_tools/index.ts +28 -0
- package/src/scripts/skill_tools/run_block_d_eval.ts +373 -0
- package/src/scripts/skill_tools/score_skill_relevance.ts +475 -0
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +288 -0
- package/src/scripts/skill_trigger_eval.ts +1046 -0
- package/src/scripts/skill_usage_collect.ts +465 -0
- package/src/scripts/skill_usage_report.ts +364 -0
- package/src/scripts/smoke/kernel.sh +4 -5
- package/src/scripts/smoke/router.sh +76 -76
- package/src/scripts/smoke/schema.sh +2 -2
- package/src/scripts/smoke/skills.sh +73 -52
- package/src/scripts/smoke_path_resolution.ts +194 -0
- package/src/scripts/smoke_quickstart.ts +224 -0
- package/src/scripts/snapshot_agent_outputs.ts +375 -0
- package/src/scripts/spotcheck_thin_root.ts +247 -0
- package/src/scripts/surface-tiers.yml +68 -0
- package/src/scripts/sync_agent_settings.ts +763 -0
- package/src/scripts/sync_github_metadata.ts +550 -0
- package/src/scripts/sync_gitignore.ts +630 -0
- package/src/scripts/sync_yaml_rt.ts +910 -0
- package/src/scripts/telegraph_stats.ts +447 -0
- package/src/scripts/tool_registry.ts +330 -0
- package/src/scripts/tools/adapter_errors.ts +93 -0
- package/src/scripts/tools/base_adapter.ts +147 -0
- package/src/scripts/tools/github_adapter.ts +229 -0
- package/src/scripts/tools/jira_adapter.ts +196 -0
- package/src/scripts/trigger_coverage.ts +251 -0
- package/src/scripts/update_counts.ts +284 -0
- package/src/scripts/update_prices.ts +219 -0
- package/src/scripts/validate_agent_settings.ts +265 -0
- package/src/scripts/validate_decision_engine.ts +366 -0
- package/src/scripts/validate_discovery_manifest.ts +160 -0
- package/src/scripts/validate_frontmatter.ts +1030 -0
- package/src/scripts/validate_pack_yaml.ts +0 -0
- package/src/scripts/validate_safe_paths.ts +164 -0
- package/src/scripts/validate_telegraph_carveouts.ts +485 -0
- package/src/scripts/verify_before_complete_hook.ts +306 -0
- package/src/scripts/verify_physical_move.ts +411 -0
- package/src/scripts/wrapper_freshness_hook.ts +179 -0
- package/dist/agent-src/commands/chat-history/learn.md +0 -184
- package/dist/agent-src/commands/chat-history/show.md +0 -113
- package/dist/agent-src/commands/fix/pr-bot-comments.md +0 -157
- package/dist/agent-src/commands/fix/pr-developer-comments.md +0 -163
- package/dist/agent-src/scripts/update_roadmap_progress.py +0 -537
- package/dist/agent-src/skills/corpus-grounding/scripts/bm25_search.py +0 -212
- package/dist/agent-src/skills/corpus-grounding/scripts/decision_engine.py +0 -438
- package/dist/agent-src/skills/corpus-grounding/scripts/ground.py +0 -166
- package/dist/agent-src/skills/corpus-grounding/scripts/schema_validator.py +0 -160
- package/dist/agent-src/skills/design-tokens/scripts/tokens.py +0 -296
- package/dist/agent-src/skills/react-shadcn-ui/scripts/shadcn_add.py +0 -299
- package/dist/agent-src/skills/tailwind-engineer/scripts/tailwind_config_gen.py +0 -463
- package/dist/agent-src/templates/agents/memory/architecture-decisions.example.yml +0 -95
- package/dist/agent-src/templates/scripts/check_memory.py +0 -283
- package/dist/agent-src/templates/scripts/check_memory_proposal.py +0 -180
- package/dist/agent-src/templates/scripts/implement_ticket/__init__.py +0 -94
- package/dist/agent-src/templates/scripts/implement_ticket/__main__.py +0 -15
- package/dist/agent-src/templates/scripts/memory_hash.py +0 -75
- package/dist/agent-src/templates/scripts/memory_lookup.py +0 -577
- package/dist/agent-src/templates/scripts/memory_report.py +0 -184
- package/dist/agent-src/templates/scripts/memory_signal.py +0 -167
- package/dist/agent-src/templates/scripts/memory_status.py +0 -257
- package/dist/agent-src/templates/scripts/pr_review_routing.py +0 -340
- package/dist/agent-src/templates/scripts/pr_risk_review.py +0 -211
- package/dist/agent-src/templates/scripts/telemetry/__init__.py +0 -42
- package/dist/agent-src/templates/scripts/telemetry/aggregator.py +0 -169
- package/dist/agent-src/templates/scripts/telemetry/boundary.py +0 -171
- package/dist/agent-src/templates/scripts/telemetry/engagement.py +0 -297
- package/dist/agent-src/templates/scripts/telemetry/report_renderer.py +0 -197
- package/dist/agent-src/templates/scripts/telemetry/settings.py +0 -177
- package/dist/agent-src/templates/scripts/telemetry_record.py +0 -179
- package/dist/agent-src/templates/scripts/telemetry_report.py +0 -161
- package/dist/agent-src/templates/scripts/telemetry_status.py +0 -142
- package/dist/agent-src/templates/scripts/tier_usage_report.py +0 -183
- package/dist/agent-src/templates/scripts/work_engine/__init__.py +0 -58
- package/dist/agent-src/templates/scripts/work_engine/__main__.py +0 -9
- package/dist/agent-src/templates/scripts/work_engine/_lib/__init__.py +0 -7
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.py +0 -840
- package/dist/agent-src/templates/scripts/work_engine/_lib/user_global_paths.py +0 -249
- package/dist/agent-src/templates/scripts/work_engine/cli.py +0 -195
- package/dist/agent-src/templates/scripts/work_engine/cli_args.py +0 -116
- package/dist/agent-src/templates/scripts/work_engine/delivery_state.py +0 -137
- package/dist/agent-src/templates/scripts/work_engine/directives/__init__.py +0 -33
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/__init__.py +0 -98
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/analyze.py +0 -98
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.py +0 -145
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/memory.py +0 -136
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/plan.py +0 -175
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/refine.py +0 -396
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/report.py +0 -227
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.py +0 -180
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.py +0 -170
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/__init__.py +0 -116
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/contract.py +0 -254
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.py +0 -229
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/ui.py +0 -231
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/__init__.py +0 -113
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/_passthrough.py +0 -44
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.py +0 -241
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.py +0 -414
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.py +0 -335
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.py +0 -513
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.py +0 -471
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/__init__.py +0 -119
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.py +0 -37
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.py +0 -165
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.py +0 -66
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/report.py +0 -62
- package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/test.py +0 -115
- package/dist/agent-src/templates/scripts/work_engine/dispatcher.py +0 -331
- package/dist/agent-src/templates/scripts/work_engine/emitters.py +0 -68
- package/dist/agent-src/templates/scripts/work_engine/errors.py +0 -19
- package/dist/agent-src/templates/scripts/work_engine/hook_bootstrap.py +0 -91
- package/dist/agent-src/templates/scripts/work_engine/hooks/__init__.py +0 -54
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +0 -35
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +0 -59
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +0 -43
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +0 -41
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.py +0 -162
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +0 -163
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.py +0 -53
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.py +0 -50
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +0 -141
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.py +0 -52
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/trace.py +0 -84
- package/dist/agent-src/templates/scripts/work_engine/hooks/context.py +0 -66
- package/dist/agent-src/templates/scripts/work_engine/hooks/events.py +0 -44
- package/dist/agent-src/templates/scripts/work_engine/hooks/exceptions.py +0 -79
- package/dist/agent-src/templates/scripts/work_engine/hooks/registry.py +0 -60
- package/dist/agent-src/templates/scripts/work_engine/hooks/runner.py +0 -73
- package/dist/agent-src/templates/scripts/work_engine/hooks/settings.py +0 -196
- package/dist/agent-src/templates/scripts/work_engine/input_builders.py +0 -163
- package/dist/agent-src/templates/scripts/work_engine/intent/__init__.py +0 -47
- package/dist/agent-src/templates/scripts/work_engine/intent/classify.py +0 -280
- package/dist/agent-src/templates/scripts/work_engine/migration/__init__.py +0 -8
- package/dist/agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +0 -231
- package/dist/agent-src/templates/scripts/work_engine/orchestration.py +0 -193
- package/dist/agent-src/templates/scripts/work_engine/persona_policy.py +0 -85
- package/dist/agent-src/templates/scripts/work_engine/resolvers/__init__.py +0 -22
- package/dist/agent-src/templates/scripts/work_engine/resolvers/diff.py +0 -106
- package/dist/agent-src/templates/scripts/work_engine/resolvers/file.py +0 -113
- package/dist/agent-src/templates/scripts/work_engine/resolvers/prompt.py +0 -90
- package/dist/agent-src/templates/scripts/work_engine/scoring/__init__.py +0 -14
- package/dist/agent-src/templates/scripts/work_engine/scoring/confidence.py +0 -300
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.py +0 -351
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_trace.py +0 -141
- package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +0 -284
- package/dist/agent-src/templates/scripts/work_engine/stack/__init__.py +0 -31
- package/dist/agent-src/templates/scripts/work_engine/stack/detect.py +0 -187
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.py +0 -481
- package/dist/agent-src/templates/scripts/work_engine/state.py +0 -694
- package/dist/agent-src/templates/scripts/work_engine/state_io.py +0 -202
- package/dist/cli/python/resolvePython.js +0 -38
- package/dist/cli/python/resolvePython.js.map +0 -1
- package/docs/case-studies/frontend-design-vs-ui-ux-pro-max.md +0 -86
- package/docs/contracts/agent-memory-contract.md +0 -159
- package/docs/parity/bench-ruflo.json +0 -58
- package/docs/parity/ruflo.md +0 -46
- package/src/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
- package/src/scripts/_archive/_backfill_skill_domains.py +0 -140
- package/src/scripts/_archive/_bootstrap_tier_frontmatter.py +0 -151
- package/src/scripts/_archive/_p43_bodies.py +0 -235
- package/src/scripts/_archive/_p43_condense.py +0 -118
- package/src/scripts/_archive/_p4_migrate.py +0 -199
- package/src/scripts/_archive/_phase2_shim_helper.py +0 -109
- package/src/scripts/_archive/_pilot_council_question.py +0 -57
- package/src/scripts/_cli/__init__.py +0 -0
- package/src/scripts/_cli/cmd_doctor.py +0 -1583
- package/src/scripts/_cli/cmd_explain.py +0 -355
- package/src/scripts/_cli/cmd_export.py +0 -157
- package/src/scripts/_cli/cmd_migrate.py +0 -524
- package/src/scripts/_cli/cmd_prune.py +0 -322
- package/src/scripts/_cli/cmd_refresh.py +0 -179
- package/src/scripts/_cli/cmd_settings_check.py +0 -171
- package/src/scripts/_cli/cmd_settings_migrate.py +0 -147
- package/src/scripts/_cli/cmd_sync.py +0 -166
- package/src/scripts/_cli/cmd_uninstall.py +0 -476
- package/src/scripts/_cli/cmd_update.py +0 -279
- package/src/scripts/_cli/cmd_upgrade.py +0 -172
- package/src/scripts/_cli/cmd_validate.py +0 -177
- package/src/scripts/_cli/cmd_versions.py +0 -160
- package/src/scripts/_cli/explain_last/__init__.py +0 -122
- package/src/scripts/_cli/explain_last/assumptions.py +0 -59
- package/src/scripts/_cli/explain_last/council.py +0 -105
- package/src/scripts/_cli/explain_last/halt.py +0 -44
- package/src/scripts/_cli/explain_last/inputs.py +0 -128
- package/src/scripts/_cli/explain_last/memory.py +0 -94
- package/src/scripts/_cli/explain_last/provider.py +0 -52
- package/src/scripts/_cli/explain_last/render.py +0 -52
- package/src/scripts/_cli/explain_last/route.py +0 -59
- package/src/scripts/_cli/explain_last/scrubber.py +0 -105
- package/src/scripts/_cli/explain_last/sections/__init__.py +0 -35
- package/src/scripts/_cli/explain_last/sections/assumptions.py +0 -21
- package/src/scripts/_cli/explain_last/sections/council.py +0 -27
- package/src/scripts/_cli/explain_last/sections/halt.py +0 -31
- package/src/scripts/_cli/explain_last/sections/header.py +0 -24
- package/src/scripts/_cli/explain_last/sections/inputs.py +0 -27
- package/src/scripts/_cli/explain_last/sections/memory.py +0 -21
- package/src/scripts/_cli/explain_last/sections/pack.py +0 -16
- package/src/scripts/_cli/explain_last/sections/provider.py +0 -26
- package/src/scripts/_cli/explain_last/sections/route.py +0 -22
- package/src/scripts/_cli/explain_last/state_loader.py +0 -76
- package/src/scripts/_emit_domain_table.py +0 -35
- package/src/scripts/_lib/__init__.py +0 -5
- package/src/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
- package/src/scripts/_lib/agent_settings.py +0 -840
- package/src/scripts/_lib/agent_src.py +0 -491
- package/src/scripts/_lib/agents_overlay.py +0 -120
- package/src/scripts/_lib/bench_ab_cache.py +0 -162
- package/src/scripts/_lib/bench_ab_scoring.py +0 -209
- package/src/scripts/_lib/bench_cost.py +0 -138
- package/src/scripts/_lib/bench_quality.py +0 -118
- package/src/scripts/_lib/bench_report.py +0 -149
- package/src/scripts/_lib/bench_telegraph.py +0 -273
- package/src/scripts/_lib/bench_telegraph_report.py +0 -151
- package/src/scripts/_lib/changelog_eras.py +0 -330
- package/src/scripts/_lib/claude_desktop_bundler.py +0 -238
- package/src/scripts/_lib/cli_wrapper.py +0 -64
- package/src/scripts/_lib/fs_atomic.py +0 -116
- package/src/scripts/_lib/global_deploy_inventory.py +0 -282
- package/src/scripts/_lib/install_regenerator.py +0 -134
- package/src/scripts/_lib/installed_lock.py +0 -256
- package/src/scripts/_lib/installed_tools.py +0 -381
- package/src/scripts/_lib/json_pointers.py +0 -260
- package/src/scripts/_lib/linked_projects.py +0 -238
- package/src/scripts/_lib/model_tier.py +0 -52
- package/src/scripts/_lib/module_detection.py +0 -223
- package/src/scripts/_lib/pin_resolver.py +0 -152
- package/src/scripts/_lib/script_output.py +0 -144
- package/src/scripts/_lib/token_count.py +0 -95
- package/src/scripts/_lib/update_check.py +0 -207
- package/src/scripts/_lib/user_global_paths.py +0 -249
- package/src/scripts/_lib/value_ladder.py +0 -696
- package/src/scripts/_lib/value_report.py +0 -455
- package/src/scripts/_phase4_bucket.py +0 -210
- package/src/scripts/_pilot_measure.py +0 -53
- package/src/scripts/_tmp_scan_framework_leakage.py +0 -119
- package/src/scripts/adoption_report.py +0 -195
- package/src/scripts/adoption_snapshot.py +0 -219
- package/src/scripts/adoption_status.py +0 -166
- package/src/scripts/adr/regenerate_index.py +0 -79
- package/src/scripts/ai-video/lib/adapter-common.sh +0 -231
- package/src/scripts/ai-video/lib/adapter-contract.md +0 -329
- package/src/scripts/ai-video/lib/fixtures/comfyui/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/fal/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/gemini-veo/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/higgsfield/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/kling/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/musetalk/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/openai-images/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/replicate/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/sora/result.json +0 -1
- package/src/scripts/ai-video/lib/fixtures/syncso/result.json +0 -1
- package/src/scripts/ai-video/lib/load-config.sh +0 -180
- package/src/scripts/ai-video/lib/redact.sh +0 -85
- package/src/scripts/ai_council/__init__.py +0 -40
- package/src/scripts/ai_council/_default_prices.py +0 -50
- package/src/scripts/ai_council/advisors.py +0 -148
- package/src/scripts/ai_council/airgap.py +0 -165
- package/src/scripts/ai_council/budget_guard.py +0 -202
- package/src/scripts/ai_council/bundler.py +0 -263
- package/src/scripts/ai_council/cli_hints.py +0 -123
- package/src/scripts/ai_council/clients.py +0 -1385
- package/src/scripts/ai_council/compile_corpus.py +0 -179
- package/src/scripts/ai_council/confidence_gate.py +0 -156
- package/src/scripts/ai_council/config.py +0 -1364
- package/src/scripts/ai_council/consensus.py +0 -329
- package/src/scripts/ai_council/events_log.py +0 -141
- package/src/scripts/ai_council/learn_low_impact_preview.py +0 -252
- package/src/scripts/ai_council/low_impact.py +0 -714
- package/src/scripts/ai_council/low_impact_corpus.py +0 -466
- package/src/scripts/ai_council/low_impact_intake.py +0 -163
- package/src/scripts/ai_council/modes.py +0 -131
- package/src/scripts/ai_council/necessity.py +0 -782
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_2a4_acceptance.py +0 -208
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_add_quiet.py +0 -149
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_budget_v2_audit.py +0 -206
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_context_layer_v1_estimate.py +0 -67
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_context_layer_v1_review.py +0 -292
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_followups_review.py +0 -259
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_inject_quiet_flag.py +0 -33
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_v2.sh +0 -36
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_verbosity.sh +0 -26
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_nondestructive_inline_audit.py +0 -209
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_per_task.sh +0 -41
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_phase4_dispatch_latency.py +0 -108
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_phase6_trigger_jaccard.py +0 -92
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_phase_2a_budget_rebalance.py +0 -257
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_phase_2a_post_revert.py +0 -197
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_rebalancing_audit.py +0 -149
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_roundtrip.py +0 -111
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_rule_hardening_v1.py +0 -251
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_silent_taskfiles.py +0 -98
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_structural_open_questions.py +0 -232
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_structural_optimization.py +0 -144
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_structural_v3_gaps.py +0 -252
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_structural_v3_review.py +0 -240
- package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +0 -180
- package/src/scripts/ai_council/orchestrator.py +0 -1206
- package/src/scripts/ai_council/pricing.py +0 -215
- package/src/scripts/ai_council/probation_gate.py +0 -152
- package/src/scripts/ai_council/project_context.py +0 -159
- package/src/scripts/ai_council/prompts.py +0 -567
- package/src/scripts/ai_council/redact_low_impact_entry.py +0 -155
- package/src/scripts/ai_council/replay.py +0 -155
- package/src/scripts/ai_council/session.py +0 -366
- package/src/scripts/ai_council/shadow_dispatch.py +0 -235
- package/src/scripts/ai_council/solo_dispatch.py +0 -226
- package/src/scripts/annotate_discovery.py +0 -149
- package/src/scripts/apply_modules_config.py +0 -290
- package/src/scripts/audit_adr_coverage.py +0 -175
- package/src/scripts/audit_auto_rules.py +0 -175
- package/src/scripts/audit_cloud_compatibility.py +0 -362
- package/src/scripts/audit_command_surface.py +0 -669
- package/src/scripts/audit_initial_context.py +0 -237
- package/src/scripts/audit_likelihood.py +0 -148
- package/src/scripts/audit_mcp_tools.py +0 -146
- package/src/scripts/audit_overlap.py +0 -145
- package/src/scripts/audit_skill_descriptions.py +0 -180
- package/src/scripts/audit_skill_overlap.py +0 -207
- package/src/scripts/audit_user_type_axis.py +0 -140
- package/src/scripts/backfill_model_tier.py +0 -184
- package/src/scripts/bench_ab_cache_dispatch.py +0 -68
- package/src/scripts/bench_ab_clone.py +0 -170
- package/src/scripts/bench_ab_diff.py +0 -220
- package/src/scripts/bench_ab_integrity.py +0 -143
- package/src/scripts/bench_ab_run.py +0 -235
- package/src/scripts/bench_ab_task_runner.py +0 -369
- package/src/scripts/bench_ab_tracka_run.py +0 -202
- package/src/scripts/bench_baseline_ready.py +0 -108
- package/src/scripts/bench_condense_memory.py +0 -168
- package/src/scripts/bench_drift_check.py +0 -151
- package/src/scripts/bench_per_tool.py +0 -216
- package/src/scripts/bench_rtk_savings.py +0 -320
- package/src/scripts/bench_run.py +0 -272
- package/src/scripts/bench_runner.py +0 -158
- package/src/scripts/build_cloud_bundle.py +0 -458
- package/src/scripts/build_discovery_manifest.py +0 -747
- package/src/scripts/build_linear_digest.py +0 -260
- package/src/scripts/build_mcp_registry_manifest.py +0 -181
- package/src/scripts/build_rule_trigger_matrix.py +0 -350
- package/src/scripts/capture_showcase_session.py +0 -361
- package/src/scripts/chat_history.py +0 -1799
- package/src/scripts/check_always_budget.py +0 -532
- package/src/scripts/check_artefact_checksums.py +0 -111
- package/src/scripts/check_augment_description_cap.py +0 -79
- package/src/scripts/check_augmentignore.py +0 -72
- package/src/scripts/check_beta_review_markers.py +0 -127
- package/src/scripts/check_bite_sized_granularity.py +0 -99
- package/src/scripts/check_cluster_patterns.py +0 -206
- package/src/scripts/check_command_count_messaging.py +0 -152
- package/src/scripts/check_condensation.py +0 -375
- package/src/scripts/check_condensed_paths.py +0 -231
- package/src/scripts/check_context_paths.py +0 -202
- package/src/scripts/check_council_layout.py +0 -125
- package/src/scripts/check_council_references.py +0 -228
- package/src/scripts/check_discovery_determinism.py +0 -70
- package/src/scripts/check_gate_paths.py +0 -128
- package/src/scripts/check_iron_law_prominence.py +0 -145
- package/src/scripts/check_kernel_rule_bundle.py +0 -151
- package/src/scripts/check_md_language.py +0 -161
- package/src/scripts/check_memory.py +0 -443
- package/src/scripts/check_memory_proposal.py +0 -182
- package/src/scripts/check_module_management_neutral.py +0 -147
- package/src/scripts/check_no_local_settings_committed.py +0 -51
- package/src/scripts/check_no_new_legacy_path.py +0 -100
- package/src/scripts/check_no_roadmap_refs.py +0 -155
- package/src/scripts/check_one_off_location.py +0 -81
- package/src/scripts/check_overlay_cascade_subdirs.py +0 -129
- package/src/scripts/check_portability.py +0 -574
- package/src/scripts/check_proposal.py +0 -269
- package/src/scripts/check_public_catalog_links.py +0 -125
- package/src/scripts/check_public_links.py +0 -185
- package/src/scripts/check_references.py +0 -557
- package/src/scripts/check_release_includes_discovery.py +0 -61
- package/src/scripts/check_release_pr_shape.py +0 -123
- package/src/scripts/check_release_published.py +0 -145
- package/src/scripts/check_release_trunk_sync.py +0 -152
- package/src/scripts/check_reply_consistency.py +0 -169
- package/src/scripts/check_roadmap_trackable.py +0 -114
- package/src/scripts/check_role_doc_links.py +0 -110
- package/src/scripts/check_safety_floor_untouched.py +0 -125
- package/src/scripts/check_skill_requires.py +0 -147
- package/src/scripts/check_template_pin_drift.py +0 -129
- package/src/scripts/check_test_coverage_diff.py +0 -180
- package/src/scripts/check_token_optimizer_freshness.py +0 -146
- package/src/scripts/check_update_banner.py +0 -86
- package/src/scripts/ci_status.py +0 -301
- package/src/scripts/ci_summary.py +0 -131
- package/src/scripts/ci_time_ratio.py +0 -168
- package/src/scripts/command_suggester/__init__.py +0 -51
- package/src/scripts/command_suggester/cooldown.py +0 -132
- package/src/scripts/command_suggester/loader.py +0 -73
- package/src/scripts/command_suggester/match.py +0 -180
- package/src/scripts/command_suggester/rank.py +0 -120
- package/src/scripts/command_suggester/render.py +0 -86
- package/src/scripts/command_suggester/sanitize.py +0 -113
- package/src/scripts/command_suggester/settings.py +0 -127
- package/src/scripts/command_suggester/types.py +0 -78
- package/src/scripts/compile_router.py +0 -232
- package/src/scripts/condense.py +0 -1919
- package/src/scripts/condense_memory.py +0 -178
- package/src/scripts/config/__init__.py +0 -9
- package/src/scripts/config/packs.py +0 -157
- package/src/scripts/config/presets.py +0 -224
- package/src/scripts/config/profile_explain.py +0 -89
- package/src/scripts/config/profiles.py +0 -191
- package/src/scripts/config/session_profiles.py +0 -542
- package/src/scripts/context_hygiene_hook.py +0 -181
- package/src/scripts/cost_by_conversation.py +0 -78
- package/src/scripts/cost_summary.py +0 -97
- package/src/scripts/council_cli.py +0 -2557
- package/src/scripts/council_prune.py +0 -81
- package/src/scripts/cross_repo_retrieve.py +0 -172
- package/src/scripts/discovery_stats.py +0 -70
- package/src/scripts/extract_audit_patterns.py +0 -202
- package/src/scripts/first_run_gate_hook.py +0 -179
- package/src/scripts/gen_discovery_baseline.py +0 -127
- package/src/scripts/generate_catalog.py +0 -116
- package/src/scripts/generate_command_flows.py +0 -191
- package/src/scripts/generate_index.py +0 -303
- package/src/scripts/generate_ownership_matrix.py +0 -378
- package/src/scripts/generate_pack_manifests.py +0 -340
- package/src/scripts/hooks/__init__.py +0 -1
- package/src/scripts/hooks/dispatch_hook.py +0 -461
- package/src/scripts/hooks/dispatch_issues.py +0 -136
- package/src/scripts/hooks/envelope.py +0 -98
- package/src/scripts/hooks/replay_hook.py +0 -144
- package/src/scripts/hooks/state_io.py +0 -145
- package/src/scripts/hooks_doctor.py +0 -223
- package/src/scripts/hooks_status.py +0 -157
- package/src/scripts/install.py +0 -5183
- package/src/scripts/inventory_abstraction_budget.py +0 -622
- package/src/scripts/inventory_frontmatter.py +0 -164
- package/src/scripts/inventory_meta_layers.py +0 -288
- package/src/scripts/iron_law_sha.py +0 -107
- package/src/scripts/linked_projects_list.py +0 -91
- package/src/scripts/lint_agent_skill_names.py +0 -150
- package/src/scripts/lint_agents_layout.py +0 -197
- package/src/scripts/lint_agents_md.py +0 -210
- package/src/scripts/lint_archived_skills.py +0 -159
- package/src/scripts/lint_artefact_frontmatter.py +0 -188
- package/src/scripts/lint_bench_ab.py +0 -172
- package/src/scripts/lint_bench_corpus.py +0 -255
- package/src/scripts/lint_command_flow_coverage.py +0 -132
- package/src/scripts/lint_command_routing.py +0 -160
- package/src/scripts/lint_command_tiers.py +0 -175
- package/src/scripts/lint_command_verbs.py +0 -206
- package/src/scripts/lint_commit_subjects.py +0 -139
- package/src/scripts/lint_context_spine_usage.py +0 -137
- package/src/scripts/lint_discovery_manifest.py +0 -176
- package/src/scripts/lint_discovery_vocabulary.py +0 -220
- package/src/scripts/lint_examples.py +0 -102
- package/src/scripts/lint_explain_trace.py +0 -80
- package/src/scripts/lint_featured_skills.py +0 -144
- package/src/scripts/lint_flows.py +0 -215
- package/src/scripts/lint_framework_leakage.py +0 -375
- package/src/scripts/lint_frontmatter_boilerplate.py +0 -77
- package/src/scripts/lint_ghostwriter_source.py +0 -242
- package/src/scripts/lint_global_paths.py +0 -148
- package/src/scripts/lint_handoffs.py +0 -217
- package/src/scripts/lint_hook_concern_budget.py +0 -207
- package/src/scripts/lint_hook_manifest.py +0 -217
- package/src/scripts/lint_load_context.py +0 -196
- package/src/scripts/lint_marketplace.py +0 -180
- package/src/scripts/lint_marketplace_install_completeness.py +0 -198
- package/src/scripts/lint_mcp_registry_manifest.py +0 -69
- package/src/scripts/lint_media_policy_linkage.py +0 -140
- package/src/scripts/lint_model_tier_coverage.py +0 -73
- package/src/scripts/lint_namespace.py +0 -135
- package/src/scripts/lint_namespace_collisions.py +0 -103
- package/src/scripts/lint_new_skill_gate.py +0 -144
- package/src/scripts/lint_no_new_atomic_commands.py +0 -180
- package/src/scripts/lint_one_off_age.py +0 -184
- package/src/scripts/lint_orchestration_dsl.py +0 -217
- package/src/scripts/lint_orchestrator_auto_detect.py +0 -111
- package/src/scripts/lint_pack_boundaries.py +0 -147
- package/src/scripts/lint_pack_dependencies.py +0 -137
- package/src/scripts/lint_pack_first_win.py +0 -121
- package/src/scripts/lint_persona_governance.py +0 -164
- package/src/scripts/lint_positioning.py +0 -143
- package/src/scripts/lint_profile_overlay_set_only.py +0 -179
- package/src/scripts/lint_readme_jargon.py +0 -131
- package/src/scripts/lint_readme_size.py +0 -33
- package/src/scripts/lint_regression.py +0 -251
- package/src/scripts/lint_roadmap_ci_steps.py +0 -186
- package/src/scripts/lint_roadmap_complexity.py +0 -220
- package/src/scripts/lint_role_experiences.py +0 -255
- package/src/scripts/lint_rule_interactions.py +0 -170
- package/src/scripts/lint_rule_tiers.py +0 -90
- package/src/scripts/lint_showcase_sessions.py +0 -148
- package/src/scripts/lint_skill_tools.py +0 -168
- package/src/scripts/lint_topics_yaml.py +0 -89
- package/src/scripts/lint_trust_coherence.py +0 -212
- package/src/scripts/lint_value_dashboard.py +0 -218
- package/src/scripts/mcp_parity_smoke.py +0 -316
- package/src/scripts/mcp_render.py +0 -173
- package/src/scripts/mcp_server/__init__.py +0 -19
- package/src/scripts/mcp_server/__main__.py +0 -12
- package/src/scripts/mcp_server/catalog.py +0 -125
- package/src/scripts/mcp_server/metadata.py +0 -75
- package/src/scripts/mcp_server/prompts.py +0 -442
- package/src/scripts/mcp_server/requirements.txt +0 -4
- package/src/scripts/mcp_server/resources.py +0 -201
- package/src/scripts/mcp_server/server.py +0 -270
- package/src/scripts/mcp_server/telemetry.py +0 -128
- package/src/scripts/mcp_server/tools.py +0 -950
- package/src/scripts/mcp_telemetry_health.py +0 -214
- package/src/scripts/mcp_telemetry_query.py +0 -203
- package/src/scripts/mcp_telemetry_store.py +0 -211
- package/src/scripts/measure_augment_budget.py +0 -214
- package/src/scripts/measure_density.py +0 -232
- package/src/scripts/measure_frugality_savings.py +0 -164
- package/src/scripts/measure_markitdown_lift.py +0 -127
- package/src/scripts/measure_patterns.py +0 -376
- package/src/scripts/measure_projection_bytes.py +0 -159
- package/src/scripts/measure_rule_budget.py +0 -347
- package/src/scripts/measure_skill_reduction.py +0 -102
- package/src/scripts/memory_hash.py +0 -75
- package/src/scripts/memory_lookup.py +0 -705
- package/src/scripts/memory_report.py +0 -336
- package/src/scripts/memory_signal.py +0 -212
- package/src/scripts/memory_status.py +0 -257
- package/src/scripts/migrate_command_suggestions.py +0 -151
- package/src/scripts/migrate_frontmatter_defaults.py +0 -245
- package/src/scripts/mine_session.py +0 -279
- package/src/scripts/minimal_safe_diff_hook.py +0 -245
- package/src/scripts/move_artefact.py +0 -143
- package/src/scripts/new_skill.py +0 -148
- package/src/scripts/onboarding_gate_hook.py +0 -142
- package/src/scripts/pack_mcp_content.py +0 -293
- package/src/scripts/plan_physical_move.py +0 -353
- package/src/scripts/prediction-pool/poisson_sim.py +0 -167
- package/src/scripts/prediction-pool/pool_winsim.py +0 -236
- package/src/scripts/prediction-pool/score_ev.py +0 -188
- package/src/scripts/print_required_checks.py +0 -196
- package/src/scripts/probe_projection_fidelity.py +0 -202
- package/src/scripts/probe_skill_registration.py +0 -413
- package/src/scripts/profile_staleness_hook.py +0 -69
- package/src/scripts/profile_use.py +0 -164
- package/src/scripts/project_thin_rules.py +0 -168
- package/src/scripts/propose_modules_config.py +0 -145
- package/src/scripts/prototype_lint_contradictions.py +0 -150
- package/src/scripts/prove_pack_extractable.py +0 -187
- package/src/scripts/readme_linter.py +0 -589
- package/src/scripts/redact_hook_capture.py +0 -148
- package/src/scripts/refine_ticket_detect.py +0 -646
- package/src/scripts/release.py +0 -1091
- package/src/scripts/render_benchmark_md.py +0 -312
- package/src/scripts/render_value_md.py +0 -347
- package/src/scripts/requirements-evals.txt +0 -8
- package/src/scripts/roadmap_progress_hook.py +0 -274
- package/src/scripts/router_telemetry.py +0 -470
- package/src/scripts/run_skill_evals.py +0 -185
- package/src/scripts/runtime_dispatcher.py +0 -276
- package/src/scripts/runtime_handler.py +0 -148
- package/src/scripts/runtime_registry.py +0 -166
- package/src/scripts/score_skill_selection.py +0 -198
- package/src/scripts/setup_eval_venv.sh +0 -58
- package/src/scripts/skill_collision_clusters.py +0 -162
- package/src/scripts/skill_discovery.py +0 -254
- package/src/scripts/skill_linter.py +0 -3694
- package/src/scripts/skill_overlap.py +0 -204
- package/src/scripts/skill_preview.py +0 -179
- package/src/scripts/skill_tools/__init__.py +0 -22
- package/src/scripts/skill_tools/audit_persona_coverage.py +0 -147
- package/src/scripts/skill_tools/audit_user_type_coverage.py +0 -148
- package/src/scripts/skill_tools/run_block_d_eval.py +0 -129
- package/src/scripts/skill_tools/score_skill_relevance.py +0 -169
- package/src/scripts/skill_tools/suggest_skill_for_task.py +0 -113
- package/src/scripts/skill_trigger_eval.py +0 -682
- package/src/scripts/skill_usage_collect.py +0 -191
- package/src/scripts/skill_usage_report.py +0 -162
- package/src/scripts/smoke_path_resolution.py +0 -93
- package/src/scripts/smoke_quickstart.py +0 -144
- package/src/scripts/snapshot_agent_outputs.py +0 -144
- package/src/scripts/spotcheck_thin_root.py +0 -134
- package/src/scripts/sync_agent_settings.py +0 -180
- package/src/scripts/sync_github_metadata.py +0 -147
- package/src/scripts/sync_gitignore.py +0 -291
- package/src/scripts/sync_yaml_rt.py +0 -734
- package/src/scripts/telegraph_stats.py +0 -119
- package/src/scripts/tool_registry.py +0 -146
- package/src/scripts/tools/__init__.py +0 -1
- package/src/scripts/tools/adapter_errors.py +0 -63
- package/src/scripts/tools/base_adapter.py +0 -91
- package/src/scripts/tools/github_adapter.py +0 -128
- package/src/scripts/tools/jira_adapter.py +0 -115
- package/src/scripts/trigger_coverage.py +0 -129
- package/src/scripts/update_counts.py +0 -199
- package/src/scripts/update_prices.py +0 -125
- package/src/scripts/validate_agent_settings.py +0 -124
- package/src/scripts/validate_decision_engine.py +0 -136
- package/src/scripts/validate_discovery_manifest.py +0 -94
- package/src/scripts/validate_frontmatter.py +0 -647
- package/src/scripts/validate_pack_yaml.py +0 -179
- package/src/scripts/validate_safe_paths.py +0 -118
- package/src/scripts/validate_telegraph_carveouts.py +0 -129
- package/src/scripts/verify_before_complete_hook.py +0 -216
- package/src/scripts/verify_physical_move.py +0 -185
- package/src/scripts/wrapper_freshness_hook.py +0 -86
- /package/dist/agent-src/skills/design-intelligence/data/{typography.csv → font-pairings-reference.csv} +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/allin1/analysis.json +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/comfyui/scene-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/fal/scene-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/gemini-veo/scene-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/higgsfield/scene-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/kling/scene-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/musetalk/lipsync-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/openai-images/scene-0001.png +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/replicate/scene-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/sora/scene-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/syncso/lipsync-0001.mp4 +0 -0
- /package/src/scripts/{ai-video → media}/lib/fixtures/whisperx/transcript.json +0 -0
- /package/src/scripts/{ai-video → media}/lib/telemetry.sh +0 -0
|
@@ -0,0 +1,4515 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Agent Config — Project Bridge Installer (TypeScript twin).
|
|
4
|
+
*
|
|
5
|
+
* TypeScript twin of `src/scripts/install.py` (ADR-200, py2ts migration).
|
|
6
|
+
* The CLI contract mirrors the Python original EXACTLY — same flags, same
|
|
7
|
+
* exit codes, same stdout/stderr split, byte-identical emitted output,
|
|
8
|
+
* same filesystem effects, same subprocess argv/cwd/env. No behaviour
|
|
9
|
+
* changes — latent quirks are replicated and flagged inline, not fixed.
|
|
10
|
+
*
|
|
11
|
+
* Generates project bridge files (.agent-settings.yml, .vscode/settings.json,
|
|
12
|
+
* etc.) so that supported AI tools can discover agent-config from the project.
|
|
13
|
+
*
|
|
14
|
+
* On first run in a project that still has the legacy flat-file
|
|
15
|
+
* `.agent-settings` (key=value), the installer migrates it to the new YAML
|
|
16
|
+
* format in `.agent-settings.yml`, leaves a one-shot backup as
|
|
17
|
+
* `.agent-settings.backup.key-value`, and deletes the legacy file. This runs
|
|
18
|
+
* exactly once; subsequent runs are idempotent.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* tsx src/scripts/install.ts # defaults: rule_loading_tier=balanced
|
|
22
|
+
* tsx src/scripts/install.ts --profile=minimal # set rule_loading_tier=minimal (kernel only)
|
|
23
|
+
* tsx src/scripts/install.ts --force # accepted (no-op): installs always overwrite
|
|
24
|
+
* tsx src/scripts/install.ts --skip-bridges # only create .agent-settings.yml
|
|
25
|
+
* tsx src/scripts/install.ts --project <dir> # override project root
|
|
26
|
+
*
|
|
27
|
+
* Idempotent — safe to run multiple times. A run always refreshes every
|
|
28
|
+
* deployed file with the current package content; user configuration
|
|
29
|
+
* (.agent-settings.yml) is merged by the settings layer, never clobbered.
|
|
30
|
+
*
|
|
31
|
+
* --- Parity notes (ADR-200) ---
|
|
32
|
+
*
|
|
33
|
+
* - `fail()` mirrors Python's `sys.exit(1)`: it prints the doctor hint then
|
|
34
|
+
* throws a `SystemExitError(1)` sentinel caught at the CLI entry guard,
|
|
35
|
+
* which sets `process.exitCode`. We never call `process.exit()` (per the
|
|
36
|
+
* migration contract) — every termination flows through a settable
|
|
37
|
+
* `process.exitCode`.
|
|
38
|
+
* - argparse errors (`parser.error`, unknown flags, bad `--scope` choice)
|
|
39
|
+
* throw `ArgparseExit(2)` — argparse's exit code for usage errors.
|
|
40
|
+
* - `-h`/`--help` throws `ArgparseExit(0)` after printing usage.
|
|
41
|
+
* - JSON byte-parity: `json.dumps(..., indent=4, ensure_ascii=False)` →
|
|
42
|
+
* `_jsonDumpsIndent(data, 4, false)`; `indent=2, sort_keys=False` →
|
|
43
|
+
* `_jsonDumpsIndent(data, 2, false)`; `separators=(",",":")` (compact
|
|
44
|
+
* NDJSON) → `_jsonDumpsCompact`. Dict insertion order is preserved (JS
|
|
45
|
+
* object key order matches Python dict order for our string keys).
|
|
46
|
+
* - `_lib.*` and `config.*` imports resolve to the `.ts` twins (never a
|
|
47
|
+
* `.py`). The Python dual-path try/except (`scripts._lib.X` vs `_lib.X`)
|
|
48
|
+
* collapses to a single static import here.
|
|
49
|
+
* - The lazy `_load_*_module()` helpers in the Python are eager static
|
|
50
|
+
* imports here; the sys.path bootstrap they performed is a Python-only
|
|
51
|
+
* import-resolution detail with no observable effect.
|
|
52
|
+
* - `scripts._cli.cmd_migrate` now has a `.ts` twin, so `_run_migrate_to_global`
|
|
53
|
+
* calls `cmd_migrate.main([], { cwd })` directly in-process — exactly as the
|
|
54
|
+
* Python original ran it via `sys.stdout`. The migrator's `out`/`err` sinks
|
|
55
|
+
* default to `process.stdout`/`process.stderr`, preserving the prior
|
|
56
|
+
* `stdio:['ignore','inherit','pipe']` observable contract (stdout text + exit
|
|
57
|
+
* code). Inline reason at the call site.
|
|
58
|
+
* - `webbrowser.open` → a platform open spawn (`open`/`xdg-open`/`start`),
|
|
59
|
+
* best-effort and never fatal, matching the Python's bare-except guard.
|
|
60
|
+
* - `threading.Thread` draining child stderr → an async line reader; the
|
|
61
|
+
* 80-line cap + observable stderr-tail behaviour is preserved.
|
|
62
|
+
* - `signal.SIGTERM`/`SIGKILL` + `os.kill(pid, 0)` liveness → `process.kill`.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
import { spawnSync } from 'node:child_process';
|
|
66
|
+
import * as crypto from 'node:crypto';
|
|
67
|
+
import * as fs from 'node:fs';
|
|
68
|
+
import * as os from 'node:os';
|
|
69
|
+
import * as path from 'node:path';
|
|
70
|
+
import process from 'node:process';
|
|
71
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
72
|
+
import type * as YamlModule from 'yaml';
|
|
73
|
+
|
|
74
|
+
import { build_merge_entries } from './_lib/json_pointers.js';
|
|
75
|
+
import * as installed_lock from './_lib/installed_lock.js';
|
|
76
|
+
import * as surface_tiers from './_lib/surface_tiers.js';
|
|
77
|
+
import * as global_deploy_inventory from './_lib/global_deploy_inventory.js';
|
|
78
|
+
import * as installed_tools from './_lib/installed_tools.js';
|
|
79
|
+
import * as user_global_paths from './_lib/user_global_paths.js';
|
|
80
|
+
import * as claude_desktop_bundler from './_lib/claude_desktop_bundler.js';
|
|
81
|
+
import { find_project_root_with_anchor, load_agent_settings } from './_lib/agent_settings.js';
|
|
82
|
+
import { detect_module_roots } from './_lib/module_detection.js';
|
|
83
|
+
import {
|
|
84
|
+
TIER_TO_CLAUDE_MODEL,
|
|
85
|
+
read_model_tier,
|
|
86
|
+
render_native_model_md,
|
|
87
|
+
} from './_lib/model_tier.js';
|
|
88
|
+
import { main as cmdMigrateMain } from './_cli/cmd_migrate.js';
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Python-runtime parity helpers
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
95
|
+
|
|
96
|
+
/** Mirror of Python `sys.exit(code)` raised by `fail()`. Caught at the CLI entry. */
|
|
97
|
+
class SystemExitError extends Error {
|
|
98
|
+
constructor(public readonly code: number) {
|
|
99
|
+
super(`system-exit-${code}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** argparse usage-error / help exit (code 2 / 0). */
|
|
104
|
+
class ArgparseExit extends Error {
|
|
105
|
+
constructor(public readonly code: number) {
|
|
106
|
+
super(`argparse-exit-${code}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** `os.path.expanduser` — expand a leading `~` / `~user` (we only handle `~`). */
|
|
111
|
+
function expanduser(p: string): string {
|
|
112
|
+
if (p === '~') return os.homedir();
|
|
113
|
+
if (p.startsWith('~/') || p.startsWith('~\\')) {
|
|
114
|
+
return path.join(os.homedir(), p.slice(2));
|
|
115
|
+
}
|
|
116
|
+
return p;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** `Path.resolve()` — absolute, symlink-resolved where possible. */
|
|
120
|
+
function resolvePath(p: string): string {
|
|
121
|
+
try {
|
|
122
|
+
return fs.realpathSync(path.resolve(p));
|
|
123
|
+
} catch {
|
|
124
|
+
return path.resolve(p);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isFile(p: string): boolean {
|
|
129
|
+
try {
|
|
130
|
+
return fs.statSync(p).isFile();
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isDir(p: string): boolean {
|
|
137
|
+
try {
|
|
138
|
+
return fs.statSync(p).isDirectory();
|
|
139
|
+
} catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function pathExists(p: string): boolean {
|
|
145
|
+
try {
|
|
146
|
+
fs.statSync(p);
|
|
147
|
+
return true;
|
|
148
|
+
} catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function isSymlink(p: string): boolean {
|
|
154
|
+
try {
|
|
155
|
+
return fs.lstatSync(p).isSymbolicLink();
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function readText(p: string): string {
|
|
162
|
+
return fs.readFileSync(p, 'utf-8');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function writeText(p: string, content: string): void {
|
|
166
|
+
fs.writeFileSync(p, content, 'utf-8');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** `path.mkdir(parents=True, exist_ok=True)`. */
|
|
170
|
+
function mkdirp(p: string): void {
|
|
171
|
+
fs.mkdirSync(p, { recursive: true });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* `sorted(Path.glob(pattern))` with PyYAML/pathlib component-wise ordering.
|
|
176
|
+
* We only need a non-recursive `*.glob("<pat>")`; pathlib sorts the matched
|
|
177
|
+
* paths lexicographically by their string form, which for sibling entries in
|
|
178
|
+
* one directory is plain string sort — matched here.
|
|
179
|
+
*/
|
|
180
|
+
function sortedGlobStems(directory: string, suffix: string): string[] {
|
|
181
|
+
let entries: string[];
|
|
182
|
+
try {
|
|
183
|
+
entries = fs.readdirSync(directory);
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
const stems: string[] = [];
|
|
188
|
+
for (const name of entries) {
|
|
189
|
+
if (name.endsWith(suffix)) {
|
|
190
|
+
stems.push(name.slice(0, name.length - suffix.length));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// pathlib glob yields full paths; sorted() compares the path strings.
|
|
194
|
+
// The directory prefix is common, so sorting by full path == sorting by
|
|
195
|
+
// filename, == sorting by stem+suffix. Sort by the full filename to match.
|
|
196
|
+
stems.sort((a, b) => {
|
|
197
|
+
const fa = a + suffix;
|
|
198
|
+
const fb = b + suffix;
|
|
199
|
+
return fa < fb ? -1 : fa > fb ? 1 : 0;
|
|
200
|
+
});
|
|
201
|
+
return stems;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Number of `.zip` files directly under `directory` (count only). */
|
|
205
|
+
function countZips(directory: string): number {
|
|
206
|
+
if (!isDir(directory)) return 0;
|
|
207
|
+
let n = 0;
|
|
208
|
+
for (const name of fs.readdirSync(directory)) {
|
|
209
|
+
if (name.endsWith('.zip')) n += 1;
|
|
210
|
+
}
|
|
211
|
+
return n;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** `hashlib.sha256(data).hexdigest()` of a file's bytes, or null when unreadable. */
|
|
215
|
+
function sha256OfFile(p: string): string | null {
|
|
216
|
+
let data: Buffer;
|
|
217
|
+
try {
|
|
218
|
+
data = fs.readFileSync(p);
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Atomic write mirroring the Python `tempfile.mkstemp(...) → os.fdopen → write
|
|
227
|
+
* → os.chmod(0o644) → os.replace` pattern in `_write_consumer_bridge_marker`
|
|
228
|
+
* and `_write_per_tool_project_anchors`. Temp file lives in the same dir so
|
|
229
|
+
* the rename is atomic; cleanup-on-failure mirrors the Python `except` arm.
|
|
230
|
+
*/
|
|
231
|
+
function atomicWrite0644(target: string, body: string, prefix: string): void {
|
|
232
|
+
const dir = path.dirname(target);
|
|
233
|
+
const tmpName = path.join(
|
|
234
|
+
dir,
|
|
235
|
+
`${prefix}${process.pid}.${crypto.randomBytes(6).toString('hex')}.yml.tmp`,
|
|
236
|
+
);
|
|
237
|
+
let fd: number | null = null;
|
|
238
|
+
try {
|
|
239
|
+
fd = fs.openSync(tmpName, 'wx', 0o644);
|
|
240
|
+
fs.writeFileSync(fd, body, 'utf-8');
|
|
241
|
+
fs.closeSync(fd);
|
|
242
|
+
fd = null;
|
|
243
|
+
fs.chmodSync(tmpName, 0o644);
|
|
244
|
+
fs.renameSync(tmpName, target);
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (fd !== null) {
|
|
247
|
+
try {
|
|
248
|
+
fs.closeSync(fd);
|
|
249
|
+
} catch {
|
|
250
|
+
/* fd may already be closed */
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
fs.unlinkSync(tmpName);
|
|
255
|
+
} catch {
|
|
256
|
+
/* best-effort cleanup */
|
|
257
|
+
}
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* `datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")` — second-precision
|
|
264
|
+
* UTC stamp with a `Z` suffix.
|
|
265
|
+
*/
|
|
266
|
+
function utcStamp(now?: Date): string {
|
|
267
|
+
const d = now ?? new Date();
|
|
268
|
+
const pad = (n: number, w = 2) => String(n).padStart(w, '0');
|
|
269
|
+
return (
|
|
270
|
+
`${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}` +
|
|
271
|
+
`T${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}Z`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// --- JSON byte-parity (ensure_ascii=False; insertion order) ---
|
|
276
|
+
|
|
277
|
+
function _jsonStrNoAscii(s: string): string {
|
|
278
|
+
// json.dumps(ensure_ascii=False): escape control chars + " + \, keep >=0x20
|
|
279
|
+
let out = '"';
|
|
280
|
+
for (const ch of s) {
|
|
281
|
+
const code = ch.codePointAt(0) as number;
|
|
282
|
+
switch (ch) {
|
|
283
|
+
case '"':
|
|
284
|
+
out += '\\"';
|
|
285
|
+
break;
|
|
286
|
+
case '\\':
|
|
287
|
+
out += '\\\\';
|
|
288
|
+
break;
|
|
289
|
+
case '\n':
|
|
290
|
+
out += '\\n';
|
|
291
|
+
break;
|
|
292
|
+
case '\r':
|
|
293
|
+
out += '\\r';
|
|
294
|
+
break;
|
|
295
|
+
case '\t':
|
|
296
|
+
out += '\\t';
|
|
297
|
+
break;
|
|
298
|
+
case '\b':
|
|
299
|
+
out += '\\b';
|
|
300
|
+
break;
|
|
301
|
+
case '\f':
|
|
302
|
+
out += '\\f';
|
|
303
|
+
break;
|
|
304
|
+
default:
|
|
305
|
+
if (code < 0x20) {
|
|
306
|
+
out += '\\u' + code.toString(16).padStart(4, '0');
|
|
307
|
+
} else {
|
|
308
|
+
out += ch;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return out + '"';
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function _jsonScalar(value: unknown): string | null {
|
|
316
|
+
if (value === null || value === undefined) return 'null';
|
|
317
|
+
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
|
318
|
+
if (typeof value === 'number') {
|
|
319
|
+
if (!Number.isFinite(value)) {
|
|
320
|
+
if (Number.isNaN(value)) return 'NaN';
|
|
321
|
+
return value > 0 ? 'Infinity' : '-Infinity';
|
|
322
|
+
}
|
|
323
|
+
// Our payloads carry only integers; render as-is.
|
|
324
|
+
return String(value);
|
|
325
|
+
}
|
|
326
|
+
if (typeof value === 'string') return _jsonStrNoAscii(value);
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function _dumpIndent(value: unknown, indent: number, depth: number): string {
|
|
331
|
+
const scalar = _jsonScalar(value);
|
|
332
|
+
if (scalar !== null) return scalar;
|
|
333
|
+
const pad = ' '.repeat(indent * (depth + 1));
|
|
334
|
+
const closePad = ' '.repeat(indent * depth);
|
|
335
|
+
if (Array.isArray(value)) {
|
|
336
|
+
if (value.length === 0) return '[]';
|
|
337
|
+
const items = value.map((v) => pad + _dumpIndent(v, indent, depth + 1));
|
|
338
|
+
return `[\n${items.join(',\n')}\n${closePad}]`;
|
|
339
|
+
}
|
|
340
|
+
if (typeof value === 'object' && value !== null) {
|
|
341
|
+
const obj = value as Record<string, unknown>;
|
|
342
|
+
const keys = Object.keys(obj);
|
|
343
|
+
if (keys.length === 0) return '{}';
|
|
344
|
+
const items = keys.map(
|
|
345
|
+
(k) => `${pad}${_jsonStrNoAscii(k)}: ${_dumpIndent(obj[k], indent, depth + 1)}`,
|
|
346
|
+
);
|
|
347
|
+
return `{\n${items.join(',\n')}\n${closePad}}`;
|
|
348
|
+
}
|
|
349
|
+
return _jsonStrNoAscii(String(value));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** `json.dumps(data, indent=N, ensure_ascii=False)` (sort_keys=False). */
|
|
353
|
+
function jsonDumpsIndent(value: unknown, indent: number): string {
|
|
354
|
+
return _dumpIndent(value, indent, 0);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** `json.dumps(obj, separators=(",", ":"))` — compact, ensure_ascii=False here. */
|
|
358
|
+
function jsonDumpsCompact(value: unknown): string {
|
|
359
|
+
const scalar = _jsonScalar(value);
|
|
360
|
+
if (scalar !== null) return scalar;
|
|
361
|
+
if (Array.isArray(value)) {
|
|
362
|
+
return '[' + value.map((v) => jsonDumpsCompact(v)).join(',') + ']';
|
|
363
|
+
}
|
|
364
|
+
if (typeof value === 'object' && value !== null) {
|
|
365
|
+
const obj = value as Record<string, unknown>;
|
|
366
|
+
return (
|
|
367
|
+
'{' +
|
|
368
|
+
Object.keys(obj)
|
|
369
|
+
.map((k) => `${_jsonStrNoAscii(k)}:${jsonDumpsCompact(obj[k])}`)
|
|
370
|
+
.join(',') +
|
|
371
|
+
'}'
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
return _jsonStrNoAscii(String(value));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** Lazy YAML safe_load mirroring PyYAML (version 1.1), `{}` on every error. */
|
|
378
|
+
function yamlSafeLoad(text: string): unknown {
|
|
379
|
+
let YAML: typeof YamlModule;
|
|
380
|
+
try {
|
|
381
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
382
|
+
YAML = require('yaml') as typeof YamlModule;
|
|
383
|
+
} catch {
|
|
384
|
+
return null; // ImportError → callers collapse to {}
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
const data = YAML.parse(text, { version: '1.1' });
|
|
388
|
+
return data;
|
|
389
|
+
} catch {
|
|
390
|
+
return undefined; // YAMLError sentinel — callers collapse to {}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ---------------------------------------------------------------------------
|
|
395
|
+
// Module-level constants (install.py:51-116)
|
|
396
|
+
// ---------------------------------------------------------------------------
|
|
397
|
+
|
|
398
|
+
const DEFAULT_PROFILE = 'balanced';
|
|
399
|
+
const SUPPORTED_PROFILES: readonly string[] = ['minimal', 'balanced', 'full'];
|
|
400
|
+
const RULE_LOADING_TIER_PLACEHOLDER = '__RULE_LOADING_TIER__';
|
|
401
|
+
const USER_TYPE_PLACEHOLDER = '__USER_TYPE__';
|
|
402
|
+
const USER_TYPES_DIR = 'user-types';
|
|
403
|
+
|
|
404
|
+
const SETTINGS_FILE = '.agent-settings.yml';
|
|
405
|
+
const LEGACY_SETTINGS_FILE = '.agent-settings';
|
|
406
|
+
const LEGACY_BACKUP_FILE = '.agent-settings.backup.key-value';
|
|
407
|
+
|
|
408
|
+
const SETTINGS_SUBDIR: readonly string[] = ['agents', 'settings'];
|
|
409
|
+
|
|
410
|
+
function _canonical_settings_target(project_root: string): string {
|
|
411
|
+
return path.join(project_root, ...SETTINGS_SUBDIR, SETTINGS_FILE);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function _resolve_settings_read(project_root: string): string {
|
|
415
|
+
const canonical = _canonical_settings_target(project_root);
|
|
416
|
+
if (pathExists(canonical)) return canonical;
|
|
417
|
+
const legacy = path.join(project_root, SETTINGS_FILE);
|
|
418
|
+
if (pathExists(legacy)) return legacy;
|
|
419
|
+
return canonical;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const LEGACY_RENAME_MAP: Record<string, string> = {
|
|
423
|
+
cost_profile: 'rule_loading_tier',
|
|
424
|
+
ide: 'personal.ide',
|
|
425
|
+
open_edited_files: 'personal.open_edited_files',
|
|
426
|
+
user_name: 'personal.user_name',
|
|
427
|
+
rtk_installed: 'personal.rtk_installed',
|
|
428
|
+
minimal_output: 'personal.minimal_output',
|
|
429
|
+
play_by_play: 'personal.play_by_play',
|
|
430
|
+
pr_comment_bot_icon: 'project.pr_comment_bot_icon',
|
|
431
|
+
pr_template: 'project.pr_template',
|
|
432
|
+
upstream_repo: 'project.upstream_repo',
|
|
433
|
+
improvement_pr_branch_prefix: 'project.improvement_pr_branch_prefix',
|
|
434
|
+
github_pr_reply_method: 'github.pr_reply_method',
|
|
435
|
+
eloquent_access_style: 'eloquent.access_style',
|
|
436
|
+
skill_improvement_pipeline: 'pipelines.skill_improvement',
|
|
437
|
+
subagent_implementer_model: 'subagents.implementer_model',
|
|
438
|
+
subagent_judge_model: 'subagents.judge_model',
|
|
439
|
+
subagent_max_parallel: 'subagents.max_parallel',
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
// --- Output helpers (module-mutable globals, like the Python QUIET/PROGRESS_NDJSON) ---
|
|
443
|
+
|
|
444
|
+
const state = {
|
|
445
|
+
QUIET: false,
|
|
446
|
+
PROGRESS_NDJSON: false,
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
function _emit_progress(obj: Record<string, unknown>): void {
|
|
450
|
+
if (!state.PROGRESS_NDJSON) return;
|
|
451
|
+
process.stdout.write(jsonDumpsCompact(obj) + '\n');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function _emit_progress_terminal(rc: number): void {
|
|
455
|
+
if (!state.PROGRESS_NDJSON) return;
|
|
456
|
+
if (rc === 0) {
|
|
457
|
+
_emit_progress({ type: 'done' });
|
|
458
|
+
} else {
|
|
459
|
+
_emit_progress({ type: 'error', code: 'E_INSTALL', exitCode: rc });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function info(msg: string): void {
|
|
464
|
+
if (!state.QUIET) process.stdout.write(` ${msg}\n`);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function success(msg: string): void {
|
|
468
|
+
if (!state.QUIET) process.stdout.write(` ✅ ${msg}\n`);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function skip(msg: string): void {
|
|
472
|
+
if (!state.QUIET) process.stdout.write(` ⏭️ ${msg}\n`);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function warn(msg: string): void {
|
|
476
|
+
process.stderr.write(` ⚠️ ${msg}\n`);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** Mirror of Python `fail()`: prints the doctor hint, then `sys.exit(1)`. */
|
|
480
|
+
function fail(msg: string): never {
|
|
481
|
+
process.stderr.write(` ❌ ${msg}\n`);
|
|
482
|
+
process.stderr.write(
|
|
483
|
+
' Diagnose: `./agent-config doctor` ' +
|
|
484
|
+
'(or `--check <id>` for a single category)\n',
|
|
485
|
+
);
|
|
486
|
+
throw new SystemExitError(1);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// --- Package detection ---
|
|
490
|
+
|
|
491
|
+
function detect_package_root(project_root: string): string {
|
|
492
|
+
const npm_path = path.join(project_root, 'node_modules', '@event4u', 'agent-config');
|
|
493
|
+
if (isDir(npm_path)) return resolvePath(npm_path);
|
|
494
|
+
|
|
495
|
+
if (pathExists(path.join(project_root, 'src', 'config', 'profiles', 'minimal.ini'))) {
|
|
496
|
+
return project_root;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
fail(
|
|
500
|
+
'Could not find agent-config package. Install via ' +
|
|
501
|
+
'`npx @event4u/agent-config init` or `npm install -g @event4u/agent-config`.',
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function detect_package_type(package_root: string): string {
|
|
506
|
+
if (package_root.split(path.sep).includes('node_modules')) return 'npm';
|
|
507
|
+
return 'local';
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function detect_package_type_for_project(project_root: string, package_root: string): string {
|
|
511
|
+
const npm_path = resolvePath(
|
|
512
|
+
path.join(project_root, 'node_modules', '@event4u', 'agent-config'),
|
|
513
|
+
);
|
|
514
|
+
const package_resolved = resolvePath(package_root);
|
|
515
|
+
if (package_resolved === npm_path) return 'npm';
|
|
516
|
+
return detect_package_type(package_root);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// --- Conflict resolution ---
|
|
520
|
+
|
|
521
|
+
function _is_interactive(): boolean {
|
|
522
|
+
try {
|
|
523
|
+
return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
|
524
|
+
} catch {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function _resolve_file_conflict(_target: string, _force_hint: boolean): string {
|
|
530
|
+
// del force_hint / del target — deploys always overwrite our own content.
|
|
531
|
+
return 'write';
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// --- File utilities ---
|
|
535
|
+
|
|
536
|
+
function ensure_directory(p: string): void {
|
|
537
|
+
mkdirp(p);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function write_file(p: string, content: string): void {
|
|
541
|
+
ensure_directory(path.dirname(p));
|
|
542
|
+
writeText(p, content);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function read_json_file(p: string): Record<string, unknown> {
|
|
546
|
+
let data: unknown;
|
|
547
|
+
try {
|
|
548
|
+
data = JSON.parse(readText(p));
|
|
549
|
+
} catch {
|
|
550
|
+
warn(`Invalid JSON in ${p}, treating as empty`);
|
|
551
|
+
return {};
|
|
552
|
+
}
|
|
553
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
|
|
554
|
+
warn(`Unexpected JSON shape in ${p}, treating as empty`);
|
|
555
|
+
return {};
|
|
556
|
+
}
|
|
557
|
+
return data as Record<string, unknown>;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function write_json_file(p: string, data: unknown): void {
|
|
561
|
+
const content = jsonDumpsIndent(data, 4) + '\n';
|
|
562
|
+
write_file(p, content);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function _isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
566
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** `copy.deepcopy` for JSON-shaped values. */
|
|
570
|
+
function deepcopy<T>(v: T): T {
|
|
571
|
+
if (v === null || typeof v !== 'object') return v;
|
|
572
|
+
if (Array.isArray(v)) return v.map((x) => deepcopy(x)) as unknown as T;
|
|
573
|
+
const out: Record<string, unknown> = {};
|
|
574
|
+
for (const k of Object.keys(v as Record<string, unknown>)) {
|
|
575
|
+
out[k] = deepcopy((v as Record<string, unknown>)[k]);
|
|
576
|
+
}
|
|
577
|
+
return out as unknown as T;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function deep_merge(
|
|
581
|
+
base: Record<string, unknown>,
|
|
582
|
+
overlay: Record<string, unknown>,
|
|
583
|
+
): Record<string, unknown> {
|
|
584
|
+
const result = deepcopy(base);
|
|
585
|
+
for (const key of Object.keys(overlay)) {
|
|
586
|
+
const value = overlay[key];
|
|
587
|
+
if (
|
|
588
|
+
Object.prototype.hasOwnProperty.call(result, key) &&
|
|
589
|
+
_isPlainObject(result[key]) &&
|
|
590
|
+
_isPlainObject(value)
|
|
591
|
+
) {
|
|
592
|
+
result[key] = deep_merge(
|
|
593
|
+
result[key] as Record<string, unknown>,
|
|
594
|
+
value as Record<string, unknown>,
|
|
595
|
+
);
|
|
596
|
+
} else {
|
|
597
|
+
result[key] = deepcopy(value);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return result;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** Deep structural equality for JSON-shaped values (Python dict `==`). */
|
|
604
|
+
function jsonEqual(a: unknown, b: unknown): boolean {
|
|
605
|
+
if (a === b) return true;
|
|
606
|
+
if (typeof a !== typeof b) return false;
|
|
607
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
608
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
609
|
+
return a.every((v, i) => jsonEqual(v, b[i]));
|
|
610
|
+
}
|
|
611
|
+
if (_isPlainObject(a) && _isPlainObject(b)) {
|
|
612
|
+
const ka = Object.keys(a);
|
|
613
|
+
const kb = Object.keys(b);
|
|
614
|
+
if (ka.length !== kb.length) return false;
|
|
615
|
+
return ka.every(
|
|
616
|
+
(k) => Object.prototype.hasOwnProperty.call(b, k) && jsonEqual(a[k], (b as Record<string, unknown>)[k]),
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function merge_json_file(
|
|
623
|
+
p: string,
|
|
624
|
+
new_data: Record<string, unknown>,
|
|
625
|
+
_force: boolean,
|
|
626
|
+
label: string,
|
|
627
|
+
): Record<string, unknown>[] {
|
|
628
|
+
// del force — our keys are always applied; the flag never gates a write.
|
|
629
|
+
const new_entries = build_merge_entries(label, new_data) as unknown as Record<
|
|
630
|
+
string,
|
|
631
|
+
unknown
|
|
632
|
+
>[];
|
|
633
|
+
|
|
634
|
+
if (!pathExists(p)) {
|
|
635
|
+
write_json_file(p, new_data);
|
|
636
|
+
success(`${label} created`);
|
|
637
|
+
return new_entries;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const existing = read_json_file(p);
|
|
641
|
+
const merged = deep_merge(existing, new_data);
|
|
642
|
+
|
|
643
|
+
if (jsonEqual(merged, existing)) {
|
|
644
|
+
skip(`${label} already configured`);
|
|
645
|
+
return new_entries;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
write_json_file(p, merged);
|
|
649
|
+
success(`${label} updated`);
|
|
650
|
+
return new_entries;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// ---------------------------------------------------------------------------
|
|
654
|
+
// Legacy settings migration
|
|
655
|
+
// ---------------------------------------------------------------------------
|
|
656
|
+
|
|
657
|
+
function _parse_legacy_settings(text: string): [Record<string, string>, string[]] {
|
|
658
|
+
const values: Record<string, string> = {};
|
|
659
|
+
const unknown: string[] = [];
|
|
660
|
+
for (const raw of text.split('\n')) {
|
|
661
|
+
const line = raw.trim();
|
|
662
|
+
if (!line || line.startsWith('#')) continue;
|
|
663
|
+
if (!line.includes('=')) continue;
|
|
664
|
+
const eq = line.indexOf('=');
|
|
665
|
+
const key = line.slice(0, eq).trim();
|
|
666
|
+
const value = line.slice(eq + 1).trim();
|
|
667
|
+
if (!key) continue;
|
|
668
|
+
values[key] = value;
|
|
669
|
+
if (!(key in LEGACY_RENAME_MAP)) unknown.push(key);
|
|
670
|
+
}
|
|
671
|
+
return [values, unknown];
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const _BARE_ID_RE = /^[a-z][a-z0-9_]*$/;
|
|
675
|
+
|
|
676
|
+
function _yaml_scalar(value: string): string {
|
|
677
|
+
if (value === '') return '""';
|
|
678
|
+
if (value === 'true' || value === 'false') return value;
|
|
679
|
+
if (value.length > 0 && /^[0-9]+$/.test(value)) return value; // str.isdigit()
|
|
680
|
+
if (_BARE_ID_RE.test(value)) return value;
|
|
681
|
+
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
682
|
+
return `"${escaped}"`;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function _replace_template_value(template: string, dotted_path: string, value: string): string {
|
|
686
|
+
return _replace_template_value_raw(template, dotted_path, _yaml_scalar(value));
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function _replace_template_value_raw(template: string, dotted_path: string, raw_yaml: string): string {
|
|
690
|
+
const parts = dotted_path.split('.');
|
|
691
|
+
if (parts.length === 0) return template;
|
|
692
|
+
|
|
693
|
+
const sections = parts.slice(0, parts.length - 1);
|
|
694
|
+
const key = parts[parts.length - 1];
|
|
695
|
+
const target_indent = ' '.repeat(sections.length);
|
|
696
|
+
|
|
697
|
+
const header_re = /^(\s*)([A-Za-z_][A-Za-z0-9_]*):\s*$/;
|
|
698
|
+
const scalar_re = /^(\s*)([A-Za-z_][A-Za-z0-9_]*):\s*\S.*$/;
|
|
699
|
+
|
|
700
|
+
const current_path: (string | null)[] = new Array(sections.length).fill(null);
|
|
701
|
+
|
|
702
|
+
const endsNl = template.endsWith('\n');
|
|
703
|
+
const lines = template.split('\n');
|
|
704
|
+
// Python splitlines() drops a trailing empty produced by a final newline.
|
|
705
|
+
if (endsNl && lines.length > 0 && lines[lines.length - 1] === '') {
|
|
706
|
+
lines.pop();
|
|
707
|
+
}
|
|
708
|
+
for (let idx = 0; idx < lines.length; idx += 1) {
|
|
709
|
+
const line = lines[idx] as string;
|
|
710
|
+
const stripped = line.trim();
|
|
711
|
+
if (!stripped || stripped.startsWith('#')) continue;
|
|
712
|
+
|
|
713
|
+
const m_header = header_re.exec(line);
|
|
714
|
+
if (m_header) {
|
|
715
|
+
const indent = m_header[1] as string;
|
|
716
|
+
const name = m_header[2] as string;
|
|
717
|
+
const depth = Math.floor(indent.length / 2);
|
|
718
|
+
if (depth < sections.length) {
|
|
719
|
+
current_path[depth] = name;
|
|
720
|
+
for (let d = depth + 1; d < sections.length; d += 1) {
|
|
721
|
+
current_path[d] = null;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const m_scalar = scalar_re.exec(line);
|
|
728
|
+
if (!m_scalar) continue;
|
|
729
|
+
const indent = m_scalar[1] as string;
|
|
730
|
+
const name = m_scalar[2] as string;
|
|
731
|
+
if (name !== key || indent !== target_indent) continue;
|
|
732
|
+
if (!arrayEqual(current_path, sections)) continue;
|
|
733
|
+
lines[idx] = `${indent}${key}: ${raw_yaml}`;
|
|
734
|
+
return lines.join('\n') + (endsNl ? '\n' : '');
|
|
735
|
+
}
|
|
736
|
+
return template;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function arrayEqual(a: (string | null)[], b: readonly string[]): boolean {
|
|
740
|
+
if (a.length !== b.length) return false;
|
|
741
|
+
return a.every((v, i) => v === b[i]);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function _append_unknown_legacy(
|
|
745
|
+
rendered: string,
|
|
746
|
+
legacy_values: Record<string, string>,
|
|
747
|
+
unknown_keys: string[],
|
|
748
|
+
): string {
|
|
749
|
+
if (unknown_keys.length === 0) return rendered;
|
|
750
|
+
const block = [
|
|
751
|
+
'',
|
|
752
|
+
'# Unknown keys from the legacy .agent-settings — review and drop.',
|
|
753
|
+
'_legacy:',
|
|
754
|
+
];
|
|
755
|
+
for (const key of [...unknown_keys].sort()) {
|
|
756
|
+
block.push(` ${key}: ${_yaml_scalar(legacy_values[key] as string)}`);
|
|
757
|
+
}
|
|
758
|
+
const suffix = block.join('\n') + '\n';
|
|
759
|
+
if (rendered.endsWith('\n')) return rendered + suffix;
|
|
760
|
+
return rendered + '\n' + suffix;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function _migrate_legacy_if_present(project_root: string, template_body: string): string | null {
|
|
764
|
+
const legacy_target = path.join(project_root, LEGACY_SETTINGS_FILE);
|
|
765
|
+
if (!isFile(legacy_target)) return null;
|
|
766
|
+
|
|
767
|
+
const legacy_text = readText(legacy_target);
|
|
768
|
+
const [values, unknown] = _parse_legacy_settings(legacy_text);
|
|
769
|
+
|
|
770
|
+
let rendered = template_body;
|
|
771
|
+
for (const flat_key of Object.keys(values)) {
|
|
772
|
+
if (flat_key in LEGACY_RENAME_MAP) {
|
|
773
|
+
rendered = _replace_template_value(
|
|
774
|
+
rendered,
|
|
775
|
+
LEGACY_RENAME_MAP[flat_key] as string,
|
|
776
|
+
values[flat_key] as string,
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
rendered = _append_unknown_legacy(rendered, values, unknown);
|
|
781
|
+
|
|
782
|
+
const backup_target = path.join(project_root, LEGACY_BACKUP_FILE);
|
|
783
|
+
writeText(backup_target, legacy_text);
|
|
784
|
+
fs.unlinkSync(legacy_target);
|
|
785
|
+
|
|
786
|
+
info(`Migrated legacy ${LEGACY_SETTINGS_FILE} → ${SETTINGS_FILE}`);
|
|
787
|
+
info(`Backup saved to ${LEGACY_BACKUP_FILE}`);
|
|
788
|
+
if (unknown.length > 0) {
|
|
789
|
+
warn(`Legacy keys not in rename map preserved under _legacy: ${[...unknown].sort().join(', ')}`);
|
|
790
|
+
}
|
|
791
|
+
return rendered;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// --- Bridge generators ---
|
|
795
|
+
|
|
796
|
+
function _parse_profile_ini(p: string): Record<string, string> {
|
|
797
|
+
const values: Record<string, string> = {};
|
|
798
|
+
for (const raw of readText(p).split('\n')) {
|
|
799
|
+
const line = raw.trim();
|
|
800
|
+
if (!line || line.startsWith(';') || line.startsWith('#')) continue;
|
|
801
|
+
if (!line.includes('=')) continue;
|
|
802
|
+
const eq = line.indexOf('=');
|
|
803
|
+
values[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
|
|
804
|
+
}
|
|
805
|
+
return values;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const _PLACEHOLDER_RE = /__[A-Z][A-Z0-9_]*__/g;
|
|
809
|
+
|
|
810
|
+
function _render_template(template: string, profile_values: Record<string, string>): string {
|
|
811
|
+
let body = template;
|
|
812
|
+
for (const key of Object.keys(profile_values)) {
|
|
813
|
+
const placeholder = `__${key.toUpperCase()}__`;
|
|
814
|
+
if (body.includes(placeholder)) {
|
|
815
|
+
body = body.split(placeholder).join(profile_values[key]);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
const leftover = [...new Set(body.match(_PLACEHOLDER_RE) ?? [])].sort();
|
|
819
|
+
if (leftover.length > 0) {
|
|
820
|
+
fail('Template has unfilled placeholders after profile render: ' + leftover.join(', '));
|
|
821
|
+
}
|
|
822
|
+
return body;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function _load_valid_user_types(package_root: string): string[] {
|
|
826
|
+
const directory = path.join(package_root, USER_TYPES_DIR);
|
|
827
|
+
if (!isDir(directory)) return [];
|
|
828
|
+
return sortedGlobStems(directory, '.yml');
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function _validate_user_type(package_root: string, value: string): string {
|
|
832
|
+
const cleaned = (value || '').trim();
|
|
833
|
+
if (!cleaned) return '';
|
|
834
|
+
const valid = _load_valid_user_types(package_root);
|
|
835
|
+
if (valid.length === 0) {
|
|
836
|
+
fail(`--user-type=${cleaned} requested but no user-types/*.yml present under ${package_root}`);
|
|
837
|
+
}
|
|
838
|
+
if (!valid.includes(cleaned)) {
|
|
839
|
+
fail(
|
|
840
|
+
`Unknown --user-type=${cleaned}. Valid: ${valid.join(', ')} ` +
|
|
841
|
+
'(empty string disables the filter).',
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
return cleaned;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function _inject_packs(body: string, packs: string[]): string {
|
|
848
|
+
if (packs.length === 0) return body;
|
|
849
|
+
const block = 'packs:\n' + packs.map((p) => ` - ${p}\n`).join('');
|
|
850
|
+
// splitlines(keepends=True)
|
|
851
|
+
const lines = splitlinesKeepends(body);
|
|
852
|
+
const out: string[] = [];
|
|
853
|
+
let inserted = false;
|
|
854
|
+
for (const line of lines) {
|
|
855
|
+
out.push(line);
|
|
856
|
+
if (!inserted && line.startsWith('rule_loading_tier:')) {
|
|
857
|
+
if (!line.endsWith('\n')) {
|
|
858
|
+
out[out.length - 1] = line + '\n';
|
|
859
|
+
}
|
|
860
|
+
out.push(block);
|
|
861
|
+
inserted = true;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
if (!inserted) {
|
|
865
|
+
if (out.length > 0 && !(out[out.length - 1] as string).endsWith('\n')) {
|
|
866
|
+
out[out.length - 1] = (out[out.length - 1] as string) + '\n';
|
|
867
|
+
}
|
|
868
|
+
out.push(block);
|
|
869
|
+
}
|
|
870
|
+
return out.join('');
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/** Python str.splitlines(keepends=True) for `\n`-terminated text. */
|
|
874
|
+
function splitlinesKeepends(text: string): string[] {
|
|
875
|
+
const out: string[] = [];
|
|
876
|
+
let start = 0;
|
|
877
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
878
|
+
if (text[i] === '\n') {
|
|
879
|
+
out.push(text.slice(start, i + 1));
|
|
880
|
+
start = i + 1;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (start < text.length) out.push(text.slice(start));
|
|
884
|
+
return out;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function ensure_agent_settings(
|
|
888
|
+
project_root: string,
|
|
889
|
+
package_root: string,
|
|
890
|
+
profile: string,
|
|
891
|
+
force: boolean,
|
|
892
|
+
user_type: string = '',
|
|
893
|
+
packs: string[] | null = null,
|
|
894
|
+
): void {
|
|
895
|
+
const target = _canonical_settings_target(project_root);
|
|
896
|
+
const profile_source = path.join(package_root, 'src', 'config', 'profiles', `${profile}.ini`);
|
|
897
|
+
const template_source = path.join(package_root, 'src', 'config', 'agent-settings.template.yml');
|
|
898
|
+
|
|
899
|
+
if (!pathExists(profile_source)) fail(`Missing profile preset: ${profile_source}`);
|
|
900
|
+
if (!pathExists(template_source)) fail(`Missing settings template: ${template_source}`);
|
|
901
|
+
|
|
902
|
+
const template = readText(template_source);
|
|
903
|
+
if (!template.includes(RULE_LOADING_TIER_PLACEHOLDER)) {
|
|
904
|
+
fail(`Template is missing placeholder ${RULE_LOADING_TIER_PLACEHOLDER}`);
|
|
905
|
+
}
|
|
906
|
+
if (!template.includes(USER_TYPE_PLACEHOLDER)) {
|
|
907
|
+
fail(`Template is missing placeholder ${USER_TYPE_PLACEHOLDER}`);
|
|
908
|
+
}
|
|
909
|
+
const profile_values = _parse_profile_ini(profile_source);
|
|
910
|
+
if (profile_values['rule_loading_tier'] !== profile) {
|
|
911
|
+
// {v!r} → Python repr of a str (single-quoted) or None.
|
|
912
|
+
const got = 'rule_loading_tier' in profile_values
|
|
913
|
+
? `'${profile_values['rule_loading_tier']}'`
|
|
914
|
+
: 'None';
|
|
915
|
+
fail(
|
|
916
|
+
`Profile preset ${path.basename(profile_source)} has rule_loading_tier=` +
|
|
917
|
+
`${got} but --profile=${profile}`,
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
profile_values['user_type'] = _validate_user_type(package_root, user_type);
|
|
921
|
+
let template_body = _render_template(template, profile_values);
|
|
922
|
+
template_body = _inject_packs(template_body, packs ?? []);
|
|
923
|
+
|
|
924
|
+
// ADR-038: relocate an existing repo-root .agent-settings.yml.
|
|
925
|
+
const legacy_root = path.join(project_root, SETTINGS_FILE);
|
|
926
|
+
if (isFile(legacy_root) && !pathExists(target)) {
|
|
927
|
+
mkdirp(path.dirname(target));
|
|
928
|
+
writeText(target, readText(legacy_root));
|
|
929
|
+
fs.unlinkSync(legacy_root);
|
|
930
|
+
success(`Migrated ${SETTINGS_FILE} → agents/settings/${SETTINGS_FILE} (ADR-038)`);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
const legacy_target = path.join(project_root, LEGACY_SETTINGS_FILE);
|
|
935
|
+
if (isFile(legacy_target) && pathExists(target)) {
|
|
936
|
+
warn(
|
|
937
|
+
`Both ${SETTINGS_FILE} and legacy ${LEGACY_SETTINGS_FILE} exist. ` +
|
|
938
|
+
`Skipping migration to avoid overwriting ${SETTINGS_FILE}. ` +
|
|
939
|
+
'Delete one of them manually and re-run.',
|
|
940
|
+
);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
const migrated = _migrate_legacy_if_present(project_root, template_body);
|
|
945
|
+
if (migrated !== null) {
|
|
946
|
+
write_file(target, migrated);
|
|
947
|
+
success(`${SETTINGS_FILE} migrated from legacy key=value`);
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
if (pathExists(target) && !force) {
|
|
952
|
+
skip(`${SETTINGS_FILE} already exists`);
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
mkdirp(path.dirname(target));
|
|
957
|
+
write_file(target, template_body);
|
|
958
|
+
const user_type_value = profile_values['user_type'] ?? '';
|
|
959
|
+
const suffix = user_type_value ? `, user_type=${user_type_value}` : '';
|
|
960
|
+
success(`${SETTINGS_FILE} created (rule_loading_tier=${profile}${suffix})`);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function ensure_vscode_bridge(project_root: string, package_type: string, force: boolean): void {
|
|
964
|
+
const plugin_paths: Record<string, string> = {
|
|
965
|
+
npm: './node_modules/@event4u/agent-config/plugin/agent-config',
|
|
966
|
+
};
|
|
967
|
+
const plugin_path = plugin_paths[package_type] ?? './plugin/agent-config';
|
|
968
|
+
const bridge = { 'chat.pluginLocations': { [plugin_path]: true } };
|
|
969
|
+
merge_json_file(
|
|
970
|
+
path.join(project_root, '.vscode', 'settings.json'),
|
|
971
|
+
bridge,
|
|
972
|
+
force,
|
|
973
|
+
'.vscode/settings.json',
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
function ensure_augment_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
|
|
978
|
+
const bridge = { enabledPlugins: { 'agent-config@event4u': true } };
|
|
979
|
+
return merge_json_file(
|
|
980
|
+
path.join(project_root, '.augment', 'settings.json'),
|
|
981
|
+
bridge,
|
|
982
|
+
force,
|
|
983
|
+
'.augment/settings.json',
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const AUGMENT_USER_DIR = path.join(os.homedir(), '.augment');
|
|
988
|
+
const AUGMENT_USER_HOOKS_DIR = path.join(AUGMENT_USER_DIR, 'hooks');
|
|
989
|
+
const AUGMENT_DISPATCHER_TRAMPOLINE = 'augment-dispatcher.sh';
|
|
990
|
+
const AUGMENT_LEGACY_TRAMPOLINES: readonly string[] = [
|
|
991
|
+
'augment-chat-history.sh',
|
|
992
|
+
'augment-roadmap-progress.sh',
|
|
993
|
+
'augment-onboarding-gate.sh',
|
|
994
|
+
'augment-context-hygiene.sh',
|
|
995
|
+
];
|
|
996
|
+
const AUGMENT_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string]> = [
|
|
997
|
+
['session_start', 'SessionStart'],
|
|
998
|
+
['session_end', 'SessionEnd'],
|
|
999
|
+
['stop', 'Stop'],
|
|
1000
|
+
['pre_tool_use', 'PreToolUse'],
|
|
1001
|
+
['post_tool_use', 'PostToolUse'],
|
|
1002
|
+
];
|
|
1003
|
+
|
|
1004
|
+
function _deploy_augment_trampoline(package_root: string, name: string, force: boolean): string | null {
|
|
1005
|
+
const src = path.join(package_root, 'scripts', 'hooks', name);
|
|
1006
|
+
if (!pathExists(src)) {
|
|
1007
|
+
skip(`augment trampoline missing in package: ${src}`);
|
|
1008
|
+
return null;
|
|
1009
|
+
}
|
|
1010
|
+
mkdirp(AUGMENT_USER_HOOKS_DIR);
|
|
1011
|
+
const dst = path.join(AUGMENT_USER_HOOKS_DIR, name);
|
|
1012
|
+
const src_text = readText(src);
|
|
1013
|
+
if (pathExists(dst) && readText(dst) === src_text && !force) {
|
|
1014
|
+
skip(`~/.augment/hooks/${name} already up to date`);
|
|
1015
|
+
} else {
|
|
1016
|
+
writeText(dst, src_text);
|
|
1017
|
+
fs.chmodSync(dst, 0o755);
|
|
1018
|
+
success(`~/.augment/hooks/${name} installed`);
|
|
1019
|
+
}
|
|
1020
|
+
return dst;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
function _remove_legacy_augment_trampolines(): void {
|
|
1024
|
+
for (const name of AUGMENT_LEGACY_TRAMPOLINES) {
|
|
1025
|
+
const legacy = path.join(AUGMENT_USER_HOOKS_DIR, name);
|
|
1026
|
+
try {
|
|
1027
|
+
if (isFile(legacy)) {
|
|
1028
|
+
fs.unlinkSync(legacy);
|
|
1029
|
+
skip(`removed legacy ~/.augment/hooks/${name}`);
|
|
1030
|
+
}
|
|
1031
|
+
} catch {
|
|
1032
|
+
/* OSError → ignore */
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
function ensure_augment_user_hooks(package_root: string, force: boolean): Record<string, unknown>[] {
|
|
1038
|
+
const dst = _deploy_augment_trampoline(package_root, AUGMENT_DISPATCHER_TRAMPOLINE, force);
|
|
1039
|
+
if (dst === null) return [];
|
|
1040
|
+
|
|
1041
|
+
_remove_legacy_augment_trampolines();
|
|
1042
|
+
|
|
1043
|
+
const per_event: Record<string, unknown[]> = {};
|
|
1044
|
+
for (const [ac_event, native] of AUGMENT_DISPATCHER_BINDINGS) {
|
|
1045
|
+
const cmd = `${dst} ${ac_event} ${native}`;
|
|
1046
|
+
const entry = { hooks: [{ type: 'command', command: cmd }] };
|
|
1047
|
+
(per_event[native] ??= []).push(entry);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
const settings_patch = { hooks: per_event };
|
|
1051
|
+
return merge_json_file(
|
|
1052
|
+
path.join(AUGMENT_USER_DIR, 'settings.json'),
|
|
1053
|
+
settings_patch,
|
|
1054
|
+
force,
|
|
1055
|
+
'~/.augment/settings.json',
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const CLAUDE_PLUGIN_ID = 'agent-config@event4u-agent-config';
|
|
1060
|
+
const CLAUDE_LEGACY_PLUGIN_IDS: readonly string[] = [
|
|
1061
|
+
'agent-conf@event4u',
|
|
1062
|
+
'agent-config@event4u',
|
|
1063
|
+
];
|
|
1064
|
+
|
|
1065
|
+
function _heal_legacy_claude_plugin_ids(p: string): string[] {
|
|
1066
|
+
if (!pathExists(p)) return [];
|
|
1067
|
+
const data = read_json_file(p);
|
|
1068
|
+
const enabled = data['enabledPlugins'];
|
|
1069
|
+
if (!_isPlainObject(enabled)) return [];
|
|
1070
|
+
const removed = CLAUDE_LEGACY_PLUGIN_IDS.filter((pid) => pid in enabled);
|
|
1071
|
+
if (removed.length === 0) return [];
|
|
1072
|
+
for (const pid of removed) {
|
|
1073
|
+
delete (enabled as Record<string, unknown>)[pid];
|
|
1074
|
+
}
|
|
1075
|
+
write_json_file(p, data);
|
|
1076
|
+
return removed;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function ensure_claude_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
|
|
1080
|
+
const target = path.join(project_root, '.claude', 'settings.json');
|
|
1081
|
+
const healed = _heal_legacy_claude_plugin_ids(target);
|
|
1082
|
+
for (const pid of healed) {
|
|
1083
|
+
success(`.claude/settings.json: removed stale plugin id \`${pid}\``);
|
|
1084
|
+
}
|
|
1085
|
+
const bridge = { enabledPlugins: { [CLAUDE_PLUGIN_ID]: true } };
|
|
1086
|
+
return merge_json_file(target, bridge, force || healed.length > 0, '.claude/settings.json');
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
const CURSOR_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string]> = [
|
|
1090
|
+
['session_start', 'sessionStart'],
|
|
1091
|
+
['session_end', 'sessionEnd'],
|
|
1092
|
+
['stop', 'stop'],
|
|
1093
|
+
['user_prompt_submit', 'beforeSubmitPrompt'],
|
|
1094
|
+
['post_tool_use', 'postToolUse'],
|
|
1095
|
+
];
|
|
1096
|
+
|
|
1097
|
+
function _cursor_dispatch_command(ac_event: string, native: string): string {
|
|
1098
|
+
return (
|
|
1099
|
+
`./agent-config dispatch:hook ` +
|
|
1100
|
+
`--platform cursor --event ${ac_event} ` +
|
|
1101
|
+
`--native-event ${native}`
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
function ensure_cursor_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
|
|
1106
|
+
const hooks: Record<string, unknown[]> = {};
|
|
1107
|
+
for (const [ac_event, native] of CURSOR_DISPATCHER_BINDINGS) {
|
|
1108
|
+
(hooks[native] ??= []).push({ command: _cursor_dispatch_command(ac_event, native) });
|
|
1109
|
+
}
|
|
1110
|
+
const bridge = { version: 1, hooks };
|
|
1111
|
+
return merge_json_file(
|
|
1112
|
+
path.join(project_root, '.cursor', 'hooks.json'),
|
|
1113
|
+
bridge,
|
|
1114
|
+
force,
|
|
1115
|
+
'.cursor/hooks.json',
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const CURSOR_USER_DIR = path.join(os.homedir(), '.cursor');
|
|
1120
|
+
const CURSOR_USER_HOOKS_DIR = path.join(CURSOR_USER_DIR, 'hooks');
|
|
1121
|
+
const CURSOR_DISPATCHER_TRAMPOLINE = 'cursor-dispatcher.sh';
|
|
1122
|
+
|
|
1123
|
+
function ensure_cursor_user_hooks(package_root: string, force: boolean): Record<string, unknown>[] {
|
|
1124
|
+
const src = path.join(package_root, 'scripts', 'hooks', CURSOR_DISPATCHER_TRAMPOLINE);
|
|
1125
|
+
if (!pathExists(src)) {
|
|
1126
|
+
skip(`cursor trampoline missing in package: ${src}`);
|
|
1127
|
+
return [];
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
mkdirp(CURSOR_USER_HOOKS_DIR);
|
|
1131
|
+
const dst = path.join(CURSOR_USER_HOOKS_DIR, CURSOR_DISPATCHER_TRAMPOLINE);
|
|
1132
|
+
const src_text = readText(src);
|
|
1133
|
+
if (pathExists(dst) && readText(dst) === src_text && !force) {
|
|
1134
|
+
skip(`~/.cursor/hooks/${CURSOR_DISPATCHER_TRAMPOLINE} already up to date`);
|
|
1135
|
+
} else {
|
|
1136
|
+
writeText(dst, src_text);
|
|
1137
|
+
fs.chmodSync(dst, 0o755);
|
|
1138
|
+
success(`~/.cursor/hooks/${CURSOR_DISPATCHER_TRAMPOLINE} installed`);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
const hooks: Record<string, unknown[]> = {};
|
|
1142
|
+
for (const [ac_event, native] of CURSOR_DISPATCHER_BINDINGS) {
|
|
1143
|
+
(hooks[native] ??= []).push({ command: `${dst} ${ac_event} ${native}` });
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const settings_patch = { version: 1, hooks };
|
|
1147
|
+
return merge_json_file(
|
|
1148
|
+
path.join(CURSOR_USER_DIR, 'hooks.json'),
|
|
1149
|
+
settings_patch,
|
|
1150
|
+
force,
|
|
1151
|
+
'~/.cursor/hooks.json',
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
const CLINE_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string]> = [
|
|
1156
|
+
['session_start', 'TaskStart'],
|
|
1157
|
+
['session_start', 'TaskResume'],
|
|
1158
|
+
['session_end', 'TaskComplete'],
|
|
1159
|
+
['stop', 'TaskCancel'],
|
|
1160
|
+
['user_prompt_submit', 'UserPromptSubmit'],
|
|
1161
|
+
['post_tool_use', 'PostToolUse'],
|
|
1162
|
+
];
|
|
1163
|
+
|
|
1164
|
+
/** `shlex.quote` — POSIX shell single-quote escaping. */
|
|
1165
|
+
function shlexQuote(s: string): string {
|
|
1166
|
+
if (s === '') return "''";
|
|
1167
|
+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(s)) return s;
|
|
1168
|
+
return "'" + s.replace(/'/g, "'\"'\"'") + "'";
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function clineProjectHookBody(native_event: string, ac_event: string, workspace_quoted: string): string {
|
|
1172
|
+
// Faithful render of CLINE_PROJECT_HOOK_TEMPLATE.format(...). The Python
|
|
1173
|
+
// template uses doubled braces ({{ }}) that .format() collapses to single.
|
|
1174
|
+
return (
|
|
1175
|
+
'#!/usr/bin/env bash\n' +
|
|
1176
|
+
'# Generated by event4u/agent-config install.py — DO NOT EDIT.\n' +
|
|
1177
|
+
`# Project-scope Cline hook for ${native_event} → agent-config ${ac_event}.\n` +
|
|
1178
|
+
'# Phase 7.6 (docs/contracts/hook-architecture-v1.md).\n' +
|
|
1179
|
+
'set -u\n' +
|
|
1180
|
+
'EVENT_DATA="$(cat)"\n' +
|
|
1181
|
+
`WORKSPACE_ROOT=${workspace_quoted}\n` +
|
|
1182
|
+
'cd "$WORKSPACE_ROOT" 2>/dev/null || { printf \'%s\\n\' \'{}\'; exit 0; }\n' +
|
|
1183
|
+
'if [ ! -x ./agent-config ]; then\n' +
|
|
1184
|
+
' printf \'%s\\n\' \'{}\'\n' +
|
|
1185
|
+
' exit 0\n' +
|
|
1186
|
+
'fi\n' +
|
|
1187
|
+
'printf \'%s\' "$EVENT_DATA" \\\n' +
|
|
1188
|
+
' | ./agent-config dispatch:hook \\\n' +
|
|
1189
|
+
' --platform cline \\\n' +
|
|
1190
|
+
` --event ${ac_event} \\\n` +
|
|
1191
|
+
` --native-event ${native_event} \\\n` +
|
|
1192
|
+
' >/dev/null 2>&1 || true\n' +
|
|
1193
|
+
'printf \'%s\\n\' \'{}\'\n' +
|
|
1194
|
+
'exit 0\n'
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
function ensure_cline_bridge(project_root: string, force: boolean): void {
|
|
1199
|
+
const hooks_dir = path.join(project_root, '.clinerules', 'hooks');
|
|
1200
|
+
mkdirp(hooks_dir);
|
|
1201
|
+
|
|
1202
|
+
const workspace_quoted = shlexQuote(resolvePath(project_root));
|
|
1203
|
+
let written = 0;
|
|
1204
|
+
for (const [ac_event, native_event] of CLINE_DISPATCHER_BINDINGS) {
|
|
1205
|
+
const target = path.join(hooks_dir, native_event);
|
|
1206
|
+
const body = clineProjectHookBody(native_event, ac_event, workspace_quoted);
|
|
1207
|
+
if (pathExists(target) && readText(target) === body && !force) {
|
|
1208
|
+
continue;
|
|
1209
|
+
}
|
|
1210
|
+
if (pathExists(target) && !force) {
|
|
1211
|
+
skip(`.clinerules/hooks/${native_event} exists, needs update (use --force)`);
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
writeText(target, body);
|
|
1215
|
+
fs.chmodSync(target, 0o755);
|
|
1216
|
+
written += 1;
|
|
1217
|
+
}
|
|
1218
|
+
if (written) {
|
|
1219
|
+
success(`.clinerules/hooks/ — ${written} script(s) installed`);
|
|
1220
|
+
} else {
|
|
1221
|
+
skip('.clinerules/hooks/ already up to date');
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
const CLINE_USER_DIR = path.join(os.homedir(), 'Documents', 'Cline', 'Hooks');
|
|
1226
|
+
const CLINE_DISPATCHER_TRAMPOLINE = 'cline-dispatcher.sh';
|
|
1227
|
+
|
|
1228
|
+
function ensure_cline_user_hooks(package_root: string, force: boolean): void {
|
|
1229
|
+
const src = path.join(package_root, 'scripts', 'hooks', CLINE_DISPATCHER_TRAMPOLINE);
|
|
1230
|
+
if (!pathExists(src)) {
|
|
1231
|
+
skip(`cline trampoline missing in package: ${src}`);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
mkdirp(CLINE_USER_DIR);
|
|
1236
|
+
const trampoline = path.join(CLINE_USER_DIR, CLINE_DISPATCHER_TRAMPOLINE);
|
|
1237
|
+
const src_text = readText(src);
|
|
1238
|
+
if (pathExists(trampoline) && readText(trampoline) === src_text && !force) {
|
|
1239
|
+
skip(`~/Documents/Cline/Hooks/${CLINE_DISPATCHER_TRAMPOLINE} already up to date`);
|
|
1240
|
+
} else {
|
|
1241
|
+
writeText(trampoline, src_text);
|
|
1242
|
+
fs.chmodSync(trampoline, 0o755);
|
|
1243
|
+
success(`~/Documents/Cline/Hooks/${CLINE_DISPATCHER_TRAMPOLINE} installed`);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
const trampoline_quoted = shlexQuote(trampoline);
|
|
1247
|
+
for (const [ac_event, native_event] of CLINE_DISPATCHER_BINDINGS) {
|
|
1248
|
+
const wrapper = path.join(CLINE_USER_DIR, native_event);
|
|
1249
|
+
const body =
|
|
1250
|
+
'#!/usr/bin/env bash\n' +
|
|
1251
|
+
'# Generated by event4u/agent-config install.py — DO NOT EDIT.\n' +
|
|
1252
|
+
`# User-scope Cline hook for ${native_event} → agent-config ${ac_event}.\n` +
|
|
1253
|
+
`exec ${trampoline_quoted} ${ac_event} ${native_event}\n`;
|
|
1254
|
+
if (pathExists(wrapper) && readText(wrapper) === body && !force) {
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
writeText(wrapper, body);
|
|
1258
|
+
fs.chmodSync(wrapper, 0o755);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
const WINDSURF_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string]> = [
|
|
1263
|
+
['session_start', 'post_setup_worktree'],
|
|
1264
|
+
['user_prompt_submit', 'pre_user_prompt'],
|
|
1265
|
+
['stop', 'post_cascade_response'],
|
|
1266
|
+
];
|
|
1267
|
+
|
|
1268
|
+
function _windsurf_dispatch_command(ac_event: string, native: string): string {
|
|
1269
|
+
return (
|
|
1270
|
+
`./agent-config dispatch:hook ` +
|
|
1271
|
+
`--platform windsurf --event ${ac_event} ` +
|
|
1272
|
+
`--native-event ${native}`
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function ensure_windsurf_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
|
|
1277
|
+
const hooks: Record<string, unknown[]> = {};
|
|
1278
|
+
for (const [ac_event, native] of WINDSURF_DISPATCHER_BINDINGS) {
|
|
1279
|
+
(hooks[native] ??= []).push({
|
|
1280
|
+
command: _windsurf_dispatch_command(ac_event, native),
|
|
1281
|
+
show_output: false,
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
const bridge = { hooks };
|
|
1285
|
+
return merge_json_file(
|
|
1286
|
+
path.join(project_root, '.windsurf', 'hooks.json'),
|
|
1287
|
+
bridge,
|
|
1288
|
+
force,
|
|
1289
|
+
'.windsurf/hooks.json',
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
const WINDSURF_USER_DIR = path.join(os.homedir(), '.codeium', 'windsurf');
|
|
1294
|
+
const WINDSURF_USER_HOOKS_DIR = path.join(WINDSURF_USER_DIR, 'hooks');
|
|
1295
|
+
const WINDSURF_DISPATCHER_TRAMPOLINE = 'windsurf-dispatcher.sh';
|
|
1296
|
+
|
|
1297
|
+
function ensure_windsurf_user_hooks(package_root: string, force: boolean): Record<string, unknown>[] {
|
|
1298
|
+
const src = path.join(package_root, 'scripts', 'hooks', WINDSURF_DISPATCHER_TRAMPOLINE);
|
|
1299
|
+
if (!pathExists(src)) {
|
|
1300
|
+
skip(`windsurf trampoline missing in package: ${src}`);
|
|
1301
|
+
return [];
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
mkdirp(WINDSURF_USER_HOOKS_DIR);
|
|
1305
|
+
const dst = path.join(WINDSURF_USER_HOOKS_DIR, WINDSURF_DISPATCHER_TRAMPOLINE);
|
|
1306
|
+
const src_text = readText(src);
|
|
1307
|
+
if (pathExists(dst) && readText(dst) === src_text && !force) {
|
|
1308
|
+
skip(`~/.codeium/windsurf/hooks/${WINDSURF_DISPATCHER_TRAMPOLINE} already up to date`);
|
|
1309
|
+
} else {
|
|
1310
|
+
writeText(dst, src_text);
|
|
1311
|
+
fs.chmodSync(dst, 0o755);
|
|
1312
|
+
success(`~/.codeium/windsurf/hooks/${WINDSURF_DISPATCHER_TRAMPOLINE} installed`);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
const hooks: Record<string, unknown[]> = {};
|
|
1316
|
+
for (const [ac_event, native] of WINDSURF_DISPATCHER_BINDINGS) {
|
|
1317
|
+
(hooks[native] ??= []).push({
|
|
1318
|
+
command: `${dst} ${ac_event} ${native}`,
|
|
1319
|
+
show_output: false,
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
const settings_patch = { hooks };
|
|
1324
|
+
return merge_json_file(
|
|
1325
|
+
path.join(WINDSURF_USER_DIR, 'hooks.json'),
|
|
1326
|
+
settings_patch,
|
|
1327
|
+
force,
|
|
1328
|
+
'~/.codeium/windsurf/hooks.json',
|
|
1329
|
+
);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
const GEMINI_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string, string]> = [
|
|
1333
|
+
['session_start', 'SessionStart', ''],
|
|
1334
|
+
['session_end', 'SessionEnd', ''],
|
|
1335
|
+
['stop', 'AfterAgent', ''],
|
|
1336
|
+
['user_prompt_submit', 'BeforeAgent', ''],
|
|
1337
|
+
['post_tool_use', 'AfterTool', '.*'],
|
|
1338
|
+
];
|
|
1339
|
+
|
|
1340
|
+
function _gemini_dispatch_command(ac_event: string, native: string): string {
|
|
1341
|
+
return (
|
|
1342
|
+
`./agent-config dispatch:hook ` +
|
|
1343
|
+
`--platform gemini --event ${ac_event} ` +
|
|
1344
|
+
`--native-event ${native}`
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
function _gemini_hooks_dict(
|
|
1349
|
+
command_factory: (ac_event: string, native: string) => string,
|
|
1350
|
+
): Record<string, unknown[]> {
|
|
1351
|
+
const out: Record<string, unknown[]> = {};
|
|
1352
|
+
for (const [ac_event, native, matcher] of GEMINI_DISPATCHER_BINDINGS) {
|
|
1353
|
+
(out[native] ??= []).push({
|
|
1354
|
+
matcher,
|
|
1355
|
+
hooks: [{ type: 'command', command: command_factory(ac_event, native) }],
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
return out;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
function ensure_gemini_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
|
|
1362
|
+
const bridge = { hooks: _gemini_hooks_dict(_gemini_dispatch_command) };
|
|
1363
|
+
return merge_json_file(
|
|
1364
|
+
path.join(project_root, '.gemini', 'settings.json'),
|
|
1365
|
+
bridge,
|
|
1366
|
+
force,
|
|
1367
|
+
'.gemini/settings.json',
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
const GEMINI_USER_DIR = path.join(os.homedir(), '.gemini');
|
|
1372
|
+
const GEMINI_USER_HOOKS_DIR = path.join(GEMINI_USER_DIR, 'hooks');
|
|
1373
|
+
const GEMINI_DISPATCHER_TRAMPOLINE = 'gemini-dispatcher.sh';
|
|
1374
|
+
|
|
1375
|
+
function ensure_gemini_user_hooks(package_root: string, force: boolean): Record<string, unknown>[] {
|
|
1376
|
+
const src = path.join(package_root, 'scripts', 'hooks', GEMINI_DISPATCHER_TRAMPOLINE);
|
|
1377
|
+
if (!pathExists(src)) {
|
|
1378
|
+
skip(`gemini trampoline missing in package: ${src}`);
|
|
1379
|
+
return [];
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
mkdirp(GEMINI_USER_HOOKS_DIR);
|
|
1383
|
+
const dst = path.join(GEMINI_USER_HOOKS_DIR, GEMINI_DISPATCHER_TRAMPOLINE);
|
|
1384
|
+
const src_text = readText(src);
|
|
1385
|
+
if (pathExists(dst) && readText(dst) === src_text && !force) {
|
|
1386
|
+
skip(`~/.gemini/hooks/${GEMINI_DISPATCHER_TRAMPOLINE} already up to date`);
|
|
1387
|
+
} else {
|
|
1388
|
+
writeText(dst, src_text);
|
|
1389
|
+
fs.chmodSync(dst, 0o755);
|
|
1390
|
+
success(`~/.gemini/hooks/${GEMINI_DISPATCHER_TRAMPOLINE} installed`);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
const settings_patch = {
|
|
1394
|
+
hooks: _gemini_hooks_dict((ac_event, native) => `${dst} ${ac_event} ${native}`),
|
|
1395
|
+
};
|
|
1396
|
+
return merge_json_file(
|
|
1397
|
+
path.join(GEMINI_USER_DIR, 'settings.json'),
|
|
1398
|
+
settings_patch,
|
|
1399
|
+
force,
|
|
1400
|
+
'~/.gemini/settings.json',
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
function ensure_copilot_bridge(project_root: string, force: boolean): void {
|
|
1405
|
+
const target = path.join(project_root, '.github', 'plugin', 'marketplace.json');
|
|
1406
|
+
const bridge = {
|
|
1407
|
+
marketplace: {
|
|
1408
|
+
name: 'event4u-agent-marketplace',
|
|
1409
|
+
plugins: [
|
|
1410
|
+
{
|
|
1411
|
+
id: 'agent-config@event4u',
|
|
1412
|
+
repository: 'https://github.com/event4u-app/agent-config',
|
|
1413
|
+
},
|
|
1414
|
+
],
|
|
1415
|
+
},
|
|
1416
|
+
};
|
|
1417
|
+
if (pathExists(target) && !force) {
|
|
1418
|
+
skip('.github/plugin/marketplace.json already exists');
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
write_json_file(target, bridge);
|
|
1422
|
+
success('.github/plugin/marketplace.json created');
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
const ROOCODE_MARKER = `# Agent Config bridge
|
|
1426
|
+
|
|
1427
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1428
|
+
|
|
1429
|
+
Roo Code reads \`.roo/rules/*.md\` as system-level instructions. The
|
|
1430
|
+
canonical rule and skill source lives under \`.augment/\` (Augment
|
|
1431
|
+
portability mirror — see \`AGENTS.md\` for orientation).
|
|
1432
|
+
|
|
1433
|
+
## How to use
|
|
1434
|
+
|
|
1435
|
+
- These rules load automatically on every Roo Code session — no
|
|
1436
|
+
manual action required.
|
|
1437
|
+
- Switch Roo Code modes (Architect / Code / Ask / Debug / Custom)
|
|
1438
|
+
via the mode switcher to invoke different cognition profiles;
|
|
1439
|
+
every mode still sees these rules.
|
|
1440
|
+
- Slash commands and skills live under \`.augment/commands/\` and
|
|
1441
|
+
\`.augment/skills/\`. Roo Code does not register them natively —
|
|
1442
|
+
invoke them by name in chat (e.g. *"run the create-pr command"*).
|
|
1443
|
+
|
|
1444
|
+
See \`docs/setup/per-ide/roocode.md\` for the full activation guide.
|
|
1445
|
+
|
|
1446
|
+
Run \`./agent-config --help\` for available commands.
|
|
1447
|
+
`;
|
|
1448
|
+
|
|
1449
|
+
function ensure_roocode_bridge(project_root: string, force: boolean): void {
|
|
1450
|
+
const target = path.join(project_root, '.roo', 'rules', 'agent-config.md');
|
|
1451
|
+
if (pathExists(target) && !force) {
|
|
1452
|
+
skip('.roo/rules/agent-config.md already exists');
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
write_file(target, ROOCODE_MARKER);
|
|
1456
|
+
success('.roo/rules/agent-config.md created');
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const CLAUDE_DESKTOP_MARKER = `# Agent Config bridge — Claude Desktop
|
|
1460
|
+
|
|
1461
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1462
|
+
|
|
1463
|
+
Claude Desktop is a **global-scope** tool — it reads config from
|
|
1464
|
+
\`~/Library/Application Support/Claude/\` (macOS) and does not
|
|
1465
|
+
auto-discover project files. This marker is informational only.
|
|
1466
|
+
|
|
1467
|
+
To wire Claude Desktop to this project's rules, run:
|
|
1468
|
+
\`npx @event4u/agent-config init --ai claude-desktop --global\`
|
|
1469
|
+
|
|
1470
|
+
Canonical rule and skill source: \`.augment/\` (see \`AGENTS.md\`).
|
|
1471
|
+
`;
|
|
1472
|
+
|
|
1473
|
+
function ensure_claude_desktop_bridge(project_root: string, force: boolean): void {
|
|
1474
|
+
const target = path.join(project_root, '.claude-desktop', 'agent-config.md');
|
|
1475
|
+
if (pathExists(target) && !force) {
|
|
1476
|
+
skip('.claude-desktop/agent-config.md already exists');
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
write_file(target, CLAUDE_DESKTOP_MARKER);
|
|
1480
|
+
success('.claude-desktop/agent-config.md created');
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
const AIDER_MARKER = `# Agent Config bridge — Aider
|
|
1484
|
+
|
|
1485
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1486
|
+
|
|
1487
|
+
Aider does not auto-discover this file. To activate it, add the
|
|
1488
|
+
following to \`.aider.conf.yml\` (create if missing):
|
|
1489
|
+
|
|
1490
|
+
\`\`\`yaml
|
|
1491
|
+
read:
|
|
1492
|
+
- .aider/agent-config.md
|
|
1493
|
+
\`\`\`
|
|
1494
|
+
|
|
1495
|
+
Or pass \`--read .aider/agent-config.md\` on the command line.
|
|
1496
|
+
|
|
1497
|
+
Canonical rule and skill source: \`.augment/\` (see \`AGENTS.md\`).
|
|
1498
|
+
`;
|
|
1499
|
+
|
|
1500
|
+
function ensure_aider_bridge(project_root: string, force: boolean): void {
|
|
1501
|
+
const target = path.join(project_root, '.aider', 'agent-config.md');
|
|
1502
|
+
if (pathExists(target) && !force) {
|
|
1503
|
+
skip('.aider/agent-config.md already exists');
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
write_file(target, AIDER_MARKER);
|
|
1507
|
+
success('.aider/agent-config.md created');
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
const CODEX_MARKER = `# Agent Config bridge — Codex CLI
|
|
1511
|
+
|
|
1512
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1513
|
+
|
|
1514
|
+
Codex CLI auto-discovers \`AGENTS.md\` at the project root — that file
|
|
1515
|
+
is the canonical entry point. This marker is informational and tells
|
|
1516
|
+
developers where the rules and skills live.
|
|
1517
|
+
|
|
1518
|
+
Canonical rule and skill source: \`.augment/\` (see project \`AGENTS.md\`).
|
|
1519
|
+
`;
|
|
1520
|
+
|
|
1521
|
+
function ensure_codex_bridge(project_root: string, force: boolean): void {
|
|
1522
|
+
const target = path.join(project_root, '.codex', 'agent-config.md');
|
|
1523
|
+
if (pathExists(target) && !force) {
|
|
1524
|
+
skip('.codex/agent-config.md already exists');
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
write_file(target, CODEX_MARKER);
|
|
1528
|
+
success('.codex/agent-config.md created');
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
const CONTINUE_MARKER = `# Agent Config bridge — Continue.dev
|
|
1532
|
+
|
|
1533
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1534
|
+
|
|
1535
|
+
Continue.dev auto-discovers \`.continue/rules/*.md\` as system-level
|
|
1536
|
+
rules per session. The canonical rule and skill source lives under
|
|
1537
|
+
\`.augment/\` (Augment portability mirror — see \`AGENTS.md\` for
|
|
1538
|
+
orientation).
|
|
1539
|
+
`;
|
|
1540
|
+
|
|
1541
|
+
function ensure_continue_bridge(project_root: string, force: boolean): void {
|
|
1542
|
+
const target = path.join(project_root, '.continue', 'rules', 'agent-config.md');
|
|
1543
|
+
if (pathExists(target) && !force) {
|
|
1544
|
+
skip('.continue/rules/agent-config.md already exists');
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
write_file(target, CONTINUE_MARKER);
|
|
1548
|
+
success('.continue/rules/agent-config.md created');
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
const KILOCODE_MARKER = `# Agent Config bridge — Kilo Code
|
|
1552
|
+
|
|
1553
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1554
|
+
|
|
1555
|
+
Kilo Code auto-discovers \`.kilocode/rules/*.md\` as system-level rules
|
|
1556
|
+
per session. The canonical rule and skill source lives under
|
|
1557
|
+
\`.augment/\` (Augment portability mirror — see \`AGENTS.md\` for
|
|
1558
|
+
orientation).
|
|
1559
|
+
|
|
1560
|
+
## How to use
|
|
1561
|
+
|
|
1562
|
+
- These rules load automatically on every Kilo Code session — no
|
|
1563
|
+
manual action required.
|
|
1564
|
+
- Switch Kilo Code modes (Architect / Code / Ask / Debug /
|
|
1565
|
+
Orchestrator) via the mode switcher to invoke different
|
|
1566
|
+
cognition profiles; every mode still sees these rules.
|
|
1567
|
+
- Slash commands and skills live under \`.augment/commands/\` and
|
|
1568
|
+
\`.augment/skills/\`. Kilo Code does not register them natively —
|
|
1569
|
+
invoke them by name in chat (e.g. *"run the create-pr command"*).
|
|
1570
|
+
|
|
1571
|
+
See \`docs/setup/per-ide/kilocode.md\` for the full activation guide.
|
|
1572
|
+
`;
|
|
1573
|
+
|
|
1574
|
+
function ensure_kilocode_bridge(project_root: string, force: boolean): void {
|
|
1575
|
+
const target = path.join(project_root, '.kilocode', 'rules', 'agent-config.md');
|
|
1576
|
+
if (pathExists(target) && !force) {
|
|
1577
|
+
skip('.kilocode/rules/agent-config.md already exists');
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
write_file(target, KILOCODE_MARKER);
|
|
1581
|
+
success('.kilocode/rules/agent-config.md created');
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
const ZED_MARKER = `# Agent Config bridge — Zed
|
|
1585
|
+
|
|
1586
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1587
|
+
|
|
1588
|
+
Zed reads \`.rules\` at the project root as system-level instructions —
|
|
1589
|
+
that file is the canonical entry point. This marker is informational
|
|
1590
|
+
and tells developers where the rules and skills live.
|
|
1591
|
+
|
|
1592
|
+
To activate agent-config under Zed, point Zed's \`.rules\` at the
|
|
1593
|
+
canonical source (or symlink it):
|
|
1594
|
+
|
|
1595
|
+
\`\`\`
|
|
1596
|
+
# Append to .rules at project root
|
|
1597
|
+
@.augment/AGENTS.md
|
|
1598
|
+
\`\`\`
|
|
1599
|
+
|
|
1600
|
+
Canonical rule and skill source: \`.augment/\` (see \`AGENTS.md\`).
|
|
1601
|
+
`;
|
|
1602
|
+
|
|
1603
|
+
function ensure_zed_bridge(project_root: string, force: boolean): void {
|
|
1604
|
+
const target = path.join(project_root, '.zed', 'agent-config.md');
|
|
1605
|
+
if (pathExists(target) && !force) {
|
|
1606
|
+
skip('.zed/agent-config.md already exists');
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
write_file(target, ZED_MARKER);
|
|
1610
|
+
success('.zed/agent-config.md created');
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
const JETBRAINS_MARKER = `# Agent Config bridge — JetBrains AI Assistant
|
|
1614
|
+
|
|
1615
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1616
|
+
|
|
1617
|
+
JetBrains AI Assistant reads custom prompts and guidelines from
|
|
1618
|
+
project-level config (\`.idea/\`) and user-scope settings. This marker
|
|
1619
|
+
is informational — to wire agent-config into JetBrains AI, point the
|
|
1620
|
+
assistant's custom-prompts path at \`.augment/\` or copy the relevant
|
|
1621
|
+
rules into your JetBrains profile.
|
|
1622
|
+
|
|
1623
|
+
Canonical rule and skill source: \`.augment/\` (see \`AGENTS.md\`).
|
|
1624
|
+
`;
|
|
1625
|
+
|
|
1626
|
+
function ensure_jetbrains_bridge(project_root: string, force: boolean): void {
|
|
1627
|
+
const target = path.join(project_root, '.jetbrains', 'agent-config.md');
|
|
1628
|
+
if (pathExists(target) && !force) {
|
|
1629
|
+
skip('.jetbrains/agent-config.md already exists');
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
write_file(target, JETBRAINS_MARKER);
|
|
1633
|
+
success('.jetbrains/agent-config.md created');
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
const KIRO_MARKER = `# Agent Config bridge — Kiro
|
|
1637
|
+
|
|
1638
|
+
This file marks the project as an \`event4u/agent-config\` consumer.
|
|
1639
|
+
|
|
1640
|
+
Kiro auto-discovers \`.kiro/steering/*.md\` as steering documents per
|
|
1641
|
+
session. The canonical rule and skill source lives under \`.augment/\`
|
|
1642
|
+
(Augment portability mirror — see \`AGENTS.md\` for orientation).
|
|
1643
|
+
|
|
1644
|
+
## How to use
|
|
1645
|
+
|
|
1646
|
+
- Steering documents load automatically on every Kiro session — no
|
|
1647
|
+
manual action required.
|
|
1648
|
+
- For structured, plan-first work, use Kiro's **Spec** workflow
|
|
1649
|
+
(the agent produces a spec → tasks → implementation under your
|
|
1650
|
+
review). For free-form work, use **Vibe**. Both honor these
|
|
1651
|
+
steering documents.
|
|
1652
|
+
- Slash commands and skills live under \`.augment/commands/\` and
|
|
1653
|
+
\`.augment/skills/\`. Kiro does not register them natively —
|
|
1654
|
+
invoke them by name in chat (e.g. *"run the create-pr command"*).
|
|
1655
|
+
|
|
1656
|
+
See \`docs/setup/per-ide/kiro.md\` for the full activation guide.
|
|
1657
|
+
`;
|
|
1658
|
+
|
|
1659
|
+
function ensure_kiro_bridge(project_root: string, force: boolean): void {
|
|
1660
|
+
const target = path.join(project_root, '.kiro', 'steering', 'agent-config.md');
|
|
1661
|
+
if (pathExists(target) && !force) {
|
|
1662
|
+
skip('.kiro/steering/agent-config.md already exists');
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
write_file(target, KIRO_MARKER);
|
|
1666
|
+
success('.kiro/steering/agent-config.md created');
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// --- Post-install smoke test ---
|
|
1670
|
+
|
|
1671
|
+
const SMOKE_PROBE_EVENTS: ReadonlyArray<readonly [string, string]> = [
|
|
1672
|
+
['augment', 'session_start'],
|
|
1673
|
+
['claude', 'SessionStart'],
|
|
1674
|
+
['cursor', 'beforeShellExecution'],
|
|
1675
|
+
['cline', 'session_start'],
|
|
1676
|
+
['windsurf', 'post_setup_worktree'],
|
|
1677
|
+
['gemini', 'SessionStart'],
|
|
1678
|
+
];
|
|
1679
|
+
|
|
1680
|
+
const SMOKE_BRIDGE_PATHS: Record<string, string> = {
|
|
1681
|
+
augment: '.augment/settings.json',
|
|
1682
|
+
claude: '.claude/settings.json',
|
|
1683
|
+
cursor: '.cursor/hooks.json',
|
|
1684
|
+
cline: '.clinerules/hooks',
|
|
1685
|
+
windsurf: '.windsurf/hooks.json',
|
|
1686
|
+
gemini: '.gemini/settings.json',
|
|
1687
|
+
};
|
|
1688
|
+
|
|
1689
|
+
function dirHasEntries(p: string): boolean {
|
|
1690
|
+
try {
|
|
1691
|
+
return fs.readdirSync(p).length > 0;
|
|
1692
|
+
} catch {
|
|
1693
|
+
return false;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Resolve how to run a `.ts` script: prefer the `tsx` binary from a
|
|
1699
|
+
* `node_modules/.bin` directory found by walking up from the script's
|
|
1700
|
+
* directory; fall back to `npx tsx`. Mirrors
|
|
1701
|
+
* `dispatch_hook.ts::_resolve_tsx_invocation` so the smoke probe runs the
|
|
1702
|
+
* dispatcher as TypeScript with no python3 dependency.
|
|
1703
|
+
*/
|
|
1704
|
+
function _resolve_tsx_invocation(
|
|
1705
|
+
scriptPath: string,
|
|
1706
|
+
scriptArgs: string[],
|
|
1707
|
+
): { command: string; args: string[] } {
|
|
1708
|
+
const binName = process.platform === 'win32' ? 'tsx.cmd' : 'tsx';
|
|
1709
|
+
let dir = path.dirname(scriptPath);
|
|
1710
|
+
for (;;) {
|
|
1711
|
+
const candidate = path.join(dir, 'node_modules', '.bin', binName);
|
|
1712
|
+
if (isFile(candidate)) {
|
|
1713
|
+
return { command: candidate, args: [scriptPath, ...scriptArgs] };
|
|
1714
|
+
}
|
|
1715
|
+
const parent = path.dirname(dir);
|
|
1716
|
+
if (parent === dir) break;
|
|
1717
|
+
dir = parent;
|
|
1718
|
+
}
|
|
1719
|
+
return { command: 'npx', args: ['tsx', scriptPath, ...scriptArgs] };
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
function _smoke_test_hooks(project_root: string, package_root: string): number {
|
|
1723
|
+
// The dispatcher is a `.ts` concern run via tsx; in an installed package
|
|
1724
|
+
// layout it ships under `scripts/hooks/dispatch_hook.ts` (the package's
|
|
1725
|
+
// projection scripts dir, not `src/scripts`).
|
|
1726
|
+
const dispatcher = path.join(package_root, 'scripts', 'hooks', 'dispatch_hook.ts');
|
|
1727
|
+
const manifest = path.join(package_root, 'scripts', 'hook_manifest.yaml');
|
|
1728
|
+
if (!isFile(dispatcher) || !isFile(manifest)) return 0;
|
|
1729
|
+
|
|
1730
|
+
const failed: string[] = [];
|
|
1731
|
+
const skipped: string[] = [];
|
|
1732
|
+
const passed: string[] = [];
|
|
1733
|
+
|
|
1734
|
+
for (const [platform, native] of SMOKE_PROBE_EVENTS) {
|
|
1735
|
+
const rel_bridge = SMOKE_BRIDGE_PATHS[platform] ?? '';
|
|
1736
|
+
const bridge_path = rel_bridge ? path.join(project_root, rel_bridge) : null;
|
|
1737
|
+
const bridge_present = Boolean(
|
|
1738
|
+
bridge_path && (isFile(bridge_path) || (isDir(bridge_path) && dirHasEntries(bridge_path))),
|
|
1739
|
+
);
|
|
1740
|
+
if (!bridge_present) {
|
|
1741
|
+
skipped.push(platform);
|
|
1742
|
+
continue;
|
|
1743
|
+
}
|
|
1744
|
+
const dispatcherArgs = [
|
|
1745
|
+
'--manifest',
|
|
1746
|
+
manifest,
|
|
1747
|
+
'--platform',
|
|
1748
|
+
platform,
|
|
1749
|
+
'--event',
|
|
1750
|
+
'session_start',
|
|
1751
|
+
'--native-event',
|
|
1752
|
+
native,
|
|
1753
|
+
'--dry-run',
|
|
1754
|
+
];
|
|
1755
|
+
const inv = _resolve_tsx_invocation(dispatcher, dispatcherArgs);
|
|
1756
|
+
let res;
|
|
1757
|
+
try {
|
|
1758
|
+
res = spawnSync(inv.command, inv.args, {
|
|
1759
|
+
input: '{}',
|
|
1760
|
+
encoding: 'utf-8',
|
|
1761
|
+
cwd: project_root,
|
|
1762
|
+
timeout: 10000,
|
|
1763
|
+
});
|
|
1764
|
+
} catch (exc) {
|
|
1765
|
+
failed.push(`${platform}: ${String(exc)}`);
|
|
1766
|
+
continue;
|
|
1767
|
+
}
|
|
1768
|
+
// spawnSync surfaces ENOENT / timeout via res.error rather than throwing.
|
|
1769
|
+
if (res.error) {
|
|
1770
|
+
failed.push(`${platform}: ${String(res.error)}`);
|
|
1771
|
+
continue;
|
|
1772
|
+
}
|
|
1773
|
+
const returncode = res.status ?? 1;
|
|
1774
|
+
if (returncode !== 0) {
|
|
1775
|
+
const errTail = (res.stderr || '').trim().slice(0, 120);
|
|
1776
|
+
failed.push(`${platform}: exit=${returncode} ${errTail}`);
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
1779
|
+
let plan: unknown;
|
|
1780
|
+
try {
|
|
1781
|
+
plan = JSON.parse(res.stdout || '{}');
|
|
1782
|
+
} catch {
|
|
1783
|
+
failed.push(`${platform}: dispatcher did not emit JSON plan`);
|
|
1784
|
+
continue;
|
|
1785
|
+
}
|
|
1786
|
+
const concerns = _isPlainObject(plan) ? (plan as Record<string, unknown>)['concerns'] : undefined;
|
|
1787
|
+
if (!Array.isArray(concerns)) {
|
|
1788
|
+
failed.push(`${platform}: plan.concerns missing or not a list`);
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
passed.push(platform);
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
if (!state.QUIET) {
|
|
1795
|
+
if (passed.length) success(`hook smoke passed: ${passed.join(', ')}`);
|
|
1796
|
+
if (skipped.length) skip(`hook smoke skipped (bridge not installed): ${skipped.join(', ')}`);
|
|
1797
|
+
for (const line of failed) warn(`hook smoke failed — ${line}`);
|
|
1798
|
+
}
|
|
1799
|
+
return failed.length ? 1 : 0;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// --- Global user-level install (ADR-007) ---
|
|
1803
|
+
|
|
1804
|
+
export const USER_SCOPE_PATHS: Record<string, string> = {
|
|
1805
|
+
'claude-code': '~/.claude/',
|
|
1806
|
+
'claude-desktop': '~/Library/Application Support/Claude/',
|
|
1807
|
+
cursor: '~/.cursor/',
|
|
1808
|
+
windsurf: '~/.codeium/windsurf/',
|
|
1809
|
+
cline: '~/Documents/Cline/Rules/',
|
|
1810
|
+
'gemini-cli': '~/.gemini/',
|
|
1811
|
+
copilot: '~/.copilot/',
|
|
1812
|
+
augment: '~/.augment/',
|
|
1813
|
+
aider: '~/.aider.conf.yml',
|
|
1814
|
+
codex: '~/.codex/',
|
|
1815
|
+
roocode: '~/.roo/',
|
|
1816
|
+
continue: '~/.continue/',
|
|
1817
|
+
kilocode: '~/.kilocode/',
|
|
1818
|
+
zed: '~/.config/zed/',
|
|
1819
|
+
jetbrains: '~/.config/JetBrains/',
|
|
1820
|
+
kiro: '~/.kiro/',
|
|
1821
|
+
qoder: '~/.qoder/',
|
|
1822
|
+
opencode: '~/.opencode/',
|
|
1823
|
+
trae: '~/.trae/',
|
|
1824
|
+
antigravity: '~/.agents/',
|
|
1825
|
+
codebuddy: '~/.codebuddy/',
|
|
1826
|
+
droid: '~/.factory/',
|
|
1827
|
+
warp: '~/.warp/',
|
|
1828
|
+
};
|
|
1829
|
+
|
|
1830
|
+
const SCOPE_SUPPORT: Record<string, string> = {
|
|
1831
|
+
'claude-code': 'global',
|
|
1832
|
+
'claude-desktop': 'global',
|
|
1833
|
+
cursor: 'global',
|
|
1834
|
+
windsurf: 'global',
|
|
1835
|
+
cline: 'global',
|
|
1836
|
+
'gemini-cli': 'global',
|
|
1837
|
+
copilot: 'both',
|
|
1838
|
+
augment: 'global',
|
|
1839
|
+
aider: 'global',
|
|
1840
|
+
codex: 'global',
|
|
1841
|
+
roocode: 'global',
|
|
1842
|
+
continue: 'global',
|
|
1843
|
+
kilocode: 'global',
|
|
1844
|
+
zed: 'global',
|
|
1845
|
+
jetbrains: 'global',
|
|
1846
|
+
kiro: 'global',
|
|
1847
|
+
qoder: 'global',
|
|
1848
|
+
opencode: 'global',
|
|
1849
|
+
trae: 'global',
|
|
1850
|
+
antigravity: 'global',
|
|
1851
|
+
codebuddy: 'global',
|
|
1852
|
+
droid: 'global',
|
|
1853
|
+
warp: 'global',
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1856
|
+
export const PROJECT_BRIDGE_MARKERS: Record<string, string> = {
|
|
1857
|
+
'claude-code': '.claude/settings.json',
|
|
1858
|
+
'claude-desktop': '.claude-desktop/agent-config.md',
|
|
1859
|
+
cursor: '.cursor/hooks.json',
|
|
1860
|
+
windsurf: '.windsurf/hooks.json',
|
|
1861
|
+
cline: '.clinerules/hooks',
|
|
1862
|
+
'gemini-cli': '.gemini/settings.json',
|
|
1863
|
+
copilot: '.github/plugin/marketplace.json',
|
|
1864
|
+
augment: '.augment/settings.json',
|
|
1865
|
+
aider: '.aider/agent-config.md',
|
|
1866
|
+
codex: '.codex/agent-config.md',
|
|
1867
|
+
roocode: '.roo/rules/agent-config.md',
|
|
1868
|
+
continue: '.continue/rules/agent-config.md',
|
|
1869
|
+
kilocode: '.kilocode/rules/agent-config.md',
|
|
1870
|
+
zed: '.zed/agent-config.md',
|
|
1871
|
+
jetbrains: '.jetbrains/agent-config.md',
|
|
1872
|
+
kiro: '.kiro/steering/agent-config.md',
|
|
1873
|
+
};
|
|
1874
|
+
|
|
1875
|
+
const _CLAUDE_SKILL_BUNDLE: ReadonlyArray<readonly [string, string]> = [
|
|
1876
|
+
['dist/agent-src/rules', 'rules'],
|
|
1877
|
+
['dist/agent-src/skills', 'skills'],
|
|
1878
|
+
['dist/agent-src/commands', 'commands'],
|
|
1879
|
+
['dist/agent-src/personas', 'personas'],
|
|
1880
|
+
];
|
|
1881
|
+
|
|
1882
|
+
const GLOBAL_DEPLOY_SOURCES: Record<string, ReadonlyArray<readonly [string, string]>> = {
|
|
1883
|
+
'claude-code': _CLAUDE_SKILL_BUNDLE,
|
|
1884
|
+
augment: [
|
|
1885
|
+
['dist/agent-src/rules', 'rules'],
|
|
1886
|
+
['dist/agent-src/skills', 'skills'],
|
|
1887
|
+
['dist/agent-src/commands', 'commands'],
|
|
1888
|
+
['dist/agent-src/contexts', 'contexts'],
|
|
1889
|
+
['dist/agent-src/personas', 'personas'],
|
|
1890
|
+
['dist/agent-src/templates', 'templates'],
|
|
1891
|
+
],
|
|
1892
|
+
cursor: [
|
|
1893
|
+
['dist/agent-src/rules', 'rules'],
|
|
1894
|
+
['dist/agent-src/commands', 'commands'],
|
|
1895
|
+
['dist/agent-src/personas', 'personas'],
|
|
1896
|
+
],
|
|
1897
|
+
windsurf: [['dist/agent-src/rules', 'rules']],
|
|
1898
|
+
cline: [['dist/agent-src/rules', '']],
|
|
1899
|
+
'gemini-cli': _CLAUDE_SKILL_BUNDLE,
|
|
1900
|
+
codex: _CLAUDE_SKILL_BUNDLE,
|
|
1901
|
+
continue: _CLAUDE_SKILL_BUNDLE,
|
|
1902
|
+
roocode: _CLAUDE_SKILL_BUNDLE,
|
|
1903
|
+
kilocode: _CLAUDE_SKILL_BUNDLE,
|
|
1904
|
+
qoder: _CLAUDE_SKILL_BUNDLE,
|
|
1905
|
+
opencode: _CLAUDE_SKILL_BUNDLE,
|
|
1906
|
+
trae: _CLAUDE_SKILL_BUNDLE,
|
|
1907
|
+
antigravity: _CLAUDE_SKILL_BUNDLE,
|
|
1908
|
+
codebuddy: _CLAUDE_SKILL_BUNDLE,
|
|
1909
|
+
droid: _CLAUDE_SKILL_BUNDLE,
|
|
1910
|
+
warp: _CLAUDE_SKILL_BUNDLE,
|
|
1911
|
+
kiro: [
|
|
1912
|
+
['dist/agent-src/rules', 'rules'],
|
|
1913
|
+
['dist/agent-src/skills', 'steering'],
|
|
1914
|
+
['dist/agent-src/personas', 'personas'],
|
|
1915
|
+
],
|
|
1916
|
+
};
|
|
1917
|
+
|
|
1918
|
+
const _CLAUDE_DESKTOP_MARKER_TEMPLATE_HEAD = `# agent-config — Claude Desktop marker
|
|
1919
|
+
|
|
1920
|
+
Installed by \`@event4u/agent-config\` (user scope, ADR-007).
|
|
1921
|
+
|
|
1922
|
+
`;
|
|
1923
|
+
|
|
1924
|
+
/** Render of _CLAUDE_DESKTOP_MARKER_TEMPLATE.format(...) — byte-faithful. */
|
|
1925
|
+
function claudeDesktopMarkerBody(
|
|
1926
|
+
lockfile: string,
|
|
1927
|
+
anchor: string,
|
|
1928
|
+
bundles_dir: string,
|
|
1929
|
+
bundle_count: number,
|
|
1930
|
+
): string {
|
|
1931
|
+
return (
|
|
1932
|
+
'# agent-config — Claude Desktop marker\n' +
|
|
1933
|
+
'\n' +
|
|
1934
|
+
'Installed by `@event4u/agent-config` (user scope, ADR-007).\n' +
|
|
1935
|
+
'\n' +
|
|
1936
|
+
`- Lockfile: \`${lockfile}\`\n` +
|
|
1937
|
+
`- Anchor: \`${anchor}\`\n` +
|
|
1938
|
+
`- Skill bundles: \`${bundles_dir}\` (${bundle_count} ZIPs)\n` +
|
|
1939
|
+
'\n' +
|
|
1940
|
+
'## Import skills into Claude Desktop\n' +
|
|
1941
|
+
'\n' +
|
|
1942
|
+
'Claude Desktop has no filesystem skill-discovery convention — skills are\n' +
|
|
1943
|
+
'imported manually via the Customize → Skills UI.\n' +
|
|
1944
|
+
'\n' +
|
|
1945
|
+
'1. Open Claude Desktop → **Settings → Customize → Skills**.\n' +
|
|
1946
|
+
'2. Click the **Upload skill** button.\n' +
|
|
1947
|
+
`3. Browse to \`${bundles_dir}\` and pick the \`<skill-name>.zip\` files you\n` +
|
|
1948
|
+
' want to install. One ZIP = one skill.\n' +
|
|
1949
|
+
'4. Repeat per skill. Claude Desktop keeps each upload until you remove it.\n' +
|
|
1950
|
+
'\n' +
|
|
1951
|
+
'The bundle directory is regenerated on every\n' +
|
|
1952
|
+
'`npx @event4u/agent-config init --tools=claude-desktop` run (only\n' +
|
|
1953
|
+
'changed skills are rewritten — content-hash idempotency).\n' +
|
|
1954
|
+
'\n' +
|
|
1955
|
+
'To remove this marker, delete this file.\n'
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
void _CLAUDE_DESKTOP_MARKER_TEMPLATE_HEAD;
|
|
1959
|
+
|
|
1960
|
+
const _CLAUDE_DESKTOP_BUNDLES_SUBPATH = 'claude-desktop/bundles';
|
|
1961
|
+
|
|
1962
|
+
const GLOBAL_ROOT = path.join(os.homedir(), '.event4u', 'agent-config');
|
|
1963
|
+
const GLOBAL_USER_SETTINGS_PATH = path.join(GLOBAL_ROOT, '.agent-user.yml');
|
|
1964
|
+
const GLOBAL_AGENT_SETTINGS_PATH = path.join(GLOBAL_ROOT, '.agent-settings.yml');
|
|
1965
|
+
void GLOBAL_USER_SETTINGS_PATH;
|
|
1966
|
+
|
|
1967
|
+
function _bridge_marker(tool_id: string, scope: string): string {
|
|
1968
|
+
if (scope === 'global') return USER_SCOPE_PATHS[tool_id] ?? '';
|
|
1969
|
+
return PROJECT_BRIDGE_MARKERS[tool_id] ?? '';
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
function _validate_scope(tools: Set<string>, scope: string, was_all: boolean): Set<string> {
|
|
1973
|
+
if (scope !== 'project' && scope !== 'global') {
|
|
1974
|
+
fail(`_validate_scope: unknown scope '${scope}'`);
|
|
1975
|
+
}
|
|
1976
|
+
if (process.env['AGENT_CONFIG_DEV_MODE'] === '1') return tools;
|
|
1977
|
+
const incompatible = [...tools]
|
|
1978
|
+
.filter((t) => {
|
|
1979
|
+
const sup = SCOPE_SUPPORT[t] ?? 'both';
|
|
1980
|
+
return sup !== 'both' && sup !== scope;
|
|
1981
|
+
})
|
|
1982
|
+
.sort();
|
|
1983
|
+
if (incompatible.length === 0) return tools;
|
|
1984
|
+
if (was_all) {
|
|
1985
|
+
return new Set([...tools].filter((t) => !incompatible.includes(t)));
|
|
1986
|
+
}
|
|
1987
|
+
const hint =
|
|
1988
|
+
scope === 'global' ? 'drop --global (project is the default scope)' : 'use --global';
|
|
1989
|
+
fail(`--tools: ${incompatible.join(', ')} does not support --${scope} scope (${hint})`);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
function _enforce_consumer_global_only(scope: string): void {
|
|
1993
|
+
if (scope !== 'project') return;
|
|
1994
|
+
if (process.env['AGENT_CONFIG_DEV_MODE'] === '1') return;
|
|
1995
|
+
fail(
|
|
1996
|
+
'--scope=project is reserved for maintainers (ADR-020 — consumer ' +
|
|
1997
|
+
'installs are global-only). Set AGENT_CONFIG_DEV_MODE=1 to opt in. ' +
|
|
1998
|
+
'See docs/maintainers/dev-mode.md.',
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
function _enforce_not_source_repo(scope: string, project_root: string): void {
|
|
2003
|
+
if (scope === 'global') return;
|
|
2004
|
+
if (process.env['AGENT_CONFIG_ALLOW_SELF_INSTALL'] === '1') return;
|
|
2005
|
+
const [is_source, signature] = _is_agent_config_source_repo(project_root);
|
|
2006
|
+
if (!is_source) return;
|
|
2007
|
+
fail(
|
|
2008
|
+
'Refusing to install agent-config into its own source checkout ' +
|
|
2009
|
+
`(detected: ${signature}). The source repo is global-only — a ` +
|
|
2010
|
+
'project-scope install would recreate the .augment/ .claude/ .cursor/ ' +
|
|
2011
|
+
'projection trees in the repo (double token cost). Run `task sync` to ' +
|
|
2012
|
+
'regenerate them from .agent-src.uncondensed/ instead, or set ' +
|
|
2013
|
+
'AGENT_CONFIG_ALLOW_SELF_INSTALL=1 to force.',
|
|
2014
|
+
);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// --- Three-layer settings reader ---
|
|
2018
|
+
|
|
2019
|
+
function _load_yaml_doc(p: string): Record<string, unknown> {
|
|
2020
|
+
if (!pathExists(p) || !isFile(p)) return {};
|
|
2021
|
+
let text: string;
|
|
2022
|
+
try {
|
|
2023
|
+
text = readText(p);
|
|
2024
|
+
} catch {
|
|
2025
|
+
return {};
|
|
2026
|
+
}
|
|
2027
|
+
const data = yamlSafeLoad(text);
|
|
2028
|
+
return _isPlainObject(data) ? (data as Record<string, unknown>) : {};
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
function _load_default_settings(package_root: string): Record<string, unknown> {
|
|
2032
|
+
const template_source = path.join(package_root, 'src', 'config', 'agent-settings.template.yml');
|
|
2033
|
+
if (!pathExists(template_source)) return {};
|
|
2034
|
+
let text: string;
|
|
2035
|
+
try {
|
|
2036
|
+
text = readText(template_source);
|
|
2037
|
+
} catch {
|
|
2038
|
+
return {};
|
|
2039
|
+
}
|
|
2040
|
+
const rendered = text
|
|
2041
|
+
.split(RULE_LOADING_TIER_PLACEHOLDER)
|
|
2042
|
+
.join(DEFAULT_PROFILE)
|
|
2043
|
+
.split(USER_TYPE_PLACEHOLDER)
|
|
2044
|
+
.join('');
|
|
2045
|
+
const data = yamlSafeLoad(rendered);
|
|
2046
|
+
return _isPlainObject(data) ? (data as Record<string, unknown>) : {};
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
function read_layered_settings(
|
|
2050
|
+
package_root: string,
|
|
2051
|
+
project_root: string | null = null,
|
|
2052
|
+
): Record<string, unknown> {
|
|
2053
|
+
let merged = _load_default_settings(package_root);
|
|
2054
|
+
merged = deep_merge(merged, _load_yaml_doc(GLOBAL_AGENT_SETTINGS_PATH));
|
|
2055
|
+
if (project_root !== null) {
|
|
2056
|
+
const project_file = _resolve_settings_read(project_root);
|
|
2057
|
+
merged = deep_merge(merged, _load_yaml_doc(project_file));
|
|
2058
|
+
}
|
|
2059
|
+
return merged;
|
|
2060
|
+
}
|
|
2061
|
+
void read_layered_settings;
|
|
2062
|
+
|
|
2063
|
+
interface Options {
|
|
2064
|
+
profile: string;
|
|
2065
|
+
user_type: string;
|
|
2066
|
+
force: boolean;
|
|
2067
|
+
skip_bridges: boolean;
|
|
2068
|
+
augment_user_hooks: boolean;
|
|
2069
|
+
cursor_user_hooks: boolean;
|
|
2070
|
+
cline_user_hooks: boolean;
|
|
2071
|
+
windsurf_user_hooks: boolean;
|
|
2072
|
+
gemini_user_hooks: boolean;
|
|
2073
|
+
project: string | null;
|
|
2074
|
+
package: string | null;
|
|
2075
|
+
quiet: boolean;
|
|
2076
|
+
tools: string; // post-merge it's always a string
|
|
2077
|
+
ai: string | null;
|
|
2078
|
+
packs: string[]; // normalized to list post-parse
|
|
2079
|
+
core_only: boolean;
|
|
2080
|
+
no_smoke: boolean;
|
|
2081
|
+
global_install: boolean;
|
|
2082
|
+
scope: string | null;
|
|
2083
|
+
custom_path: string | null;
|
|
2084
|
+
offline: boolean;
|
|
2085
|
+
minimal: boolean;
|
|
2086
|
+
interactive: boolean;
|
|
2087
|
+
no_ui: boolean;
|
|
2088
|
+
dry_run: boolean;
|
|
2089
|
+
apply_payload: string | null;
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
function _resolve_scope(
|
|
2093
|
+
opts: Options,
|
|
2094
|
+
detected: string,
|
|
2095
|
+
detect_reason: string,
|
|
2096
|
+
custom_path: string | null,
|
|
2097
|
+
): string {
|
|
2098
|
+
if (opts.scope === 'project') return 'project';
|
|
2099
|
+
if (opts.scope === 'global') return 'global';
|
|
2100
|
+
if (opts.scope === 'prompt') {
|
|
2101
|
+
return _run_scope_prompt(opts, detect_reason || 'forced by --scope=prompt', custom_path);
|
|
2102
|
+
}
|
|
2103
|
+
if (opts.scope === 'auto') {
|
|
2104
|
+
if (detected === 'prompt') return _run_scope_prompt(opts, detect_reason, custom_path);
|
|
2105
|
+
if (!state.QUIET) info(`Scope: ${detected} (auto-detected; ${detect_reason})`);
|
|
2106
|
+
return detected;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
if (opts.global_install) return 'global';
|
|
2110
|
+
|
|
2111
|
+
if (detected === 'prompt') return _run_scope_prompt(opts, detect_reason, custom_path);
|
|
2112
|
+
if (!state.QUIET) {
|
|
2113
|
+
info(
|
|
2114
|
+
`Scope detection: ${detected} (${detect_reason}). Using project default for ` +
|
|
2115
|
+
'backward compatibility; pass --scope=auto to honor detection.',
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
return 'project';
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
function _run_scope_prompt(opts: Options, reason: string, custom_path: string | null): string {
|
|
2122
|
+
if (!process.stdin.isTTY && custom_path === null) {
|
|
2123
|
+
fail(
|
|
2124
|
+
'Ambiguous install scope detected and stdin is not a TTY. ' +
|
|
2125
|
+
'Pass --scope=project|global (or --custom-path=<dir>) to override.',
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
const choice = prompt_scope_choice(reason);
|
|
2129
|
+
if (choice === 'project') return 'project';
|
|
2130
|
+
if (choice === 'global') return 'global';
|
|
2131
|
+
let cp = custom_path;
|
|
2132
|
+
if (cp === null) {
|
|
2133
|
+
let raw: string | null;
|
|
2134
|
+
raw = _read_line('Custom destination path: ');
|
|
2135
|
+
if (raw === null) {
|
|
2136
|
+
fail('Custom-path prompt aborted (EOF on stdin)');
|
|
2137
|
+
}
|
|
2138
|
+
if (!raw) fail('Custom-path prompt requires a non-empty path');
|
|
2139
|
+
cp = resolvePath(expanduser(raw));
|
|
2140
|
+
opts.custom_path = cp;
|
|
2141
|
+
}
|
|
2142
|
+
if (!state.QUIET) info(`Custom destination: ${cp}`);
|
|
2143
|
+
return 'project';
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
const SCOPE_DETECT_MANIFESTS: readonly string[] = [
|
|
2147
|
+
'package.json',
|
|
2148
|
+
'composer.json',
|
|
2149
|
+
'pyproject.toml',
|
|
2150
|
+
'Cargo.toml',
|
|
2151
|
+
'go.mod',
|
|
2152
|
+
'Gemfile',
|
|
2153
|
+
];
|
|
2154
|
+
const SCOPE_DETECT_AI_DIRS: readonly string[] = [
|
|
2155
|
+
'.claude',
|
|
2156
|
+
'.cursor',
|
|
2157
|
+
'.windsurf',
|
|
2158
|
+
'.augment',
|
|
2159
|
+
'.clinerules',
|
|
2160
|
+
'.copilot',
|
|
2161
|
+
'.gemini',
|
|
2162
|
+
'.codex',
|
|
2163
|
+
'.aider',
|
|
2164
|
+
'.continue',
|
|
2165
|
+
'.roo',
|
|
2166
|
+
'.kilocode',
|
|
2167
|
+
];
|
|
2168
|
+
const SCOPE_DETECT_AI_FILES: readonly string[] = [
|
|
2169
|
+
'CLAUDE.md',
|
|
2170
|
+
'AGENTS.md',
|
|
2171
|
+
'GEMINI.md',
|
|
2172
|
+
'.windsurfrules',
|
|
2173
|
+
'.aider.conf.yml',
|
|
2174
|
+
];
|
|
2175
|
+
|
|
2176
|
+
function detect_scope(cwd: string): [string, string] {
|
|
2177
|
+
if (pathExists(_resolve_settings_read(cwd))) {
|
|
2178
|
+
return ['project', `existing ${SETTINGS_FILE}`];
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
const has_manifest = SCOPE_DETECT_MANIFESTS.find((m) => pathExists(path.join(cwd, m))) ?? null;
|
|
2182
|
+
const has_ai_dir = SCOPE_DETECT_AI_DIRS.find((d) => isDir(path.join(cwd, d))) ?? null;
|
|
2183
|
+
const has_ai_file = SCOPE_DETECT_AI_FILES.find((f) => pathExists(path.join(cwd, f))) ?? null;
|
|
2184
|
+
|
|
2185
|
+
if (has_manifest && (has_ai_dir || has_ai_file)) {
|
|
2186
|
+
const marker = has_ai_dir || has_ai_file;
|
|
2187
|
+
return ['prompt', `manifest (${has_manifest}) + AI-tool config (${marker})`];
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
return ['global', 'no project-scope signals'];
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// --- Interactive prompts ---
|
|
2194
|
+
|
|
2195
|
+
const SCOPE_CUSTOM = 'custom';
|
|
2196
|
+
|
|
2197
|
+
/** `input(prompt).strip()` — returns null on EOF (Python raises EOFError). */
|
|
2198
|
+
function _read_line(prompt_text: string): string | null {
|
|
2199
|
+
const line = readLineSyncRaw(prompt_text);
|
|
2200
|
+
if (line === null) return null;
|
|
2201
|
+
return line.trim();
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
/**
|
|
2205
|
+
* Synchronous single-line read from stdin with a prompt on stdout (no newline),
|
|
2206
|
+
* mirroring CPython `input()`. Returns null at EOF. Implemented via a blocking
|
|
2207
|
+
* fd read because Node has no built-in sync stdin line read.
|
|
2208
|
+
*/
|
|
2209
|
+
function readLineSyncRaw(promptText: string): string | null {
|
|
2210
|
+
process.stdout.write(promptText);
|
|
2211
|
+
const buf = Buffer.alloc(1);
|
|
2212
|
+
const bytes: number[] = [];
|
|
2213
|
+
let sawAny = false;
|
|
2214
|
+
for (;;) {
|
|
2215
|
+
let n: number;
|
|
2216
|
+
try {
|
|
2217
|
+
n = fs.readSync(0, buf, 0, 1, null);
|
|
2218
|
+
} catch (err) {
|
|
2219
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
2220
|
+
if (code === 'EAGAIN') {
|
|
2221
|
+
continue;
|
|
2222
|
+
}
|
|
2223
|
+
if (code === 'EOF') {
|
|
2224
|
+
break;
|
|
2225
|
+
}
|
|
2226
|
+
throw err;
|
|
2227
|
+
}
|
|
2228
|
+
if (n === 0) break; // EOF
|
|
2229
|
+
sawAny = true;
|
|
2230
|
+
const ch = buf[0] as number;
|
|
2231
|
+
if (ch === 0x0a) {
|
|
2232
|
+
// strip a trailing \r (CRLF)
|
|
2233
|
+
if (bytes.length > 0 && bytes[bytes.length - 1] === 0x0d) bytes.pop();
|
|
2234
|
+
return Buffer.from(bytes).toString('utf-8');
|
|
2235
|
+
}
|
|
2236
|
+
bytes.push(ch);
|
|
2237
|
+
}
|
|
2238
|
+
if (!sawAny && bytes.length === 0) return null; // EOFError
|
|
2239
|
+
return Buffer.from(bytes).toString('utf-8');
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
function prompt_scope_choice(reason: string): string {
|
|
2243
|
+
process.stdout.write('\n');
|
|
2244
|
+
info(`Ambiguous install scope: ${reason}.`);
|
|
2245
|
+
info('Choose where to install:');
|
|
2246
|
+
process.stdout.write(' 1) Project — install into the current directory\n');
|
|
2247
|
+
process.stdout.write(' 2) User — install into ~/ (recommended; one install per machine)\n');
|
|
2248
|
+
process.stdout.write(' 3) Custom — specify an explicit destination path\n');
|
|
2249
|
+
process.stdout.write('\n');
|
|
2250
|
+
let attempts = 0;
|
|
2251
|
+
while (attempts < 3) {
|
|
2252
|
+
const reply = _read_line('Choose [1/2/3]: ');
|
|
2253
|
+
if (reply === null) {
|
|
2254
|
+
fail('Scope prompt aborted (EOF on stdin); pass --scope=project|global to override');
|
|
2255
|
+
}
|
|
2256
|
+
if (['1', 'project', 'p'].includes(reply)) return 'project';
|
|
2257
|
+
if (['2', 'global', 'user', 'u', 'g'].includes(reply)) return 'global';
|
|
2258
|
+
if (['3', 'custom', 'c'].includes(reply)) return SCOPE_CUSTOM;
|
|
2259
|
+
attempts += 1;
|
|
2260
|
+
warn(`Invalid choice '${reply}'. Enter 1, 2, or 3.`);
|
|
2261
|
+
}
|
|
2262
|
+
fail('Scope prompt aborted (3 invalid replies); pass --scope=project|global to override');
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
function prompt_collision_choice(p: string): string {
|
|
2266
|
+
process.stdout.write('\n');
|
|
2267
|
+
warn(`Existing file at ${p}`);
|
|
2268
|
+
info('Choose how to handle the collision:');
|
|
2269
|
+
process.stdout.write(' 1) Merge — append our content, preserve theirs\n');
|
|
2270
|
+
process.stdout.write(' 2) Backup and replace — rename existing to .bak.<ts>, write fresh\n');
|
|
2271
|
+
process.stdout.write(' 3) Abort — leave the file untouched, exit non-zero\n');
|
|
2272
|
+
process.stdout.write('\n');
|
|
2273
|
+
let attempts = 0;
|
|
2274
|
+
while (attempts < 3) {
|
|
2275
|
+
const reply = _read_line('Choose [1/2/3]: ');
|
|
2276
|
+
if (reply === null) {
|
|
2277
|
+
fail(`Collision prompt aborted (EOF on stdin) for ${p}`);
|
|
2278
|
+
}
|
|
2279
|
+
if (['1', 'merge', 'm'].includes(reply)) return 'merge';
|
|
2280
|
+
if (['2', 'backup', 'b'].includes(reply)) return 'backup';
|
|
2281
|
+
if (['3', 'abort', 'a'].includes(reply)) return 'abort';
|
|
2282
|
+
attempts += 1;
|
|
2283
|
+
warn(`Invalid choice '${reply}'. Enter 1, 2, or 3.`);
|
|
2284
|
+
}
|
|
2285
|
+
fail(`Collision prompt aborted (3 invalid replies) for ${p}`);
|
|
2286
|
+
}
|
|
2287
|
+
void prompt_collision_choice;
|
|
2288
|
+
|
|
2289
|
+
// --- Manifest / inventory helpers (lazy-import twins are eager static here) ---
|
|
2290
|
+
|
|
2291
|
+
function _sha256_of_file(p: string): string | null {
|
|
2292
|
+
return sha256OfFile(p);
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
function _file_entry(p: string, kind: string, hash_content: boolean): Record<string, unknown> {
|
|
2296
|
+
return {
|
|
2297
|
+
path: p,
|
|
2298
|
+
kind,
|
|
2299
|
+
sha256: hash_content ? _sha256_of_file(p) : null,
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
type DeployResult = [number, number, string, string[]];
|
|
2304
|
+
|
|
2305
|
+
function _files_by_tool_from_deploy(
|
|
2306
|
+
deploy_results: Record<string, DeployResult>,
|
|
2307
|
+
): Record<string, Record<string, unknown>[]> {
|
|
2308
|
+
const out: Record<string, Record<string, unknown>[]> = {};
|
|
2309
|
+
for (const tool_id of Object.keys(deploy_results)) {
|
|
2310
|
+
const [, , status, paths] = deploy_results[tool_id] as DeployResult;
|
|
2311
|
+
if (status === 'deployed') {
|
|
2312
|
+
out[tool_id] = paths.map((p) => _file_entry(p, 'deployed', true));
|
|
2313
|
+
} else if (status === 'marker') {
|
|
2314
|
+
out[tool_id] = paths.map((p) => _file_entry(p, 'marker', true));
|
|
2315
|
+
} else {
|
|
2316
|
+
out[tool_id] = [];
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
return out;
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
function _files_by_tool_from_bridges(
|
|
2323
|
+
tools: Set<string>,
|
|
2324
|
+
project_root: string,
|
|
2325
|
+
scope: string,
|
|
2326
|
+
): Record<string, Record<string, unknown>[]> {
|
|
2327
|
+
const out: Record<string, Record<string, unknown>[]> = {};
|
|
2328
|
+
for (const tool_id of [...tools].sort()) {
|
|
2329
|
+
const marker = _bridge_marker(tool_id, scope);
|
|
2330
|
+
if (!marker) continue;
|
|
2331
|
+
let marker_path = marker;
|
|
2332
|
+
if (!path.isAbsolute(marker_path)) {
|
|
2333
|
+
marker_path = path.join(project_root, marker_path);
|
|
2334
|
+
}
|
|
2335
|
+
out[tool_id] = [_file_entry(marker_path, 'bridge', false)];
|
|
2336
|
+
}
|
|
2337
|
+
return out;
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
function _update_installed_tools_manifest(
|
|
2341
|
+
project_root: string,
|
|
2342
|
+
tools: Set<string>,
|
|
2343
|
+
scope: string,
|
|
2344
|
+
force: boolean,
|
|
2345
|
+
files_by_tool: Record<string, Record<string, unknown>[]> | null = null,
|
|
2346
|
+
merged_keys_by_tool: Record<string, Record<string, unknown>[]> | null = null,
|
|
2347
|
+
): number {
|
|
2348
|
+
const target = installed_tools.manifest_path(project_root);
|
|
2349
|
+
const existing = (installed_tools.read_manifest(target) ?? {}) as Record<string, unknown>;
|
|
2350
|
+
let entries = Array.isArray(existing['tools'])
|
|
2351
|
+
? ([...(existing['tools'] as unknown[])] as Record<string, unknown>[])
|
|
2352
|
+
: [];
|
|
2353
|
+
|
|
2354
|
+
const version = installed_lock.current_package_version();
|
|
2355
|
+
|
|
2356
|
+
for (const tool_id of [...tools].sort()) {
|
|
2357
|
+
const marker = _bridge_marker(tool_id, scope);
|
|
2358
|
+
if (!marker) continue;
|
|
2359
|
+
const files = files_by_tool ? (files_by_tool[tool_id] ?? null) : null;
|
|
2360
|
+
const merged_keys = merged_keys_by_tool ? (merged_keys_by_tool[tool_id] ?? null) : null;
|
|
2361
|
+
try {
|
|
2362
|
+
entries = installed_tools.upsert_tool(entries, {
|
|
2363
|
+
name: tool_id,
|
|
2364
|
+
scope,
|
|
2365
|
+
bridge_marker: marker,
|
|
2366
|
+
force,
|
|
2367
|
+
files,
|
|
2368
|
+
merged_keys,
|
|
2369
|
+
});
|
|
2370
|
+
} catch (exc) {
|
|
2371
|
+
if (exc instanceof installed_tools.ScopeMismatchError) {
|
|
2372
|
+
if (!state.QUIET) {
|
|
2373
|
+
warn(String(exc.message));
|
|
2374
|
+
info(` Manifest: ${target}`);
|
|
2375
|
+
info(' Override: re-run with `--force` to rewrite the entry');
|
|
2376
|
+
}
|
|
2377
|
+
return 1;
|
|
2378
|
+
}
|
|
2379
|
+
throw exc;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
installed_tools.write_manifest(target, version, entries);
|
|
2384
|
+
if (!state.QUIET) {
|
|
2385
|
+
const rel = isRelativeTo(target, project_root) ? path.relative(project_root, target) : target;
|
|
2386
|
+
info(`Manifest updated: ${rel}`);
|
|
2387
|
+
}
|
|
2388
|
+
return 0;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
/** `Path.is_relative_to`. */
|
|
2392
|
+
function isRelativeTo(child: string, parent: string): boolean {
|
|
2393
|
+
const rel = path.relative(parent, child);
|
|
2394
|
+
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
// --- Global content deployment ---
|
|
2398
|
+
|
|
2399
|
+
function _resolve_package_root_for_global(): string {
|
|
2400
|
+
const here = resolvePath(_HERE);
|
|
2401
|
+
const candidate = path.dirname(path.dirname(path.dirname(here)));
|
|
2402
|
+
if (!pathExists(path.join(candidate, 'src', 'config', 'profiles', 'minimal.ini'))) {
|
|
2403
|
+
fail(
|
|
2404
|
+
`Could not locate agent-config package root from ${here}. ` +
|
|
2405
|
+
'Expected src/config/profiles/minimal.ini at the parent directory.',
|
|
2406
|
+
);
|
|
2407
|
+
}
|
|
2408
|
+
return candidate;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
const CONSUMER_BRIDGE_MARKER_RELPATH = path.join('agents', '.event4u-bridge.yml');
|
|
2412
|
+
|
|
2413
|
+
const MIGRATE_LEGACY_YAML_FILES: readonly string[] = ['.agent-settings.yml', '.agent-user.yml'];
|
|
2414
|
+
const MIGRATE_LEGACY_TOOL_DIRS: readonly string[] = ['.augment', '.claude', '.cursor'];
|
|
2415
|
+
const AGENT_CONFIG_PACKAGE_NAME = '@event4u/agent-config';
|
|
2416
|
+
|
|
2417
|
+
// Return `[is_source_repo, signature]` for the maintainer auto-detect.
|
|
2418
|
+
// Signature #2: `.agent-src.uncondensed/` exists at `project_root` (legacy
|
|
2419
|
+
// layout) OR under `packages/<name>/` (current layout) — both unique to the
|
|
2420
|
+
// source repo. Hits skip the ADR-020 migration prompt automatically.
|
|
2421
|
+
function _is_agent_config_source_repo(project_root: string): [boolean, string] {
|
|
2422
|
+
if (process.env['AGENT_CONFIG_CONSUMER_MODE'] === '1') {
|
|
2423
|
+
return [false, 'consumer-mode-override'];
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
const pkg_json = path.join(project_root, 'package.json');
|
|
2427
|
+
if (isFile(pkg_json)) {
|
|
2428
|
+
let data: unknown = {};
|
|
2429
|
+
try {
|
|
2430
|
+
data = JSON.parse(readText(pkg_json));
|
|
2431
|
+
} catch {
|
|
2432
|
+
data = {};
|
|
2433
|
+
}
|
|
2434
|
+
if (_isPlainObject(data) && (data as Record<string, unknown>)['name'] === AGENT_CONFIG_PACKAGE_NAME) {
|
|
2435
|
+
return [true, 'package.json:name'];
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
if (isDir(path.join(project_root, '.agent-src.uncondensed'))) {
|
|
2440
|
+
return [true, '.agent-src.uncondensed/'];
|
|
2441
|
+
}
|
|
2442
|
+
const packages_dir = path.join(project_root, 'packages');
|
|
2443
|
+
if (isDir(packages_dir)) {
|
|
2444
|
+
for (const child of fs.readdirSync(packages_dir)) {
|
|
2445
|
+
if (isDir(path.join(packages_dir, child, '.agent-src.uncondensed'))) {
|
|
2446
|
+
return [true, `packages/${child}/.agent-src.uncondensed/`];
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
const installer_self = path.join(project_root, 'scripts', 'install.py');
|
|
2452
|
+
try {
|
|
2453
|
+
if (isFile(installer_self) && resolvePath(installer_self) === resolvePath(_HERE)) {
|
|
2454
|
+
return [true, 'src/scripts/install.py (self)'];
|
|
2455
|
+
}
|
|
2456
|
+
} catch {
|
|
2457
|
+
/* OSError → pass */
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
return [false, ''];
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
function _detect_legacy_for_migration(project_root: string): string[] {
|
|
2464
|
+
if (process.env['AGENT_CONFIG_DEV_MODE'] === '1') return [];
|
|
2465
|
+
|
|
2466
|
+
const [is_source, signature] = _is_agent_config_source_repo(project_root);
|
|
2467
|
+
if (is_source) {
|
|
2468
|
+
if (!state.QUIET) {
|
|
2469
|
+
warn(
|
|
2470
|
+
'Maintainer mode auto-detected — agent-config source repo ' +
|
|
2471
|
+
`(signature: ${signature}). Skipping ADR-020 migration ` +
|
|
2472
|
+
'prompt; the working tree stays intact. Set ' +
|
|
2473
|
+
'AGENT_CONFIG_CONSUMER_MODE=1 to override for end-to-end ' +
|
|
2474
|
+
'consumer-flow testing.',
|
|
2475
|
+
);
|
|
2476
|
+
}
|
|
2477
|
+
return [];
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
if (isFile(path.join(project_root, CONSUMER_BRIDGE_MARKER_RELPATH))) return [];
|
|
2481
|
+
|
|
2482
|
+
const found: string[] = [];
|
|
2483
|
+
for (const name of MIGRATE_LEGACY_YAML_FILES) {
|
|
2484
|
+
if (isFile(path.join(project_root, name))) {
|
|
2485
|
+
found.push(name);
|
|
2486
|
+
} else if (isFile(path.join(project_root, 'settings', name))) {
|
|
2487
|
+
found.push(`settings/${name}`);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
for (const name of MIGRATE_LEGACY_TOOL_DIRS) {
|
|
2491
|
+
const p = path.join(project_root, name);
|
|
2492
|
+
if (isDir(p) && !isSymlink(p)) {
|
|
2493
|
+
found.push(`${name}/`);
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
return found.sort();
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
function _prompt_migrate_to_global(project_root: string, artefacts: string[]): boolean {
|
|
2500
|
+
if (!state.QUIET) {
|
|
2501
|
+
process.stdout.write('\n');
|
|
2502
|
+
warn('Legacy project-local artefacts detected — pre-ADR-020 layout:');
|
|
2503
|
+
for (const rel of artefacts) {
|
|
2504
|
+
info(` ${path.join(project_root, rel)}`);
|
|
2505
|
+
}
|
|
2506
|
+
info('The unified `agent-config migrate` sweeps these in one pass.');
|
|
2507
|
+
info('The wizard recreates fresh config afterwards.');
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
if (!_is_interactive()) {
|
|
2511
|
+
if (!state.QUIET) info('Non-interactive mode → defaulting to YES (run migration).');
|
|
2512
|
+
return true;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
let attempts = 0;
|
|
2516
|
+
while (attempts < 3) {
|
|
2517
|
+
const reply = _read_line('Run `agent-config migrate` now? [Y/n]: ');
|
|
2518
|
+
if (reply === null) return false;
|
|
2519
|
+
if (reply === '' || ['y', 'yes'].includes(reply.toLowerCase())) return true;
|
|
2520
|
+
if (['n', 'no'].includes(reply.toLowerCase())) return false;
|
|
2521
|
+
attempts += 1;
|
|
2522
|
+
warn(`Invalid choice '${reply}'. Enter Y or n.`);
|
|
2523
|
+
}
|
|
2524
|
+
return false;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
function _run_migrate_to_global(project_root: string): number {
|
|
2528
|
+
// The .py runs `scripts._cli.cmd_migrate.main([], cwd, out=sys.stdout)`
|
|
2529
|
+
// in-process. The `.ts` twin (`./_cli/cmd_migrate.ts`) is statically
|
|
2530
|
+
// imported above, so this is a direct in-process call — no subprocess.
|
|
2531
|
+
// cmd_migrate.main defaults its `out` sink to `process.stdout` and its
|
|
2532
|
+
// `err` sink to `process.stderr`, reproducing the old
|
|
2533
|
+
// `stdio:['ignore','inherit','pipe']` semantics (child stdout → parent
|
|
2534
|
+
// stdout; migrator stderr → parent stderr). It returns the int exit code.
|
|
2535
|
+
//
|
|
2536
|
+
// The old python3-spawn path returned 1 on a MIGRATE_UNAVAILABLE (exit 99)
|
|
2537
|
+
// sentinel or a spawn error. With a static import the migrator can never be
|
|
2538
|
+
// "unavailable", so that arm is dead — but the defensive try/catch is kept:
|
|
2539
|
+
// any unexpected throw maps to the same exit-1 contract.
|
|
2540
|
+
try {
|
|
2541
|
+
return cmdMigrateMain([], { cwd: project_root });
|
|
2542
|
+
} catch (exc) {
|
|
2543
|
+
warn(`migrate unavailable: ${String(exc)}`);
|
|
2544
|
+
return 1;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
function _format_global_root_for_marker(global_root: string): string {
|
|
2549
|
+
const home = resolvePath(os.homedir());
|
|
2550
|
+
const resolved = resolvePath(global_root);
|
|
2551
|
+
const rel = path.relative(home, resolved);
|
|
2552
|
+
if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
2553
|
+
return global_root;
|
|
2554
|
+
}
|
|
2555
|
+
return `~/${rel.split(path.sep).join('/')}`;
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
/**
|
|
2559
|
+
* Write `agents/.event4u-bridge.yml`. Skipped under `AGENT_CONFIG_DEV_MODE=1`
|
|
2560
|
+
* and when the project root is the agent-config source repo
|
|
2561
|
+
* (`.agent-src.uncondensed/` present) — same rationale. Atomic write.
|
|
2562
|
+
*/
|
|
2563
|
+
function _write_consumer_bridge_marker(
|
|
2564
|
+
project_root: string,
|
|
2565
|
+
installer_version: string,
|
|
2566
|
+
env: NodeJS.ProcessEnv | null = null,
|
|
2567
|
+
now: Date | null = null,
|
|
2568
|
+
): string | null {
|
|
2569
|
+
const env_map = env ?? process.env;
|
|
2570
|
+
if (env_map['AGENT_CONFIG_DEV_MODE'] === '1') return null;
|
|
2571
|
+
if (isDir(path.join(project_root, '.agent-src.uncondensed'))) return null;
|
|
2572
|
+
|
|
2573
|
+
const global_root_str = _format_global_root_for_marker(
|
|
2574
|
+
user_global_paths.event4u_root(env_map),
|
|
2575
|
+
);
|
|
2576
|
+
const stamp = utcStamp(now ?? undefined);
|
|
2577
|
+
|
|
2578
|
+
const body =
|
|
2579
|
+
'# event4u/agent-config — consumer bridge marker (auto-written).\n' +
|
|
2580
|
+
'# Spec: docs/contracts/consumer-bridge.md (event4u-bridge/v1).\n' +
|
|
2581
|
+
'# Reader contract: expand ~ against the current $HOME; fail closed\n' +
|
|
2582
|
+
'# when global_root is missing on disk; never write back through it.\n' +
|
|
2583
|
+
'schema: event4u-bridge/v1\n' +
|
|
2584
|
+
`global_root: ${global_root_str}\n` +
|
|
2585
|
+
`installed_at: ${stamp}\n` +
|
|
2586
|
+
`installer_version: ${installer_version}\n`;
|
|
2587
|
+
|
|
2588
|
+
const target = path.join(project_root, CONSUMER_BRIDGE_MARKER_RELPATH);
|
|
2589
|
+
mkdirp(path.dirname(target));
|
|
2590
|
+
atomicWrite0644(target, body, '.event4u-bridge.');
|
|
2591
|
+
return target;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
const PROJECT_ANCHOR_TOOLS: Record<string, string> = {
|
|
2595
|
+
windsurf: '.windsurf/agent-config.bridge.yml',
|
|
2596
|
+
cline: '.clinerules/agent-config.bridge.yml',
|
|
2597
|
+
'gemini-cli': '.gemini/agent-config.bridge.yml',
|
|
2598
|
+
};
|
|
2599
|
+
|
|
2600
|
+
/**
|
|
2601
|
+
* Plant thin pointer files for PROJECT_ANCHOR_TOOLS. Same gate as the bridge
|
|
2602
|
+
* marker: skipped under dev mode and inside the agent-config source repo
|
|
2603
|
+
* (`.agent-src.uncondensed/` present). Atomic write per file.
|
|
2604
|
+
*/
|
|
2605
|
+
function _write_per_tool_project_anchors(
|
|
2606
|
+
project_root: string,
|
|
2607
|
+
tools: Set<string>,
|
|
2608
|
+
env: NodeJS.ProcessEnv | null = null,
|
|
2609
|
+
now: Date | null = null,
|
|
2610
|
+
): string[] {
|
|
2611
|
+
const env_map = env ?? process.env;
|
|
2612
|
+
if (env_map['AGENT_CONFIG_DEV_MODE'] === '1') return [];
|
|
2613
|
+
if (isDir(path.join(project_root, '.agent-src.uncondensed'))) return [];
|
|
2614
|
+
|
|
2615
|
+
const global_root_str = _format_global_root_for_marker(
|
|
2616
|
+
user_global_paths.event4u_root(env_map),
|
|
2617
|
+
);
|
|
2618
|
+
const stamp = utcStamp(now ?? undefined);
|
|
2619
|
+
const written: string[] = [];
|
|
2620
|
+
|
|
2621
|
+
for (const tool_id of Object.keys(PROJECT_ANCHOR_TOOLS).sort()) {
|
|
2622
|
+
const rel_path = PROJECT_ANCHOR_TOOLS[tool_id] as string;
|
|
2623
|
+
if (!tools.has(tool_id)) continue;
|
|
2624
|
+
const target = path.join(project_root, rel_path);
|
|
2625
|
+
mkdirp(path.dirname(target));
|
|
2626
|
+
|
|
2627
|
+
const bridge_abs = path.join(project_root, CONSUMER_BRIDGE_MARKER_RELPATH);
|
|
2628
|
+
const bridge_rel = path.relative(path.dirname(target), bridge_abs);
|
|
2629
|
+
|
|
2630
|
+
const body =
|
|
2631
|
+
'# event4u/agent-config — per-tool project anchor (auto-written).\n' +
|
|
2632
|
+
'# Spec: docs/contracts/consumer-bridge.md § Per-tool anchor strategy.\n' +
|
|
2633
|
+
`# Tool: ${tool_id}. Bridge marker: agents/.event4u-bridge.yml.\n` +
|
|
2634
|
+
'schema: event4u-bridge/v1\n' +
|
|
2635
|
+
`tool: ${tool_id}\n` +
|
|
2636
|
+
`bridge: ${bridge_rel}\n` +
|
|
2637
|
+
`global_root: ${global_root_str}\n` +
|
|
2638
|
+
`installed_at: ${stamp}\n`;
|
|
2639
|
+
|
|
2640
|
+
atomicWrite0644(target, body, '.agent-config.bridge.');
|
|
2641
|
+
written.push(target);
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
return written;
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
const PACKAGE_TAG_ID = 'event4u/agent-config';
|
|
2648
|
+
|
|
2649
|
+
function _inject_package_tag(
|
|
2650
|
+
target: string,
|
|
2651
|
+
source: string | null,
|
|
2652
|
+
package_root: string | null,
|
|
2653
|
+
): void {
|
|
2654
|
+
if (path.extname(target) !== '.md') return;
|
|
2655
|
+
let text: string;
|
|
2656
|
+
try {
|
|
2657
|
+
text = readText(target);
|
|
2658
|
+
} catch {
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
if (!text.startsWith('---\n') && !text.startsWith('---\r\n')) return;
|
|
2662
|
+
const lines = splitlinesKeepends(text);
|
|
2663
|
+
let close_idx: number | null = null;
|
|
2664
|
+
for (let i = 1; i < lines.length; i += 1) {
|
|
2665
|
+
if ((lines[i] as string).replace(/[\r\n]+$/, '') === '---') {
|
|
2666
|
+
close_idx = i;
|
|
2667
|
+
break;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
if (close_idx === null) return;
|
|
2671
|
+
let fm_lines = lines.slice(1, close_idx);
|
|
2672
|
+
const body_lines = lines.slice(close_idx);
|
|
2673
|
+
|
|
2674
|
+
let source_value: string | null = null;
|
|
2675
|
+
if (source !== null) {
|
|
2676
|
+
let resolved_src: string;
|
|
2677
|
+
try {
|
|
2678
|
+
resolved_src = resolvePath(source);
|
|
2679
|
+
} catch {
|
|
2680
|
+
resolved_src = source;
|
|
2681
|
+
}
|
|
2682
|
+
if (package_root !== null) {
|
|
2683
|
+
const rel = path.relative(resolvePath(package_root), resolved_src);
|
|
2684
|
+
if (rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel)) {
|
|
2685
|
+
source_value = rel;
|
|
2686
|
+
} else {
|
|
2687
|
+
source_value = resolved_src;
|
|
2688
|
+
}
|
|
2689
|
+
} else {
|
|
2690
|
+
source_value = resolved_src;
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
const _set_key = (block: string[], key: string, value: string): string[] => {
|
|
2695
|
+
const prefix = `${key}:`;
|
|
2696
|
+
const rendered = `${key}: ${value}\n`;
|
|
2697
|
+
for (let idx = 0; idx < block.length; idx += 1) {
|
|
2698
|
+
if ((block[idx] as string).startsWith(prefix)) {
|
|
2699
|
+
block[idx] = rendered;
|
|
2700
|
+
return block;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
block.push(rendered);
|
|
2704
|
+
return block;
|
|
2705
|
+
};
|
|
2706
|
+
|
|
2707
|
+
fm_lines = _set_key(fm_lines, 'package', PACKAGE_TAG_ID);
|
|
2708
|
+
if (source_value !== null) {
|
|
2709
|
+
fm_lines = _set_key(fm_lines, 'source_path', source_value);
|
|
2710
|
+
}
|
|
2711
|
+
const new_text = [lines[0], ...fm_lines, ...body_lines].join('');
|
|
2712
|
+
if (new_text !== text) {
|
|
2713
|
+
writeText(target, new_text);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
function _copy_dir_dereferencing_symlinks(
|
|
2718
|
+
src: string,
|
|
2719
|
+
dest: string,
|
|
2720
|
+
force: boolean,
|
|
2721
|
+
package_root: string | null = null,
|
|
2722
|
+
): [number, number, string[]] {
|
|
2723
|
+
let written = 0;
|
|
2724
|
+
let skipped = 0;
|
|
2725
|
+
const written_paths: string[] = [];
|
|
2726
|
+
if (!pathExists(src)) return [0, 0, written_paths];
|
|
2727
|
+
if (!isDir(src)) {
|
|
2728
|
+
mkdirp(path.dirname(dest));
|
|
2729
|
+
const decision = _resolve_file_conflict(dest, force);
|
|
2730
|
+
if (decision === 'skip') return [0, 1, written_paths];
|
|
2731
|
+
fs.copyFileSync(src, dest); // follow_symlinks=True is fs.copyFileSync default
|
|
2732
|
+
_inject_package_tag(dest, src, package_root);
|
|
2733
|
+
written_paths.push(dest);
|
|
2734
|
+
return [1, 0, written_paths];
|
|
2735
|
+
}
|
|
2736
|
+
mkdirp(dest);
|
|
2737
|
+
// Python uses src.rglob("*") (os.scandir order, non-deterministic). We use
|
|
2738
|
+
// a sorted depth-first walk to match the sibling inventory twin
|
|
2739
|
+
// (expected_deploy_files) and yield a deterministic manifest files[] order.
|
|
2740
|
+
const walk = (node: string): string[] => {
|
|
2741
|
+
const acc: string[] = [];
|
|
2742
|
+
const names = fs
|
|
2743
|
+
.readdirSync(node)
|
|
2744
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
2745
|
+
for (const name of names) {
|
|
2746
|
+
const entry = path.join(node, name);
|
|
2747
|
+
acc.push(entry);
|
|
2748
|
+
const lst = fs.lstatSync(entry);
|
|
2749
|
+
if (lst.isDirectory() && !lst.isSymbolicLink()) {
|
|
2750
|
+
acc.push(...walk(entry));
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
return acc;
|
|
2754
|
+
};
|
|
2755
|
+
for (const entry of walk(src)) {
|
|
2756
|
+
const rel = path.relative(src, entry);
|
|
2757
|
+
const target = path.join(dest, rel);
|
|
2758
|
+
const lst = fs.lstatSync(entry);
|
|
2759
|
+
if (lst.isDirectory() && !lst.isSymbolicLink()) {
|
|
2760
|
+
mkdirp(target);
|
|
2761
|
+
continue;
|
|
2762
|
+
}
|
|
2763
|
+
let resolvedIsDir = false;
|
|
2764
|
+
let resolved = entry;
|
|
2765
|
+
try {
|
|
2766
|
+
resolved = fs.realpathSync(entry);
|
|
2767
|
+
resolvedIsDir = fs.statSync(entry).isDirectory();
|
|
2768
|
+
} catch {
|
|
2769
|
+
resolvedIsDir = false;
|
|
2770
|
+
}
|
|
2771
|
+
if (resolvedIsDir) {
|
|
2772
|
+
mkdirp(target);
|
|
2773
|
+
const [sub_w, sub_s, sub_p] = _copy_dir_dereferencing_symlinks(
|
|
2774
|
+
resolved,
|
|
2775
|
+
target,
|
|
2776
|
+
force,
|
|
2777
|
+
package_root,
|
|
2778
|
+
);
|
|
2779
|
+
written += sub_w;
|
|
2780
|
+
skipped += sub_s;
|
|
2781
|
+
written_paths.push(...sub_p);
|
|
2782
|
+
continue;
|
|
2783
|
+
}
|
|
2784
|
+
const decision = _resolve_file_conflict(target, force);
|
|
2785
|
+
if (decision === 'skip') {
|
|
2786
|
+
skipped += 1;
|
|
2787
|
+
continue;
|
|
2788
|
+
}
|
|
2789
|
+
mkdirp(path.dirname(target));
|
|
2790
|
+
fs.copyFileSync(resolved, target);
|
|
2791
|
+
_inject_package_tag(target, resolved, package_root);
|
|
2792
|
+
written += 1;
|
|
2793
|
+
written_paths.push(target);
|
|
2794
|
+
}
|
|
2795
|
+
return [written, skipped, written_paths];
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
function _claude_desktop_bundles_dir(): string {
|
|
2799
|
+
return user_global_paths.write_target(_CLAUDE_DESKTOP_BUNDLES_SUBPATH);
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
function _write_claude_desktop_marker(
|
|
2803
|
+
_force: boolean,
|
|
2804
|
+
lockfile_path: string,
|
|
2805
|
+
bundles_dir: string,
|
|
2806
|
+
bundle_count: number,
|
|
2807
|
+
): [number, number, string[]] {
|
|
2808
|
+
const anchor = expanduser(USER_SCOPE_PATHS['claude-desktop'] as string);
|
|
2809
|
+
const target = path.join(anchor, 'agent-config.md');
|
|
2810
|
+
mkdirp(anchor);
|
|
2811
|
+
const body = claudeDesktopMarkerBody(lockfile_path, anchor, bundles_dir, bundle_count);
|
|
2812
|
+
writeText(target, body);
|
|
2813
|
+
return [1, 0, [target]];
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
function _deploy_claude_desktop(
|
|
2817
|
+
force: boolean,
|
|
2818
|
+
package_root: string,
|
|
2819
|
+
lockfile_path: string,
|
|
2820
|
+
): DeployResult {
|
|
2821
|
+
const bundles_dir = _claude_desktop_bundles_dir();
|
|
2822
|
+
claude_desktop_bundler.build_skill_bundles(package_root, bundles_dir, force);
|
|
2823
|
+
claude_desktop_bundler.build_command_bundles(package_root, bundles_dir, force);
|
|
2824
|
+
const bundle_count = countZips(bundles_dir);
|
|
2825
|
+
const [, , marker_paths] = _write_claude_desktop_marker(
|
|
2826
|
+
force,
|
|
2827
|
+
lockfile_path,
|
|
2828
|
+
bundles_dir,
|
|
2829
|
+
bundle_count,
|
|
2830
|
+
);
|
|
2831
|
+
return [bundle_count, 0, 'deployed', [bundles_dir, ...marker_paths]];
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
function _deploy_global_content(
|
|
2835
|
+
tools: Set<string>,
|
|
2836
|
+
force: boolean,
|
|
2837
|
+
package_root: string,
|
|
2838
|
+
lockfile_path: string,
|
|
2839
|
+
): Record<string, DeployResult> {
|
|
2840
|
+
const results: Record<string, DeployResult> = {};
|
|
2841
|
+
for (const tool_id of [...tools].sort()) {
|
|
2842
|
+
if (tool_id === 'claude-desktop') {
|
|
2843
|
+
results[tool_id] = _deploy_claude_desktop(force, package_root, lockfile_path);
|
|
2844
|
+
continue;
|
|
2845
|
+
}
|
|
2846
|
+
const plan = GLOBAL_DEPLOY_SOURCES[tool_id];
|
|
2847
|
+
if (plan === undefined) {
|
|
2848
|
+
const status = ['copilot', 'aider', 'zed', 'jetbrains'].includes(tool_id)
|
|
2849
|
+
? 'hint'
|
|
2850
|
+
: 'unsupported';
|
|
2851
|
+
results[tool_id] = [0, 0, status, []];
|
|
2852
|
+
continue;
|
|
2853
|
+
}
|
|
2854
|
+
const anchor_raw = USER_SCOPE_PATHS[tool_id];
|
|
2855
|
+
if (!anchor_raw) {
|
|
2856
|
+
results[tool_id] = [0, 0, 'unsupported', []];
|
|
2857
|
+
continue;
|
|
2858
|
+
}
|
|
2859
|
+
const anchor = expanduser(anchor_raw);
|
|
2860
|
+
let written_total = 0;
|
|
2861
|
+
let skipped_total = 0;
|
|
2862
|
+
const written_paths: string[] = [];
|
|
2863
|
+
let current_files = new Set<string>();
|
|
2864
|
+
for (const [src_rel, dest_sub] of plan) {
|
|
2865
|
+
const src = path.join(package_root, src_rel);
|
|
2866
|
+
const dest = dest_sub ? path.join(anchor, dest_sub) : anchor;
|
|
2867
|
+
const [w, s, paths] = _copy_dir_dereferencing_symlinks(src, dest, force, package_root);
|
|
2868
|
+
written_total += w;
|
|
2869
|
+
skipped_total += s;
|
|
2870
|
+
written_paths.push(...paths);
|
|
2871
|
+
current_files = setUnion(
|
|
2872
|
+
current_files,
|
|
2873
|
+
global_deploy_inventory.expected_deploy_files(src, dest_sub ? dest_sub : ''),
|
|
2874
|
+
);
|
|
2875
|
+
}
|
|
2876
|
+
const missing_targets = _verify_deploy_targets(anchor, plan);
|
|
2877
|
+
if (missing_targets.length > 0) {
|
|
2878
|
+
if (!state.QUIET) {
|
|
2879
|
+
warn(
|
|
2880
|
+
`${tool_id}: deploy postcheck failed — ` +
|
|
2881
|
+
`missing/empty: ${missing_targets.join(', ')}`,
|
|
2882
|
+
);
|
|
2883
|
+
}
|
|
2884
|
+
_emit_progress({ type: 'verify_failed', tool: tool_id, missing: missing_targets });
|
|
2885
|
+
results[tool_id] = [written_total, skipped_total, 'deploy_failed', written_paths];
|
|
2886
|
+
continue;
|
|
2887
|
+
}
|
|
2888
|
+
_emit_progress({ type: 'verified', tool: tool_id });
|
|
2889
|
+
|
|
2890
|
+
const inventory = global_deploy_inventory.load_inventory();
|
|
2891
|
+
let reaped: string[] = [];
|
|
2892
|
+
const inv_tools = (inventory['tools'] as Record<string, unknown> | undefined) ?? {};
|
|
2893
|
+
if (tool_id in inv_tools) {
|
|
2894
|
+
reaped = reaped.concat(
|
|
2895
|
+
global_deploy_inventory.reap_stale(tool_id, anchor, current_files, inventory),
|
|
2896
|
+
);
|
|
2897
|
+
}
|
|
2898
|
+
reaped = reaped.concat(
|
|
2899
|
+
global_deploy_inventory.reap_tagged_orphans(
|
|
2900
|
+
anchor,
|
|
2901
|
+
plan.map(([, dest_sub]) => dest_sub),
|
|
2902
|
+
current_files,
|
|
2903
|
+
PACKAGE_TAG_ID,
|
|
2904
|
+
),
|
|
2905
|
+
);
|
|
2906
|
+
reaped = [...new Set(reaped)].sort();
|
|
2907
|
+
global_deploy_inventory.record_deploy(tool_id, anchor_raw, current_files, inventory);
|
|
2908
|
+
global_deploy_inventory.save_inventory(inventory);
|
|
2909
|
+
if (reaped.length > 0 && !state.QUIET) {
|
|
2910
|
+
info(
|
|
2911
|
+
` ${tool_id}: reaped ${reaped.length} stale deployed file(s) ` +
|
|
2912
|
+
'from a previous install',
|
|
2913
|
+
);
|
|
2914
|
+
}
|
|
2915
|
+
_emit_progress({ type: 'reaped', tool: tool_id, count: reaped.length });
|
|
2916
|
+
results[tool_id] = [written_total, skipped_total, 'deployed', written_paths];
|
|
2917
|
+
}
|
|
2918
|
+
return results;
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
function setUnion(a: Set<string>, b: Set<string>): Set<string> {
|
|
2922
|
+
const out = new Set(a);
|
|
2923
|
+
for (const v of b) out.add(v);
|
|
2924
|
+
return out;
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
function _preview_global_reap(
|
|
2928
|
+
tools: Set<string>,
|
|
2929
|
+
package_root: string,
|
|
2930
|
+
): Record<string, string[]> {
|
|
2931
|
+
const inventory = global_deploy_inventory.load_inventory();
|
|
2932
|
+
const preview: Record<string, string[]> = {};
|
|
2933
|
+
for (const tool_id of [...tools].sort()) {
|
|
2934
|
+
const plan = GLOBAL_DEPLOY_SOURCES[tool_id];
|
|
2935
|
+
if (plan === undefined) continue;
|
|
2936
|
+
const anchor_raw = USER_SCOPE_PATHS[tool_id];
|
|
2937
|
+
if (!anchor_raw) continue;
|
|
2938
|
+
const anchor = expanduser(anchor_raw);
|
|
2939
|
+
let current_files = new Set<string>();
|
|
2940
|
+
for (const [src_rel, dest_sub] of plan) {
|
|
2941
|
+
const src = path.join(package_root, src_rel);
|
|
2942
|
+
current_files = setUnion(
|
|
2943
|
+
current_files,
|
|
2944
|
+
global_deploy_inventory.expected_deploy_files(src, dest_sub ? dest_sub : ''),
|
|
2945
|
+
);
|
|
2946
|
+
}
|
|
2947
|
+
let would_reap: string[] = [];
|
|
2948
|
+
const inv_tools = (inventory['tools'] as Record<string, unknown> | undefined) ?? {};
|
|
2949
|
+
if (tool_id in inv_tools) {
|
|
2950
|
+
would_reap = would_reap.concat(
|
|
2951
|
+
global_deploy_inventory.reap_stale(tool_id, anchor, current_files, inventory, true),
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
would_reap = would_reap.concat(
|
|
2955
|
+
global_deploy_inventory.reap_tagged_orphans(
|
|
2956
|
+
anchor,
|
|
2957
|
+
plan.map(([, dest_sub]) => dest_sub),
|
|
2958
|
+
current_files,
|
|
2959
|
+
PACKAGE_TAG_ID,
|
|
2960
|
+
true,
|
|
2961
|
+
),
|
|
2962
|
+
);
|
|
2963
|
+
const paths = [...new Set(would_reap.map((p) => String(p)))].sort();
|
|
2964
|
+
if (paths.length > 0) preview[tool_id] = paths;
|
|
2965
|
+
}
|
|
2966
|
+
return preview;
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
function _verify_deploy_targets(anchor: string, plan: ReadonlyArray<readonly [string, string]>): string[] {
|
|
2970
|
+
const missing: string[] = [];
|
|
2971
|
+
for (const [, dest_sub] of plan) {
|
|
2972
|
+
const target = dest_sub ? path.join(anchor, dest_sub) : anchor;
|
|
2973
|
+
const label = dest_sub || '.';
|
|
2974
|
+
if (!isDir(target)) {
|
|
2975
|
+
missing.push(label);
|
|
2976
|
+
continue;
|
|
2977
|
+
}
|
|
2978
|
+
try {
|
|
2979
|
+
const entries = fs.readdirSync(target);
|
|
2980
|
+
if (entries.length === 0) missing.push(label);
|
|
2981
|
+
} catch {
|
|
2982
|
+
missing.push(label);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
return missing;
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
/**
|
|
2989
|
+
* Remove lab-tier skills/commands from a completed deploy (core-only).
|
|
2990
|
+
*
|
|
2991
|
+
* road-to-install-contract-stability Phase 2 Step 2. Skills are pruned by
|
|
2992
|
+
* whole directory (tier decided by the skill's `SKILL.md` frontmatter);
|
|
2993
|
+
* commands by file (own frontmatter). Rules / personas / contexts / templates
|
|
2994
|
+
* are core/shared and left intact. Returns `[pruned_count, adjusted_results]`
|
|
2995
|
+
* with the lab paths removed from each tool's `written_paths` so the manifest
|
|
2996
|
+
* never records them.
|
|
2997
|
+
*/
|
|
2998
|
+
function _prune_lab_modules(
|
|
2999
|
+
deploy_results: Record<string, DeployResult>,
|
|
3000
|
+
lab_ids: Set<string>,
|
|
3001
|
+
): [number, Record<string, DeployResult>] {
|
|
3002
|
+
let pruned = 0;
|
|
3003
|
+
const adjusted: Record<string, DeployResult> = {};
|
|
3004
|
+
for (const tool_id of Object.keys(deploy_results)) {
|
|
3005
|
+
const [written, skipped, status, paths] = deploy_results[tool_id] as DeployResult;
|
|
3006
|
+
const lab_skill_dirs = new Set<string>();
|
|
3007
|
+
for (const p of paths) {
|
|
3008
|
+
const parts = p.split(path.sep);
|
|
3009
|
+
if (parts.includes('skills')) {
|
|
3010
|
+
const i = parts.indexOf('skills');
|
|
3011
|
+
if (i + 1 < parts.length) {
|
|
3012
|
+
const skill_root = parts.slice(0, i + 2).join(path.sep);
|
|
3013
|
+
if (!lab_skill_dirs.has(skill_root)) {
|
|
3014
|
+
const skillmd = path.join(skill_root, 'SKILL.md');
|
|
3015
|
+
if (pathExists(skillmd) && surface_tiers.is_lab_artefact(skillmd, lab_ids)) {
|
|
3016
|
+
lab_skill_dirs.add(skill_root);
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
const keep: string[] = [];
|
|
3023
|
+
const delete_files: string[] = [];
|
|
3024
|
+
for (const p of paths) {
|
|
3025
|
+
const parts = p.split(path.sep);
|
|
3026
|
+
let is_lab = false;
|
|
3027
|
+
if (parts.includes('skills')) {
|
|
3028
|
+
const i = parts.indexOf('skills');
|
|
3029
|
+
if (i + 1 < parts.length && lab_skill_dirs.has(parts.slice(0, i + 2).join(path.sep))) {
|
|
3030
|
+
is_lab = true;
|
|
3031
|
+
}
|
|
3032
|
+
} else if (
|
|
3033
|
+
parts.includes('commands') &&
|
|
3034
|
+
path.extname(p) === '.md' &&
|
|
3035
|
+
surface_tiers.is_lab_artefact(p, lab_ids)
|
|
3036
|
+
) {
|
|
3037
|
+
is_lab = true;
|
|
3038
|
+
}
|
|
3039
|
+
(is_lab ? delete_files : keep).push(p);
|
|
3040
|
+
}
|
|
3041
|
+
for (const d of lab_skill_dirs) {
|
|
3042
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
3043
|
+
}
|
|
3044
|
+
for (const p of delete_files) {
|
|
3045
|
+
if (p.split(path.sep).includes('commands') && pathExists(p)) {
|
|
3046
|
+
try {
|
|
3047
|
+
fs.unlinkSync(p);
|
|
3048
|
+
} catch {
|
|
3049
|
+
// OSError → swallow, mirroring the .py.
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
pruned += delete_files.length;
|
|
3054
|
+
adjusted[tool_id] = [Math.max(0, written - delete_files.length), skipped, status, keep];
|
|
3055
|
+
}
|
|
3056
|
+
return [pruned, adjusted];
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
function install_global(
|
|
3060
|
+
tools: Set<string>,
|
|
3061
|
+
force: boolean,
|
|
3062
|
+
project_root: string | null = null,
|
|
3063
|
+
core_only = false,
|
|
3064
|
+
): number {
|
|
3065
|
+
const migrated = user_global_paths.migrate_legacy_namespace();
|
|
3066
|
+
if (migrated && !state.QUIET) {
|
|
3067
|
+
info(
|
|
3068
|
+
'🔁 Migrated user-global config to ' +
|
|
3069
|
+
`${user_global_paths.event4u_root()} (legacy ` +
|
|
3070
|
+
`${user_global_paths.legacy_xdg_root()} preserved as fallback)`,
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
const installed_version = installed_lock.current_package_version();
|
|
3075
|
+
const read_path = installed_lock.lockfile_path();
|
|
3076
|
+
const write_path = installed_lock.lockfile_write_path();
|
|
3077
|
+
const [, recorded] = installed_lock.check_version(installed_version, { path: read_path });
|
|
3078
|
+
const classification = installed_lock.classify_mismatch(installed_version, recorded);
|
|
3079
|
+
|
|
3080
|
+
if (classification === 'downgrade' && !force) {
|
|
3081
|
+
if (!state.QUIET) {
|
|
3082
|
+
process.stdout.write('\n');
|
|
3083
|
+
warn('Refusing global install: lockfile records a newer version.');
|
|
3084
|
+
info(` Lockfile: ${read_path}`);
|
|
3085
|
+
info(` Recorded version: ${recorded}`);
|
|
3086
|
+
info(` Current package: ${installed_version}`);
|
|
3087
|
+
info(' Fix: upgrade the package, or re-run with `--force`');
|
|
3088
|
+
process.stdout.write('\n');
|
|
3089
|
+
}
|
|
3090
|
+
return 1;
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
if (['upgrade', 'unparseable'].includes(classification) && !state.QUIET) {
|
|
3094
|
+
info(`🔄 Upgrading lockfile from ${recorded} to ${installed_version}, redeploying tools`);
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
// Install-ABI migration (road-to-install-contract-stability Phase 1.5):
|
|
3098
|
+
// an installed tree at install_layout_version < current (absent =
|
|
3099
|
+
// pre-freeze v0) is migrated in place before the deploy, surgical-uninstall
|
|
3100
|
+
// pointers preserved. At the freeze baseline this only stamps the version;
|
|
3101
|
+
// future layout versions add concrete shape transforms in migrate_layout.
|
|
3102
|
+
const migration = installed_lock.migrate_layout({ path: write_path });
|
|
3103
|
+
if (migration && migration.changed.length > 0 && !state.QUIET) {
|
|
3104
|
+
info(
|
|
3105
|
+
'🔧 Migrated install layout ' +
|
|
3106
|
+
`v${migration.from} → v${migration.to}: ` +
|
|
3107
|
+
migration.changed.join('; '),
|
|
3108
|
+
);
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
if (!state.QUIET) {
|
|
3112
|
+
process.stdout.write('\n');
|
|
3113
|
+
info('Agent Config — Global (user-scope) install [ADR-007]');
|
|
3114
|
+
info('Per-tool anchor paths:');
|
|
3115
|
+
for (const tool_id of [...tools].sort()) {
|
|
3116
|
+
const anchor = USER_SCOPE_PATHS[tool_id];
|
|
3117
|
+
if (anchor === undefined) continue;
|
|
3118
|
+
process.stdout.write(` ${tool_id.padEnd(15)} → ${anchor}\n`);
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
const existing = installed_lock.read_lockfile(read_path) ?? {};
|
|
3123
|
+
const existing_tools = Array.isArray((existing as Record<string, unknown>)['tools'])
|
|
3124
|
+
? ((existing as Record<string, unknown>)['tools'] as string[])
|
|
3125
|
+
: [];
|
|
3126
|
+
const merged_tools = [...new Set([...existing_tools, ...tools])].sort();
|
|
3127
|
+
const written = installed_lock.write_lockfile(installed_version, merged_tools, { path: write_path });
|
|
3128
|
+
|
|
3129
|
+
if (!state.QUIET) {
|
|
3130
|
+
process.stdout.write('\n');
|
|
3131
|
+
info(`Lockfile written: ${written}`);
|
|
3132
|
+
info(` schema_version=1, agent_config_version=${installed_version}`);
|
|
3133
|
+
info(` tools=${merged_tools.join(',')}`);
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
const package_root = _resolve_package_root_for_global();
|
|
3137
|
+
let deploy_results = _deploy_global_content(tools, force, package_root, written);
|
|
3138
|
+
|
|
3139
|
+
// Core-only mode (road-to-install-contract-stability Phase 2 Step 2): prune
|
|
3140
|
+
// lab-tier skills/commands from the just-deployed tree so the installed
|
|
3141
|
+
// surface carries zero lab modules. Runs after the deploy postcheck so the
|
|
3142
|
+
// prune operates on a verified-complete tree; the adjusted results feed the
|
|
3143
|
+
// manifest so lab paths are never recorded.
|
|
3144
|
+
if (core_only) {
|
|
3145
|
+
const lab_ids = surface_tiers.load_lab_pack_ids(package_root);
|
|
3146
|
+
let pruned: number;
|
|
3147
|
+
[pruned, deploy_results] = _prune_lab_modules(deploy_results, lab_ids);
|
|
3148
|
+
if (!state.QUIET) {
|
|
3149
|
+
info(
|
|
3150
|
+
`🧹 Core-only install: pruned ${pruned} lab-tier artefact(s) ` +
|
|
3151
|
+
`(packs: ${[...lab_ids].sort().join(', ')}).`,
|
|
3152
|
+
);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
const failed_tools = new Set<string>(
|
|
3157
|
+
Object.keys(deploy_results).filter(
|
|
3158
|
+
(tool_id) => (deploy_results[tool_id] as DeployResult)[2] === 'deploy_failed',
|
|
3159
|
+
),
|
|
3160
|
+
);
|
|
3161
|
+
if (failed_tools.size > 0) {
|
|
3162
|
+
const corrected_tools = merged_tools.filter((t) => !failed_tools.has(t));
|
|
3163
|
+
if (!arrayStrEqual(corrected_tools, merged_tools)) {
|
|
3164
|
+
installed_lock.write_lockfile(installed_version, corrected_tools, { path: write_path });
|
|
3165
|
+
if (!state.QUIET) {
|
|
3166
|
+
warn(
|
|
3167
|
+
'Lockfile corrected after deploy postcheck — dropped ' +
|
|
3168
|
+
`${[...failed_tools].sort().join(', ')} (verification failed).`,
|
|
3169
|
+
);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
if (state.PROGRESS_NDJSON) {
|
|
3175
|
+
const ordered = Object.keys(deploy_results).sort();
|
|
3176
|
+
const total = ordered.length;
|
|
3177
|
+
ordered.forEach((tool_id, i) => {
|
|
3178
|
+
const [, , status] = deploy_results[tool_id] as DeployResult;
|
|
3179
|
+
_emit_progress({
|
|
3180
|
+
type: 'file',
|
|
3181
|
+
file: tool_id,
|
|
3182
|
+
status,
|
|
3183
|
+
written: i + 1,
|
|
3184
|
+
total,
|
|
3185
|
+
});
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
if (!state.QUIET) {
|
|
3190
|
+
process.stdout.write('\n');
|
|
3191
|
+
info('Deployed per-tool content:');
|
|
3192
|
+
for (const tool_id of Object.keys(deploy_results).sort()) {
|
|
3193
|
+
const [w, s, status] = deploy_results[tool_id] as DeployResult;
|
|
3194
|
+
const anchor = USER_SCOPE_PATHS[tool_id] ?? '';
|
|
3195
|
+
if (status === 'deployed' && tool_id === 'claude-desktop') {
|
|
3196
|
+
const bundles_dir = _claude_desktop_bundles_dir();
|
|
3197
|
+
process.stdout.write(` ${tool_id.padEnd(15)} → ${bundles_dir} (${w} bundles)\n`);
|
|
3198
|
+
} else if (status === 'deployed') {
|
|
3199
|
+
process.stdout.write(` ${tool_id.padEnd(15)} → ${anchor} (${w} files, ${s} skipped)\n`);
|
|
3200
|
+
} else if (status === 'marker') {
|
|
3201
|
+
process.stdout.write(
|
|
3202
|
+
` ${tool_id.padEnd(15)} → ${anchor}agent-config.md (${w ? 'written' : 'skipped'})\n`,
|
|
3203
|
+
);
|
|
3204
|
+
} else if (status === 'hint') {
|
|
3205
|
+
process.stdout.write(
|
|
3206
|
+
` ${tool_id.padEnd(15)} → no user-scope convention; use \`agent-config export --tool=${tool_id}\`\n`,
|
|
3207
|
+
);
|
|
3208
|
+
} else {
|
|
3209
|
+
process.stdout.write(
|
|
3210
|
+
` ${tool_id.padEnd(15)} → no global-scope content yet (project-scope install supported)\n`,
|
|
3211
|
+
);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
// Skipped inside the agent-config source repo (detected by
|
|
3217
|
+
// `.agent-src.uncondensed/`) — maintainers dogfood with their own
|
|
3218
|
+
// `.agent-settings.yml` and the manifest would be untracked noise.
|
|
3219
|
+
if (
|
|
3220
|
+
project_root !== null &&
|
|
3221
|
+
pathExists(_resolve_settings_read(project_root)) &&
|
|
3222
|
+
!isDir(path.join(project_root, '.agent-src.uncondensed'))
|
|
3223
|
+
) {
|
|
3224
|
+
const files_by_tool = _files_by_tool_from_deploy(deploy_results);
|
|
3225
|
+
const rc = _update_installed_tools_manifest(project_root, tools, 'global', force, files_by_tool);
|
|
3226
|
+
if (rc !== 0) return rc;
|
|
3227
|
+
|
|
3228
|
+
// Consumer bridge marker (Phase 4.2). The surrounding
|
|
3229
|
+
// `.agent-src.uncondensed` guard already covers the source-repo case;
|
|
3230
|
+
// the dev-mode skip is enforced inside the writer.
|
|
3231
|
+
const marker_path = _write_consumer_bridge_marker(project_root, installed_version);
|
|
3232
|
+
if (marker_path !== null && !state.QUIET) {
|
|
3233
|
+
const rel = isRelativeTo(marker_path, project_root)
|
|
3234
|
+
? path.relative(project_root, marker_path)
|
|
3235
|
+
: marker_path;
|
|
3236
|
+
info(`Bridge marker written: ${rel}`);
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
const anchor_paths = _write_per_tool_project_anchors(project_root, tools);
|
|
3240
|
+
if (anchor_paths.length > 0 && !state.QUIET) {
|
|
3241
|
+
for (const p of anchor_paths) {
|
|
3242
|
+
const rel = isRelativeTo(p, project_root) ? path.relative(project_root, p) : p;
|
|
3243
|
+
info(`Project anchor written: ${rel}`);
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
if (!state.QUIET) {
|
|
3249
|
+
process.stdout.write('\n');
|
|
3250
|
+
success('Global install completed.');
|
|
3251
|
+
process.stdout.write('\n');
|
|
3252
|
+
}
|
|
3253
|
+
return 0;
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
function arrayStrEqual(a: string[], b: string[]): boolean {
|
|
3257
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
// --- Argument parsing ---
|
|
3261
|
+
|
|
3262
|
+
function _merge_tools_aliases(tools: string | null, ai: string | null): string {
|
|
3263
|
+
const items: string[] = [];
|
|
3264
|
+
for (const raw of [tools, ai]) {
|
|
3265
|
+
if (!raw) continue;
|
|
3266
|
+
for (const piece of raw.split(',')) {
|
|
3267
|
+
const stripped = piece.trim();
|
|
3268
|
+
if (stripped && !items.includes(stripped)) items.push(stripped);
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
return items.length > 0 ? items.join(',') : 'all';
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
const PROG = 'install.py';
|
|
3275
|
+
// Verbatim argparse usage block at COLUMNS=80 (captured from the .py). The
|
|
3276
|
+
// `--help` BODY (per-flag descriptions) is a documented divergence — argparse
|
|
3277
|
+
// re-wraps it to the live terminal width, which is not worth reproducing; the
|
|
3278
|
+
// golden tests assert the `usage:` token + exit code, not the body prose.
|
|
3279
|
+
const USAGE =
|
|
3280
|
+
`usage: ${PROG} [-h] [--profile PROFILE] [--user-type USER_TYPE] [--force]\n` +
|
|
3281
|
+
' [--skip-bridges] [--augment-user-hooks]\n' +
|
|
3282
|
+
' [--cursor-user-hooks] [--cline-user-hooks]\n' +
|
|
3283
|
+
' [--windsurf-user-hooks] [--gemini-user-hooks]\n' +
|
|
3284
|
+
' [--project PROJECT] [--package PACKAGE] [--quiet]\n' +
|
|
3285
|
+
' [--tools TOOLS] [--ai AI] [--packs PACKS] [--core-only]\n' +
|
|
3286
|
+
' [--no-smoke] [--global]\n' +
|
|
3287
|
+
' [--scope {project,global,prompt,auto}]\n' +
|
|
3288
|
+
' [--custom-path CUSTOM_PATH] [--offline] [--minimal]\n' +
|
|
3289
|
+
' [--interactive] [--no-ui] [--dry-run]\n' +
|
|
3290
|
+
' [--apply-payload APPLY_PAYLOAD]\n';
|
|
3291
|
+
|
|
3292
|
+
const _STORE_TRUE_FLAGS: Record<string, keyof Options> = {
|
|
3293
|
+
'--force': 'force',
|
|
3294
|
+
'--skip-bridges': 'skip_bridges',
|
|
3295
|
+
'--augment-user-hooks': 'augment_user_hooks',
|
|
3296
|
+
'--cursor-user-hooks': 'cursor_user_hooks',
|
|
3297
|
+
'--cline-user-hooks': 'cline_user_hooks',
|
|
3298
|
+
'--windsurf-user-hooks': 'windsurf_user_hooks',
|
|
3299
|
+
'--gemini-user-hooks': 'gemini_user_hooks',
|
|
3300
|
+
'--quiet': 'quiet',
|
|
3301
|
+
'--core-only': 'core_only',
|
|
3302
|
+
'--no-smoke': 'no_smoke',
|
|
3303
|
+
'--global': 'global_install',
|
|
3304
|
+
'--offline': 'offline',
|
|
3305
|
+
'--minimal': 'minimal',
|
|
3306
|
+
'--settings-only': 'minimal',
|
|
3307
|
+
'--interactive': 'interactive',
|
|
3308
|
+
'--no-ui': 'no_ui',
|
|
3309
|
+
'--dry-run': 'dry_run',
|
|
3310
|
+
};
|
|
3311
|
+
|
|
3312
|
+
const _VALUE_FLAGS: Record<string, keyof Options> = {
|
|
3313
|
+
'--profile': 'profile',
|
|
3314
|
+
'--user-type': 'user_type',
|
|
3315
|
+
'--project': 'project',
|
|
3316
|
+
'--package': 'package',
|
|
3317
|
+
'--tools': 'tools',
|
|
3318
|
+
'--ai': 'ai',
|
|
3319
|
+
'--packs': 'packs',
|
|
3320
|
+
'--scope': 'scope',
|
|
3321
|
+
'--custom-path': 'custom_path',
|
|
3322
|
+
'--apply-payload': 'apply_payload',
|
|
3323
|
+
};
|
|
3324
|
+
|
|
3325
|
+
function _argError(msg: string): never {
|
|
3326
|
+
process.stderr.write(USAGE);
|
|
3327
|
+
process.stderr.write(`${PROG}: error: ${msg}\n`);
|
|
3328
|
+
throw new ArgparseExit(2);
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
function parse_options(argv: string[]): Options {
|
|
3332
|
+
const opts: Record<string, unknown> = {
|
|
3333
|
+
profile: DEFAULT_PROFILE,
|
|
3334
|
+
user_type: '',
|
|
3335
|
+
force: false,
|
|
3336
|
+
skip_bridges: false,
|
|
3337
|
+
augment_user_hooks: false,
|
|
3338
|
+
cursor_user_hooks: false,
|
|
3339
|
+
cline_user_hooks: false,
|
|
3340
|
+
windsurf_user_hooks: false,
|
|
3341
|
+
gemini_user_hooks: false,
|
|
3342
|
+
project: null,
|
|
3343
|
+
package: null,
|
|
3344
|
+
quiet: false,
|
|
3345
|
+
tools: null,
|
|
3346
|
+
ai: null,
|
|
3347
|
+
packs: null,
|
|
3348
|
+
core_only: false,
|
|
3349
|
+
no_smoke: false,
|
|
3350
|
+
global_install: false,
|
|
3351
|
+
scope: null,
|
|
3352
|
+
custom_path: null,
|
|
3353
|
+
offline: false,
|
|
3354
|
+
minimal: false,
|
|
3355
|
+
interactive: false,
|
|
3356
|
+
no_ui: false,
|
|
3357
|
+
dry_run: false,
|
|
3358
|
+
apply_payload: null,
|
|
3359
|
+
};
|
|
3360
|
+
|
|
3361
|
+
const positionals: string[] = [];
|
|
3362
|
+
let i = 0;
|
|
3363
|
+
while (i < argv.length) {
|
|
3364
|
+
const a = argv[i] as string;
|
|
3365
|
+
if (a === '-h' || a === '--help') {
|
|
3366
|
+
process.stdout.write(USAGE);
|
|
3367
|
+
throw new ArgparseExit(0);
|
|
3368
|
+
}
|
|
3369
|
+
// --flag=value form
|
|
3370
|
+
const eq = a.startsWith('--') ? a.indexOf('=') : -1;
|
|
3371
|
+
const flag = eq >= 0 ? a.slice(0, eq) : a;
|
|
3372
|
+
const inlineVal = eq >= 0 ? a.slice(eq + 1) : null;
|
|
3373
|
+
|
|
3374
|
+
const storeTrueDest = _STORE_TRUE_FLAGS[flag];
|
|
3375
|
+
if (storeTrueDest !== undefined) {
|
|
3376
|
+
if (inlineVal !== null) {
|
|
3377
|
+
_argError(`argument ${flag}: ignored explicit argument '${inlineVal}'`);
|
|
3378
|
+
}
|
|
3379
|
+
opts[storeTrueDest] = true;
|
|
3380
|
+
i += 1;
|
|
3381
|
+
continue;
|
|
3382
|
+
}
|
|
3383
|
+
const valueDest = _VALUE_FLAGS[flag];
|
|
3384
|
+
if (valueDest !== undefined) {
|
|
3385
|
+
let value: string;
|
|
3386
|
+
if (inlineVal !== null) {
|
|
3387
|
+
value = inlineVal;
|
|
3388
|
+
} else {
|
|
3389
|
+
if (i + 1 >= argv.length) _argError(`argument ${flag}: expected one argument`);
|
|
3390
|
+
value = argv[i + 1] as string;
|
|
3391
|
+
i += 1;
|
|
3392
|
+
}
|
|
3393
|
+
if (flag === '--scope' && !['project', 'global', 'prompt', 'auto'].includes(value)) {
|
|
3394
|
+
_argError(
|
|
3395
|
+
`argument --scope: invalid choice: '${value}' ` +
|
|
3396
|
+
"(choose from 'project', 'global', 'prompt', 'auto')",
|
|
3397
|
+
);
|
|
3398
|
+
}
|
|
3399
|
+
opts[valueDest] = value;
|
|
3400
|
+
i += 1;
|
|
3401
|
+
continue;
|
|
3402
|
+
}
|
|
3403
|
+
if (a.startsWith('-') && a !== '-') {
|
|
3404
|
+
_argError(`unrecognized arguments: ${a}`);
|
|
3405
|
+
}
|
|
3406
|
+
positionals.push(a);
|
|
3407
|
+
i += 1;
|
|
3408
|
+
}
|
|
3409
|
+
if (positionals.length > 0) {
|
|
3410
|
+
_argError(`unrecognized arguments: ${positionals.join(' ')}`);
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
opts.tools = _merge_tools_aliases(opts.tools as string | null, opts.ai as string | null);
|
|
3414
|
+
const rawPacks = opts.packs;
|
|
3415
|
+
opts.packs =
|
|
3416
|
+
typeof rawPacks === 'string'
|
|
3417
|
+
? rawPacks
|
|
3418
|
+
.split(',')
|
|
3419
|
+
.map((p) => p.trim())
|
|
3420
|
+
.filter((p) => p)
|
|
3421
|
+
: [];
|
|
3422
|
+
if (opts.scope === 'global' && opts.custom_path) {
|
|
3423
|
+
fail('--custom-path is incompatible with --scope=global');
|
|
3424
|
+
}
|
|
3425
|
+
if (opts.global_install && opts.custom_path) {
|
|
3426
|
+
fail('--custom-path is incompatible with --global');
|
|
3427
|
+
}
|
|
3428
|
+
if (opts.scope !== null && opts.global_install && opts.scope !== 'global') {
|
|
3429
|
+
fail(`--scope=${opts.scope} conflicts with --global; pick one`);
|
|
3430
|
+
}
|
|
3431
|
+
return opts as unknown as Options;
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
const _VALID_TOOLS: ReadonlySet<string> = new Set([
|
|
3435
|
+
'claude-code',
|
|
3436
|
+
'claude-desktop',
|
|
3437
|
+
'cursor',
|
|
3438
|
+
'windsurf',
|
|
3439
|
+
'cline',
|
|
3440
|
+
'gemini-cli',
|
|
3441
|
+
'copilot',
|
|
3442
|
+
'augment',
|
|
3443
|
+
'aider',
|
|
3444
|
+
'codex',
|
|
3445
|
+
'roocode',
|
|
3446
|
+
'continue',
|
|
3447
|
+
'kilocode',
|
|
3448
|
+
'zed',
|
|
3449
|
+
'jetbrains',
|
|
3450
|
+
'kiro',
|
|
3451
|
+
'qoder',
|
|
3452
|
+
'opencode',
|
|
3453
|
+
'trae',
|
|
3454
|
+
'antigravity',
|
|
3455
|
+
'codebuddy',
|
|
3456
|
+
'droid',
|
|
3457
|
+
'warp',
|
|
3458
|
+
'all',
|
|
3459
|
+
]);
|
|
3460
|
+
|
|
3461
|
+
function _parse_tools(raw: string): Set<string> {
|
|
3462
|
+
if (!raw || !raw.trim()) fail('--tools requires a non-empty value');
|
|
3463
|
+
const items = raw.split(',').map((s) => s.trim()).filter((s) => s);
|
|
3464
|
+
if (items.length === 0) fail('--tools requires at least one ID');
|
|
3465
|
+
const unknown = items.filter((s) => !_VALID_TOOLS.has(s));
|
|
3466
|
+
if (unknown.length > 0) {
|
|
3467
|
+
fail(
|
|
3468
|
+
`--tools: unknown ID(s): ${unknown.join(', ')} ` +
|
|
3469
|
+
`(valid: ${[..._VALID_TOOLS].sort().join(', ')})`,
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3472
|
+
if (items.includes('all')) {
|
|
3473
|
+
return new Set([..._VALID_TOOLS].filter((t) => t !== 'all'));
|
|
3474
|
+
}
|
|
3475
|
+
return new Set(items);
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
function _tools_was_all(raw: string): boolean {
|
|
3479
|
+
if (!raw || !raw.trim()) return false;
|
|
3480
|
+
const items = raw.split(',').map((s) => s.trim()).filter((s) => s);
|
|
3481
|
+
return items.includes('all');
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
function _is_tool_enabled(tools: Set<string>, tool_id: string): boolean {
|
|
3485
|
+
return tools.has(tool_id);
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3488
|
+
// --- Minimal init ---
|
|
3489
|
+
|
|
3490
|
+
function _minimal_templates_root(): string {
|
|
3491
|
+
const start = resolvePath(_HERE);
|
|
3492
|
+
const chain = [start];
|
|
3493
|
+
let cur = start;
|
|
3494
|
+
for (;;) {
|
|
3495
|
+
const parent = path.dirname(cur);
|
|
3496
|
+
if (parent === cur) break;
|
|
3497
|
+
chain.push(parent);
|
|
3498
|
+
cur = parent;
|
|
3499
|
+
}
|
|
3500
|
+
for (const ancestor of chain) {
|
|
3501
|
+
const candidate = path.join(ancestor, 'src', 'templates', 'minimal');
|
|
3502
|
+
if (isDir(candidate)) return candidate;
|
|
3503
|
+
}
|
|
3504
|
+
fail('Could not locate src/templates/minimal/ — package install is corrupt.');
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
const INSTALL_MODE_MARKER_REL = 'agents/.agent-state/install-mode.txt';
|
|
3508
|
+
|
|
3509
|
+
function _write_install_mode_marker(project_root: string, mode: string): void {
|
|
3510
|
+
if (mode !== 'minimal' && mode !== 'full') return;
|
|
3511
|
+
const marker = path.join(project_root, INSTALL_MODE_MARKER_REL);
|
|
3512
|
+
try {
|
|
3513
|
+
mkdirp(path.dirname(marker));
|
|
3514
|
+
writeText(marker, `${mode}\n`);
|
|
3515
|
+
} catch {
|
|
3516
|
+
/* advisory marker; never abort */
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3520
|
+
function install_minimal(target_root_in: string, force: boolean, user_type: string = ''): number {
|
|
3521
|
+
let target_root = resolvePath(target_root_in);
|
|
3522
|
+
mkdirp(target_root);
|
|
3523
|
+
|
|
3524
|
+
const parent = path.dirname(target_root);
|
|
3525
|
+
if (parent !== target_root) {
|
|
3526
|
+
const existing = find_project_root_with_anchor(parent);
|
|
3527
|
+
if (existing !== null && existing[0] !== target_root) {
|
|
3528
|
+
const [root, anchor] = existing;
|
|
3529
|
+
fail(
|
|
3530
|
+
'Refusing to nest an agent-config layer inside an existing ' +
|
|
3531
|
+
`project (anchor: ${anchor}). Existing root: ${root}. ` +
|
|
3532
|
+
'Remove the parent layer first or run `--minimal` outside it.',
|
|
3533
|
+
);
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
const templates = _minimal_templates_root();
|
|
3538
|
+
const settings_src = path.join(templates, SETTINGS_FILE);
|
|
3539
|
+
const overrides_gitkeep_src = path.join(templates, 'overrides-gitkeep');
|
|
3540
|
+
const overrides_readme_src = path.join(templates, 'agents-overrides-readme.md');
|
|
3541
|
+
|
|
3542
|
+
if (!isFile(settings_src)) fail(`Bundled minimal settings template missing under ${templates}`);
|
|
3543
|
+
if (!isFile(overrides_gitkeep_src) || !isFile(overrides_readme_src)) {
|
|
3544
|
+
fail(`Bundled overrides scaffold templates missing under ${templates}`);
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
info(`Minimal init → ${target_root}`);
|
|
3548
|
+
|
|
3549
|
+
const overrides_root = path.join(target_root, 'agents', 'overrides');
|
|
3550
|
+
mkdirp(overrides_root);
|
|
3551
|
+
const gitkeep_body = readText(overrides_gitkeep_src);
|
|
3552
|
+
for (const sub of ['rules', 'skills', 'commands']) {
|
|
3553
|
+
const sub_dir = path.join(overrides_root, sub);
|
|
3554
|
+
mkdirp(sub_dir);
|
|
3555
|
+
const gitkeep_dst = path.join(sub_dir, '.gitkeep');
|
|
3556
|
+
if (pathExists(gitkeep_dst) && !force) {
|
|
3557
|
+
skip(`agents/overrides/${sub}/.gitkeep already exists (use --force to overwrite)`);
|
|
3558
|
+
} else {
|
|
3559
|
+
writeText(gitkeep_dst, gitkeep_body);
|
|
3560
|
+
success(`Wrote agents/overrides/${sub}/.gitkeep`);
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3564
|
+
const readme_dst = path.join(overrides_root, 'README.md');
|
|
3565
|
+
if (pathExists(readme_dst) && !force) {
|
|
3566
|
+
skip('agents/overrides/README.md already exists (use --force to overwrite)');
|
|
3567
|
+
} else {
|
|
3568
|
+
writeText(readme_dst, readText(overrides_readme_src));
|
|
3569
|
+
success('Wrote agents/overrides/README.md');
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3572
|
+
if (user_type) {
|
|
3573
|
+
const settings_dst = _canonical_settings_target(target_root);
|
|
3574
|
+
if (pathExists(settings_dst) && !force) {
|
|
3575
|
+
skip(`${SETTINGS_FILE} already exists (use --force to overwrite)`);
|
|
3576
|
+
} else {
|
|
3577
|
+
const body =
|
|
3578
|
+
readText(settings_src).replace(/\s+$/, '') +
|
|
3579
|
+
'\n\n# --- Personal (step-9 user-type axis) ---\n' +
|
|
3580
|
+
'personal:\n' +
|
|
3581
|
+
` user_type: ${user_type}\n`;
|
|
3582
|
+
mkdirp(path.dirname(settings_dst));
|
|
3583
|
+
writeText(settings_dst, body);
|
|
3584
|
+
success(`Wrote ${SETTINGS_FILE} (user_type=${user_type})`);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3588
|
+
const installed_version = installed_lock.current_package_version();
|
|
3589
|
+
const marker_path = _write_consumer_bridge_marker(target_root, installed_version);
|
|
3590
|
+
if (marker_path !== null) {
|
|
3591
|
+
const rel = isRelativeTo(marker_path, target_root)
|
|
3592
|
+
? path.relative(target_root, marker_path)
|
|
3593
|
+
: marker_path;
|
|
3594
|
+
success(`Wrote ${rel}`);
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
_write_install_mode_marker(target_root, 'minimal');
|
|
3598
|
+
|
|
3599
|
+
if (!state.QUIET) {
|
|
3600
|
+
process.stderr.write(
|
|
3601
|
+
'ℹ️ Minimal install — run `agent-config install --force` ' +
|
|
3602
|
+
'to add AGENTS.md, bridges, and tool integrations.\n',
|
|
3603
|
+
);
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
if (!state.QUIET) {
|
|
3607
|
+
process.stdout.write('\n');
|
|
3608
|
+
info('Next steps:');
|
|
3609
|
+
info(' • Ensure `agent-config` is on $PATH: npm install -g @event4u/agent-config');
|
|
3610
|
+
info(' • Drop project-scoped overrides under `agents/overrides/{rules,skills,commands}/`.');
|
|
3611
|
+
info(' • Run `agent-config doctor` to verify the layer is picked up.');
|
|
3612
|
+
}
|
|
3613
|
+
return 0;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
// --- Interactive init ---
|
|
3617
|
+
|
|
3618
|
+
const _INTERACTIVE_USER_TYPES: ReadonlyArray<readonly [string, string]> = [
|
|
3619
|
+
['creator', 'Content / writing / publishing'],
|
|
3620
|
+
['founder', 'Early-stage company building'],
|
|
3621
|
+
['consultant', 'Advisory / strategy / discovery'],
|
|
3622
|
+
['gtm', 'Sales / marketing / revenue ops'],
|
|
3623
|
+
['finance', 'Finance / FP&A / unit economics'],
|
|
3624
|
+
['ops', 'Operations / incident / compliance'],
|
|
3625
|
+
['developer', 'Engineering / code-heavy work'],
|
|
3626
|
+
];
|
|
3627
|
+
const _INTERACTIVE_STACKS: ReadonlyArray<readonly [string, string]> = [
|
|
3628
|
+
['none', 'No code project / pure content'],
|
|
3629
|
+
['laravel', 'PHP / Laravel'],
|
|
3630
|
+
['nextjs', 'TypeScript / Next.js / React'],
|
|
3631
|
+
['python', 'Python / FastAPI / Django'],
|
|
3632
|
+
['symfony', 'PHP / Symfony'],
|
|
3633
|
+
['generic', 'Other / mixed stack'],
|
|
3634
|
+
];
|
|
3635
|
+
const _INTERACTIVE_VERBOSITIES: ReadonlyArray<readonly [string, string]> = [
|
|
3636
|
+
['quiet', 'Telegraph / minimal output'],
|
|
3637
|
+
['normal', 'Default verbosity'],
|
|
3638
|
+
['verbose', 'Full intent announcements + play-by-play'],
|
|
3639
|
+
];
|
|
3640
|
+
const _LOCAL_CONFIG_FILE = '.agent-config.local.json';
|
|
3641
|
+
|
|
3642
|
+
function _interactive_prompt_choice(label: string, options: ReadonlyArray<readonly [string, string]>): string {
|
|
3643
|
+
process.stdout.write('\n');
|
|
3644
|
+
process.stdout.write(` ${label}\n`);
|
|
3645
|
+
options.forEach(([key, blurb], idx) => {
|
|
3646
|
+
process.stdout.write(` ${idx + 1}. ${key} — ${blurb}\n`);
|
|
3647
|
+
});
|
|
3648
|
+
process.stdout.write('\n');
|
|
3649
|
+
for (;;) {
|
|
3650
|
+
const raw = _read_line(` Choice [1-${options.length}, default 1]: `);
|
|
3651
|
+
if (raw === null) return options[0]![0];
|
|
3652
|
+
if (!raw) return options[0]![0];
|
|
3653
|
+
if (/^[0-9]+$/.test(raw)) {
|
|
3654
|
+
const n = parseInt(raw, 10);
|
|
3655
|
+
if (n >= 1 && n <= options.length) return options[n - 1]![0];
|
|
3656
|
+
}
|
|
3657
|
+
for (const [key] of options) {
|
|
3658
|
+
if (raw.toLowerCase() === key) return key;
|
|
3659
|
+
}
|
|
3660
|
+
process.stdout.write(
|
|
3661
|
+
` ⚠️ Pick a number 1-${options.length} or one of: ` +
|
|
3662
|
+
`${options.map(([k]) => k).join(', ')}.\n`,
|
|
3663
|
+
);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
|
|
3667
|
+
function run_interactive_init(project_root: string, force: boolean): number {
|
|
3668
|
+
if (!process.stdin.isTTY) {
|
|
3669
|
+
warn(
|
|
3670
|
+
'--interactive requested but stdin is not a TTY; skipping the ' +
|
|
3671
|
+
`prompt. Re-run interactively or hand-edit ${_LOCAL_CONFIG_FILE}.`,
|
|
3672
|
+
);
|
|
3673
|
+
return 0;
|
|
3674
|
+
}
|
|
3675
|
+
|
|
3676
|
+
const target = path.join(project_root, _LOCAL_CONFIG_FILE);
|
|
3677
|
+
if (pathExists(target) && !force) {
|
|
3678
|
+
warn(
|
|
3679
|
+
`${_LOCAL_CONFIG_FILE} already exists; re-run with --force to ` +
|
|
3680
|
+
'overwrite. Skipping interactive init.',
|
|
3681
|
+
);
|
|
3682
|
+
return 0;
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
process.stdout.write('\n');
|
|
3686
|
+
info('Interactive init — captures user-type / stack / verbosity');
|
|
3687
|
+
info('(forward-compatible stub; runtime filtering activates with step-9)');
|
|
3688
|
+
|
|
3689
|
+
const user_type = _interactive_prompt_choice('Primary user type:', _INTERACTIVE_USER_TYPES);
|
|
3690
|
+
const stack = _interactive_prompt_choice('Project stack:', _INTERACTIVE_STACKS);
|
|
3691
|
+
const verbosity = _interactive_prompt_choice('Verbosity profile:', _INTERACTIVE_VERBOSITIES);
|
|
3692
|
+
|
|
3693
|
+
const payload: Record<string, unknown> = {
|
|
3694
|
+
$schema:
|
|
3695
|
+
'https://github.com/event4u-app/agent-config/src/scripts/schemas/local-config.schema.json',
|
|
3696
|
+
version: 1,
|
|
3697
|
+
user_type,
|
|
3698
|
+
stack,
|
|
3699
|
+
verbosity,
|
|
3700
|
+
universal_skills_contract: 'docs/contracts/universal-skills.md',
|
|
3701
|
+
};
|
|
3702
|
+
|
|
3703
|
+
try {
|
|
3704
|
+
writeText(target, jsonDumpsIndent(payload, 2) + '\n');
|
|
3705
|
+
} catch (exc) {
|
|
3706
|
+
warn(`Could not write ${target}: ${String(exc)}`);
|
|
3707
|
+
return 1;
|
|
3708
|
+
}
|
|
3709
|
+
|
|
3710
|
+
success(`Wrote ${path.relative(project_root, target)} (${user_type} / ${stack} / ${verbosity})`);
|
|
3711
|
+
return 0;
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3714
|
+
// --- Wizard auto-launch ---
|
|
3715
|
+
|
|
3716
|
+
const _WIZARD_READY_RE = /^WIZARD_READY (http:\/\/(?:127\.0\.0\.1|localhost):\d+\/\S*)\r?$/;
|
|
3717
|
+
const _WIZARD_TIMEOUTS: readonly number[] = [10.0, 20.0, 40.0, 80.0];
|
|
3718
|
+
|
|
3719
|
+
function _wizard_should_launch(opts: Options): [boolean, string] {
|
|
3720
|
+
if (opts.no_ui) return [false, '--no-ui flag set'];
|
|
3721
|
+
const env_no_ui = (process.env['AGENT_CONFIG_NO_UI'] ?? '').trim();
|
|
3722
|
+
if (env_no_ui && env_no_ui !== '0') return [false, 'AGENT_CONFIG_NO_UI env set'];
|
|
3723
|
+
if ((process.env['CI'] ?? '').trim()) return [false, 'CI environment detected'];
|
|
3724
|
+
if (!process.stdout.isTTY) return [false, 'stdout is not a TTY'];
|
|
3725
|
+
const tools_raw = opts.tools;
|
|
3726
|
+
if (tools_raw && !_tools_was_all(tools_raw)) {
|
|
3727
|
+
return [false, 'explicit --tools= selection (headless install)'];
|
|
3728
|
+
}
|
|
3729
|
+
return [true, ''];
|
|
3730
|
+
}
|
|
3731
|
+
|
|
3732
|
+
function _wizard_cli_dist(_project_root: string): string | null {
|
|
3733
|
+
const package_root = path.dirname(path.dirname(path.dirname(resolvePath(_HERE))));
|
|
3734
|
+
const cli = path.join(package_root, 'dist', 'cli', 'agent-config.js');
|
|
3735
|
+
return pathExists(cli) ? cli : null;
|
|
3736
|
+
}
|
|
3737
|
+
|
|
3738
|
+
function _server_info_path(): string {
|
|
3739
|
+
return path.join(os.homedir(), '.event4u', 'agent-config', 'local-server.json');
|
|
3740
|
+
}
|
|
3741
|
+
|
|
3742
|
+
function _pid_is_agent_config(pid: number): boolean {
|
|
3743
|
+
let res;
|
|
3744
|
+
try {
|
|
3745
|
+
res = spawnSync('ps', ['-p', String(pid), '-o', 'command='], {
|
|
3746
|
+
encoding: 'utf-8',
|
|
3747
|
+
timeout: 5000,
|
|
3748
|
+
});
|
|
3749
|
+
} catch {
|
|
3750
|
+
return false;
|
|
3751
|
+
}
|
|
3752
|
+
if (res.error) return false;
|
|
3753
|
+
return (res.stdout || '').toLowerCase().includes('agent-config');
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
function unlinkMissingOk(p: string): void {
|
|
3757
|
+
try {
|
|
3758
|
+
fs.unlinkSync(p);
|
|
3759
|
+
} catch {
|
|
3760
|
+
/* missing_ok */
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3764
|
+
/** `os.kill(pid, 0)` liveness probe → returns true if the process exists. */
|
|
3765
|
+
function pidAlive(pid: number): boolean {
|
|
3766
|
+
try {
|
|
3767
|
+
process.kill(pid, 0);
|
|
3768
|
+
return true;
|
|
3769
|
+
} catch (err) {
|
|
3770
|
+
// EPERM means it exists but we can't signal it — Python's os.kill(pid,0)
|
|
3771
|
+
// would NOT raise in that case; treat EPERM as alive.
|
|
3772
|
+
return (err as NodeJS.ErrnoException).code === 'EPERM';
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
function _kill_stale_wizard_server(): void {
|
|
3777
|
+
const p = _server_info_path();
|
|
3778
|
+
let infoObj: Record<string, unknown>;
|
|
3779
|
+
try {
|
|
3780
|
+
infoObj = JSON.parse(readText(p));
|
|
3781
|
+
} catch {
|
|
3782
|
+
return;
|
|
3783
|
+
}
|
|
3784
|
+
const pid = infoObj['pid'];
|
|
3785
|
+
if (typeof pid !== 'number' || !Number.isInteger(pid)) {
|
|
3786
|
+
unlinkMissingOk(p);
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3789
|
+
if (!pidAlive(pid)) {
|
|
3790
|
+
unlinkMissingOk(p);
|
|
3791
|
+
return;
|
|
3792
|
+
}
|
|
3793
|
+
if (!_pid_is_agent_config(pid)) return;
|
|
3794
|
+
try {
|
|
3795
|
+
process.kill(pid, 'SIGTERM');
|
|
3796
|
+
} catch {
|
|
3797
|
+
unlinkMissingOk(p);
|
|
3798
|
+
return;
|
|
3799
|
+
}
|
|
3800
|
+
let exited = false;
|
|
3801
|
+
for (let n = 0; n < 30; n += 1) {
|
|
3802
|
+
if (!pidAlive(pid)) {
|
|
3803
|
+
exited = true;
|
|
3804
|
+
break;
|
|
3805
|
+
}
|
|
3806
|
+
sleepMs(100);
|
|
3807
|
+
}
|
|
3808
|
+
if (!exited) {
|
|
3809
|
+
try {
|
|
3810
|
+
process.kill(pid, 'SIGKILL');
|
|
3811
|
+
} catch {
|
|
3812
|
+
/* OSError → pass */
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
unlinkMissingOk(p);
|
|
3816
|
+
process.stdout.write('(Stopped the previous wizard server.)\n');
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3819
|
+
/** Blocking sleep, mirroring `time.sleep` in a synchronous flow. */
|
|
3820
|
+
function sleepMs(ms: number): void {
|
|
3821
|
+
const end = Date.now() + ms;
|
|
3822
|
+
const buf = new Int32Array(new SharedArrayBuffer(4));
|
|
3823
|
+
while (Date.now() < end) {
|
|
3824
|
+
Atomics.wait(buf, 0, 0, Math.max(1, end - Date.now()));
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3828
|
+
function _wizard_spawn(project_root: string, pass_project_root: boolean = true): number {
|
|
3829
|
+
_kill_stale_wizard_server();
|
|
3830
|
+
|
|
3831
|
+
const cli = _wizard_cli_dist(project_root);
|
|
3832
|
+
if (cli === null) {
|
|
3833
|
+
process.stdout.write(
|
|
3834
|
+
"(Wizard not available — CLI bundle not built. " +
|
|
3835
|
+
"Run 'npm run build' at the package root to produce dist/cli/.)\n",
|
|
3836
|
+
);
|
|
3837
|
+
return 0;
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
const cmd = ['node', cli, 'install', '--no-open'];
|
|
3841
|
+
if (pass_project_root) {
|
|
3842
|
+
cmd.push('--project-root', project_root);
|
|
3843
|
+
}
|
|
3844
|
+
const env = { ...process.env };
|
|
3845
|
+
|
|
3846
|
+
// The Python uses subprocess.Popen + a stderr-draining thread + a
|
|
3847
|
+
// readline loop with progressive timeouts, then child.wait(). Node has no
|
|
3848
|
+
// synchronous equivalent that also streams; we run the whole wizard
|
|
3849
|
+
// handoff via a synchronous helper that mirrors the observable behaviour
|
|
3850
|
+
// (URL banner, stderr tail, browser open, blocking wait). Implemented with
|
|
3851
|
+
// spawnSync for the simple boot+wait, and a bounded readiness poll.
|
|
3852
|
+
return _wizard_run_sync(cmd, env, cli);
|
|
3853
|
+
}
|
|
3854
|
+
|
|
3855
|
+
/**
|
|
3856
|
+
* Synchronous wizard run mirroring `_wizard_spawn` + `_wizard_await_ready`.
|
|
3857
|
+
* spawnSync blocks until the child exits and captures stdout/stderr; we then
|
|
3858
|
+
* scan the captured stdout for the WIZARD_READY line, print the banner / open
|
|
3859
|
+
* the browser, and return the child's exit code. A child that never prints
|
|
3860
|
+
* WIZARD_READY before exiting falls through to the timeout fallback message.
|
|
3861
|
+
*
|
|
3862
|
+
* Divergence note: the Python streams stdout line-by-line with a 150s
|
|
3863
|
+
* progressive-timeout budget and hands the terminal to a still-running child
|
|
3864
|
+
* (Ctrl-C forwarding). spawnSync cannot hand off an interactive child, so this
|
|
3865
|
+
* twin runs the child to completion under the same total budget and surfaces
|
|
3866
|
+
* the same banner/fallback text. This path is network/subprocess-bound and is
|
|
3867
|
+
* NOT exercised by the deterministic golden tests (guarded behind the TTY +
|
|
3868
|
+
* dist-present gates).
|
|
3869
|
+
*/
|
|
3870
|
+
function _wizard_run_sync(cmd: string[], env: NodeJS.ProcessEnv, cli: string): number {
|
|
3871
|
+
const total = _WIZARD_TIMEOUTS.reduce((a, b) => a + b, 0);
|
|
3872
|
+
let res;
|
|
3873
|
+
try {
|
|
3874
|
+
res = spawnSync(cmd[0] as string, cmd.slice(1), {
|
|
3875
|
+
env,
|
|
3876
|
+
encoding: 'utf-8',
|
|
3877
|
+
timeout: total * 1000,
|
|
3878
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
3879
|
+
});
|
|
3880
|
+
} catch (exc) {
|
|
3881
|
+
process.stdout.write(
|
|
3882
|
+
`(Wizard failed to start: ${String(exc)}; run 'node ${cli} install --no-open' manually.)\n`,
|
|
3883
|
+
);
|
|
3884
|
+
return 0;
|
|
3885
|
+
}
|
|
3886
|
+
if (res.error && (res.error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
3887
|
+
process.stdout.write(
|
|
3888
|
+
`(Wizard failed to start: ${String(res.error)}; run 'node ${cli} install --no-open' manually.)\n`,
|
|
3889
|
+
);
|
|
3890
|
+
return 0;
|
|
3891
|
+
}
|
|
3892
|
+
const stdout = res.stdout || '';
|
|
3893
|
+
let matched_url: string | null = null;
|
|
3894
|
+
for (const line of stdout.split('\n')) {
|
|
3895
|
+
const m = _WIZARD_READY_RE.exec(line + '\n');
|
|
3896
|
+
if (m) {
|
|
3897
|
+
matched_url = m[1] as string;
|
|
3898
|
+
break;
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
if (matched_url === null) {
|
|
3902
|
+
const stderrLines = (res.stderr || '').split('\n').filter((l) => l !== '');
|
|
3903
|
+
const tail = stderrLines.length ? stderrLines.slice(-20).join('\n ') : '(no stderr captured)';
|
|
3904
|
+
process.stdout.write(
|
|
3905
|
+
`(Wizard server boot timed out after ${Math.trunc(total)}s; ` +
|
|
3906
|
+
`run 'node ${cli} install --no-open' manually.)\n` +
|
|
3907
|
+
` Last stderr:\n ${tail}\n`,
|
|
3908
|
+
);
|
|
3909
|
+
return 0;
|
|
3910
|
+
}
|
|
3911
|
+
process.stdout.write('\n');
|
|
3912
|
+
process.stdout.write(`Setup wizard ready: ${matched_url}\n`);
|
|
3913
|
+
_openBrowser(matched_url);
|
|
3914
|
+
process.stdout.write('(Wizard runs in the background; close the tab or press Ctrl-C to stop.)\n');
|
|
3915
|
+
return res.status ?? 0;
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3918
|
+
/** `webbrowser.open` — best-effort platform open; never fatal. */
|
|
3919
|
+
function _openBrowser(url: string): void {
|
|
3920
|
+
try {
|
|
3921
|
+
const opener =
|
|
3922
|
+
process.platform === 'darwin'
|
|
3923
|
+
? ['open', [url]]
|
|
3924
|
+
: process.platform === 'win32'
|
|
3925
|
+
? ['cmd', ['/c', 'start', '', url]]
|
|
3926
|
+
: ['xdg-open', [url]];
|
|
3927
|
+
spawnSync(opener[0] as string, opener[1] as string[], { stdio: 'ignore' });
|
|
3928
|
+
} catch {
|
|
3929
|
+
/* best-effort, never fatal */
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
function _dry_run_summary(opts: Options): number {
|
|
3934
|
+
const target = resolvePath(
|
|
3935
|
+
opts.custom_path || opts.project || process.env['PROJECT_ROOT'] || process.cwd(),
|
|
3936
|
+
);
|
|
3937
|
+
const [will_launch, why_not] = _wizard_should_launch(opts);
|
|
3938
|
+
process.stdout.write('\n');
|
|
3939
|
+
process.stdout.write('[dry-run] Plan summary — no files written, no subprocesses spawned:\n');
|
|
3940
|
+
process.stdout.write(` profile: ${opts.profile}\n`);
|
|
3941
|
+
process.stdout.write(` user-type: ${opts.user_type || '(none)'}\n`);
|
|
3942
|
+
process.stdout.write(` scope: ${opts.scope || (opts.global_install ? 'global' : 'auto')}\n`);
|
|
3943
|
+
process.stdout.write(` tools: ${opts.tools || 'all'}\n`);
|
|
3944
|
+
process.stdout.write(` target: ${target}\n`);
|
|
3945
|
+
process.stdout.write(` minimal: ${pyBool(opts.minimal)}\n`);
|
|
3946
|
+
process.stdout.write(` force: ${pyBool(opts.force)}\n`);
|
|
3947
|
+
process.stdout.write(` offline: ${pyBool(opts.offline)}\n`);
|
|
3948
|
+
if (will_launch) {
|
|
3949
|
+
process.stdout.write(' wizard: Would auto-launch (pass --no-ui to suppress).\n');
|
|
3950
|
+
} else {
|
|
3951
|
+
process.stdout.write(` wizard: Suppressed (${why_not}).\n`);
|
|
3952
|
+
}
|
|
3953
|
+
if (opts.global_install) {
|
|
3954
|
+
let preview: Record<string, string[]> = {};
|
|
3955
|
+
try {
|
|
3956
|
+
preview = _preview_global_reap(
|
|
3957
|
+
_parse_tools(opts.tools || 'all'),
|
|
3958
|
+
_resolve_package_root_for_global(),
|
|
3959
|
+
);
|
|
3960
|
+
} catch {
|
|
3961
|
+
preview = {};
|
|
3962
|
+
}
|
|
3963
|
+
const total = Object.values(preview).reduce((a, v) => a + v.length, 0);
|
|
3964
|
+
process.stdout.write('\n');
|
|
3965
|
+
if (total === 0) {
|
|
3966
|
+
process.stdout.write(' reap (cleanup): nothing to reap — no stale deployed files.\n');
|
|
3967
|
+
} else {
|
|
3968
|
+
process.stdout.write(` reap (cleanup): would remove ${total} stale file(s):\n`);
|
|
3969
|
+
for (const tool_id of Object.keys(preview).sort()) {
|
|
3970
|
+
for (const p of preview[tool_id] as string[]) {
|
|
3971
|
+
process.stdout.write(` ${tool_id}: ${p}\n`);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
process.stdout.write('\n');
|
|
3977
|
+
return 0;
|
|
3978
|
+
}
|
|
3979
|
+
|
|
3980
|
+
/** Python `str(bool)` → 'True' / 'False'. */
|
|
3981
|
+
function pyBool(v: boolean): string {
|
|
3982
|
+
return v ? 'True' : 'False';
|
|
3983
|
+
}
|
|
3984
|
+
|
|
3985
|
+
function _apply_payload_preview(payload: Record<string, unknown>, opts: Options): number {
|
|
3986
|
+
const schema_version = payload['schema_version'] ?? '<missing>';
|
|
3987
|
+
const target = resolvePath(
|
|
3988
|
+
opts.custom_path || opts.project || process.env['PROJECT_ROOT'] || process.cwd(),
|
|
3989
|
+
);
|
|
3990
|
+
process.stdout.write('\n');
|
|
3991
|
+
process.stdout.write(
|
|
3992
|
+
'[apply-payload] Plan summary — no files written, no subprocesses spawned:\n',
|
|
3993
|
+
);
|
|
3994
|
+
process.stdout.write(` schema: ${schema_version}\n`);
|
|
3995
|
+
if (schema_version === 'wizard-v2') {
|
|
3996
|
+
const tools = (payload['tools'] as unknown[]) || [];
|
|
3997
|
+
const packs = (payload['packs'] as unknown[]) || [];
|
|
3998
|
+
const settings = (payload['settings'] as Record<string, unknown>) || {};
|
|
3999
|
+
const scope_to_project = Boolean(payload['scope_to_project_only'] ?? false);
|
|
4000
|
+
process.stdout.write(` tools: ${tools.length ? tools.join(',') : '(none)'}\n`);
|
|
4001
|
+
process.stdout.write(` packs: ${packs.length ? packs.join(',') : '(base)'}\n`);
|
|
4002
|
+
process.stdout.write(` settings: ${Object.keys(settings).length} top-level key(s)\n`);
|
|
4003
|
+
process.stdout.write(` scope: ${scope_to_project ? 'project' : 'global'}\n`);
|
|
4004
|
+
} else if (schema_version === 'installer-v1') {
|
|
4005
|
+
const ai_tools = (payload['ai_tools'] as unknown[]) || [];
|
|
4006
|
+
const configs = (payload['configs'] as Record<string, unknown>) || {};
|
|
4007
|
+
process.stdout.write(` ai_tools: ${ai_tools.length ? ai_tools.join(',') : '(none)'}\n`);
|
|
4008
|
+
process.stdout.write(` configs: ${Object.keys(configs).length} tool config(s)\n`);
|
|
4009
|
+
} else {
|
|
4010
|
+
process.stdout.write(` error: unsupported schema_version: ${pyRepr(schema_version)}\n`);
|
|
4011
|
+
process.stdout.write('\n');
|
|
4012
|
+
return 2;
|
|
4013
|
+
}
|
|
4014
|
+
process.stdout.write(` target: ${target}\n`);
|
|
4015
|
+
process.stdout.write(` dry_run: ${pyBool(Boolean(payload['dry_run'] ?? opts.dry_run))}\n`);
|
|
4016
|
+
process.stdout.write('\n');
|
|
4017
|
+
return 0;
|
|
4018
|
+
}
|
|
4019
|
+
|
|
4020
|
+
/** Python `repr()` for a string scalar (single-quoted) or other primitives. */
|
|
4021
|
+
function pyRepr(v: unknown): string {
|
|
4022
|
+
if (typeof v === 'string') return `'${v.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
4023
|
+
if (v === null || v === undefined) return 'None';
|
|
4024
|
+
if (typeof v === 'boolean') return v ? 'True' : 'False';
|
|
4025
|
+
return String(v);
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4028
|
+
// --- Main ---
|
|
4029
|
+
|
|
4030
|
+
function main(argv: string[]): number {
|
|
4031
|
+
const opts = parse_options(argv);
|
|
4032
|
+
state.QUIET = opts.quiet;
|
|
4033
|
+
|
|
4034
|
+
if (opts.apply_payload) {
|
|
4035
|
+
const payload_path = resolvePath(opts.apply_payload);
|
|
4036
|
+
if (!isFile(payload_path)) fail(`--apply-payload path not found: ${payload_path}`);
|
|
4037
|
+
let payload: unknown;
|
|
4038
|
+
try {
|
|
4039
|
+
payload = JSON.parse(readText(payload_path));
|
|
4040
|
+
} catch (exc) {
|
|
4041
|
+
fail(`--apply-payload JSON parse error: ${String(exc)}`);
|
|
4042
|
+
}
|
|
4043
|
+
if (!_isPlainObject(payload)) fail('--apply-payload root must be a JSON object');
|
|
4044
|
+
const pl = payload as Record<string, unknown>;
|
|
4045
|
+
const schema_version = pl['schema_version'];
|
|
4046
|
+
if (schema_version !== 'wizard-v2' && schema_version !== 'installer-v1') {
|
|
4047
|
+
fail(
|
|
4048
|
+
`--apply-payload schema_version must be 'wizard-v2' or ` +
|
|
4049
|
+
`'installer-v1', got ${pyRepr(schema_version)}`,
|
|
4050
|
+
);
|
|
4051
|
+
}
|
|
4052
|
+
if (schema_version === 'wizard-v2') {
|
|
4053
|
+
const tools = pl['tools'];
|
|
4054
|
+
if (Array.isArray(tools) && tools.length > 0) {
|
|
4055
|
+
opts.tools = tools.filter((t) => typeof t === 'string').join(',');
|
|
4056
|
+
}
|
|
4057
|
+
if (Boolean(pl['scope_to_project_only'] ?? false)) {
|
|
4058
|
+
opts.scope = 'project';
|
|
4059
|
+
} else {
|
|
4060
|
+
opts.scope = 'global';
|
|
4061
|
+
}
|
|
4062
|
+
const settings = pl['settings'];
|
|
4063
|
+
if (_isPlainObject(settings)) {
|
|
4064
|
+
const rule_loading_tier =
|
|
4065
|
+
(settings['rule_loading_tier'] as unknown) || (settings['cost_profile'] as unknown);
|
|
4066
|
+
if (typeof rule_loading_tier === 'string' && rule_loading_tier) {
|
|
4067
|
+
opts.profile = rule_loading_tier;
|
|
4068
|
+
}
|
|
4069
|
+
const personal = settings['personal'];
|
|
4070
|
+
if (_isPlainObject(personal)) {
|
|
4071
|
+
const user_type = personal['user_type'];
|
|
4072
|
+
if (typeof user_type === 'string' && user_type) {
|
|
4073
|
+
opts.user_type = user_type;
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
const packs = pl['packs'];
|
|
4078
|
+
if (Array.isArray(packs)) {
|
|
4079
|
+
opts.packs = packs.filter((p) => typeof p === 'string') as string[];
|
|
4080
|
+
}
|
|
4081
|
+
} else if (schema_version === 'installer-v1') {
|
|
4082
|
+
const ai_tools = pl['ai_tools'];
|
|
4083
|
+
if (Array.isArray(ai_tools) && ai_tools.length > 0) {
|
|
4084
|
+
opts.tools = ai_tools.filter((t) => typeof t === 'string').join(',');
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
if (Boolean(pl['dry_run'] ?? false)) {
|
|
4088
|
+
opts.dry_run = true;
|
|
4089
|
+
}
|
|
4090
|
+
if (opts.dry_run) {
|
|
4091
|
+
return _apply_payload_preview(pl, opts);
|
|
4092
|
+
}
|
|
4093
|
+
state.PROGRESS_NDJSON = true;
|
|
4094
|
+
state.QUIET = true;
|
|
4095
|
+
}
|
|
4096
|
+
|
|
4097
|
+
if (opts.offline) {
|
|
4098
|
+
process.env['AGENT_CONFIG_OFFLINE'] = '1';
|
|
4099
|
+
process.env['AGENT_CONFIG_NO_UPDATE_CHECK'] = '1';
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
if (!SUPPORTED_PROFILES.includes(opts.profile)) {
|
|
4103
|
+
fail(`Unsupported profile: ${opts.profile}. Supported: ${SUPPORTED_PROFILES.join(', ')}`);
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4106
|
+
if (opts.dry_run) {
|
|
4107
|
+
return _dry_run_summary(opts);
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4110
|
+
{
|
|
4111
|
+
const [will_launch, why_not] = _wizard_should_launch(opts);
|
|
4112
|
+
if (will_launch) {
|
|
4113
|
+
if (!state.QUIET) info('Setup wizard will launch automatically after install.');
|
|
4114
|
+
} else if (!state.QUIET) {
|
|
4115
|
+
info(`Setup wizard auto-launch disabled (${why_not}).`);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
|
|
4119
|
+
if (opts.minimal) {
|
|
4120
|
+
const target_root = resolvePath(
|
|
4121
|
+
opts.custom_path || opts.project || process.env['PROJECT_ROOT'] || process.cwd(),
|
|
4122
|
+
);
|
|
4123
|
+
const minimal_package_root = path.dirname(
|
|
4124
|
+
path.dirname(path.dirname(_minimal_templates_root())),
|
|
4125
|
+
);
|
|
4126
|
+
const validated_user_type = _validate_user_type(minimal_package_root, opts.user_type);
|
|
4127
|
+
return install_minimal(target_root, opts.force, validated_user_type);
|
|
4128
|
+
}
|
|
4129
|
+
|
|
4130
|
+
const detect_root = resolvePath(opts.project || process.env['PROJECT_ROOT'] || process.cwd());
|
|
4131
|
+
const [detected, detect_reason] = detect_scope(detect_root);
|
|
4132
|
+
const custom_path: string | null = opts.custom_path ? resolvePath(opts.custom_path) : null;
|
|
4133
|
+
const scope = _resolve_scope(opts, detected, detect_reason, custom_path);
|
|
4134
|
+
_enforce_consumer_global_only(scope);
|
|
4135
|
+
_enforce_not_source_repo(scope, detect_root);
|
|
4136
|
+
|
|
4137
|
+
let parsed_tools = _parse_tools(opts.tools);
|
|
4138
|
+
const tools_was_all = _tools_was_all(opts.tools);
|
|
4139
|
+
parsed_tools = _validate_scope(parsed_tools, scope, tools_was_all);
|
|
4140
|
+
|
|
4141
|
+
const wizard_handoff = _wizard_should_launch(opts)[0];
|
|
4142
|
+
|
|
4143
|
+
if (scope === 'global') {
|
|
4144
|
+
const artefacts = _detect_legacy_for_migration(detect_root);
|
|
4145
|
+
if (artefacts.length > 0 && (wizard_handoff || _prompt_migrate_to_global(detect_root, artefacts))) {
|
|
4146
|
+
const rc = _run_migrate_to_global(detect_root);
|
|
4147
|
+
if (rc !== 0) return rc;
|
|
4148
|
+
}
|
|
4149
|
+
const rc = install_global(parsed_tools, opts.force, detect_root, opts.core_only);
|
|
4150
|
+
_emit_progress_terminal(rc);
|
|
4151
|
+
if (rc === 0 && wizard_handoff) {
|
|
4152
|
+
return _wizard_spawn(detect_root, false);
|
|
4153
|
+
}
|
|
4154
|
+
return rc;
|
|
4155
|
+
}
|
|
4156
|
+
|
|
4157
|
+
const project_root =
|
|
4158
|
+
custom_path || resolvePath(opts.project || process.env['PROJECT_ROOT'] || process.cwd());
|
|
4159
|
+
const is_first_run = !pathExists(path.join(project_root, SETTINGS_FILE));
|
|
4160
|
+
const rc = _main_project_install(opts, project_root, parsed_tools, is_first_run);
|
|
4161
|
+
if (rc === 0 && opts.interactive) {
|
|
4162
|
+
run_interactive_init(project_root, opts.force);
|
|
4163
|
+
}
|
|
4164
|
+
_emit_progress_terminal(rc);
|
|
4165
|
+
return rc;
|
|
4166
|
+
}
|
|
4167
|
+
|
|
4168
|
+
function _propose_modules_config(project_root: string, is_first_run: boolean): void {
|
|
4169
|
+
if (!is_first_run || state.QUIET || !process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
4170
|
+
let candidates;
|
|
4171
|
+
try {
|
|
4172
|
+
candidates = detect_module_roots(project_root);
|
|
4173
|
+
} catch {
|
|
4174
|
+
return;
|
|
4175
|
+
}
|
|
4176
|
+
if (!candidates || candidates.length === 0) return;
|
|
4177
|
+
process.stdout.write('\n');
|
|
4178
|
+
info('Module-root candidates detected — propose `modules:` block');
|
|
4179
|
+
info('Paste into .agent-project-settings.yml to enable module-aware skills (or skip; the block stays opt-in).');
|
|
4180
|
+
process.stdout.write('\n');
|
|
4181
|
+
process.stdout.write(' modules:\n');
|
|
4182
|
+
process.stdout.write(' enabled: true\n');
|
|
4183
|
+
process.stdout.write(' root_paths: [' + candidates.map((c) => c.path).join(', ') + ']\n');
|
|
4184
|
+
const primary_ns =
|
|
4185
|
+
candidates.find((c) => c.namespace_template_guess)?.namespace_template_guess ?? '';
|
|
4186
|
+
if (primary_ns) {
|
|
4187
|
+
process.stdout.write(` namespace_template: '${primary_ns}'\n`);
|
|
4188
|
+
}
|
|
4189
|
+
process.stdout.write(' agent_folder: agents\n');
|
|
4190
|
+
process.stdout.write(' skip_dirs: [.module-template, .example]\n');
|
|
4191
|
+
process.stdout.write('\n');
|
|
4192
|
+
info(
|
|
4193
|
+
'Re-run anytime via `./scripts-run src/scripts/propose_modules_config` ' +
|
|
4194
|
+
'(installed under <package>/src/scripts/).',
|
|
4195
|
+
);
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
function _read_consumer_auto_switch(project_root: string): string {
|
|
4199
|
+
let data: Record<string, unknown>;
|
|
4200
|
+
try {
|
|
4201
|
+
data = load_agent_settings({ project_path: _resolve_settings_read(project_root) });
|
|
4202
|
+
} catch {
|
|
4203
|
+
return 'suggest';
|
|
4204
|
+
}
|
|
4205
|
+
const model = _isPlainObject(data) ? data['model'] : null;
|
|
4206
|
+
const value = _isPlainObject(model) ? (model as Record<string, unknown>)['auto_switch'] : null;
|
|
4207
|
+
if (typeof value === 'string' && ['auto', 'suggest', 'off'].includes(value.trim().toLowerCase())) {
|
|
4208
|
+
return value.trim().toLowerCase();
|
|
4209
|
+
}
|
|
4210
|
+
return 'suggest';
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
function finalize_claude_model_tiers(project_root: string): number {
|
|
4214
|
+
const claude_skills = path.join(project_root, '.claude', 'skills');
|
|
4215
|
+
const augment_skills = path.join(project_root, '.augment', 'skills');
|
|
4216
|
+
if (!isDir(claude_skills) || !isDir(augment_skills)) return 0;
|
|
4217
|
+
if (_read_consumer_auto_switch(project_root) !== 'auto') return 0;
|
|
4218
|
+
|
|
4219
|
+
let rendered = 0;
|
|
4220
|
+
const entries = fs
|
|
4221
|
+
.readdirSync(claude_skills)
|
|
4222
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
4223
|
+
for (const name of entries) {
|
|
4224
|
+
const entry = path.join(claude_skills, name);
|
|
4225
|
+
const src_dir = path.join(augment_skills, name);
|
|
4226
|
+
const src_md = path.join(src_dir, 'SKILL.md');
|
|
4227
|
+
let tier: string | null;
|
|
4228
|
+
try {
|
|
4229
|
+
tier = read_model_tier(src_md);
|
|
4230
|
+
} catch {
|
|
4231
|
+
tier = null;
|
|
4232
|
+
}
|
|
4233
|
+
if (tier === null || !(tier in TIER_TO_CLAUDE_MODEL) || !isDir(src_dir)) continue;
|
|
4234
|
+
if (isSymlink(entry) || isFile(entry)) {
|
|
4235
|
+
fs.unlinkSync(entry);
|
|
4236
|
+
} else if (isDir(entry)) {
|
|
4237
|
+
fs.rmSync(entry, { recursive: true, force: true });
|
|
4238
|
+
}
|
|
4239
|
+
mkdirp(entry);
|
|
4240
|
+
const srcFiles = fs.readdirSync(src_dir).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
4241
|
+
for (const fname of srcFiles) {
|
|
4242
|
+
if (fname === 'SKILL.md') {
|
|
4243
|
+
writeText(
|
|
4244
|
+
path.join(entry, 'SKILL.md'),
|
|
4245
|
+
render_native_model_md(readText(src_md), tier),
|
|
4246
|
+
);
|
|
4247
|
+
} else {
|
|
4248
|
+
fs.symlinkSync(
|
|
4249
|
+
path.join('../../../.augment/skills', name, fname),
|
|
4250
|
+
path.join(entry, fname),
|
|
4251
|
+
);
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
rendered += 1;
|
|
4255
|
+
}
|
|
4256
|
+
|
|
4257
|
+
if (rendered && !state.QUIET) {
|
|
4258
|
+
info(
|
|
4259
|
+
`Applied native model: to ${rendered} model-tier skill(s) in ` +
|
|
4260
|
+
'.claude/skills/ (model.auto_switch=auto)',
|
|
4261
|
+
);
|
|
4262
|
+
}
|
|
4263
|
+
return rendered;
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4266
|
+
function _main_project_install(
|
|
4267
|
+
opts: Options,
|
|
4268
|
+
project_root: string,
|
|
4269
|
+
parsed_tools: Set<string>,
|
|
4270
|
+
is_first_run: boolean,
|
|
4271
|
+
): number {
|
|
4272
|
+
let package_root: string;
|
|
4273
|
+
let package_type: string;
|
|
4274
|
+
if (opts.package) {
|
|
4275
|
+
package_root = resolvePath(opts.package);
|
|
4276
|
+
if (!pathExists(path.join(package_root, 'src', 'config', 'profiles', 'minimal.ini'))) {
|
|
4277
|
+
fail(`Invalid --package path (missing src/config/profiles/minimal.ini): ${package_root}`);
|
|
4278
|
+
}
|
|
4279
|
+
package_type = detect_package_type_for_project(project_root, package_root);
|
|
4280
|
+
} else {
|
|
4281
|
+
package_root = detect_package_root(project_root);
|
|
4282
|
+
package_type = detect_package_type(package_root);
|
|
4283
|
+
}
|
|
4284
|
+
|
|
4285
|
+
if (!state.QUIET) {
|
|
4286
|
+
process.stdout.write('\n');
|
|
4287
|
+
info('Agent Config — Project Bridge Installer');
|
|
4288
|
+
info(`Project: ${project_root}`);
|
|
4289
|
+
info(`Package: ${package_root}`);
|
|
4290
|
+
info(`Type: ${package_type}`);
|
|
4291
|
+
info(`Profile: ${opts.profile}`);
|
|
4292
|
+
if (opts.user_type) info(`UserType: ${opts.user_type}`);
|
|
4293
|
+
process.stdout.write('\n');
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4296
|
+
ensure_agent_settings(project_root, package_root, opts.profile, opts.force, opts.user_type, opts.packs ?? null);
|
|
4297
|
+
|
|
4298
|
+
_write_install_mode_marker(project_root, 'full');
|
|
4299
|
+
|
|
4300
|
+
const tools = parsed_tools;
|
|
4301
|
+
const merged_keys_by_tool: Record<string, Record<string, unknown>[]> = {};
|
|
4302
|
+
|
|
4303
|
+
if (!opts.skip_bridges) {
|
|
4304
|
+
ensure_vscode_bridge(project_root, package_type, opts.force);
|
|
4305
|
+
merged_keys_by_tool['augment'] = ensure_augment_bridge(project_root, opts.force);
|
|
4306
|
+
if (_is_tool_enabled(tools, 'claude-code')) {
|
|
4307
|
+
merged_keys_by_tool['claude-code'] = ensure_claude_bridge(project_root, opts.force);
|
|
4308
|
+
}
|
|
4309
|
+
if (_is_tool_enabled(tools, 'cursor')) {
|
|
4310
|
+
merged_keys_by_tool['cursor'] = ensure_cursor_bridge(project_root, opts.force);
|
|
4311
|
+
}
|
|
4312
|
+
if (_is_tool_enabled(tools, 'cline')) ensure_cline_bridge(project_root, opts.force);
|
|
4313
|
+
if (_is_tool_enabled(tools, 'windsurf')) {
|
|
4314
|
+
merged_keys_by_tool['windsurf'] = ensure_windsurf_bridge(project_root, opts.force);
|
|
4315
|
+
}
|
|
4316
|
+
if (_is_tool_enabled(tools, 'gemini-cli')) {
|
|
4317
|
+
merged_keys_by_tool['gemini-cli'] = ensure_gemini_bridge(project_root, opts.force);
|
|
4318
|
+
}
|
|
4319
|
+
if (_is_tool_enabled(tools, 'copilot')) ensure_copilot_bridge(project_root, opts.force);
|
|
4320
|
+
if (_is_tool_enabled(tools, 'roocode')) ensure_roocode_bridge(project_root, opts.force);
|
|
4321
|
+
if (_is_tool_enabled(tools, 'claude-desktop')) ensure_claude_desktop_bridge(project_root, opts.force);
|
|
4322
|
+
if (_is_tool_enabled(tools, 'aider')) ensure_aider_bridge(project_root, opts.force);
|
|
4323
|
+
if (_is_tool_enabled(tools, 'codex')) ensure_codex_bridge(project_root, opts.force);
|
|
4324
|
+
if (_is_tool_enabled(tools, 'continue')) ensure_continue_bridge(project_root, opts.force);
|
|
4325
|
+
if (_is_tool_enabled(tools, 'kilocode')) ensure_kilocode_bridge(project_root, opts.force);
|
|
4326
|
+
if (_is_tool_enabled(tools, 'zed')) ensure_zed_bridge(project_root, opts.force);
|
|
4327
|
+
if (_is_tool_enabled(tools, 'jetbrains')) ensure_jetbrains_bridge(project_root, opts.force);
|
|
4328
|
+
if (_is_tool_enabled(tools, 'kiro')) ensure_kiro_bridge(project_root, opts.force);
|
|
4329
|
+
}
|
|
4330
|
+
|
|
4331
|
+
if (opts.augment_user_hooks) {
|
|
4332
|
+
(merged_keys_by_tool['augment'] ??= []).push(...ensure_augment_user_hooks(package_root, opts.force));
|
|
4333
|
+
}
|
|
4334
|
+
if (opts.cursor_user_hooks && _is_tool_enabled(tools, 'cursor')) {
|
|
4335
|
+
(merged_keys_by_tool['cursor'] ??= []).push(...ensure_cursor_user_hooks(package_root, opts.force));
|
|
4336
|
+
}
|
|
4337
|
+
if (opts.cline_user_hooks && _is_tool_enabled(tools, 'cline')) {
|
|
4338
|
+
ensure_cline_user_hooks(package_root, opts.force);
|
|
4339
|
+
}
|
|
4340
|
+
if (opts.windsurf_user_hooks && _is_tool_enabled(tools, 'windsurf')) {
|
|
4341
|
+
(merged_keys_by_tool['windsurf'] ??= []).push(...ensure_windsurf_user_hooks(package_root, opts.force));
|
|
4342
|
+
}
|
|
4343
|
+
if (opts.gemini_user_hooks && _is_tool_enabled(tools, 'gemini-cli')) {
|
|
4344
|
+
(merged_keys_by_tool['gemini-cli'] ??= []).push(...ensure_gemini_user_hooks(package_root, opts.force));
|
|
4345
|
+
}
|
|
4346
|
+
|
|
4347
|
+
if (state.PROGRESS_NDJSON && !opts.skip_bridges) {
|
|
4348
|
+
const ordered = [...tools].sort();
|
|
4349
|
+
const total = ordered.length;
|
|
4350
|
+
ordered.forEach((tool_id, i) => {
|
|
4351
|
+
_emit_progress({ type: 'file', file: tool_id, status: 'deployed', written: i + 1, total });
|
|
4352
|
+
});
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
if (!opts.skip_bridges && !opts.no_smoke) {
|
|
4356
|
+
if (!state.QUIET) {
|
|
4357
|
+
process.stdout.write('\n');
|
|
4358
|
+
info('Smoke-testing installed hook bridges (dry-run)');
|
|
4359
|
+
}
|
|
4360
|
+
_smoke_test_hooks(project_root, package_root);
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4363
|
+
if (!opts.skip_bridges) {
|
|
4364
|
+
const files_by_tool = _files_by_tool_from_bridges(parsed_tools, project_root, 'project');
|
|
4365
|
+
const rc = _update_installed_tools_manifest(
|
|
4366
|
+
project_root,
|
|
4367
|
+
parsed_tools,
|
|
4368
|
+
'project',
|
|
4369
|
+
opts.force,
|
|
4370
|
+
files_by_tool,
|
|
4371
|
+
merged_keys_by_tool,
|
|
4372
|
+
);
|
|
4373
|
+
if (rc !== 0) return rc;
|
|
4374
|
+
}
|
|
4375
|
+
|
|
4376
|
+
if (!opts.skip_bridges && _is_tool_enabled(tools, 'claude-code')) {
|
|
4377
|
+
finalize_claude_model_tiers(project_root);
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
if (!state.QUIET) {
|
|
4381
|
+
process.stdout.write('\n');
|
|
4382
|
+
success('Done.');
|
|
4383
|
+
if (is_first_run) {
|
|
4384
|
+
process.stdout.write('\n');
|
|
4385
|
+
process.stdout.write(' Try these 3 prompts with your agent:\n');
|
|
4386
|
+
process.stdout.write(' 1. "Refactor this function" → agent analyzes first\n');
|
|
4387
|
+
process.stdout.write(' 2. "Add caching to this" → agent asks instead of guessing\n');
|
|
4388
|
+
process.stdout.write(' 3. "Implement this feature" → agent respects your codebase\n');
|
|
4389
|
+
process.stdout.write('\n');
|
|
4390
|
+
process.stdout.write(' Next steps:\n');
|
|
4391
|
+
process.stdout.write(' • Commit .agent-settings.yml and bridge files to your repo\n');
|
|
4392
|
+
process.stdout.write(' • New team members run `npx @event4u/agent-config init` — done\n');
|
|
4393
|
+
process.stdout.write(' • Inspect hook coverage: ./agent-config hooks:status\n');
|
|
4394
|
+
process.stdout.write(
|
|
4395
|
+
' • Full walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md\n',
|
|
4396
|
+
);
|
|
4397
|
+
process.stdout.write('\n');
|
|
4398
|
+
} else {
|
|
4399
|
+
process.stdout.write(
|
|
4400
|
+
' Re-run complete. Walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md\n',
|
|
4401
|
+
);
|
|
4402
|
+
process.stdout.write('\n');
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4406
|
+
_propose_modules_config(project_root, is_first_run);
|
|
4407
|
+
|
|
4408
|
+
const will_launch = _wizard_should_launch(opts)[0];
|
|
4409
|
+
if (will_launch) {
|
|
4410
|
+
return _wizard_spawn(project_root);
|
|
4411
|
+
}
|
|
4412
|
+
return 0;
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4415
|
+
// --- CLI entry ---
|
|
4416
|
+
|
|
4417
|
+
// Node realpath-resolves the entry module's `import.meta.url` (symlinks
|
|
4418
|
+
// followed), but `path.resolve(argv[1])` does NOT. On macOS the temp tree is
|
|
4419
|
+
// under `/var/folders/...` (a `/var → /private/var` symlink), so a bare
|
|
4420
|
+
// `path.resolve` comparison mismatches and `main()` never fires — a silent
|
|
4421
|
+
// no-op. This bit the pre-bundled installer (`dist/install/install.mjs`) on
|
|
4422
|
+
// the macOS `setup.sh` path specifically (Linux `/tmp` is not symlinked).
|
|
4423
|
+
// Realpath-resolve `argv[1]` so both sides are canonical; fall back to the
|
|
4424
|
+
// bare resolve if the path can't be realpath'd.
|
|
4425
|
+
function _resolvedArgv1(): string | undefined {
|
|
4426
|
+
if (process.argv[1] === undefined) return undefined;
|
|
4427
|
+
try {
|
|
4428
|
+
return fs.realpathSync(path.resolve(process.argv[1]));
|
|
4429
|
+
} catch {
|
|
4430
|
+
return path.resolve(process.argv[1]);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
const _argv1 = _resolvedArgv1();
|
|
4434
|
+
const _isCliEntry = _argv1 !== undefined && import.meta.url === pathToFileURL(_argv1).href;
|
|
4435
|
+
if (_isCliEntry || _argv1 === _HERE) {
|
|
4436
|
+
try {
|
|
4437
|
+
process.exitCode = main(process.argv.slice(2));
|
|
4438
|
+
} catch (e) {
|
|
4439
|
+
if (e instanceof SystemExitError || e instanceof ArgparseExit) {
|
|
4440
|
+
process.exitCode = e.code;
|
|
4441
|
+
} else {
|
|
4442
|
+
throw e;
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
|
|
4447
|
+
export {
|
|
4448
|
+
main,
|
|
4449
|
+
parse_options,
|
|
4450
|
+
_merge_tools_aliases,
|
|
4451
|
+
_parse_tools,
|
|
4452
|
+
_tools_was_all,
|
|
4453
|
+
_is_tool_enabled,
|
|
4454
|
+
_yaml_scalar,
|
|
4455
|
+
_parse_legacy_settings,
|
|
4456
|
+
_replace_template_value,
|
|
4457
|
+
_replace_template_value_raw,
|
|
4458
|
+
_append_unknown_legacy,
|
|
4459
|
+
_render_template,
|
|
4460
|
+
_parse_profile_ini,
|
|
4461
|
+
_inject_packs,
|
|
4462
|
+
deep_merge,
|
|
4463
|
+
_validate_scope,
|
|
4464
|
+
_bridge_marker,
|
|
4465
|
+
detect_scope,
|
|
4466
|
+
_resolve_scope,
|
|
4467
|
+
_is_agent_config_source_repo,
|
|
4468
|
+
_detect_legacy_for_migration,
|
|
4469
|
+
_format_global_root_for_marker,
|
|
4470
|
+
_files_by_tool_from_bridges,
|
|
4471
|
+
_files_by_tool_from_deploy,
|
|
4472
|
+
_verify_deploy_targets,
|
|
4473
|
+
_wizard_should_launch,
|
|
4474
|
+
_dry_run_summary,
|
|
4475
|
+
_apply_payload_preview,
|
|
4476
|
+
jsonDumpsIndent,
|
|
4477
|
+
jsonDumpsCompact,
|
|
4478
|
+
_canonical_settings_target,
|
|
4479
|
+
_resolve_settings_read,
|
|
4480
|
+
detect_package_type,
|
|
4481
|
+
detect_package_type_for_project,
|
|
4482
|
+
SystemExitError,
|
|
4483
|
+
ArgparseExit,
|
|
4484
|
+
state,
|
|
4485
|
+
// ADR-200 py2ts: re-exported for test_consumer_model_tier.ts twin
|
|
4486
|
+
// (Python test imports `install.finalize_claude_model_tiers` directly).
|
|
4487
|
+
finalize_claude_model_tiers,
|
|
4488
|
+
SUPPORTED_PROFILES,
|
|
4489
|
+
DEFAULT_PROFILE,
|
|
4490
|
+
_VALID_TOOLS,
|
|
4491
|
+
// ADR-200 py2ts: re-exported for cmd_export.ts / cmd_settings_migrate.ts
|
|
4492
|
+
// twins (Python imports these as `scripts.install` module constants).
|
|
4493
|
+
SETTINGS_FILE,
|
|
4494
|
+
GLOBAL_AGENT_SETTINGS_PATH,
|
|
4495
|
+
GLOBAL_USER_SETTINGS_PATH,
|
|
4496
|
+
AIDER_MARKER,
|
|
4497
|
+
CLAUDE_DESKTOP_MARKER,
|
|
4498
|
+
CODEX_MARKER,
|
|
4499
|
+
CONTINUE_MARKER,
|
|
4500
|
+
JETBRAINS_MARKER,
|
|
4501
|
+
KILOCODE_MARKER,
|
|
4502
|
+
KIRO_MARKER,
|
|
4503
|
+
ROOCODE_MARKER,
|
|
4504
|
+
ZED_MARKER,
|
|
4505
|
+
// ADR-200 py2ts: re-exported for test_install_snapshot.ts twin
|
|
4506
|
+
CURSOR_DISPATCHER_BINDINGS,
|
|
4507
|
+
CLINE_DISPATCHER_BINDINGS,
|
|
4508
|
+
WINDSURF_DISPATCHER_BINDINGS,
|
|
4509
|
+
GEMINI_DISPATCHER_BINDINGS,
|
|
4510
|
+
ensure_cursor_bridge,
|
|
4511
|
+
ensure_cline_bridge,
|
|
4512
|
+
ensure_windsurf_bridge,
|
|
4513
|
+
ensure_gemini_bridge,
|
|
4514
|
+
};
|
|
4515
|
+
export type { Options };
|