create-claude-cabinet 0.46.0 → 0.48.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/README.md +3 -6
- package/lib/CLAUDE.md +218 -0
- package/lib/cli.js +397 -382
- package/lib/copy.js +182 -6
- package/lib/db-setup.js +122 -17
- package/lib/engagement-server-setup.js +1 -17
- package/lib/engagement-setup.js +1 -1
- package/lib/installer-gate.js +135 -0
- package/lib/metadata.js +51 -2
- package/lib/modules.js +292 -0
- package/lib/mux-setup.js +1 -17
- package/lib/project-context.js +16 -96
- package/lib/settings-merge.js +194 -13
- package/lib/site-audit-setup.js +23 -7
- package/lib/verify-setup.js +20 -9
- package/lib/watchtower-setup.js +23 -7
- package/package.json +1 -1
- package/templates/CLAUDE.md +959 -0
- package/templates/briefing/_briefing-template.md +6 -11
- package/templates/cabinet/advisories-state-schema.md +1 -1
- package/templates/cabinet/checklist-stats-schema.md +15 -1
- package/templates/cabinet/eval-protocol.md +27 -10
- package/templates/cabinet/memory-lifecycle-contract.md +135 -0
- package/templates/cabinet/pib-db-access.md +13 -0
- package/templates/cabinet/watchtower-contracts.md +453 -0
- package/templates/cabinet/worktree-invocation-contract.md +111 -0
- package/templates/engagement/OVERVIEW.md +12 -0
- package/templates/engagement/__tests__/checklist-visibility.test.mjs +76 -0
- package/templates/engagement/__tests__/engagement.test.mjs +177 -2
- package/templates/engagement/__tests__/invoice-doc.test.mjs +195 -0
- package/templates/engagement/engagement-checklist.mjs +26 -2
- package/templates/engagement/engagement-preview.mjs +100 -0
- package/templates/engagement/engagement-schema.md +248 -6
- package/templates/engagement/engagement.mjs +391 -11
- package/templates/engagement/pib-db-patches/pib-db-lib.mjs +49 -9
- package/templates/engagement/pib-db-patches/pib-db-mcp-server.mjs +5 -44
- package/templates/engagement/pib-db-patches/pib-db-schema.sql +1 -1
- package/templates/engagement/pib-db-patches/pib-db.mjs +9 -3
- package/templates/engagement/sql-constants.mjs +9 -1
- package/templates/engagement-server/__tests__/cross-tenant-auth.test.mjs +87 -0
- package/templates/engagement-server/server.mjs +65 -40
- package/templates/hooks/action-completion-gate.sh +19 -3
- package/templates/hooks/action-quality-gate.sh +10 -0
- package/templates/hooks/memory-index-guard.sh +17 -11
- package/templates/hooks/skill-telemetry.sh +10 -5
- package/templates/hooks/skill-tool-telemetry.sh +11 -5
- package/templates/hooks/watchtower-session-start.sh +24 -2
- package/templates/mcp/pib-db.json +1 -4
- package/templates/mux/bin/mux +8 -1
- package/templates/mux/config/help.txt +1 -0
- package/templates/rules/acknowledge-when-corrected.md +33 -0
- package/templates/rules/enforcement-pipeline.md +0 -12
- package/templates/rules/maintainability.md +11 -0
- package/templates/rules/memory-capture.md +26 -4
- package/templates/rules/plan-before-bulk-or-irreversible-actions.md +48 -0
- package/templates/rules/verify-before-asserting.md +79 -0
- package/templates/scripts/__tests__/ahead-check-origin.test.mjs +355 -0
- package/templates/scripts/__tests__/api-usage-idle-gate.test.mjs +81 -0
- package/templates/scripts/__tests__/batch-disposition.test.mjs +98 -0
- package/templates/scripts/__tests__/bsql-loader-verify.test.mjs +47 -0
- package/templates/scripts/__tests__/claude-churn-authored.test.mjs +90 -0
- package/templates/scripts/__tests__/cross-ring-reader.test.mjs +505 -0
- package/templates/scripts/__tests__/dx-captures-briefing.test.mjs +169 -0
- package/templates/scripts/__tests__/hook-runner.test.mjs +247 -0
- package/templates/scripts/__tests__/inbox-assessment.test.mjs +341 -0
- package/templates/scripts/__tests__/memory-reachability.test.mjs +150 -0
- package/templates/scripts/__tests__/narrative-corpus.test.mjs +247 -0
- package/templates/scripts/__tests__/phase-shim.test.mjs +112 -0
- package/templates/scripts/__tests__/qa-handoff-merge-state.test.mjs +162 -0
- package/templates/scripts/__tests__/resolve-cli.test.mjs +314 -0
- package/templates/scripts/__tests__/ring-state-ownership.test.mjs +120 -0
- package/templates/scripts/__tests__/ring1-content-detector.test.mjs +168 -0
- package/templates/scripts/__tests__/ring1-flagged-actions.test.mjs +135 -0
- package/templates/scripts/__tests__/ring1-pib-error-surfacing.test.mjs +69 -0
- package/templates/scripts/__tests__/ring1-script-drift.test.mjs +126 -0
- package/templates/scripts/__tests__/ring1-stale-open-counts.test.mjs +143 -0
- package/templates/scripts/__tests__/ring2-queue-reader.test.mjs +80 -0
- package/templates/scripts/__tests__/ring2-recall-canary.test.mjs +150 -0
- package/templates/scripts/__tests__/ring2-roster-review.test.mjs +311 -0
- package/templates/scripts/__tests__/ring3-chunk-merge.test.mjs +122 -0
- package/templates/scripts/__tests__/ring3-close-lenses.test.mjs +359 -0
- package/templates/scripts/__tests__/ring3-dedup.test.mjs +92 -12
- package/templates/scripts/__tests__/ring3-memory-titles.test.mjs +187 -0
- package/templates/scripts/__tests__/ring3-novelty-rescue.test.mjs +148 -0
- package/templates/scripts/__tests__/ring3-recent-slice.test.mjs +101 -0
- package/templates/scripts/__tests__/ring3-thread-capture.test.mjs +213 -0
- package/templates/scripts/__tests__/ring4-reconcile.test.mjs +393 -0
- package/templates/scripts/__tests__/shared-thread-reader.test.mjs +187 -0
- package/templates/scripts/__tests__/suppression-ledger.test.mjs +148 -0
- package/templates/scripts/__tests__/verify-backfill.test.mjs +103 -0
- package/templates/scripts/__tests__/verify-coverage.test.mjs +80 -0
- package/templates/scripts/__tests__/watchtower-snapshot.test.mjs +261 -0
- package/templates/scripts/__tests__/watchtower-status-ring4.test.mjs +89 -0
- package/templates/scripts/__tests__/watchtower-sync.test.mjs +311 -0
- package/templates/scripts/audit-coherence-check.mjs +123 -0
- package/templates/scripts/audit-synth.mjs +117 -0
- package/templates/scripts/load-triage-history.js +5 -2
- package/templates/scripts/merge-findings.js +4 -1
- package/templates/scripts/patterns-scope-check.mjs +122 -0
- package/templates/scripts/pib-db-lib.mjs +61 -11
- package/templates/scripts/pib-db-mcp-server.mjs +7 -45
- package/templates/scripts/pib-db-path.mjs +61 -0
- package/templates/scripts/pib-db.mjs +7 -3
- package/templates/scripts/review-server.mjs +7 -12
- package/templates/scripts/triage-server.mjs +7 -8
- package/templates/scripts/validate-memory.mjs +214 -16
- package/templates/scripts/watchtower-advisories.mjs +7 -3
- package/templates/scripts/watchtower-build-context.mjs +192 -8
- package/templates/scripts/watchtower-cross-ring-reader.mjs +714 -0
- package/templates/scripts/watchtower-hook-runner.mjs +422 -0
- package/templates/scripts/watchtower-inbox-assessment.mjs +706 -0
- package/templates/scripts/watchtower-lib.mjs +555 -8
- package/templates/scripts/watchtower-narrative-corpus.mjs +385 -0
- package/templates/scripts/watchtower-phase-shim.mjs +171 -0
- package/templates/scripts/watchtower-queue.mjs +341 -3
- package/templates/scripts/watchtower-ring1.mjs +481 -81
- package/templates/scripts/watchtower-ring2.mjs +748 -80
- package/templates/scripts/watchtower-ring3-close.mjs +1147 -167
- package/templates/scripts/watchtower-ring4-runner.sh +85 -0
- package/templates/scripts/watchtower-ring4.mjs +753 -0
- package/templates/scripts/watchtower-routines.mjs +1 -1
- package/templates/scripts/watchtower-snapshot.mjs +452 -0
- package/templates/scripts/watchtower-status.sh +17 -0
- package/templates/scripts/watchtower-sync.mjs +393 -0
- package/templates/scripts/work-tracker-server.mjs +26 -22
- package/templates/skills/audit/SKILL.md +18 -5
- package/templates/skills/audit/phases/structural-checks.md +22 -0
- package/templates/skills/briefing/SKILL.md +289 -41
- package/templates/skills/cabinet-accessibility/SKILL.md +1 -1
- package/templates/skills/cabinet-process-therapist/SKILL.md +28 -0
- package/templates/skills/cabinet-security/SKILL.md +11 -0
- package/templates/skills/catch-up/SKILL.md +113 -0
- package/templates/skills/cc-publish/SKILL.md +89 -18
- package/templates/skills/cc-remember/SKILL.md +45 -0
- package/templates/skills/close/SKILL.md +107 -0
- package/templates/skills/collab-client/SKILL.md +22 -5
- package/templates/skills/collab-consultant/SKILL.md +110 -2
- package/templates/skills/debrief/SKILL.md +21 -4
- package/templates/skills/debrief/phases/audit-pattern-capture.md +13 -0
- package/templates/skills/debrief-classic/SKILL.md +696 -0
- package/templates/skills/debrief-classic/calibration.md +44 -0
- package/templates/skills/debrief-classic/phases/audit-pattern-capture.md +78 -0
- package/templates/skills/debrief-classic/phases/auto-maintenance.md +48 -0
- package/templates/skills/debrief-classic/phases/checklist-feedback.md +123 -0
- package/templates/skills/debrief-classic/phases/close-work.md +163 -0
- package/templates/skills/debrief-classic/phases/health-checks.md +54 -0
- package/templates/skills/debrief-classic/phases/inventory.md +40 -0
- package/templates/skills/debrief-classic/phases/loose-ends.md +52 -0
- package/templates/skills/debrief-classic/phases/methodology-capture.md +223 -0
- package/templates/skills/debrief-classic/phases/qa-handoff-sweep.md +78 -0
- package/templates/skills/debrief-classic/phases/record-lessons.md +177 -0
- package/templates/skills/debrief-classic/phases/report.md +59 -0
- package/templates/skills/debrief-classic/phases/update-state.md +48 -0
- package/templates/skills/debrief-classic/phases/upstream-feedback.md +185 -0
- package/templates/skills/debrief-classic/phases/verify-coverage.md +101 -0
- package/templates/skills/execute/SKILL.md +71 -6
- package/templates/skills/execute/phases/commit-and-deploy.md +8 -0
- package/templates/skills/execute-group/SKILL.md +23 -26
- package/templates/skills/generate-plan-groups/SKILL.md +20 -0
- package/templates/skills/inbox/SKILL.md +177 -31
- package/templates/skills/memory/SKILL.md +22 -6
- package/templates/skills/orient/SKILL.md +75 -5
- package/templates/skills/orient-classic/SKILL.md +770 -0
- package/templates/skills/orient-classic/phases/auto-maintenance.md +52 -0
- package/templates/skills/orient-classic/phases/briefing.md +53 -0
- package/templates/skills/orient-classic/phases/cabinet.md +46 -0
- package/templates/skills/orient-classic/phases/checklist-status.md +54 -0
- package/templates/skills/orient-classic/phases/context.md +88 -0
- package/templates/skills/orient-classic/phases/data-sync.md +35 -0
- package/templates/skills/orient-classic/phases/deferred-check.md +55 -0
- package/templates/skills/orient-classic/phases/dx-captures.md +53 -0
- package/templates/skills/orient-classic/phases/health-checks.md +50 -0
- package/templates/skills/orient-classic/phases/verify-backfill.md +109 -0
- package/templates/skills/orient-classic/phases/work-scan.md +69 -0
- package/templates/skills/pulse/SKILL.md +10 -8
- package/templates/skills/qa-drain/SKILL.md +38 -0
- package/templates/skills/qa-handoff/SKILL.md +287 -46
- package/templates/skills/setup-accounts/SKILL.md +38 -16
- package/templates/skills/spring-clean/SKILL.md +1 -1
- package/templates/skills/spring-clean/phases/execute-decisions.md +1 -1
- package/templates/skills/threads/SKILL.md +4 -0
- package/templates/skills/validate/phases/validators.md +127 -0
- package/templates/skills/watchtower/SKILL.md +174 -30
- package/templates/verify-runtime/CONVENTIONS.md +9 -0
- package/templates/verify-runtime/README.md +37 -0
- package/templates/watchtower/config.json.template +8 -2
- package/templates/workflows/deliberative-audit.js +258 -87
- package/templates/workflows/execute-group-complete.js +26 -3
- package/templates/workflows/execute-group-implement.js +16 -2
package/lib/cli.js
CHANGED
|
@@ -3,10 +3,10 @@ const path = require('path');
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const crypto = require('crypto');
|
|
6
|
-
const { copyTemplates, recordSkip } = require('./copy');
|
|
7
|
-
const { mergeSettings, healUserSettings, mergeWatchtowerHooks, mergeMuxHooks, mergeBashCompressHooks } = require('./settings-merge');
|
|
8
|
-
const { create: createMetadata, read: readMetadata } = require('./metadata');
|
|
9
|
-
const { setupDb } = require('./db-setup');
|
|
6
|
+
const { copyTemplates, recordSkip, isProjectOwnedSeed, classifyFileOwnership } = require('./copy');
|
|
7
|
+
const { mergeSettings, healUserSettings, mergeWatchtowerHooks, mergeMuxHooks, mergeBashCompressHooks, mergeCcDevHooks, mergeSkillOverrides } = require('./settings-merge');
|
|
8
|
+
const { create: createMetadata, read: readMetadata, readFrozenModules, setFrozenModules } = require('./metadata');
|
|
9
|
+
const { setupDb, shouldSkipInit, readBundledSchemaVersion } = require('./db-setup');
|
|
10
10
|
const { setupVerifyRuntime } = require('./verify-setup');
|
|
11
11
|
const { setupSiteAuditRuntime } = require('./site-audit-setup');
|
|
12
12
|
const { setupEngagement } = require('./engagement-setup');
|
|
@@ -442,255 +442,10 @@ function generateAgentWrappers(projectDir) {
|
|
|
442
442
|
return wanted.size;
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
// MODULES
|
|
446
|
-
//
|
|
447
|
-
//
|
|
448
|
-
|
|
449
|
-
// before v0.27.2 (test/manifest-integrity guards against recurrence).
|
|
450
|
-
//
|
|
451
|
-
// Intentional orphans (maintainer-only, must NOT ship to consumers) are
|
|
452
|
-
// allowlisted in test/manifest-integrity. Currently: skills/cc-publish
|
|
453
|
-
// (CC-source-repo release tooling, like scripts/migrate-all-consumers.js).
|
|
454
|
-
const MODULES = {
|
|
455
|
-
'session-loop': {
|
|
456
|
-
name: 'Session Loop (orient + debrief)',
|
|
457
|
-
description: 'The briefing cycle. Claude starts each session informed, ends by preparing the next briefing.',
|
|
458
|
-
mandatory: true,
|
|
459
|
-
templates: [
|
|
460
|
-
'skills/orient',
|
|
461
|
-
'skills/orient-quick',
|
|
462
|
-
'skills/debrief',
|
|
463
|
-
'skills/debrief-quick',
|
|
464
|
-
'skills/session-handoff',
|
|
465
|
-
// Instruction phases — always ship, overriding the default skip-phases rule in copy.js
|
|
466
|
-
'skills/debrief/phases/audit-pattern-capture.md',
|
|
467
|
-
'skills/debrief/phases/methodology-capture.md',
|
|
468
|
-
'skills/debrief/phases/record-lessons.md',
|
|
469
|
-
'skills/debrief/phases/upstream-feedback.md',
|
|
470
|
-
'skills/debrief/phases/qa-handoff-sweep.md',
|
|
471
|
-
'skills/menu',
|
|
472
|
-
],
|
|
473
|
-
},
|
|
474
|
-
'hooks': {
|
|
475
|
-
name: 'Git Guardrails + Telemetry',
|
|
476
|
-
description: 'Block destructive git ops (force push, hard reset). Track skill usage via JSONL telemetry.',
|
|
477
|
-
mandatory: false,
|
|
478
|
-
default: true,
|
|
479
|
-
lean: true,
|
|
480
|
-
templates: ['hooks/git-guardrails.sh', 'hooks/cc-upstream-guard.sh', 'hooks/skill-telemetry.sh', 'hooks/skill-tool-telemetry.sh', 'hooks/work-tracker-guard.sh', 'hooks/action-quality-gate.sh', 'hooks/action-completion-gate.sh', 'hooks/memory-index-guard.sh', 'scripts/cc-drift-check.cjs', 'scripts/skill-usage.mjs'],
|
|
481
|
-
},
|
|
482
|
-
'work-tracking': {
|
|
483
|
-
name: 'Work Tracking (pib-db or markdown)',
|
|
484
|
-
description: 'Track work items for orient/debrief. Default: SQLite (pib-db). Also supports markdown (tasks.md) or external systems. Choice made during /onboard.',
|
|
485
|
-
mandatory: false,
|
|
486
|
-
default: true,
|
|
487
|
-
lean: false,
|
|
488
|
-
templates: ['scripts/pib-db.mjs', 'scripts/pib-db-lib.mjs', 'scripts/pib-db-mcp-server.mjs', 'scripts/pib-db-schema.sql', 'scripts/work-tracker-server.mjs', 'scripts/work-tracker-ui.html', 'skills/work-tracker', 'skills/spring-clean'],
|
|
489
|
-
needsDb: true,
|
|
490
|
-
},
|
|
491
|
-
'planning': {
|
|
492
|
-
name: 'Planning + Execution (plan + execute)',
|
|
493
|
-
description: 'Structured planning with cabinet critique — members weigh in before you build.',
|
|
494
|
-
mandatory: false,
|
|
495
|
-
default: true,
|
|
496
|
-
lean: true,
|
|
497
|
-
templates: ['skills/plan', 'skills/execute', 'skills/execute/phases/post-impl-checklist.md', 'skills/debrief/phases/checklist-feedback.md', 'skills/checklist-discover', 'skills/generate-plan-groups', 'skills/execute-group', 'workflows/execute-group-implement.js', 'workflows/execute-group-complete.js', 'skills/investigate', 'cabinet/checkpoint-protocol.md', 'cabinet/elicitation-methods.md', 'cabinet/qa-dimensions-template.yaml', 'cabinet/checklist-stats-schema.md', 'scripts/qa-dimensions-validator.cjs', 'skills/orient/phases/checklist-status.md', 'skills/audit/phases/checklist-pruning.md'],
|
|
498
|
-
},
|
|
499
|
-
'compliance': {
|
|
500
|
-
name: 'Compliance Stack (rules + enforcement)',
|
|
501
|
-
description: 'Scoped instructions that load by path. Enforcement pipeline for promoting patterns to rules.',
|
|
502
|
-
mandatory: false,
|
|
503
|
-
default: true,
|
|
504
|
-
lean: false,
|
|
505
|
-
templates: ['rules/enforcement-pipeline.md', 'rules/maintainability.md', 'rules/markdown-prose.md', 'skills/unwrap', 'memory/patterns/_pattern-template.md', 'memory/patterns/pattern-intelligence-first.md'],
|
|
506
|
-
},
|
|
507
|
-
'memory': {
|
|
508
|
-
name: 'Built-In Memory (cc-remember + reader + validator)',
|
|
509
|
-
description: 'Curated write/validate layer over Claude Code\'s built-in file memory. /cc-remember writes indexed memories, /memory browses them, validate-memory.mjs guards MEMORY.md integrity. Replaced the retired omega engine in v0.27.',
|
|
510
|
-
mandatory: false,
|
|
511
|
-
default: true,
|
|
512
|
-
// lean:true is load-bearing — it keeps the skill triad, scripts, and
|
|
513
|
-
// rule installing together as an atomic unit. compliance is lean:false,
|
|
514
|
-
// so the memory-capture rule must NOT live there or lean installs get a
|
|
515
|
-
// partial, incoherent set.
|
|
516
|
-
lean: true,
|
|
517
|
-
templates: [
|
|
518
|
-
'skills/cc-remember',
|
|
519
|
-
'skills/memory',
|
|
520
|
-
'rules/memory-capture.md',
|
|
521
|
-
'scripts/write-memory-file.mjs',
|
|
522
|
-
'scripts/validate-memory.mjs',
|
|
523
|
-
// project-context.cjs ships as a co-located sibling: the two .mjs
|
|
524
|
-
// scripts require('./project-context.cjs') and consumers have no lib/.
|
|
525
|
-
'scripts/project-context.cjs',
|
|
526
|
-
],
|
|
527
|
-
},
|
|
528
|
-
'audit': {
|
|
529
|
-
name: 'Audit Loop (audit + triage + cabinet)',
|
|
530
|
-
description: '27 expert cabinet members review your project. Convene the full cabinet or just one committee.',
|
|
531
|
-
mandatory: false,
|
|
532
|
-
default: true,
|
|
533
|
-
lean: true,
|
|
534
|
-
templates: [
|
|
535
|
-
'skills/audit', 'skills/pulse', 'skills/triage-audit', 'skills/cabinet',
|
|
536
|
-
'cabinet', 'briefing',
|
|
537
|
-
'skills/cabinet-accessibility', 'skills/cabinet-anti-confirmation',
|
|
538
|
-
'skills/cabinet-architecture', 'skills/cabinet-automation',
|
|
539
|
-
'skills/cabinet-boundary-man',
|
|
540
|
-
'skills/cabinet-anthropic-insider', 'skills/cabinet-cc-health',
|
|
541
|
-
'skills/cabinet-data-integrity',
|
|
542
|
-
'skills/cabinet-debugger', 'skills/cabinet-deployment',
|
|
543
|
-
'skills/cabinet-historian',
|
|
544
|
-
'skills/cabinet-organized-mind', 'skills/cabinet-process-therapist',
|
|
545
|
-
'skills/cabinet-qa', 'skills/cabinet-record-keeper',
|
|
546
|
-
'skills/cabinet-roster-check', 'skills/cabinet-security',
|
|
547
|
-
'skills/cabinet-seo',
|
|
548
|
-
'skills/cabinet-small-screen', 'skills/cabinet-speed-freak',
|
|
549
|
-
'skills/cabinet-system-advocate', 'skills/cabinet-technical-debt',
|
|
550
|
-
'skills/cabinet-usability', 'skills/cabinet-workflow-cop',
|
|
551
|
-
'skills/cabinet-framework-quality', 'skills/cabinet-goal-alignment',
|
|
552
|
-
'skills/cabinet-information-design', 'skills/cabinet-mantine-quality',
|
|
553
|
-
'skills/cabinet-ui-experimentalist', 'skills/cabinet-user-advocate',
|
|
554
|
-
'skills/cabinet-vision',
|
|
555
|
-
'skills/cabinet-narrative-architect', 'skills/cabinet-interactive-storyteller',
|
|
556
|
-
'skills/cabinet-elegance',
|
|
557
|
-
'scripts/merge-findings.js', 'scripts/load-triage-history.js',
|
|
558
|
-
'scripts/triage-server.mjs', 'scripts/triage-ui.html',
|
|
559
|
-
'scripts/finding-schema.json', 'scripts/resolve-committees.cjs',
|
|
560
|
-
'scripts/review-server.mjs', 'scripts/review-ui.html',
|
|
561
|
-
'workflows/deliberative-audit.js', 'cabinet/critique-contract.md',
|
|
562
|
-
],
|
|
563
|
-
},
|
|
564
|
-
'lifecycle': {
|
|
565
|
-
name: 'Lifecycle (onboard + seed + cc-upgrade + link)',
|
|
566
|
-
description: 'Onboarding prepares the briefings. Seed proposes new cabinet members when you adopt new tech.',
|
|
567
|
-
mandatory: false,
|
|
568
|
-
default: true,
|
|
569
|
-
lean: true,
|
|
570
|
-
templates: ['skills/onboard', 'skills/seed', 'skills/cc-upgrade', 'skills/cc-link', 'skills/cc-unlink', 'skills/cc-extract', 'skills/cc-feedback', 'cabinet/elicitation-methods.md'],
|
|
571
|
-
},
|
|
572
|
-
'validate': {
|
|
573
|
-
name: 'Validate',
|
|
574
|
-
description: 'Structural validation checks with unified summary. Define your own validators.',
|
|
575
|
-
mandatory: false,
|
|
576
|
-
default: true,
|
|
577
|
-
lean: false,
|
|
578
|
-
templates: ['skills/validate', 'scripts/skill-validator.sh'],
|
|
579
|
-
},
|
|
580
|
-
'verify': {
|
|
581
|
-
name: 'Verification Harness (Cucumber + Playwright)',
|
|
582
|
-
description: 'Walkthrough verification with human-in-the-loop verdict pauses. Replaces flat AC lists with re-runnable user-journey scenarios. Includes /verify skeleton skill + cabinet-verify npm runtime + opt-in integration phases for /plan, /execute, /debrief.',
|
|
583
|
-
mandatory: false,
|
|
584
|
-
default: false,
|
|
585
|
-
lean: false,
|
|
586
|
-
templates: [
|
|
587
|
-
'skills/verify',
|
|
588
|
-
'skills/plan/phases/verify-plan.md',
|
|
589
|
-
'skills/execute/phases/verify-emit.md',
|
|
590
|
-
'skills/debrief/phases/verify-coverage.md',
|
|
591
|
-
'skills/orient/phases/verify-backfill.md',
|
|
592
|
-
],
|
|
593
|
-
postInstall: 'verify-setup',
|
|
594
|
-
},
|
|
595
|
-
'site-audit': {
|
|
596
|
-
name: 'Site Audit (deployed-site quality)',
|
|
597
|
-
description: '14-check audit for deployed websites: performance, accessibility (3 engines), security, SEO, DNS, privacy, sustainability. Standalone HTML report + comparison mode.',
|
|
598
|
-
mandatory: false,
|
|
599
|
-
default: false,
|
|
600
|
-
lean: false,
|
|
601
|
-
templates: [
|
|
602
|
-
'skills/cc-site-audit',
|
|
603
|
-
'site-audit-runtime',
|
|
604
|
-
],
|
|
605
|
-
postInstall: 'site-audit-setup',
|
|
606
|
-
},
|
|
607
|
-
engagement: {
|
|
608
|
-
name: 'Engagement management (with secure credential handoff)',
|
|
609
|
-
description: 'Ongoing client-engagement management built on pib-db: per-recipient packets (rendered projections of the work backlog), role-gated billing, client feedback flowing back as events, and secure credential handoff (encrypted capture via OS dialog, pluggable email/MCP/file transport). Three primary skills across consultant and client sides, plus redirect stubs. Requires work-tracking (the pib-db adapter is the engine\'s data source).',
|
|
610
|
-
mandatory: false,
|
|
611
|
-
default: false,
|
|
612
|
-
lean: false,
|
|
613
|
-
requires: ['work-tracking'],
|
|
614
|
-
postInstall: 'engagement-setup',
|
|
615
|
-
templates: [
|
|
616
|
-
'skills/collab-client',
|
|
617
|
-
'skills/collab-consultant',
|
|
618
|
-
'skills/engagement-help',
|
|
619
|
-
'skills/engagement-create',
|
|
620
|
-
'skills/setup-accounts',
|
|
621
|
-
'skills/guide',
|
|
622
|
-
'engagement',
|
|
623
|
-
],
|
|
624
|
-
},
|
|
625
|
-
watchtower: {
|
|
626
|
-
name: 'Watchtower (continuous background state)',
|
|
627
|
-
description: 'Replaces orient/debrief with continuous background processing. Three rings (mechanical cron, Claude intelligence, session-aware), ambient state injection via SessionStart hook, and an inbox for extracted knowledge and signals. Sessions start informed with minimal context cost.',
|
|
628
|
-
mandatory: false,
|
|
629
|
-
default: false,
|
|
630
|
-
lean: false,
|
|
631
|
-
templates: [
|
|
632
|
-
'scripts/watchtower-validate.mjs',
|
|
633
|
-
'cabinet/watchtower-contracts.md',
|
|
634
|
-
'scripts/watchtower-lib.mjs',
|
|
635
|
-
'scripts/watchtower-queue.mjs',
|
|
636
|
-
'scripts/watchtower-routines.mjs',
|
|
637
|
-
'scripts/watchtower-advisories.mjs',
|
|
638
|
-
'cabinet/advisories-state-schema.md',
|
|
639
|
-
'skills/inbox',
|
|
640
|
-
'hooks/watchtower-session-start.sh',
|
|
641
|
-
'scripts/watchtower-build-context.mjs',
|
|
642
|
-
'scripts/watchtower-ring1.mjs',
|
|
643
|
-
'scripts/watchtower-ring1-runner.sh',
|
|
644
|
-
'skills/watchtower',
|
|
645
|
-
'watchtower/com.claude-cabinet.watchtower-ring1.plist',
|
|
646
|
-
'watchtower/watchtower-ring1.service',
|
|
647
|
-
'watchtower/watchtower-ring1.timer',
|
|
648
|
-
'scripts/watchtower-ring2.mjs',
|
|
649
|
-
'scripts/watchtower-ring2-runner.sh',
|
|
650
|
-
'watchtower/com.claude-cabinet.watchtower-ring2-fast.plist',
|
|
651
|
-
'watchtower/com.claude-cabinet.watchtower-ring2-slow.plist',
|
|
652
|
-
'watchtower/watchtower-ring2-fast.service',
|
|
653
|
-
'watchtower/watchtower-ring2-fast.timer',
|
|
654
|
-
'watchtower/watchtower-ring2-slow.service',
|
|
655
|
-
'watchtower/watchtower-ring2-slow.timer',
|
|
656
|
-
'hooks/watchtower-session-end.sh',
|
|
657
|
-
'scripts/watchtower-ring3-close.mjs',
|
|
658
|
-
'scripts/watchtower-status.sh',
|
|
659
|
-
'skills/briefing',
|
|
660
|
-
'skills/threads',
|
|
661
|
-
'skills/qa-handoff',
|
|
662
|
-
'skills/qa-drain',
|
|
663
|
-
],
|
|
664
|
-
},
|
|
665
|
-
mux: {
|
|
666
|
-
name: 'Mux (tmux project manager)',
|
|
667
|
-
description: 'Multi-project terminal manager. Desks (tmux sessions), auto-worktrees, trail logging, sticky notes, portal color-switching, and an MCP server for Claude. Installs to user-level paths (~/.local/bin/, ~/.config/mux/).',
|
|
668
|
-
mandatory: false,
|
|
669
|
-
default: false,
|
|
670
|
-
lean: false,
|
|
671
|
-
postInstall: 'mux-setup',
|
|
672
|
-
templates: ['skills/orient/phases/dx-captures.md', 'skills/dx-feedback'],
|
|
673
|
-
},
|
|
674
|
-
'engagement-server': {
|
|
675
|
-
name: 'Engagement Server',
|
|
676
|
-
description: 'Central multi-engagement API server. Deploys once to Railway/Fly, serves all client engagements. User-level infrastructure.',
|
|
677
|
-
mandatory: false,
|
|
678
|
-
default: false,
|
|
679
|
-
lean: false,
|
|
680
|
-
requires: ['engagement', 'work-tracking'],
|
|
681
|
-
postInstall: 'engagement-server-setup',
|
|
682
|
-
templates: [],
|
|
683
|
-
},
|
|
684
|
-
'bash-compress': {
|
|
685
|
-
name: 'Bash Output Compression Hook',
|
|
686
|
-
description: 'PostToolUse hook that compresses noisy Bash stdout (git status walls, npm/yarn install output, find/ls dumps) to reclaim context in long sessions. Off by default. stderr and error/warning lines pass through verbatim; every rewrite carries a visible [compressed] marker; fail-open on any error. Requires the hooks module (it wires into .claude/settings.json).',
|
|
687
|
-
mandatory: false,
|
|
688
|
-
default: false,
|
|
689
|
-
lean: false,
|
|
690
|
-
requires: ['hooks'],
|
|
691
|
-
templates: ['hooks/bash-output-compress.sh'],
|
|
692
|
-
},
|
|
693
|
-
};
|
|
445
|
+
// The MODULES manifest (the module → template-path registry) lives in its
|
|
446
|
+
// own file so this ~2000-line orchestrator stays navigable (architecture-0005).
|
|
447
|
+
// Re-exported below for back-compat with importers of `require('../lib/cli').MODULES`.
|
|
448
|
+
const { MODULES } = require('./modules');
|
|
694
449
|
|
|
695
450
|
/** Recursively collect all relative file paths under a directory. */
|
|
696
451
|
function walkDir(dir, base) {
|
|
@@ -708,6 +463,94 @@ function walkDir(dir, base) {
|
|
|
708
463
|
return results;
|
|
709
464
|
}
|
|
710
465
|
|
|
466
|
+
// Map a module template path to its install-time manifest key (the path
|
|
467
|
+
// relative to the project root). Top-level so both the install copy loop and
|
|
468
|
+
// computeRemovals derive the key the same way — single source of truth.
|
|
469
|
+
function manifestPath(tmpl) {
|
|
470
|
+
if (tmpl.startsWith('scripts/')) return tmpl;
|
|
471
|
+
return '.claude/' + tmpl;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Expand a declared frozen-module list by its `requires` closure (act:c1013967).
|
|
476
|
+
* Freezing a module must also pin the modules it depends on, or a frozen
|
|
477
|
+
* caller can be left bound to a refreshed callee contract (the cross-runtime
|
|
478
|
+
* version-skew failure: green in the dev tree, broken on a fresh install).
|
|
479
|
+
* E.g. freezing `engagement` (requires work-tracking) also pins work-tracking.
|
|
480
|
+
* Returns a Set of module keys. Unknown keys are ignored (validated elsewhere).
|
|
481
|
+
*/
|
|
482
|
+
function expandFrozenClosure(frozenKeys, modules = MODULES) {
|
|
483
|
+
const closure = new Set();
|
|
484
|
+
const queue = [...(frozenKeys || [])];
|
|
485
|
+
while (queue.length) {
|
|
486
|
+
const key = queue.shift();
|
|
487
|
+
if (closure.has(key)) continue;
|
|
488
|
+
closure.add(key);
|
|
489
|
+
const mod = modules[key];
|
|
490
|
+
for (const req of (mod && mod.requires) || []) {
|
|
491
|
+
if (!closure.has(req)) queue.push(req);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return closure;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Classify which manifest entries should be removed because they are no longer
|
|
499
|
+
* shipped upstream — the pure core of the cleanup/reconcile loop, extracted so
|
|
500
|
+
* it is unit-testable in isolation (the inline loop never was). Returns
|
|
501
|
+
* `{ toRemove, kept }`: `toRemove` is the list of project-relative paths whose
|
|
502
|
+
* files exist on disk and are safe to delete; `kept` carries `{path, reason}`
|
|
503
|
+
* for files deliberately spared, which the caller logs.
|
|
504
|
+
*
|
|
505
|
+
* The four safeguards from the original loop are preserved exactly: S1 only
|
|
506
|
+
* delete known-template paths; S2 only from selected modules; project-owned
|
|
507
|
+
* seeds and non-CC project skills are never deleted. A FROZEN file needs no
|
|
508
|
+
* special case here — it was RETAINED in `allManifest` (at its on-disk hash),
|
|
509
|
+
* so it never reaches the `!allManifest[oldPath]` test; a frozen file removed
|
|
510
|
+
* upstream is kept by S1 ("non-template").
|
|
511
|
+
*/
|
|
512
|
+
function computeRemovals({ existingManifest, allManifest, selectedModules, templateRoot, projectDir, modules = MODULES }) {
|
|
513
|
+
const collectPaths = (modKeys) => {
|
|
514
|
+
const set = new Set();
|
|
515
|
+
for (const modKey of modKeys) {
|
|
516
|
+
const mod = modules[modKey];
|
|
517
|
+
for (const tmpl of (mod && mod.templates) || []) {
|
|
518
|
+
const srcPath = path.join(templateRoot, tmpl);
|
|
519
|
+
if (fs.existsSync(srcPath) && fs.statSync(srcPath).isDirectory()) {
|
|
520
|
+
for (const rel of walkDir(srcPath)) set.add(manifestPath(tmpl) + '/' + rel);
|
|
521
|
+
} else if (fs.existsSync(srcPath)) {
|
|
522
|
+
set.add(manifestPath(tmpl));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return set;
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const allTemplatePaths = collectPaths(Object.keys(modules));
|
|
530
|
+
const selectedTemplatePaths = collectPaths(selectedModules);
|
|
531
|
+
|
|
532
|
+
const ccSkillNames = new Set();
|
|
533
|
+
for (const mod of Object.values(modules)) {
|
|
534
|
+
for (const tmpl of mod.templates || []) {
|
|
535
|
+
if (tmpl.startsWith('skills/')) ccSkillNames.add(tmpl.split('/')[1]);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const toRemove = [];
|
|
540
|
+
const kept = [];
|
|
541
|
+
for (const oldPath of Object.keys(existingManifest)) {
|
|
542
|
+
if (allManifest[oldPath]) continue;
|
|
543
|
+
if (/\/phases\//.test(oldPath)) continue; // user-customizable
|
|
544
|
+
if (isProjectOwnedSeed(oldPath)) { kept.push({ path: oldPath, reason: 'project-owned seed' }); continue; }
|
|
545
|
+
if (!allTemplatePaths.has(oldPath)) { kept.push({ path: oldPath, reason: 'non-template file' }); continue; }
|
|
546
|
+
if (!selectedTemplatePaths.has(oldPath)) continue; // deselected module — leave it
|
|
547
|
+
const skillDirMatch = oldPath.match(/^\.claude\/skills\/([^/]+)\//);
|
|
548
|
+
if (skillDirMatch && !ccSkillNames.has(skillDirMatch[1])) { kept.push({ path: oldPath, reason: 'project skill' }); continue; }
|
|
549
|
+
if (fs.existsSync(path.join(projectDir, oldPath))) toRemove.push(oldPath);
|
|
550
|
+
}
|
|
551
|
+
return { toRemove, kept };
|
|
552
|
+
}
|
|
553
|
+
|
|
711
554
|
// Signals that a directory contains a real project (not just empty)
|
|
712
555
|
const PROJECT_SIGNALS = [
|
|
713
556
|
'package.json', 'Cargo.toml', 'requirements.txt', 'pyproject.toml',
|
|
@@ -730,6 +573,13 @@ function detectProjectState(dir) {
|
|
|
730
573
|
return 'empty';
|
|
731
574
|
}
|
|
732
575
|
|
|
576
|
+
// A `--freeze`/`--unfreeze` value token vs the positional target dir. Module
|
|
577
|
+
// keys are simple kebab-case identifiers; a flag or any path-like token
|
|
578
|
+
// (separator, or . / ..) is the target directory, never a module list.
|
|
579
|
+
function looksLikeModuleArg(s) {
|
|
580
|
+
return Boolean(s) && !s.startsWith('-') && !s.includes('/') && !s.includes('\\') && s !== '.' && s !== '..';
|
|
581
|
+
}
|
|
582
|
+
|
|
733
583
|
function parseArgs(argv) {
|
|
734
584
|
const args = argv.slice(2);
|
|
735
585
|
const flags = {
|
|
@@ -752,11 +602,32 @@ function parseArgs(argv) {
|
|
|
752
602
|
else if (arg === '--help' || arg === '-h') flags.help = true;
|
|
753
603
|
else if (arg === '--reset') flags.reset = true;
|
|
754
604
|
else if (arg === '--force') flags.force = true;
|
|
605
|
+
else if (arg === '--reindex') flags.reindex = true;
|
|
755
606
|
else if (arg === '--migrate-memory') flags.migrateMemory = true;
|
|
756
607
|
else if (arg === '--unmigrate-memory') flags.unmigrateMemory = true;
|
|
608
|
+
else if (arg === '--list-frozen') flags.listFrozen = true;
|
|
757
609
|
else if (arg === '--modules' && i + 1 < args.length) {
|
|
758
610
|
flags.modules = args[++i].split(',').map(s => s.trim()).filter(Boolean);
|
|
759
611
|
}
|
|
612
|
+
// --freeze <mods> / --unfreeze [mods]: edit .ccrc.json frozenModules and
|
|
613
|
+
// exit. Consume the next token as the module list ONLY when it looks like
|
|
614
|
+
// one — a flag, or a path-like token (contains a separator, or is . / ..),
|
|
615
|
+
// is the target directory, not a value. Module keys are simple kebab-case
|
|
616
|
+
// identifiers and never contain a path separator, so this can't swallow a
|
|
617
|
+
// real module while it does stop `--unfreeze /path/to/project` from being
|
|
618
|
+
// misread as a module (which would silently fail to clear-all). An empty
|
|
619
|
+
// --freeze is an error (dispatch warns, never a silent no-op); an empty
|
|
620
|
+
// --unfreeze clears all.
|
|
621
|
+
else if (arg === '--freeze') {
|
|
622
|
+
flags.freeze = looksLikeModuleArg(args[i + 1])
|
|
623
|
+
? args[++i].split(',').map(s => s.trim()).filter(Boolean)
|
|
624
|
+
: [];
|
|
625
|
+
}
|
|
626
|
+
else if (arg === '--unfreeze') {
|
|
627
|
+
flags.unfreeze = looksLikeModuleArg(args[i + 1])
|
|
628
|
+
? args[++i].split(',').map(s => s.trim()).filter(Boolean)
|
|
629
|
+
: []; // clear all
|
|
630
|
+
}
|
|
760
631
|
else if (arg === '--frontier-model' && i + 1 < args.length) {
|
|
761
632
|
// Empty/whitespace values are treated as absent: '' is a substring of
|
|
762
633
|
// every model ID, which would match everything and leave the watchdog
|
|
@@ -782,6 +653,16 @@ function printHelp() {
|
|
|
782
653
|
--modules <keys> Comma-separated module keys to install (e.g., 'verify').
|
|
783
654
|
Mandatory modules are always included.
|
|
784
655
|
--dry-run Show what would be copied without writing
|
|
656
|
+
--reindex Regenerate .claude/skills/_index.json in place (picks up
|
|
657
|
+
directives-project.yaml mandate/routine edits without a full
|
|
658
|
+
reinstall) and exit
|
|
659
|
+
--freeze <keys> Pin module(s) (comma-separated) in .ccrc.json so the
|
|
660
|
+
NEXT reinstall holds their CC-owned files at the current
|
|
661
|
+
version (e.g. --freeze engagement while a client engagement
|
|
662
|
+
is live). Also pins the requires-closure. Edits config, exits.
|
|
663
|
+
--unfreeze [keys] Clear the freeze for the given module(s), or all if no
|
|
664
|
+
keys. Re-run the installer to refresh the unfrozen module(s).
|
|
665
|
+
--list-frozen Show which modules are frozen (+ their closure) and exit.
|
|
785
666
|
--reset Remove Claude Cabinet files (uses manifest for safety)
|
|
786
667
|
--force With --reset: remove even customized files;
|
|
787
668
|
with --migrate-memory: override already-migrated guard
|
|
@@ -823,6 +704,80 @@ async function run() {
|
|
|
823
704
|
return;
|
|
824
705
|
}
|
|
825
706
|
|
|
707
|
+
// Standalone skill-index regen (act:98d41fa2). Advisor standing-mandates and
|
|
708
|
+
// routine declarations are read from .claude/skills/_index.json, which is
|
|
709
|
+
// otherwise regenerated only by a full install — coupling every overlay
|
|
710
|
+
// change (e.g. adding a session-close mandate to directives-project.yaml) to
|
|
711
|
+
// an upgrade cycle. This regenerates the index in place so a change is
|
|
712
|
+
// verifiable immediately, no reinstall needed.
|
|
713
|
+
if (flags.reindex) {
|
|
714
|
+
const projectDir = path.resolve(flags.targetDir);
|
|
715
|
+
if (!fs.existsSync(path.join(projectDir, '.claude', 'skills'))) {
|
|
716
|
+
console.log(` No .claude/skills/ in ${projectDir} — nothing to reindex.`);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const n = generateSkillIndex(projectDir);
|
|
720
|
+
console.log(` 📇 Reindexed ${n} skill${n === 1 ? '' : 's'} in .claude/skills/_index.json`);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Standalone freeze management (act:c1013967) — edit .ccrc.json frozenModules
|
|
725
|
+
// and exit, no install. Freeze pins a module's CC-owned files across a
|
|
726
|
+
// reinstall (the "reinstall maginnis without disturbing Ed's engagement" fix);
|
|
727
|
+
// the install body reads frozenModules + expands the requires closure.
|
|
728
|
+
if (flags.listFrozen || flags.freeze || flags.unfreeze) {
|
|
729
|
+
const projectDir = path.resolve(flags.targetDir);
|
|
730
|
+
const current = readFrozenModules(projectDir);
|
|
731
|
+
|
|
732
|
+
const describe = (mods) => {
|
|
733
|
+
if (mods.length === 0) { console.log(' No modules are frozen.'); return; }
|
|
734
|
+
const closure = expandFrozenClosure(mods, MODULES);
|
|
735
|
+
const pulledIn = [...closure].filter(k => !mods.includes(k));
|
|
736
|
+
console.log(` ❄ Frozen modules: ${mods.join(', ')}`);
|
|
737
|
+
if (pulledIn.length) console.log(` + required closure (also pinned): ${pulledIn.join(', ')}`);
|
|
738
|
+
console.log(' A reinstall holds these modules’ CC-owned files at the current version.');
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
if (flags.listFrozen) {
|
|
742
|
+
describe(current);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (flags.freeze) {
|
|
747
|
+
if (flags.freeze.length === 0) {
|
|
748
|
+
console.log(' ✗ --freeze needs at least one module key, e.g. --freeze engagement');
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const known = Object.keys(MODULES);
|
|
752
|
+
const unknown = flags.freeze.filter(m => !known.includes(m));
|
|
753
|
+
if (unknown.length) console.log(` ⚠ Unknown module key(s) ignored: ${unknown.join(', ')} (known: ${known.join(', ')})`);
|
|
754
|
+
const valid = flags.freeze.filter(m => known.includes(m));
|
|
755
|
+
const next = [...new Set([...current, ...valid])];
|
|
756
|
+
if (flags.dryRun) {
|
|
757
|
+
console.log(` [dry run] Would set frozenModules to: ${next.join(', ') || '(none)'}`);
|
|
758
|
+
} else {
|
|
759
|
+
setFrozenModules(projectDir, next);
|
|
760
|
+
console.log(' ✓ Updated .ccrc.json frozenModules.');
|
|
761
|
+
}
|
|
762
|
+
describe(next);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (flags.unfreeze) {
|
|
767
|
+
const next = flags.unfreeze.length === 0
|
|
768
|
+
? [] // --unfreeze → clear all
|
|
769
|
+
: current.filter(m => !flags.unfreeze.includes(m));
|
|
770
|
+
if (flags.dryRun) {
|
|
771
|
+
console.log(` [dry run] Would set frozenModules to: ${next.join(', ') || '(none)'}`);
|
|
772
|
+
} else {
|
|
773
|
+
setFrozenModules(projectDir, next);
|
|
774
|
+
console.log(' ✓ Updated .ccrc.json frozenModules. Re-run the installer to refresh the unfrozen module(s).');
|
|
775
|
+
}
|
|
776
|
+
describe(next);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
826
781
|
if (flags.migrateMemory) {
|
|
827
782
|
const { migrateMemoryCmd } = require('./migrate-memory-cmd');
|
|
828
783
|
const projectDir = path.resolve(flags.targetDir);
|
|
@@ -1151,18 +1106,24 @@ async function run() {
|
|
|
1151
1106
|
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
1152
1107
|
}
|
|
1153
1108
|
|
|
1154
|
-
//
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1109
|
+
// Frozen modules (act:c1013967): the operator pinned these in .ccrc.json so
|
|
1110
|
+
// a reinstall holds their CC-owned files at the current version. Expand by
|
|
1111
|
+
// the `requires` closure (freezing engagement also pins work-tracking, so a
|
|
1112
|
+
// frozen caller is never left bound to a refreshed callee), then intersect
|
|
1113
|
+
// with what is actually installed. `effectiveFrozen` gates ALL freeze
|
|
1114
|
+
// behavior — never the raw declared list — so a typo'd / uninstalled / dead
|
|
1115
|
+
// key can't silently pin global state or claim protection it isn't giving.
|
|
1116
|
+
const declaredFrozen = readFrozenModules(projectDir);
|
|
1117
|
+
const effectiveFrozen = new Set(
|
|
1118
|
+
[...expandFrozenClosure(declaredFrozen, MODULES)].filter(
|
|
1119
|
+
(k) => MODULES[k] && selectedModules.includes(k)
|
|
1120
|
+
)
|
|
1121
|
+
);
|
|
1122
|
+
const frozenCounts = {}; // modKey -> # files held this install
|
|
1163
1123
|
|
|
1164
1124
|
for (const modKey of selectedModules) {
|
|
1165
1125
|
const mod = MODULES[modKey];
|
|
1126
|
+
const frozen = effectiveFrozen.has(modKey);
|
|
1166
1127
|
for (const tmpl of mod.templates) {
|
|
1167
1128
|
const srcPath = path.join(templateRoot, tmpl);
|
|
1168
1129
|
if (!fs.existsSync(srcPath)) {
|
|
@@ -1211,7 +1172,9 @@ async function run() {
|
|
|
1211
1172
|
projectRoot: projectDir,
|
|
1212
1173
|
existingManifest,
|
|
1213
1174
|
instructionPhases: instrPhases,
|
|
1175
|
+
frozen,
|
|
1214
1176
|
});
|
|
1177
|
+
if (results.frozen.length) frozenCounts[modKey] = (frozenCounts[modKey] || 0) + results.frozen.length;
|
|
1215
1178
|
totalCopied += results.copied.length;
|
|
1216
1179
|
totalSkipped += results.skipped.length;
|
|
1217
1180
|
totalOverwritten += results.overwritten.length;
|
|
@@ -1229,53 +1192,88 @@ async function run() {
|
|
|
1229
1192
|
const incoming = fs.readFileSync(srcPath, 'utf8');
|
|
1230
1193
|
const incomingHash = hashContent(incoming);
|
|
1231
1194
|
const mPath = manifestPath(tmpl);
|
|
1195
|
+
const destExists = fs.existsSync(destPath);
|
|
1196
|
+
const existingContent = destExists ? fs.readFileSync(destPath, 'utf8') : null;
|
|
1197
|
+
// Instruction phases explicitly listed in MODULES are CC-owned and must
|
|
1198
|
+
// always overwrite (they're not customizable); other phase files are
|
|
1199
|
+
// preserved when the operator has edited them.
|
|
1200
|
+
const isPhaseFile = tmpl.includes('/phases/') || tmpl.includes('\\phases\\');
|
|
1201
|
+
const isInstructionPhase = isPhaseFile && mod.templates.includes(tmpl);
|
|
1202
|
+
|
|
1203
|
+
// Single decision spine — the ownership cascade lives in copy.js
|
|
1204
|
+
// (classifyFileOwnership) so this branch can't drift from the directory
|
|
1205
|
+
// walk. The leak this closes: the hand-inlined copy here was missing
|
|
1206
|
+
// the project-owned-seed guard (elegance-0002, act:a6377c82).
|
|
1207
|
+
const decision = classifyFileOwnership({
|
|
1208
|
+
relPath: tmpl,
|
|
1209
|
+
destExists,
|
|
1210
|
+
existingContent,
|
|
1211
|
+
incoming,
|
|
1212
|
+
isPhaseFile,
|
|
1213
|
+
isInstructionPhase,
|
|
1214
|
+
frozen,
|
|
1215
|
+
isCcOwned: Boolean(existingManifest[mPath]),
|
|
1216
|
+
skipConflicts: flags.yes || dirState === 'existing-install',
|
|
1217
|
+
});
|
|
1232
1218
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1219
|
+
switch (decision.action) {
|
|
1220
|
+
case 'seed':
|
|
1221
|
+
// Project-owned seed (act:2001bf54): seed if absent, NEVER
|
|
1222
|
+
// overwrite, ALWAYS omit from the manifest.
|
|
1223
|
+
if (!destExists) {
|
|
1224
|
+
if (!flags.dryRun) fs.copyFileSync(srcPath, destPath);
|
|
1225
|
+
totalCopied++;
|
|
1226
|
+
} else {
|
|
1227
|
+
totalSkipped++;
|
|
1228
|
+
}
|
|
1229
|
+
recordSkip(allManifest, mPath);
|
|
1230
|
+
continue;
|
|
1231
|
+
case 'preserve-phase':
|
|
1232
|
+
console.log(` Preserved customized phase: ${tmpl}`);
|
|
1233
|
+
totalSkipped++;
|
|
1234
|
+
recordSkip(allManifest, mPath);
|
|
1235
|
+
continue;
|
|
1236
|
+
case 'skip-identical':
|
|
1236
1237
|
totalSkipped++;
|
|
1237
1238
|
recordSkip(allManifest, mPath, { identical: true, incomingHash });
|
|
1238
1239
|
continue;
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
// Phase file customization guard — mirrors copy.js:48-59 for the
|
|
1242
|
-
// single-file install path. If the destination path is a phase
|
|
1243
|
-
// file and the on-disk content differs from upstream (operator
|
|
1244
|
-
// edited it), preserve it. This keeps the documented "customize
|
|
1245
|
-
// by editing the phase file" affordance intact across upgrades.
|
|
1246
|
-
// Exception: instruction phases explicitly listed in MODULES are
|
|
1247
|
-
// CC-owned and must always overwrite (they're not customizable).
|
|
1248
|
-
const isPhaseFile = tmpl.includes('/phases/') || tmpl.includes('\\phases\\');
|
|
1249
|
-
const isInstructionPhase = isPhaseFile && mod.templates.includes(tmpl);
|
|
1250
|
-
if (isPhaseFile && !isInstructionPhase && existingContent.trim() !== '' && existingContent.trim() !== incoming.trim()) {
|
|
1251
|
-
console.log(` Preserved customized phase: ${tmpl}`);
|
|
1240
|
+
case 'freeze-hold':
|
|
1252
1241
|
totalSkipped++;
|
|
1253
|
-
|
|
1254
|
-
|
|
1242
|
+
frozenCounts[modKey] = (frozenCounts[modKey] || 0) + 1;
|
|
1243
|
+
allManifest[mPath] = decision.manifestHash;
|
|
1244
|
+
console.log(` ❄ Frozen (held): ${path.relative(projectDir, destPath)}`);
|
|
1245
|
+
continue;
|
|
1246
|
+
case 'overwrite':
|
|
1247
|
+
if (!flags.dryRun) fs.copyFileSync(srcPath, destPath);
|
|
1248
|
+
totalOverwritten++;
|
|
1249
|
+
// Log single-file overwrites too — the directory path (copy.js)
|
|
1250
|
+
// already does. Without this, scripts/ updates are invisible in
|
|
1251
|
+
// install output, masking whether a changed script propagated.
|
|
1252
|
+
console.log(` Updated: ${path.relative(projectDir, destPath)}`);
|
|
1253
|
+
allManifest[mPath] = incomingHash;
|
|
1254
|
+
continue;
|
|
1255
|
+
case 'skip-omit':
|
|
1256
|
+
totalSkipped++;
|
|
1257
|
+
// Project-created file → omit from manifest entirely. Ownership
|
|
1258
|
+
// classification is manifest-PRESENCE-based, so recording ANY hash
|
|
1259
|
+
// here would mark the file upstream-owned and the NEXT install
|
|
1260
|
+
// would silently overwrite it (act:bf21c95b).
|
|
1255
1261
|
recordSkip(allManifest, mPath);
|
|
1256
1262
|
continue;
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
//
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
totalSkipped++;
|
|
1272
|
-
// Project-created file → omit from manifest entirely. Ownership
|
|
1273
|
-
// classification is manifest-PRESENCE-based, so recording ANY
|
|
1274
|
-
// hash here would mark the file upstream-owned and the NEXT
|
|
1275
|
-
// install would silently overwrite it (act:bf21c95b).
|
|
1276
|
-
recordSkip(allManifest, mPath);
|
|
1277
|
-
}
|
|
1278
|
-
} else {
|
|
1263
|
+
case 'freeze-hold-new':
|
|
1264
|
+
// New file in a frozen module: hold it back (don't create) so the
|
|
1265
|
+
// module stays internally consistent at its pinned version. Report
|
|
1266
|
+
// only — never a manifest entry (no on-disk content to hash).
|
|
1267
|
+
frozenCounts[modKey] = (frozenCounts[modKey] || 0) + 1;
|
|
1268
|
+
console.log(` ❄ Frozen (held new): ${path.relative(projectDir, destPath)}`);
|
|
1269
|
+
continue;
|
|
1270
|
+
case 'copy-new':
|
|
1271
|
+
if (!flags.dryRun) fs.copyFileSync(srcPath, destPath);
|
|
1272
|
+
totalCopied++;
|
|
1273
|
+
allManifest[mPath] = incomingHash;
|
|
1274
|
+
continue;
|
|
1275
|
+
case 'prompt':
|
|
1276
|
+
default: {
|
|
1279
1277
|
const response = await prompts({
|
|
1280
1278
|
type: 'select',
|
|
1281
1279
|
name: 'action',
|
|
@@ -1296,11 +1294,8 @@ async function run() {
|
|
|
1296
1294
|
// the manifest so it is never mistaken for upstream content.
|
|
1297
1295
|
recordSkip(allManifest, mPath);
|
|
1298
1296
|
}
|
|
1297
|
+
continue;
|
|
1299
1298
|
}
|
|
1300
|
-
} else {
|
|
1301
|
-
if (!flags.dryRun) fs.copyFileSync(srcPath, destPath);
|
|
1302
|
-
totalCopied++;
|
|
1303
|
-
allManifest[mPath] = incomingHash;
|
|
1304
1299
|
}
|
|
1305
1300
|
}
|
|
1306
1301
|
}
|
|
@@ -1308,6 +1303,28 @@ async function run() {
|
|
|
1308
1303
|
|
|
1309
1304
|
console.log(` 📁 Files: ${totalCopied} copied, ${totalOverwritten} overwritten, ${totalSkipped} unchanged`);
|
|
1310
1305
|
|
|
1306
|
+
// --- Frozen-module report (act:c1013967) ---
|
|
1307
|
+
// Plain-English, per-module: surface WHICH modules were held and how many
|
|
1308
|
+
// files, so a gitignored freeze can never quietly change install behavior.
|
|
1309
|
+
// Note in plain words when the requires-closure pinned a module the operator
|
|
1310
|
+
// didn't name directly.
|
|
1311
|
+
if (effectiveFrozen.size > 0) {
|
|
1312
|
+
const declaredSet = new Set(declaredFrozen);
|
|
1313
|
+
for (const modKey of effectiveFrozen) {
|
|
1314
|
+
const n = frozenCounts[modKey] || 0;
|
|
1315
|
+
let reason = '';
|
|
1316
|
+
if (!declaredSet.has(modKey)) {
|
|
1317
|
+
const namedBy = [...declaredSet].find(d => expandFrozenClosure([d], MODULES).has(modKey) && d !== modKey);
|
|
1318
|
+
if (namedBy) reason = ` (pinned because ${namedBy} requires it)`;
|
|
1319
|
+
}
|
|
1320
|
+
if (n > 0) {
|
|
1321
|
+
console.log(` ❄ Frozen: ${n} file${n === 1 ? '' : 's'} in module '${modKey}'${reason} held at current version — unfreeze to update.`);
|
|
1322
|
+
} else {
|
|
1323
|
+
console.log(` ❄ Frozen: module '${modKey}'${reason} — no CC-owned files differed (nothing to hold this run).`);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1311
1328
|
// --- Make hook scripts executable ---
|
|
1312
1329
|
const hooksDir = path.join(claudeDir, 'hooks');
|
|
1313
1330
|
if (!flags.dryRun && fs.existsSync(hooksDir)) {
|
|
@@ -1339,6 +1356,27 @@ async function run() {
|
|
|
1339
1356
|
mergeBashCompressHooks(settingsPath);
|
|
1340
1357
|
console.log(' ⚙️ Registered bash-output compression PostToolUse hook');
|
|
1341
1358
|
}
|
|
1359
|
+
|
|
1360
|
+
// Repo-local maintainer hooks — no-op unless this IS the CC source repo
|
|
1361
|
+
// (dogfood). Registers the node --check PostToolUse gate durably so a
|
|
1362
|
+
// reinstall can't silently regenerate it away (architecture-0004).
|
|
1363
|
+
if (mergeCcDevHooks(projectDir)) {
|
|
1364
|
+
console.log(' ⚙️ Registered repo-local node --check PostToolUse hook (CC source repo)');
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// cabinet-* skills are ~half the skill-listing budget and are spawned by
|
|
1368
|
+
// agentType (name), never auto-invoked by description — so set them
|
|
1369
|
+
// "name-only" to free budget for the routing skills that need full trigger
|
|
1370
|
+
// keywords (act:bee0fa16). Only-if-absent, so a user override is preserved.
|
|
1371
|
+
if (selectedModules.includes('audit')) {
|
|
1372
|
+
const cabinetSkills = (MODULES.audit.templates || [])
|
|
1373
|
+
.map((t) => (t.match(/^skills\/(cabinet-[^/]+)$/) || [])[1])
|
|
1374
|
+
.filter(Boolean);
|
|
1375
|
+
const added = mergeSkillOverrides(projectDir, cabinetSkills);
|
|
1376
|
+
if (added > 0) {
|
|
1377
|
+
console.log(` ⚙️ Set ${added} cabinet-* skill${added === 1 ? '' : 's'} to name-only (frees skill-listing budget)`);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1342
1380
|
}
|
|
1343
1381
|
|
|
1344
1382
|
// --- Heal user-level ~/.claude/settings.json ---
|
|
@@ -1378,10 +1416,34 @@ async function run() {
|
|
|
1378
1416
|
}
|
|
1379
1417
|
|
|
1380
1418
|
// --- Set up database ---
|
|
1419
|
+
// dbSchemaVersion is recorded into .ccrc.json so a future reinstall can
|
|
1420
|
+
// tell, WITHOUT loading better-sqlite3, that no migration is pending and
|
|
1421
|
+
// skip the init step entirely (lib/db-setup.js shouldSkipInit).
|
|
1422
|
+
let dbSchemaVersion;
|
|
1381
1423
|
if (includeDb && selectedModules.includes('work-tracking') && !flags.dryRun) {
|
|
1382
1424
|
try {
|
|
1383
|
-
|
|
1384
|
-
|
|
1425
|
+
// Freeze insurance (act:c1013967): when a module is frozen and a schema
|
|
1426
|
+
// migration is actually pending, back up pib.db before db-setup runs so
|
|
1427
|
+
// the operator has true one-file rollback for the store holding live
|
|
1428
|
+
// engagement data. Reuses db-setup's own skip decision (no logic
|
|
1429
|
+
// duplication): if work-tracking is itself frozen, its pib-db-lib.mjs
|
|
1430
|
+
// stayed old → bundled == recorded → no migration → no backup. The
|
|
1431
|
+
// existsSync guard means a fresh install (no pib.db yet) still creates
|
|
1432
|
+
// the db normally; there's just nothing to back up.
|
|
1433
|
+
if (effectiveFrozen.size > 0) {
|
|
1434
|
+
const dbPath = path.join(projectDir, 'pib.db');
|
|
1435
|
+
const recordedVersion = (readMetadata(projectDir) || {}).dbSchemaVersion;
|
|
1436
|
+
const bundledVersion = readBundledSchemaVersion(path.join(projectDir, 'scripts'));
|
|
1437
|
+
const willMigrate = !shouldSkipInit({ dbExists: fs.existsSync(dbPath), recordedVersion, bundledVersion });
|
|
1438
|
+
if (willMigrate && fs.existsSync(dbPath)) {
|
|
1439
|
+
const backupPath = `${dbPath}.cc-bak-${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
1440
|
+
fs.copyFileSync(dbPath, backupPath);
|
|
1441
|
+
console.log(` ❄ Backed up pib.db → ${path.basename(backupPath)} before migration (freeze active).`);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
const dbResult = setupDb(projectDir);
|
|
1445
|
+
for (const r of dbResult.results) console.log(` 🗄️ ${r}`);
|
|
1446
|
+
if (dbResult.schemaVersion != null) dbSchemaVersion = dbResult.schemaVersion;
|
|
1385
1447
|
} catch (err) {
|
|
1386
1448
|
console.log(` ⚠ Database setup failed: ${err.message}`);
|
|
1387
1449
|
console.log(' You can set it up later: node scripts/pib-db.mjs init');
|
|
@@ -1487,78 +1549,26 @@ async function run() {
|
|
|
1487
1549
|
// S3: Itemize — list all deletions and confirm before proceeding
|
|
1488
1550
|
// S4: Backup — copy files to .cc-backup/<timestamp>/ before deletion
|
|
1489
1551
|
if (Object.keys(existingManifest).length > 0) {
|
|
1490
|
-
//
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
const srcPath = path.join(templateRoot, tmpl);
|
|
1511
|
-
if (fs.existsSync(srcPath) && fs.statSync(srcPath).isDirectory()) {
|
|
1512
|
-
for (const rel of walkDir(srcPath)) {
|
|
1513
|
-
selectedTemplatePaths.add(manifestPath(tmpl) + '/' + rel);
|
|
1514
|
-
}
|
|
1515
|
-
} else if (fs.existsSync(srcPath)) {
|
|
1516
|
-
selectedTemplatePaths.add(manifestPath(tmpl));
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
// Build CC template skill name set for project-skill guard (act:4ac281ba)
|
|
1522
|
-
const ccSkillNames = new Set();
|
|
1523
|
-
for (const mod of Object.values(MODULES)) {
|
|
1524
|
-
for (const tmpl of mod.templates || []) {
|
|
1525
|
-
if (tmpl.startsWith('skills/')) {
|
|
1526
|
-
const skillName = tmpl.split('/')[1];
|
|
1527
|
-
ccSkillNames.add(skillName);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
// Collect files that would be removed
|
|
1533
|
-
const toRemove = [];
|
|
1534
|
-
for (const oldPath of Object.keys(existingManifest)) {
|
|
1535
|
-
if (!allManifest[oldPath]) {
|
|
1536
|
-
// Skip phase files — they may be user-customized
|
|
1537
|
-
if (/\/phases\//.test(oldPath)) continue;
|
|
1538
|
-
|
|
1539
|
-
// S1: Only delete if the path maps to a known CC template
|
|
1540
|
-
if (!allTemplatePaths.has(oldPath)) {
|
|
1541
|
-
console.log(` Keeping non-template file: ${oldPath}`);
|
|
1542
|
-
continue;
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
// S2: Only delete from selected modules (don't purge deselected modules)
|
|
1546
|
-
if (!selectedTemplatePaths.has(oldPath)) {
|
|
1547
|
-
continue;
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1550
|
-
// Project-skill guard: never delete skills CC didn't create
|
|
1551
|
-
const skillDirMatch = oldPath.match(/^\.claude\/skills\/([^/]+)\//);
|
|
1552
|
-
if (skillDirMatch && !ccSkillNames.has(skillDirMatch[1])) {
|
|
1553
|
-
console.log(` Keeping project skill: ${oldPath}`);
|
|
1554
|
-
continue;
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
const fullPath = path.join(projectDir, oldPath);
|
|
1558
|
-
if (fs.existsSync(fullPath)) {
|
|
1559
|
-
toRemove.push(oldPath);
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1552
|
+
// Classification is the pure `computeRemovals` (top-level, unit-tested): S1
|
|
1553
|
+
// only known-template paths, S2 only selected modules, plus the
|
|
1554
|
+
// project-owned-seed and project-skill guards. A FROZEN file needs no case
|
|
1555
|
+
// here — retain-in-manifest (the freeze copy path) kept it in allManifest,
|
|
1556
|
+
// so it never reaches the removal test; a frozen file removed upstream is
|
|
1557
|
+
// kept by S1 ("non-template"). The S3 itemize/confirm + S4 backup/delete
|
|
1558
|
+
// stay inline below.
|
|
1559
|
+
const { toRemove, kept } = computeRemovals({
|
|
1560
|
+
existingManifest,
|
|
1561
|
+
allManifest,
|
|
1562
|
+
selectedModules,
|
|
1563
|
+
templateRoot,
|
|
1564
|
+
projectDir,
|
|
1565
|
+
});
|
|
1566
|
+
for (const k of kept) {
|
|
1567
|
+
// 'deselected module' files are silently left (matching the prior loop's
|
|
1568
|
+
// bare `continue` for that S2 case); the rest log their keep reason.
|
|
1569
|
+
if (k.reason === 'project-owned seed') console.log(` Keeping project-owned seed: ${k.path}`);
|
|
1570
|
+
else if (k.reason === 'non-template file') console.log(` Keeping non-template file: ${k.path}`);
|
|
1571
|
+
else if (k.reason === 'project skill') console.log(` Keeping project skill: ${k.path}`);
|
|
1562
1572
|
}
|
|
1563
1573
|
|
|
1564
1574
|
// S3: Itemize and confirm before deleting
|
|
@@ -1629,6 +1639,7 @@ async function run() {
|
|
|
1629
1639
|
skipped: skippedModules,
|
|
1630
1640
|
version: VERSION,
|
|
1631
1641
|
manifest: allManifest,
|
|
1642
|
+
dbSchemaVersion,
|
|
1632
1643
|
});
|
|
1633
1644
|
console.log(' 📝 Created .ccrc.json');
|
|
1634
1645
|
}
|
|
@@ -1684,9 +1695,13 @@ async function run() {
|
|
|
1684
1695
|
} else {
|
|
1685
1696
|
console.log(' 📋 Registered in project registry');
|
|
1686
1697
|
}
|
|
1687
|
-
// Self-announcing: print the effective designation on every run
|
|
1698
|
+
// Self-announcing: print the effective designation on every run, and
|
|
1699
|
+
// the inert-state notice when none is set — a never-configured
|
|
1700
|
+
// watchdog must not read the same as a passing one (act:77f7b7b2).
|
|
1688
1701
|
if (registry.frontierModel) {
|
|
1689
1702
|
console.log(` 🛰 Frontier model: ${registry.frontierModel} (visibility watchdog — /orient + SessionStart warn on mismatch; nothing is pinned)`);
|
|
1703
|
+
} else {
|
|
1704
|
+
console.log(' 🛰 Frontier model: none designated — watchdog inert. Set with: npx create-claude-cabinet --frontier-model <value>');
|
|
1690
1705
|
}
|
|
1691
1706
|
} catch (err) {
|
|
1692
1707
|
// Non-fatal — registry is nice-to-have
|
|
@@ -1710,4 +1725,4 @@ async function run() {
|
|
|
1710
1725
|
console.log('');
|
|
1711
1726
|
}
|
|
1712
1727
|
|
|
1713
|
-
module.exports = { run, MODULES, generateAgentWrappers };
|
|
1728
|
+
module.exports = { run, MODULES, generateAgentWrappers, generateSkillIndex, expandFrozenClosure, computeRemovals, manifestPath };
|