devflow-kit 3.7.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +479 -1
- package/README.md +15 -15
- package/dist/agents/code.md +6 -8
- package/dist/agents/design.md +1 -1
- package/dist/agents/fix.md +7 -8
- package/dist/agents/git.md +3 -3
- package/dist/agents/harden.md +1 -1
- package/dist/agents/refactor.md +2 -4
- package/dist/agents/review.md +4 -4
- package/dist/agents/skim.md +2 -2
- package/dist/agents/triage.md +9 -2
- package/dist/cli/commands/capture.js +1 -1
- package/dist/cli/commands/curation-move-io.js +102 -0
- package/dist/cli/commands/curation-move-step.js +374 -0
- package/dist/cli/commands/curation.js +808 -0
- package/dist/cli/commands/gate-level-prompts.js +428 -0
- package/dist/cli/commands/gates.js +35 -11
- package/dist/cli/commands/init-seed.js +11 -8
- package/dist/cli/commands/init.js +244 -57
- package/dist/cli/commands/memory.js +60 -13
- package/dist/cli/commands/prompt-io.js +37 -0
- package/dist/cli/commands/uninstall.js +157 -17
- package/dist/cli.js +2 -2
- package/dist/commands/bug-analysis.md +25 -36
- package/dist/commands/code-review.md +40 -44
- package/dist/commands/debug.md +5 -5
- package/dist/commands/dynamic-build.md +26 -25
- package/dist/commands/dynamic-plan.md +5 -5
- package/dist/commands/dynamic-profile.md +5 -5
- package/dist/commands/dynamic-tickets.md +5 -5
- package/dist/commands/explore.md +6 -6
- package/dist/commands/implement.md +53 -82
- package/dist/commands/plan.md +6 -6
- package/dist/commands/release.md +5 -5
- package/dist/commands/research.md +6 -6
- package/dist/commands/resolve.md +39 -41
- package/dist/commands/self-review.md +5 -13
- package/dist/core/area-pause-notice.js +44 -0
- package/dist/core/assets.js +20 -20
- package/dist/core/curate-tuning-config.js +5 -6
- package/dist/core/curation-move-notice.js +84 -0
- package/dist/core/curation-move-skip.js +35 -0
- package/dist/core/curation-move.js +35 -0
- package/dist/core/curation-queue-cleanup.js +135 -0
- package/dist/core/curation-store.js +99 -0
- package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
- package/dist/core/echo-value.js +11 -5
- package/dist/core/evidence-policy.js +11 -9
- package/dist/core/feature-switch.js +95 -19
- package/dist/core/fs-atomic.js +1 -1
- package/dist/core/gate-level.js +10 -2
- package/dist/core/git.js +16 -14
- package/dist/core/ledger-root.js +82 -22
- package/dist/core/linked-path.js +4 -4
- package/dist/core/manifest-lock.js +238 -0
- package/dist/core/manifest.js +58 -14
- package/dist/core/migrations.js +1 -13
- package/dist/core/observations.js +2 -2
- package/dist/core/plugins.js +19 -19
- package/dist/core/pool.js +26 -0
- package/dist/core/project-paths.js +103 -34
- package/dist/core/queue-drain.js +2 -2
- package/dist/core/tracked-areas.js +159 -0
- package/dist/core/tracker.js +1 -1
- package/dist/{learning-off → curation-off}/agents/code.md +6 -8
- package/dist/{learning-off → curation-off}/agents/fix.md +7 -8
- package/dist/{learning-off → curation-off}/agents/refactor.md +2 -4
- package/dist/{learning-off → curation-off}/agents/review.md +3 -3
- package/dist/{learning-off → curation-off}/agents/triage.md +8 -1
- package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
- package/dist/{learning-off → curation-off}/commands/code-review.md +37 -41
- package/dist/{learning-off → curation-off}/commands/debug.md +2 -2
- package/dist/{learning-off → curation-off}/commands/dynamic-build.md +23 -22
- package/dist/{learning-off → curation-off}/commands/explore.md +2 -2
- package/dist/{learning-off → curation-off}/commands/implement.md +50 -79
- package/dist/{learning-off → curation-off}/commands/plan.md +2 -2
- package/dist/{learning-off → curation-off}/commands/resolve.md +36 -38
- package/dist/{learning-off → curation-off}/commands/self-review.md +2 -10
- package/dist/hud/components/{learning-counts.js → curation-counts.js} +63 -17
- package/dist/hud/config.js +1 -1
- package/dist/hud/index.js +6 -6
- package/dist/hud/render.js +3 -3
- package/dist/skills/git/references/pr/post-resolution-summary.md +12 -11
- package/dist/skills/git/references/pr/post-review-summary.md +12 -11
- package/dist/skills/git/references/pr/update-pr-evidence.md +2 -2
- package/dist/skills/git/references/pr/validate-branch.md +8 -5
- package/dist/skills/git/references/publication-gate.md +6 -7
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/github/manage-debt.md +6 -2
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/jira/manage-debt.md +6 -2
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/linear/manage-debt.md +6 -2
- package/dist/targets/claude-code/{learning-install.js → curation-install.js} +85 -65
- package/dist/targets/claude-code/installer.js +15 -15
- package/dist/targets/claude-code/language-stamp.js +5 -5
- package/dist/targets/claude-code/post-install.js +2 -2
- package/dist/targets/claude-code/tracker-servers.js +1 -1
- package/package.json +3 -2
- package/src/assets/agents/_partials/_code.mds +11 -11
- package/src/assets/agents/code.mds +6 -6
- package/src/assets/agents/curate.md +8 -8
- package/src/assets/agents/debug.mds +6 -6
- package/src/assets/agents/design.mds +18 -18
- package/src/assets/agents/evaluate.md +6 -12
- package/src/assets/agents/fix.mds +7 -6
- package/src/assets/agents/git.mds +3 -3
- package/src/assets/agents/harden.mds +7 -7
- package/src/assets/agents/knowledge.mds +23 -23
- package/src/assets/agents/refactor.mds +2 -2
- package/src/assets/agents/research.mds +15 -15
- package/src/assets/agents/review.mds +43 -43
- package/src/assets/agents/skim.mds +11 -11
- package/src/assets/agents/triage.mds +29 -22
- package/src/assets/agents/validate.md +30 -50
- package/src/assets/commands/_partials/_decisions.mds +22 -20
- package/src/assets/commands/_partials/_engine.mds +8 -8
- package/src/assets/commands/_partials/_knowledge.mds +29 -15
- package/src/assets/commands/_partials/_preamble.mds +4 -4
- package/src/assets/commands/_partials/_settings.mds +3 -3
- package/src/assets/commands/_partials/_wave.mds +7 -7
- package/src/assets/commands/bug-analysis.mds +37 -48
- package/src/assets/commands/code-review.mds +53 -57
- package/src/assets/commands/debug.mds +14 -14
- package/src/assets/commands/dynamic-build.mds +38 -37
- package/src/assets/commands/dynamic-plan.mds +22 -22
- package/src/assets/commands/dynamic-profile.mds +2 -2
- package/src/assets/commands/dynamic-tickets.mds +8 -8
- package/src/assets/commands/explore.mds +20 -20
- package/src/assets/commands/implement.mds +77 -98
- package/src/assets/commands/plan.mds +27 -27
- package/src/assets/commands/release.mds +12 -12
- package/src/assets/commands/research.mds +19 -19
- package/src/assets/commands/resolve.mds +66 -60
- package/src/assets/commands/self-review.mds +20 -20
- package/src/assets/mds/git/_ops.mds +9 -2
- package/src/assets/mds/git/_ops_pr.mds +21 -11
- package/src/assets/mds/git/_ops_steps.mds +20 -4
- package/src/assets/mds/git/_pr.mds +21 -18
- package/src/assets/mds/git/_references.mds +6 -7
- package/src/assets/mds/tracker/_github.mds +1 -4
- package/src/assets/mds/tracker/_jira.mds +1 -4
- package/src/assets/mds/tracker/_linear.mds +1 -4
- package/src/assets/rules/quality.md +1 -0
- package/src/assets/scripts/ci-wait.cjs +389 -92
- package/src/assets/scripts/hooks/background-memory-update +81 -3
- package/src/assets/scripts/hooks/capture-prompt +12 -12
- package/src/assets/scripts/hooks/capture-question +11 -11
- package/src/assets/scripts/hooks/capture-turn +12 -12
- package/src/assets/scripts/hooks/{learning-lock → curation-lock} +2 -2
- package/src/assets/scripts/hooks/curation-move +293 -0
- package/src/assets/scripts/hooks/ensure-devflow-init +45 -13
- package/src/assets/scripts/hooks/ensure-proxy +4 -4
- package/src/assets/scripts/hooks/ensure-root-gitignore +4 -4
- package/src/assets/scripts/hooks/git-marker +81 -0
- package/src/assets/scripts/hooks/json-helper.cjs +176 -51
- package/src/assets/scripts/hooks/json-parse +1 -1
- package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
- package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +308 -122
- package/src/assets/scripts/hooks/lib/decisions-format.cjs +3 -3
- package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +24 -7
- package/src/assets/scripts/hooks/lib/project-paths.cjs +132 -39
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +42 -22
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +452 -0
- package/src/assets/scripts/hooks/memory-permission +10 -2
- package/src/assets/scripts/hooks/memory-worker +12 -1
- package/src/assets/scripts/hooks/pre-compact-memory +17 -16
- package/src/assets/scripts/hooks/queue-append +444 -102
- package/src/assets/scripts/hooks/resolve-project-root +2 -2
- package/src/assets/scripts/hooks/session-start-context +198 -72
- package/src/assets/scripts/hooks/session-start-memory +42 -43
- package/src/assets/scripts/lib/project-config.cjs +12 -5
- package/src/assets/scripts/pr-evidence.cjs +5 -2
- package/src/assets/scripts/resolve-base.cjs +1043 -0
- package/src/assets/scripts/resolve-settings.cjs +283 -45
- package/src/assets/scripts/verify-evidence.cjs +307 -75
- package/src/assets/skills/agent-references/SKILL.md +11 -2
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +29 -0
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
- package/src/assets/skills/agent-references/references/commands/resolve-levels.md +25 -0
- package/src/assets/skills/agent-references/references/synthesize/review.md +4 -4
- package/src/assets/skills/agent-references/references/triage/severity-tiers.md +43 -0
- package/src/assets/skills/agent-references/references/validate/passed-alone.md +14 -0
- package/src/assets/skills/apply-decisions/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +7 -7
- package/src/assets/skills/docs-framework/references/patterns.md +3 -4
- package/src/assets/skills/quality-gates/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +2 -2
- package/templates/lint-pack/.lint-pack.conf +86 -0
- package/templates/lint-pack/README.md +190 -0
- package/templates/lint-pack/lint-pack.sh +905 -0
- package/dist/cli/commands/learning.js +0 -554
- package/dist/core/learning-queue-cleanup.js +0 -27
- package/dist/core/learning-store.js +0 -61
- /package/dist/{learning-off → curation-off}/agents/debug.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/design.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/harden.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/knowledge.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/research.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/skim.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-plan.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-profile.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-tickets.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/release.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/research.md +0 -0
|
@@ -8,7 +8,11 @@ import { resolveInstallationPaths } from '../../targets/claude-code/claude-paths
|
|
|
8
8
|
import { getGitRoot } from '../../core/git.js';
|
|
9
9
|
import { isSameLocation, withoutHomeRoots } from '../../core/same-location.js';
|
|
10
10
|
import { pruneHookLogDirs, MAX_HOOK_LOG_DIRS } from '../../core/hook-log-dirs.js';
|
|
11
|
-
import {
|
|
11
|
+
import { LEDGER_ROOT_TIMEOUT_MS, lookupLedgerRoot } from '../../core/ledger-root.js';
|
|
12
|
+
import { formatCurationStoreUnavailable } from '../../core/curation-store.js';
|
|
13
|
+
import { loadCurationMove } from '../../core/curation-move.js';
|
|
14
|
+
import { describeMoveText, describeOutcome, runInitCurationMove, shownSkipAt } from './curation-move-step.js';
|
|
15
|
+
import { realGlobalTuningMoveIO, realProjectLedgerMoveIO } from './curation-move-io.js';
|
|
12
16
|
import { describeEchoedValue } from '../../core/echo-value.js';
|
|
13
17
|
import { installViaFileCopy, composeScripts } from '../../targets/claude-code/installer.js';
|
|
14
18
|
import { formatOverlaySummary, formatSkillScopeSummary, formatTrackerAssetSummary, isPluginListUnchanged } from './install-report.js';
|
|
@@ -19,7 +23,7 @@ import { LEGACY_SKILL_NAMES } from '../../targets/claude-code/legacy.js';
|
|
|
19
23
|
import { detectPlatform, detectShell, getProfilePath, getSafeDeleteInfo, hasSafeDelete } from '../../core/safe-delete.js';
|
|
20
24
|
import { generateSafeDeleteBlock, installToProfile, removeFromProfile, getInstalledVersion, SAFE_DELETE_BLOCK_VERSION } from '../../core/safe-delete-install.js';
|
|
21
25
|
import { convergeAmbientHooks } from './ambient.js';
|
|
22
|
-
import { convergeMemoryHooks, drainMemoryQueue, workingMemoryNote } from './memory.js';
|
|
26
|
+
import { convergeMemoryHooks, drainMemoryQueue, formatMemoryNotDrained, workingMemoryNote } from './memory.js';
|
|
23
27
|
import { addCaptureHooks, removeCaptureHooks } from './capture.js';
|
|
24
28
|
import { removeDreamHook } from './legacy-hooks.js';
|
|
25
29
|
import { addProxyHooks, removeProxyHooks, applyProxyEnv, stripProxyEnv, runProxyPreflight, buildRealPreflightDeps } from './proxy.js';
|
|
@@ -34,8 +38,7 @@ import { convergeFlagsIntoSettings, countActiveFlags, readViewMode } from '../..
|
|
|
34
38
|
import { addContextHook, removeContextHook, hasContextHook } from './context.js';
|
|
35
39
|
import { writeSettingsFileAtomic } from '../../core/fs-atomic.js';
|
|
36
40
|
import { writeManagedConfig, readConfigIfPresent, DEFAULT_CONFIG } from '../../core/feature-config.js';
|
|
37
|
-
import {
|
|
38
|
-
import { formatRefusedDrain } from '../../core/queue-drain.js';
|
|
41
|
+
import { drainDisabledCurationQueue, formatCurationNotDrained } from '../../core/curation-queue-cleanup.js';
|
|
39
42
|
import { removeManagedDenyList, describeManagedDenyRemoval } from './security.js';
|
|
40
43
|
import { resolveInitSeed, applyCliToggles, resolveResetGatedInputs, resolvePluginsToInstall } from './init-seed.js';
|
|
41
44
|
import { parseFrameworkList, normalizeFrameworks } from '../../core/compliance.js';
|
|
@@ -44,9 +47,10 @@ import { shouldRunBehaviorStep, runBehaviorStep, buildClackBehaviorPrompts } fro
|
|
|
44
47
|
import { convergeBehaviorFromManifest } from '../../targets/claude-code/behavior-install.js';
|
|
45
48
|
import { applyTrackerSentinel, effectiveTrackerServers, parseTrackerId, rearmTrackerInference, DEFAULT_TRACKER_PROVIDER, } from '../../core/tracker.js';
|
|
46
49
|
import { formatTrackerSummary, shouldRunTrackerStep, runTrackerStep, buildClackTrackerPrompts, } from './tracker-prompts.js';
|
|
50
|
+
import { buildClackGateLevelPrompts, carriedGateLevel, formatGateLevelRow, formatGateLevelSummary, readStoredGateLevel, resolveGateLevelForInit, resolveGateLevelInitState, } from './gate-level-prompts.js';
|
|
47
51
|
import { shouldRunAttributionStep, runAttributionStep, buildClackAttributionPrompts, applyAttributionAnswer, attributionSeedFrom, } from './attribution-prompts.js';
|
|
48
52
|
import { convergeFromManifest } from '../../targets/claude-code/compliance-install.js';
|
|
49
|
-
import {
|
|
53
|
+
import { convergeCurationVariants, installedVersionMatches, readRunningVersion } from '../../targets/claude-code/curation-install.js';
|
|
50
54
|
import { auditAndRecordClaudeMd, formatClaudeMdAuditNote, formatClaudeMdAuditUnavailable, } from '../../core/claude-md-audit.js';
|
|
51
55
|
import * as os from 'os';
|
|
52
56
|
// Re-export pure functions for tests (canonical source is post-install.ts)
|
|
@@ -68,7 +72,7 @@ import { getPackageRoot } from '../../core/paths.js';
|
|
|
68
72
|
* migrations are vacuously applied per D37 semantics).
|
|
69
73
|
*
|
|
70
74
|
* Migrations are a one-time cleanup pass over ~/.devflow runtime data
|
|
71
|
-
* (memory,
|
|
75
|
+
* (memory, curation, knowledge). They never touch the installer's
|
|
72
76
|
* copy targets (skills, agents, rules, commands, scripts), so ordering
|
|
73
77
|
* relative to installViaFileCopy carries no data dependency.
|
|
74
78
|
*
|
|
@@ -428,9 +432,73 @@ export async function persistManifestThenConvergeTracker(opts) {
|
|
|
428
432
|
};
|
|
429
433
|
}
|
|
430
434
|
/**
|
|
431
|
-
*
|
|
435
|
+
* The curation move step (#529): rename the global tuning file, then move each target
|
|
436
|
+
* project's ledger folder from the old name to the new one. The targets are the
|
|
437
|
+
* discovered projects (else the current repository) plus the current repository's own
|
|
438
|
+
* ledger root, which the step lists once. State-checked on every run, with no run-once
|
|
439
|
+
* record. Never throws: a project that cannot move is a line, not an exit code.
|
|
440
|
+
*
|
|
441
|
+
* Init runs it after the legacy sweep and BEFORE the manifest is persisted and the
|
|
442
|
+
* disabled-feature queues drain, so the drain sees the moved folder.
|
|
443
|
+
*
|
|
444
|
+
* The own ledger root is looked up within the shared budget (`lookupOwnLedgerRoot`). A
|
|
445
|
+
* lookup that times out is no ledger root: the step moves the other targets, and one
|
|
446
|
+
* skipped line names the repository, unless the repository is itself a project root,
|
|
447
|
+
* where the step's own lookup reports it.
|
|
448
|
+
*/
|
|
449
|
+
export async function moveCurationData(opts, lookup = lookupLedgerRoot) {
|
|
450
|
+
const loaded = loadCurationMove();
|
|
451
|
+
const roots = projectRoots([...opts.discoveredProjects], opts.gitRoot);
|
|
452
|
+
const own = await lookupOwnLedgerRoot(opts.gitRoot, lookup);
|
|
453
|
+
const report = await runInitCurationMove({
|
|
454
|
+
projectRoots: roots,
|
|
455
|
+
ownLedgerRoot: own.root,
|
|
456
|
+
homeDir: opts.homeDir,
|
|
457
|
+
machineDir: opts.devflowDir,
|
|
458
|
+
}, {
|
|
459
|
+
ledger: loaded.ok ? realProjectLedgerMoveIO(loaded.value) : { unavailable: formatCurationStoreUnavailable(loaded.error) },
|
|
460
|
+
tuning: realGlobalTuningMoveIO(),
|
|
461
|
+
});
|
|
462
|
+
if (!own.timedOut || opts.gitRoot === null || roots.includes(opts.gitRoot))
|
|
463
|
+
return report;
|
|
464
|
+
const skipped = describeOutcome({ kind: 'skipped', root: opts.gitRoot, reason: 'git-root-timed-out' });
|
|
465
|
+
return { ...report, lines: [...report.lines, ...skipped] };
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* The ledger root of the current repository for init's own move and drain
|
|
469
|
+
* (D-LEDGER-MAIN-WORKTREE): one lookup from the working directory, bounded by
|
|
470
|
+
* LEDGER_ROOT_TIMEOUT_MS like the HUD's and the step's lookups, so a repository on a
|
|
471
|
+
* hung mount costs a bounded wait and never stalls init. A lookup that timed out is no
|
|
472
|
+
* root (`root` null, `timedOut` true), so the caller acts on nothing and says so.
|
|
473
|
+
* Outside a git repository (`gitRoot` null) nothing is asked.
|
|
474
|
+
*/
|
|
475
|
+
export async function lookupOwnLedgerRoot(gitRoot, lookup = lookupLedgerRoot) {
|
|
476
|
+
if (gitRoot === null)
|
|
477
|
+
return { root: null, timedOut: false };
|
|
478
|
+
const found = await lookup(undefined, { timeoutMs: LEDGER_ROOT_TIMEOUT_MS });
|
|
479
|
+
return { root: found.kind === 'root' ? found.root : null, timedOut: found.kind === 'timed-out' };
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* The ledger root init's disabled-feature drain acts on, and the one line to print when
|
|
483
|
+
* there is none because git did not answer in time. Asks only when the drain can run: curation
|
|
484
|
+
* is off, in a git repository, and the manifest holding the switch was written. With curation
|
|
485
|
+
* on there is nothing to drain, and with no manifest written the drain does not run, so a
|
|
486
|
+
* line that says it was not drained for want of the root would name the wrong cause.
|
|
487
|
+
*/
|
|
488
|
+
export async function lookupDrainLedgerRoot(opts, lookup = lookupLedgerRoot) {
|
|
489
|
+
if (opts.curationEnabled || opts.gitRoot === null || !opts.manifestWritten)
|
|
490
|
+
return { ledgerRoot: null, warning: null };
|
|
491
|
+
const own = await lookupOwnLedgerRoot(opts.gitRoot, lookup);
|
|
492
|
+
const warning = own.timedOut
|
|
493
|
+
? `The curation queue was not drained for ${describeMoveText(opts.gitRoot)}: git did not answer in time. The next init tries again.`
|
|
494
|
+
: null;
|
|
495
|
+
return { ledgerRoot: own.root, warning };
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Drain this repo's memory and curation queues for each feature init switched
|
|
432
499
|
* off, so stale turns are not processed on a future re-enable — the same drains
|
|
433
|
-
* `devflow memory --disable` and `devflow
|
|
500
|
+
* `devflow memory --disable` and `devflow curation --disable` perform (the curation
|
|
501
|
+
* drain also removes the pending rows of the backups a move left). Other
|
|
434
502
|
* repos' queues are inert: every gate reads the machine-wide switch, so nothing
|
|
435
503
|
* appends to or processes them while the feature is off.
|
|
436
504
|
*
|
|
@@ -443,25 +511,34 @@ export async function persistManifestThenConvergeTracker(opts) {
|
|
|
443
511
|
* everywhere, so its queue is live and is left alone.
|
|
444
512
|
*
|
|
445
513
|
* D-LEDGER-MAIN-WORKTREE: each queue drains where the hooks write it — memory at
|
|
446
|
-
* this checkout's toplevel (`gitRoot`),
|
|
447
|
-
*
|
|
514
|
+
* this checkout's toplevel (`gitRoot`), curation at the ledger root (`ledgerRoot`,
|
|
515
|
+
* lookupOwnLedgerRoot), which in a linked worktree is the main checkout.
|
|
448
516
|
*
|
|
449
517
|
* Returns one warning for each drain refused because a folder on the way to its
|
|
450
|
-
* queue is a symbolic link (D-CLI-NO-SYMLINK)
|
|
518
|
+
* queue is a symbolic link (D-CLI-NO-SYMLINK), one for a curation drain that
|
|
519
|
+
* could not list the backups (the live queue is drained then), one for a curation
|
|
520
|
+
* drain that left the project alone because git tracks a data folder or did not answer
|
|
521
|
+
* (the fixed notice of a paused curation area), and one for a memory drain that left the project
|
|
522
|
+
* alone because git tracks the memory folder or did not answer (the fixed notice of a
|
|
523
|
+
* paused memory area); the caller prints them. When the move step has printed its own
|
|
524
|
+
* notice of a skipped move for the ledger root (`shownSkip`, its reason), the curation
|
|
525
|
+
* drain's line is only the short sentence that the queue was not drained.
|
|
451
526
|
*/
|
|
452
|
-
export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue,
|
|
527
|
+
export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue, drainCurationQueue: drainDisabledCurationQueue }) {
|
|
453
528
|
if (!opts.manifestWritten)
|
|
454
529
|
return [];
|
|
455
530
|
const refused = [];
|
|
456
531
|
if (!opts.memoryEnabled && opts.gitRoot !== null) {
|
|
457
532
|
const memory = await io.drainMemoryQueue(opts.gitRoot);
|
|
458
533
|
if (!memory.drained)
|
|
459
|
-
refused.push(
|
|
534
|
+
refused.push(formatMemoryNotDrained(memory));
|
|
460
535
|
}
|
|
461
|
-
if (!opts.
|
|
462
|
-
const
|
|
463
|
-
if (!
|
|
464
|
-
refused.push(
|
|
536
|
+
if (!opts.curationEnabled && opts.ledgerRoot !== null) {
|
|
537
|
+
const curation = await io.drainCurationQueue(opts.ledgerRoot);
|
|
538
|
+
if (!curation.drained)
|
|
539
|
+
refused.push(formatCurationNotDrained(curation, opts.shownSkip ?? null));
|
|
540
|
+
else if (curation.warning !== undefined)
|
|
541
|
+
refused.push(curation.warning);
|
|
465
542
|
}
|
|
466
543
|
return refused;
|
|
467
544
|
}
|
|
@@ -472,18 +549,23 @@ export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue,
|
|
|
472
549
|
* nothing else, so the manifest keeps every recorded value — plugins, version,
|
|
473
550
|
* installedAt and every feature — and only `features.hud` turns on. The
|
|
474
551
|
* earlier shape rewrote the whole record as a HUD-only fresh install (ambient,
|
|
475
|
-
* memory,
|
|
552
|
+
* memory, curation, knowledge, rules and proxy all `false`, plugins `[]`) while
|
|
476
553
|
* leaving those features' artifacts on disk: the record stopped describing the
|
|
477
554
|
* machine, the next re-init seeded every feature off, and with
|
|
478
|
-
* memory/
|
|
555
|
+
* memory/curation/knowledge switched by the manifest alone
|
|
479
556
|
* (D-FEATURES-NARROW-ONLY) a HUD install would have really disabled them
|
|
480
557
|
* everywhere. `version` is kept too: --hud-only reinstalls no plugin, and a
|
|
481
558
|
* bumped version would make the next init skip the upgrade it still owes.
|
|
482
559
|
*
|
|
483
560
|
* With no prior manifest the result is the fresh HUD-only record: every other
|
|
484
|
-
* feature off, which is what is installed.
|
|
561
|
+
* feature off, which is what is installed. `readManifest()` also reads a manifest it
|
|
562
|
+
* refuses as no manifest, so `refusedGateLevel` carries the machine gate level found in
|
|
563
|
+
* the raw file (`readStoredGateLevel`): the fresh record keeps it exactly as stored,
|
|
564
|
+
* malformed and `null` included, and holds no key when it is `undefined`. Init never
|
|
565
|
+
* writes a gate level the user did not choose. It is ignored over a prior install,
|
|
566
|
+
* whose own level is already kept.
|
|
485
567
|
*/
|
|
486
|
-
export function buildHudOnlyManifest(existing, version, now) {
|
|
568
|
+
export function buildHudOnlyManifest(existing, version, now, refusedGateLevel) {
|
|
487
569
|
if (existing !== null) {
|
|
488
570
|
return {
|
|
489
571
|
...existing,
|
|
@@ -497,12 +579,13 @@ export function buildHudOnlyManifest(existing, version, now) {
|
|
|
497
579
|
scope: 'user',
|
|
498
580
|
features: {
|
|
499
581
|
ambient: false, memory: false, hud: true, knowledge: false,
|
|
500
|
-
|
|
582
|
+
curation: false, rules: false, flags: {}, proxy: false,
|
|
501
583
|
behavior: false,
|
|
502
584
|
compliance: { enabled: false, frameworks: [] },
|
|
503
585
|
// The exported default, not a literal — the one constant every other
|
|
504
586
|
// module reads, so a moved default moves here too.
|
|
505
587
|
tracker: { provider: DEFAULT_TRACKER_PROVIDER },
|
|
588
|
+
...(refusedGateLevel !== undefined ? { gateLevel: refusedGateLevel } : {}),
|
|
506
589
|
},
|
|
507
590
|
installedAt: now,
|
|
508
591
|
updatedAt: now,
|
|
@@ -613,8 +696,8 @@ export const initCommand = new Command('init')
|
|
|
613
696
|
.option('--no-hud', 'Disable HUD status line')
|
|
614
697
|
.option('--knowledge', 'Enable feature knowledge bases')
|
|
615
698
|
.option('--no-knowledge', 'Disable feature knowledge bases')
|
|
616
|
-
.option('--
|
|
617
|
-
.option('--no-
|
|
699
|
+
.option('--curation', 'Enable curation (decision/pitfall tracking)')
|
|
700
|
+
.option('--no-curation', 'Disable curation (decision/pitfall tracking)')
|
|
618
701
|
.option('--rules', 'Enable rules (always-on engineering principles)')
|
|
619
702
|
.option('--no-rules', 'Disable rules')
|
|
620
703
|
.option('--proxy', 'Enable external model routing (GPT models via your OpenAI/Codex subscription)')
|
|
@@ -624,6 +707,7 @@ export const initCommand = new Command('init')
|
|
|
624
707
|
.option('--compliance <list>', 'Enable compliance with comma-separated framework IDs (e.g., gdpr,hipaa)')
|
|
625
708
|
.option('--no-compliance', 'Disable compliance (removes the rule; the skill and framework references stay installed; frameworks remembered for re-enable)')
|
|
626
709
|
.option('--tracker <id>', 'The machine\'s default issue tracker provider: github, jira, or linear')
|
|
710
|
+
.option('--gate-level <full|standard|lean>', 'The machine\'s default gate level: full, standard, or lean (skips the gate level prompt)')
|
|
627
711
|
.option('--security <mode>', 'Security deny list location: user, managed, or none', /^(user|managed|none)$/i)
|
|
628
712
|
.option('--hud-only', 'Install only the HUD (no plugins, hooks, or extras)')
|
|
629
713
|
.option('--recommended', 'Apply recommended defaults after plugin selection (skip advanced prompts)')
|
|
@@ -649,6 +733,19 @@ export const initCommand = new Command('init')
|
|
|
649
733
|
p.log.error('--reset and --plugin are mutually exclusive. Use --reset alone to restore defaults, or --plugin to update a specific plugin.');
|
|
650
734
|
process.exit(1);
|
|
651
735
|
}
|
|
736
|
+
// Parse --gate-level at the boundary, before anything is written (--hud-only writes early).
|
|
737
|
+
// Strict — reject, never repair — so a typo'd level exits here rather than being ignored.
|
|
738
|
+
let cliGateLevel;
|
|
739
|
+
{
|
|
740
|
+
const gateLevelResult = resolveGateLevelInitState(options.gateLevel, !!options.hudOnly);
|
|
741
|
+
if (gateLevelResult !== undefined) {
|
|
742
|
+
if (!gateLevelResult.ok) {
|
|
743
|
+
p.log.error(gateLevelResult.error);
|
|
744
|
+
process.exit(1);
|
|
745
|
+
}
|
|
746
|
+
cliGateLevel = gateLevelResult.value;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
652
749
|
// D-SCOPE-RETIRED: refuse a retired --scope value before anything is written.
|
|
653
750
|
const scopeDecision = resolveRetiredScopeOption(options.scope);
|
|
654
751
|
if (scopeDecision.kind === 'refuse') {
|
|
@@ -700,10 +797,25 @@ export const initCommand = new Command('init')
|
|
|
700
797
|
existingHudManifest = await readManifest(devflowDir);
|
|
701
798
|
}
|
|
702
799
|
catch { /* absent on fresh install — existingHudManifest stays null */ }
|
|
800
|
+
// A manifest readManifest() refuses reads as none, but its machine gate level is still
|
|
801
|
+
// stored there: --hud-only keeps it and says so, as the full init does. --reset does not
|
|
802
|
+
// apply here, so nothing asks to forget it.
|
|
803
|
+
const hudStoredGate = await readStoredGateLevel(devflowDir, existingHudManifest, false);
|
|
804
|
+
let hudManifestWritten = false;
|
|
703
805
|
try {
|
|
704
|
-
await writeManifest(devflowDir, buildHudOnlyManifest(existingHudManifest, version, new Date().toISOString()));
|
|
806
|
+
await writeManifest(devflowDir, buildHudOnlyManifest(existingHudManifest, version, new Date().toISOString(), hudStoredGate.value));
|
|
807
|
+
hudManifestWritten = true;
|
|
705
808
|
}
|
|
706
809
|
catch { /* non-fatal */ }
|
|
810
|
+
if (hudStoredGate.origin !== 'manifest') {
|
|
811
|
+
logSummaryLines(formatGateLevelSummary({
|
|
812
|
+
level: hudStoredGate.value,
|
|
813
|
+
priorLevel: hudStoredGate.value,
|
|
814
|
+
priorOrigin: hudStoredGate.origin,
|
|
815
|
+
kept: true,
|
|
816
|
+
manifestWritten: hudManifestWritten,
|
|
817
|
+
}));
|
|
818
|
+
}
|
|
707
819
|
const hudOnlySkew = formatHudOnlySkew(existingHudManifest?.version ?? null, await readRunningVersion());
|
|
708
820
|
if (hudOnlySkew !== null)
|
|
709
821
|
p.log.warn(hudOnlySkew);
|
|
@@ -718,6 +830,15 @@ export const initCommand = new Command('init')
|
|
|
718
830
|
existingManifest = await readManifest(devflowDir, { onWarning: (message) => p.log.warn(message) });
|
|
719
831
|
}
|
|
720
832
|
catch { /* unreadable manifest — seeded as a fresh install */ }
|
|
833
|
+
// The stored machine gate level, and where it was found. readManifest() reads a manifest it
|
|
834
|
+
// refuses (a missing required field, a byte-order mark, unparseable JSON) as no manifest, so
|
|
835
|
+
// the seed holds no level for it, yet `devflow gates` and the settings resolver still read
|
|
836
|
+
// that file's level. Init never writes a level the user did not choose, so when readManifest()
|
|
837
|
+
// returned none the raw file decides: a level found in it is the level the run replaces, and
|
|
838
|
+
// is kept as stored unless --reset asks to forget it. A file that cannot be read at all is
|
|
839
|
+
// reported as "unreadable". This is an init-side override; the seed itself still comes from
|
|
840
|
+
// the manifest alone.
|
|
841
|
+
const storedGate = await readStoredGateLevel(devflowDir, existingManifest, !!options.reset);
|
|
721
842
|
// D-INIT-NOT-HOME (same-location.ts): a repository rooted at HOME is no
|
|
722
843
|
// project, so init treats it as no repository — no .devflow/config.json (that
|
|
723
844
|
// would be the machine root's), no .claudeignore, no .gitignore block, no
|
|
@@ -935,7 +1056,7 @@ export const initCommand = new Command('init')
|
|
|
935
1056
|
let memoryEnabled = seed.features.memory;
|
|
936
1057
|
let hudEnabled = seed.features.hud;
|
|
937
1058
|
let knowledgeEnabled = seed.features.knowledge;
|
|
938
|
-
let
|
|
1059
|
+
let curationEnabled = seed.features.curation;
|
|
939
1060
|
let rulesEnabled = seed.features.rules;
|
|
940
1061
|
// proxy: Advanced-only; Recommended path carries seed value unchanged.
|
|
941
1062
|
// Fresh installs → false (FEATURE_DEFAULTS.proxy). Re-inits → prior manifest value.
|
|
@@ -956,11 +1077,21 @@ export const initCommand = new Command('init')
|
|
|
956
1077
|
// alone, so init carries the prior value through untouched: it stamps the installed agents
|
|
957
1078
|
// with it and writes it back to the manifest. Absent stays absent (the default list).
|
|
958
1079
|
const trackerServers = seed.features.tracker.servers;
|
|
959
|
-
// The machine gate level (features.gateLevel)
|
|
960
|
-
//
|
|
961
|
-
//
|
|
962
|
-
//
|
|
963
|
-
|
|
1080
|
+
// The machine gate level (features.gateLevel), shared with `devflow gates --set`. The seed holds
|
|
1081
|
+
// it raw: `undefined` only for an absent key or a --reset seed, a malformed value or `null` as
|
|
1082
|
+
// stored. When readManifest() returned none, the raw level (`storedGate`, read above) stands in
|
|
1083
|
+
// for the seed, as `carriedGateLevel` decides. It stays raw unless --gate-level or the wizard
|
|
1084
|
+
// step sets it, so a plain init never defaults, narrows or heals it; the full manifest write
|
|
1085
|
+
// below then leaves an absent key out. Each wizard path assigns the decision `runGateLevelStepAt`
|
|
1086
|
+
// makes; that is the only precedence site (--gate-level, then the wizard answer, then this
|
|
1087
|
+
// carried level), and `carried` says whether the level is still the carried one.
|
|
1088
|
+
const carriedGate = carriedGateLevel({
|
|
1089
|
+
manifest: existingManifest,
|
|
1090
|
+
reset: !!options.reset,
|
|
1091
|
+
stored: storedGate,
|
|
1092
|
+
seedLevel: seed.features.gateLevel,
|
|
1093
|
+
});
|
|
1094
|
+
let gateDecision = { level: carriedGate.value, carried: true };
|
|
964
1095
|
let enabledFlags = { ...seed.flags };
|
|
965
1096
|
// viewModeExplicit: true when --reset is passed; signals resolveFinalViewMode to let the
|
|
966
1097
|
// seed-time view-mode win over an externally-set value in settings.json.
|
|
@@ -1018,6 +1149,31 @@ export const initCommand = new Command('init')
|
|
|
1018
1149
|
}
|
|
1019
1150
|
return trackerStep.state;
|
|
1020
1151
|
};
|
|
1152
|
+
/**
|
|
1153
|
+
* Decide the machine gate level for one wizard path: --gate-level, else the wizard step
|
|
1154
|
+
* (gated on modePromptShown), else the stored value untouched. The step's outcome line is
|
|
1155
|
+
* the post-install gate level summary row, on both paths.
|
|
1156
|
+
*
|
|
1157
|
+
* Returns the decision, the level raw as `resolveGateLevelForInit` returns it and whether it
|
|
1158
|
+
* is the carried stored level. The caller assigns it to `gateDecision`: that value is the
|
|
1159
|
+
* only precedence decision, and nothing after the call revises it.
|
|
1160
|
+
*/
|
|
1161
|
+
const runGateLevelStepAt = async (mode) => {
|
|
1162
|
+
const outcome = await resolveGateLevelForInit({
|
|
1163
|
+
cliLevel: cliGateLevel,
|
|
1164
|
+
stored: gateDecision.level,
|
|
1165
|
+
storedOrigin: carriedGate.origin,
|
|
1166
|
+
mode,
|
|
1167
|
+
modePromptShown,
|
|
1168
|
+
isTTY: process.stdin.isTTY,
|
|
1169
|
+
prompts: buildClackGateLevelPrompts(),
|
|
1170
|
+
});
|
|
1171
|
+
if (outcome.kind === 'cancelled') {
|
|
1172
|
+
p.cancel('Installation cancelled.');
|
|
1173
|
+
process.exit(0);
|
|
1174
|
+
}
|
|
1175
|
+
return { level: outcome.level, carried: outcome.carried };
|
|
1176
|
+
};
|
|
1021
1177
|
// Safe-delete detection (both paths need this)
|
|
1022
1178
|
const platform = detectPlatform();
|
|
1023
1179
|
const shell = detectShell();
|
|
@@ -1057,6 +1213,9 @@ export const initCommand = new Command('init')
|
|
|
1057
1213
|
// Tracker line via formatTrackerSummary, so the step's own outcome line
|
|
1058
1214
|
// would be a duplicate.
|
|
1059
1215
|
const wizardTracker = await runTrackerStepAt('recommended', seed.features.tracker.provider, false);
|
|
1216
|
+
// Gate level wizard step — same gate; --recommended and !isTTY keep the stored value.
|
|
1217
|
+
// The decision is final here: applyCliToggles below carries no gate level.
|
|
1218
|
+
gateDecision = await runGateLevelStepAt('recommended');
|
|
1060
1219
|
// No attribution step here: the suppress-attribution question is Advanced-only (D27).
|
|
1061
1220
|
// Recommended silently carries the seeded value in enabledFlags — fresh installs get
|
|
1062
1221
|
// the registry default (off), re-inits get prior state. See shouldRunAttributionStep.
|
|
@@ -1068,7 +1227,7 @@ export const initCommand = new Command('init')
|
|
|
1068
1227
|
memory: options.memory,
|
|
1069
1228
|
hud: options.hud,
|
|
1070
1229
|
knowledge: options.knowledge,
|
|
1071
|
-
|
|
1230
|
+
curation: options.curation,
|
|
1072
1231
|
rules: options.rules,
|
|
1073
1232
|
proxy: options.proxy,
|
|
1074
1233
|
behavior: options.behavior,
|
|
@@ -1080,7 +1239,7 @@ export const initCommand = new Command('init')
|
|
|
1080
1239
|
memoryEnabled = effectiveFeatures.memory;
|
|
1081
1240
|
hudEnabled = effectiveFeatures.hud;
|
|
1082
1241
|
knowledgeEnabled = effectiveFeatures.knowledge;
|
|
1083
|
-
|
|
1242
|
+
curationEnabled = effectiveFeatures.curation;
|
|
1084
1243
|
rulesEnabled = effectiveFeatures.rules;
|
|
1085
1244
|
proxyEnabled = effectiveFeatures.proxy;
|
|
1086
1245
|
behaviorEnabled = effectiveFeatures.behavior;
|
|
@@ -1119,7 +1278,7 @@ export const initCommand = new Command('init')
|
|
|
1119
1278
|
const summaryLines = [
|
|
1120
1279
|
`Ambient mode: ${ambientEnabled ? 'enabled' : 'disabled'}`,
|
|
1121
1280
|
`Working memory: ${memoryEnabled ? 'enabled' : 'disabled'}`,
|
|
1122
|
-
`
|
|
1281
|
+
`Curation: ${curationEnabled ? 'enabled' : 'disabled'}`,
|
|
1123
1282
|
`Rules: ${rulesEnabled ? 'enabled' : 'disabled'}`,
|
|
1124
1283
|
`HUD: ${hudEnabled ? 'enabled' : 'disabled'}`,
|
|
1125
1284
|
`Knowledge bases: ${knowledgeEnabled ? 'enabled' : 'disabled'}`,
|
|
@@ -1130,6 +1289,9 @@ export const initCommand = new Command('init')
|
|
|
1130
1289
|
// tracker step's ONLY surface on this path — both surfaces or it is
|
|
1131
1290
|
// invisible on one path.
|
|
1132
1291
|
`Tracker: ${formatTrackerSummary(trackerProvider)}`,
|
|
1292
|
+
// The level this run will record, or the stored one it keeps; the summary after the
|
|
1293
|
+
// install reports what the run really recorded.
|
|
1294
|
+
`Gate level: ${formatGateLevelRow(gateDecision.level, carriedGate.origin)}`,
|
|
1133
1295
|
`View mode: ${readViewMode(enabledFlags)}`,
|
|
1134
1296
|
`Claude Code flags: ${defaultFlagCount} configured`,
|
|
1135
1297
|
`.claudeignore: ${resolveClaudeignoreOutcome(claudeignorePresent)}`,
|
|
@@ -1216,22 +1378,22 @@ export const initCommand = new Command('init')
|
|
|
1216
1378
|
}
|
|
1217
1379
|
knowledgeEnabled = knowledgeChoice;
|
|
1218
1380
|
}
|
|
1219
|
-
if (options.
|
|
1220
|
-
|
|
1381
|
+
if (options.curation !== undefined) {
|
|
1382
|
+
curationEnabled = options.curation;
|
|
1221
1383
|
}
|
|
1222
1384
|
else {
|
|
1223
1385
|
p.note('Detects architectural decisions and pitfalls from your session\n' +
|
|
1224
1386
|
'dialogs. Runs a background agent on session stop that consumes\n' +
|
|
1225
|
-
'additional tokens. Applies to every project.', '
|
|
1226
|
-
const
|
|
1227
|
-
message: 'Enable
|
|
1228
|
-
initialValue: seed.features.
|
|
1387
|
+
'additional tokens. Applies to every project.', 'Curation (Decision/Pitfall Tracking)');
|
|
1388
|
+
const curationChoice = await p.confirm({
|
|
1389
|
+
message: 'Enable curation? (Recommended)',
|
|
1390
|
+
initialValue: seed.features.curation,
|
|
1229
1391
|
});
|
|
1230
|
-
if (p.isCancel(
|
|
1392
|
+
if (p.isCancel(curationChoice)) {
|
|
1231
1393
|
p.cancel('Installation cancelled.');
|
|
1232
1394
|
process.exit(0);
|
|
1233
1395
|
}
|
|
1234
|
-
|
|
1396
|
+
curationEnabled = curationChoice;
|
|
1235
1397
|
}
|
|
1236
1398
|
if (options.rules !== undefined) {
|
|
1237
1399
|
rulesEnabled = options.rules;
|
|
@@ -1361,6 +1523,8 @@ export const initCommand = new Command('init')
|
|
|
1361
1523
|
else
|
|
1362
1524
|
p.log.info(overrideLine.text);
|
|
1363
1525
|
}
|
|
1526
|
+
// Gate level wizard step — same gate as the Recommended path; re-init lands here.
|
|
1527
|
+
gateDecision = await runGateLevelStepAt('advanced');
|
|
1364
1528
|
// Attribution feature (after compliance, before flags). This is the ONLY call site —
|
|
1365
1529
|
// the attribution question is Advanced-only (D27); the Recommended path never asks and
|
|
1366
1530
|
// silently carries the seeded value. The gate stays an explicit predicate call so the
|
|
@@ -1645,7 +1809,7 @@ export const initCommand = new Command('init')
|
|
|
1645
1809
|
// The SETTLED switch, after the flag, the prompt and the seed have all had
|
|
1646
1810
|
// their say (D-LEARNING-VARIANT-INSTALL): the files installed here and the
|
|
1647
1811
|
// manifest written at the end of init cannot disagree about it.
|
|
1648
|
-
|
|
1812
|
+
curation: curationEnabled,
|
|
1649
1813
|
trackerServers,
|
|
1650
1814
|
// Non-fatal install notices with no other channel (skipped symlinks in the
|
|
1651
1815
|
// generated reference tree, mode-normalisation failures) reach the user rather
|
|
@@ -1658,7 +1822,7 @@ export const initCommand = new Command('init')
|
|
|
1658
1822
|
p.log.error(`${error}`);
|
|
1659
1823
|
process.exit(1);
|
|
1660
1824
|
}
|
|
1661
|
-
// D-LEARNING-VARIANT-INSTALL: converge the
|
|
1825
|
+
// D-LEARNING-VARIANT-INSTALL: converge the curation variants. The file copy above
|
|
1662
1826
|
// installed the variant of everything IT installed; on a `--plugin` install the
|
|
1663
1827
|
// other plugins' files, already on disk, may still be the other variant (this run
|
|
1664
1828
|
// may have flipped the switch), and the apply-decisions skill is removed here when
|
|
@@ -1666,21 +1830,21 @@ export const initCommand = new Command('init')
|
|
|
1666
1830
|
// Warn-not-abort: either mismatch is safe (an on variant is still gated at run
|
|
1667
1831
|
// time, an off variant loads no decisions). The agent mapping is reapplied below.
|
|
1668
1832
|
try {
|
|
1669
|
-
const
|
|
1833
|
+
const curationConverge = await convergeCurationVariants({
|
|
1670
1834
|
claudeDir,
|
|
1671
1835
|
devflowDir,
|
|
1672
|
-
|
|
1836
|
+
curation: curationEnabled,
|
|
1673
1837
|
plugins: effectivePlugins,
|
|
1674
1838
|
warn: (msg) => installWarnings.push(msg),
|
|
1675
1839
|
});
|
|
1676
|
-
if (verbose &&
|
|
1677
|
-
p.log.info(`
|
|
1678
|
-
`${
|
|
1679
|
-
`rewritten, ${
|
|
1840
|
+
if (verbose && curationConverge.converged) {
|
|
1841
|
+
p.log.info(`Curation ${curationEnabled ? 'on' : 'off'} variants: ` +
|
|
1842
|
+
`${curationConverge.commandsRewritten.length} command(s) and ${curationConverge.agentsRewritten.length} agent(s) ` +
|
|
1843
|
+
`rewritten, ${curationConverge.unchanged} already current`);
|
|
1680
1844
|
}
|
|
1681
1845
|
}
|
|
1682
1846
|
catch (err) {
|
|
1683
|
-
installWarnings.push(`
|
|
1847
|
+
installWarnings.push(`Curation variant convergence failed — ${err instanceof Error ? err.message : String(err)}`);
|
|
1684
1848
|
}
|
|
1685
1849
|
// Converge compliance artifacts (always converge, never short-circuit).
|
|
1686
1850
|
// Called unconditionally so that enabling/disabling compliance during init
|
|
@@ -1826,6 +1990,8 @@ export const initCommand = new Command('init')
|
|
|
1826
1990
|
'lib/staleness.cjs',
|
|
1827
1991
|
'dream-lock',
|
|
1828
1992
|
'decisions-usage-scan.cjs',
|
|
1993
|
+
'learning-lock',
|
|
1994
|
+
'lib/learning-store.cjs',
|
|
1829
1995
|
];
|
|
1830
1996
|
const hooksDir = path.join(devflowDir, 'scripts', 'hooks');
|
|
1831
1997
|
for (const legacy of LEGACY_HOOK_FILES) {
|
|
@@ -1835,6 +2001,10 @@ export const initCommand = new Command('init')
|
|
|
1835
2001
|
}
|
|
1836
2002
|
catch { /* doesn't exist */ }
|
|
1837
2003
|
}
|
|
2004
|
+
// The curation move (#529): after the legacy sweep, before the manifest persist and
|
|
2005
|
+
// the disabled-feature drain below, so the drain sees the moved folder. Its lines
|
|
2006
|
+
// and its one notice print with the install warnings, after the spinner stops.
|
|
2007
|
+
const curationMove = await moveCurationData({ discoveredProjects, gitRoot, homeDir, devflowDir });
|
|
1838
2008
|
// === Settings & hooks (all automatic based on collected choices) ===
|
|
1839
2009
|
s.message('Configuring settings');
|
|
1840
2010
|
await installSettings(claudeDir, rootDir, devflowDir, verbose);
|
|
@@ -2061,7 +2231,7 @@ export const initCommand = new Command('init')
|
|
|
2061
2231
|
'Manifest records intended state; run devflow init again to retry.');
|
|
2062
2232
|
}
|
|
2063
2233
|
// Write .devflow/config.json — facts about this repo. The machine switches for
|
|
2064
|
-
// memory/
|
|
2234
|
+
// memory/curation/knowledge are the manifest's; a hand-written `features`
|
|
2065
2235
|
// object here only narrows them and is carried, never written
|
|
2066
2236
|
// (D-FEATURES-NARROW-ONLY; the managed write drops the retired top-level
|
|
2067
2237
|
// per-repo keys). A managed
|
|
@@ -2278,6 +2448,10 @@ export const initCommand = new Command('init')
|
|
|
2278
2448
|
logSummaryLines(formatSkillScopeSummary(installReport, pluginListUnchanged));
|
|
2279
2449
|
for (const warning of installWarnings)
|
|
2280
2450
|
p.log.warn(warning);
|
|
2451
|
+
for (const line of curationMove.lines)
|
|
2452
|
+
p.log.warn(describeMoveText(line));
|
|
2453
|
+
if (curationMove.notice !== null)
|
|
2454
|
+
p.log.info(curationMove.notice);
|
|
2281
2455
|
const installedSet = new Set(pluginsToInstall.flatMap(p => p.commands).filter(c => c.length > 0));
|
|
2282
2456
|
const orderedCommands = WORKFLOW_ORDER.filter(cmd => installedSet.has(cmd));
|
|
2283
2457
|
if (orderedCommands.length > 0) {
|
|
@@ -2325,7 +2499,7 @@ export const initCommand = new Command('init')
|
|
|
2325
2499
|
memory: memoryEnabled,
|
|
2326
2500
|
hud: hudEnabled,
|
|
2327
2501
|
knowledge: knowledgeEnabled,
|
|
2328
|
-
|
|
2502
|
+
curation: curationEnabled,
|
|
2329
2503
|
rules: rulesEnabled,
|
|
2330
2504
|
// FlagsRecord written directly — key-presence encodes "known".
|
|
2331
2505
|
// view-mode is encoded as flags['view-mode'] (the resolved final value).
|
|
@@ -2345,8 +2519,9 @@ export const initCommand = new Command('init')
|
|
|
2345
2519
|
// through parseTrackerId (CLI), the typed wizard select, or the seed, which
|
|
2346
2520
|
// itself came through normalizeTrackerFeature on read.
|
|
2347
2521
|
tracker: { provider: trackerProvider, ...(trackerServers !== undefined ? { servers: trackerServers } : {}) },
|
|
2348
|
-
// The
|
|
2349
|
-
|
|
2522
|
+
// The machine gate level: --gate-level, the wizard answer, or the prior value raw
|
|
2523
|
+
// (see where `gateDecision` is read above).
|
|
2524
|
+
...(gateDecision.level !== undefined ? { gateLevel: gateDecision.level } : {}),
|
|
2350
2525
|
},
|
|
2351
2526
|
installedAt: existingManifest?.installedAt ?? now,
|
|
2352
2527
|
updatedAt: now,
|
|
@@ -2368,12 +2543,16 @@ export const initCommand = new Command('init')
|
|
|
2368
2543
|
p.log.info(msg.text);
|
|
2369
2544
|
}
|
|
2370
2545
|
// Only now that the machine-wide switch is on disk (D-INIT-DRAIN-AFTER-SWITCH).
|
|
2546
|
+
const drainRoot = await lookupDrainLedgerRoot({ gitRoot, curationEnabled, manifestWritten: trackerLifecycle.manifestWritten });
|
|
2547
|
+
if (drainRoot.warning !== null)
|
|
2548
|
+
p.log.warn(drainRoot.warning);
|
|
2371
2549
|
const refusedDrains = await drainDisabledFeatureQueues({
|
|
2372
2550
|
gitRoot,
|
|
2373
|
-
ledgerRoot:
|
|
2551
|
+
ledgerRoot: drainRoot.ledgerRoot,
|
|
2374
2552
|
memoryEnabled,
|
|
2375
|
-
|
|
2553
|
+
curationEnabled,
|
|
2376
2554
|
manifestWritten: trackerLifecycle.manifestWritten,
|
|
2555
|
+
shownSkip: shownSkipAt(curationMove, drainRoot.ledgerRoot),
|
|
2377
2556
|
});
|
|
2378
2557
|
for (const line of refusedDrains)
|
|
2379
2558
|
p.log.warn(line);
|
|
@@ -2401,6 +2580,14 @@ export const initCommand = new Command('init')
|
|
|
2401
2580
|
agent: trackerLifecycle.agent,
|
|
2402
2581
|
});
|
|
2403
2582
|
logSummaryLines(trackerLines);
|
|
2583
|
+
// The gate level this run recorded, from the manifest the run wrote (D-INIT-REAL-OUTCOME).
|
|
2584
|
+
logSummaryLines(formatGateLevelSummary({
|
|
2585
|
+
level: gateDecision.level,
|
|
2586
|
+
priorLevel: storedGate.value,
|
|
2587
|
+
priorOrigin: storedGate.origin,
|
|
2588
|
+
kept: gateDecision.carried && carriedGate.origin === 'rejected',
|
|
2589
|
+
manifestWritten: trackerLifecycle.manifestWritten,
|
|
2590
|
+
}));
|
|
2404
2591
|
// CLAUDE.md import audit (D-CLAUDE-MD-IMPORT-AUDIT, D-AUDIT-STAMP): after the install, on
|
|
2405
2592
|
// both paths, through claudeMdAuditStep. Global roots always; the project's roots when init
|
|
2406
2593
|
// runs inside a git repository that is not HOME, at its toplevel. The stamp is written here,
|