devflow-kit 3.6.0 → 3.8.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 +438 -1
- package/README.md +15 -11
- package/dist/agents/code.md +9 -15
- package/dist/agents/design.md +1 -1
- package/dist/agents/fix.md +11 -13
- package/dist/agents/git.md +1 -1
- package/dist/agents/harden.md +1 -1
- package/dist/agents/refactor.md +7 -10
- package/dist/agents/review.md +6 -4
- package/dist/agents/skim.md +2 -2
- package/dist/agents/triage.md +2 -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/{learning.js → curation.js} +302 -99
- package/dist/cli/commands/gate-level-prompts.js +426 -0
- package/dist/cli/commands/gates.js +299 -0
- package/dist/cli/commands/init-seed.js +16 -5
- package/dist/cli/commands/init.js +263 -54
- package/dist/cli/commands/memory.js +20 -2
- package/dist/cli/commands/prompt-io.js +37 -0
- package/dist/cli/commands/uninstall.js +81 -9
- package/dist/cli.js +4 -2
- package/dist/commands/bug-analysis.md +25 -36
- package/dist/commands/code-review.md +77 -73
- package/dist/commands/debug.md +5 -5
- package/dist/commands/dynamic-build.md +76 -83
- 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 +68 -102
- package/dist/commands/plan.md +19 -29
- package/dist/commands/release.md +5 -5
- package/dist/commands/research.md +6 -6
- package/dist/commands/resolve.md +29 -31
- package/dist/commands/self-review.md +5 -13
- 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 +36 -0
- package/dist/core/curation-move.js +35 -0
- package/dist/core/curation-queue-cleanup.js +134 -0
- package/dist/core/curation-store.js +98 -0
- package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
- package/dist/core/echo-value.js +48 -0
- package/dist/core/evidence-policy.js +18 -6
- package/dist/core/feature-switch.js +95 -19
- package/dist/core/fs-atomic.js +1 -1
- package/dist/core/gate-level.js +221 -0
- 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 +84 -24
- package/dist/core/migrations.js +1 -13
- package/dist/core/model-discovery.js +7 -1
- package/dist/core/observations.js +2 -2
- package/dist/core/plugins.js +15 -15
- 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 +35 -0
- package/dist/core/tracker.js +6 -34
- package/dist/{learning-off → curation-off}/agents/code.md +9 -15
- package/dist/{learning-off → curation-off}/agents/fix.md +11 -13
- package/dist/{learning-off → curation-off}/agents/refactor.md +7 -10
- package/dist/{learning-off → curation-off}/agents/review.md +5 -3
- package/dist/{learning-off → curation-off}/agents/triage.md +1 -1
- package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
- package/dist/{learning-off → curation-off}/commands/code-review.md +74 -70
- package/dist/{learning-off → curation-off}/commands/debug.md +2 -2
- package/dist/{learning-off → curation-off}/commands/dynamic-build.md +73 -80
- package/dist/{learning-off → curation-off}/commands/explore.md +2 -2
- package/dist/{learning-off → curation-off}/commands/implement.md +65 -99
- package/dist/{learning-off → curation-off}/commands/plan.md +15 -25
- package/dist/{learning-off → curation-off}/commands/resolve.md +26 -28
- package/dist/{learning-off → curation-off}/commands/self-review.md +2 -10
- package/dist/hud/components/{learning-counts.js → curation-counts.js} +36 -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/validate-branch.md +8 -5
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +20 -14
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +20 -20
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +20 -20
- package/dist/targets/claude-code/{learning-install.js → curation-install.js} +99 -67
- 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 +16 -17
- package/src/assets/agents/code.mds +4 -7
- package/src/assets/agents/curate.md +12 -14
- package/src/assets/agents/debug.mds +6 -6
- package/src/assets/agents/design.mds +18 -18
- package/src/assets/agents/fix.mds +6 -5
- package/src/assets/agents/git.mds +1 -1
- 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 +45 -43
- package/src/assets/agents/skim.mds +11 -11
- package/src/assets/agents/synthesize.md +2 -2
- package/src/assets/agents/triage.mds +22 -22
- package/src/assets/agents/validate.md +13 -26
- package/src/assets/commands/_partials/_decisions.mds +13 -13
- 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 +17 -24
- package/src/assets/commands/bug-analysis.mds +37 -48
- package/src/assets/commands/code-review.mds +90 -86
- package/src/assets/commands/debug.mds +14 -14
- package/src/assets/commands/dynamic-build.mds +78 -78
- 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 +92 -118
- package/src/assets/commands/plan.mds +41 -51
- package/src/assets/commands/release.mds +12 -12
- package/src/assets/commands/research.mds +19 -19
- package/src/assets/commands/resolve.mds +56 -50
- package/src/assets/commands/self-review.mds +20 -20
- package/src/assets/mds/git/_ops.mds +3 -1
- package/src/assets/mds/git/_ops_pr.mds +1 -1
- package/src/assets/mds/git/_ops_steps.mds +13 -7
- package/src/assets/mds/git/_pr.mds +7 -4
- package/src/assets/mds/tracker/_common.mds +23 -12
- package/src/assets/mds/tracker/_github.mds +2 -9
- package/src/assets/mds/tracker/_jira.mds +2 -15
- package/src/assets/mds/tracker/_linear.mds +2 -15
- package/src/assets/rules/quality.md +1 -0
- package/src/assets/scripts/ci-wait.cjs +22 -4
- package/src/assets/scripts/claude-md-audit.cjs +66 -51
- package/src/assets/scripts/hooks/background-memory-update +406 -97
- 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 +145 -51
- package/src/assets/scripts/hooks/json-parse +19 -4
- package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
- package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +242 -118
- 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 +34 -22
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +259 -0
- package/src/assets/scripts/hooks/memory-permission +358 -0
- package/src/assets/scripts/hooks/memory-worker +1 -1
- package/src/assets/scripts/hooks/pre-compact-memory +6 -16
- package/src/assets/scripts/hooks/queue-append +228 -103
- package/src/assets/scripts/hooks/resolve-project-root +2 -2
- package/src/assets/scripts/hooks/session-start-context +145 -71
- package/src/assets/scripts/hooks/session-start-memory +27 -46
- package/src/assets/scripts/lib/project-config.cjs +31 -9
- package/src/assets/scripts/resolve-base.cjs +1043 -0
- package/src/assets/scripts/resolve-settings.cjs +238 -46
- package/src/assets/skills/agent-references/SKILL.md +9 -2
- package/src/assets/skills/agent-references/references/commands/code-review-levels.md +19 -0
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +27 -0
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
- package/src/assets/skills/agent-references/references/commands/plan-levels.md +40 -0
- package/src/assets/skills/agent-references/references/commands/resolve-levels.md +24 -0
- package/src/assets/skills/agent-references/references/synthesize/planning.md +9 -22
- package/src/assets/skills/agent-references/references/synthesize/review.md +11 -11
- 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/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,12 @@ 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';
|
|
16
|
+
import { describeEchoedValue } from '../../core/echo-value.js';
|
|
12
17
|
import { installViaFileCopy, composeScripts } from '../../targets/claude-code/installer.js';
|
|
13
18
|
import { formatOverlaySummary, formatSkillScopeSummary, formatTrackerAssetSummary, isPluginListUnchanged } from './install-report.js';
|
|
14
19
|
import { convergeTrackerArtifacts } from '../../targets/claude-code/tracker-install.js';
|
|
@@ -18,7 +23,7 @@ import { LEGACY_SKILL_NAMES } from '../../targets/claude-code/legacy.js';
|
|
|
18
23
|
import { detectPlatform, detectShell, getProfilePath, getSafeDeleteInfo, hasSafeDelete } from '../../core/safe-delete.js';
|
|
19
24
|
import { generateSafeDeleteBlock, installToProfile, removeFromProfile, getInstalledVersion, SAFE_DELETE_BLOCK_VERSION } from '../../core/safe-delete-install.js';
|
|
20
25
|
import { convergeAmbientHooks } from './ambient.js';
|
|
21
|
-
import { convergeMemoryHooks, drainMemoryQueue } from './memory.js';
|
|
26
|
+
import { convergeMemoryHooks, drainMemoryQueue, workingMemoryNote } from './memory.js';
|
|
22
27
|
import { addCaptureHooks, removeCaptureHooks } from './capture.js';
|
|
23
28
|
import { removeDreamHook } from './legacy-hooks.js';
|
|
24
29
|
import { addProxyHooks, removeProxyHooks, applyProxyEnv, stripProxyEnv, runProxyPreflight, buildRealPreflightDeps } from './proxy.js';
|
|
@@ -33,7 +38,7 @@ import { convergeFlagsIntoSettings, countActiveFlags, readViewMode } from '../..
|
|
|
33
38
|
import { addContextHook, removeContextHook, hasContextHook } from './context.js';
|
|
34
39
|
import { writeSettingsFileAtomic } from '../../core/fs-atomic.js';
|
|
35
40
|
import { writeManagedConfig, readConfigIfPresent, DEFAULT_CONFIG } from '../../core/feature-config.js';
|
|
36
|
-
import {
|
|
41
|
+
import { drainDisabledCurationQueue, formatCurationNotDrained } from '../../core/curation-queue-cleanup.js';
|
|
37
42
|
import { formatRefusedDrain } from '../../core/queue-drain.js';
|
|
38
43
|
import { removeManagedDenyList, describeManagedDenyRemoval } from './security.js';
|
|
39
44
|
import { resolveInitSeed, applyCliToggles, resolveResetGatedInputs, resolvePluginsToInstall } from './init-seed.js';
|
|
@@ -43,9 +48,10 @@ import { shouldRunBehaviorStep, runBehaviorStep, buildClackBehaviorPrompts } fro
|
|
|
43
48
|
import { convergeBehaviorFromManifest } from '../../targets/claude-code/behavior-install.js';
|
|
44
49
|
import { applyTrackerSentinel, effectiveTrackerServers, parseTrackerId, rearmTrackerInference, DEFAULT_TRACKER_PROVIDER, } from '../../core/tracker.js';
|
|
45
50
|
import { formatTrackerSummary, shouldRunTrackerStep, runTrackerStep, buildClackTrackerPrompts, } from './tracker-prompts.js';
|
|
51
|
+
import { buildClackGateLevelPrompts, carriedGateLevel, formatGateLevelRow, formatGateLevelSummary, readStoredGateLevel, resolveGateLevelForInit, resolveGateLevelInitState, } from './gate-level-prompts.js';
|
|
46
52
|
import { shouldRunAttributionStep, runAttributionStep, buildClackAttributionPrompts, applyAttributionAnswer, attributionSeedFrom, } from './attribution-prompts.js';
|
|
47
53
|
import { convergeFromManifest } from '../../targets/claude-code/compliance-install.js';
|
|
48
|
-
import {
|
|
54
|
+
import { convergeCurationVariants, installedVersionMatches, readRunningVersion } from '../../targets/claude-code/curation-install.js';
|
|
49
55
|
import { auditAndRecordClaudeMd, formatClaudeMdAuditNote, formatClaudeMdAuditUnavailable, } from '../../core/claude-md-audit.js';
|
|
50
56
|
import * as os from 'os';
|
|
51
57
|
// Re-export pure functions for tests (canonical source is post-install.ts)
|
|
@@ -67,7 +73,7 @@ import { getPackageRoot } from '../../core/paths.js';
|
|
|
67
73
|
* migrations are vacuously applied per D37 semantics).
|
|
68
74
|
*
|
|
69
75
|
* Migrations are a one-time cleanup pass over ~/.devflow runtime data
|
|
70
|
-
* (memory,
|
|
76
|
+
* (memory, curation, knowledge). They never touch the installer's
|
|
71
77
|
* copy targets (skills, agents, rules, commands, scripts), so ordering
|
|
72
78
|
* relative to installViaFileCopy carries no data dependency.
|
|
73
79
|
*
|
|
@@ -427,9 +433,73 @@ export async function persistManifestThenConvergeTracker(opts) {
|
|
|
427
433
|
};
|
|
428
434
|
}
|
|
429
435
|
/**
|
|
430
|
-
*
|
|
436
|
+
* The curation move step (#529): rename the global tuning file, then move each target
|
|
437
|
+
* project's ledger folder from the old name to the new one. The targets are the
|
|
438
|
+
* discovered projects (else the current repository) plus the current repository's own
|
|
439
|
+
* ledger root, which the step lists once. State-checked on every run, with no run-once
|
|
440
|
+
* record. Never throws: a project that cannot move is a line, not an exit code.
|
|
441
|
+
*
|
|
442
|
+
* Init runs it after the legacy sweep and BEFORE the manifest is persisted and the
|
|
443
|
+
* disabled-feature queues drain, so the drain sees the moved folder.
|
|
444
|
+
*
|
|
445
|
+
* The own ledger root is looked up within the shared budget (`lookupOwnLedgerRoot`). A
|
|
446
|
+
* lookup that times out is no ledger root: the step moves the other targets, and one
|
|
447
|
+
* skipped line names the repository, unless the repository is itself a project root,
|
|
448
|
+
* where the step's own lookup reports it.
|
|
449
|
+
*/
|
|
450
|
+
export async function moveCurationData(opts, lookup = lookupLedgerRoot) {
|
|
451
|
+
const loaded = loadCurationMove();
|
|
452
|
+
const roots = projectRoots([...opts.discoveredProjects], opts.gitRoot);
|
|
453
|
+
const own = await lookupOwnLedgerRoot(opts.gitRoot, lookup);
|
|
454
|
+
const report = await runInitCurationMove({
|
|
455
|
+
projectRoots: roots,
|
|
456
|
+
ownLedgerRoot: own.root,
|
|
457
|
+
homeDir: opts.homeDir,
|
|
458
|
+
machineDir: opts.devflowDir,
|
|
459
|
+
}, {
|
|
460
|
+
ledger: loaded.ok ? realProjectLedgerMoveIO(loaded.value) : { unavailable: formatCurationStoreUnavailable(loaded.error) },
|
|
461
|
+
tuning: realGlobalTuningMoveIO(),
|
|
462
|
+
});
|
|
463
|
+
if (!own.timedOut || opts.gitRoot === null || roots.includes(opts.gitRoot))
|
|
464
|
+
return report;
|
|
465
|
+
const skipped = describeOutcome({ kind: 'skipped', root: opts.gitRoot, reason: 'git-root-timed-out' });
|
|
466
|
+
return { ...report, lines: [...report.lines, ...skipped] };
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* The ledger root of the current repository for init's own move and drain
|
|
470
|
+
* (D-LEDGER-MAIN-WORKTREE): one lookup from the working directory, bounded by
|
|
471
|
+
* LEDGER_ROOT_TIMEOUT_MS like the HUD's and the step's lookups, so a repository on a
|
|
472
|
+
* hung mount costs a bounded wait and never stalls init. A lookup that timed out is no
|
|
473
|
+
* root (`root` null, `timedOut` true), so the caller acts on nothing and says so.
|
|
474
|
+
* Outside a git repository (`gitRoot` null) nothing is asked.
|
|
475
|
+
*/
|
|
476
|
+
export async function lookupOwnLedgerRoot(gitRoot, lookup = lookupLedgerRoot) {
|
|
477
|
+
if (gitRoot === null)
|
|
478
|
+
return { root: null, timedOut: false };
|
|
479
|
+
const found = await lookup(undefined, { timeoutMs: LEDGER_ROOT_TIMEOUT_MS });
|
|
480
|
+
return { root: found.kind === 'root' ? found.root : null, timedOut: found.kind === 'timed-out' };
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* The ledger root init's disabled-feature drain acts on, and the one line to print when
|
|
484
|
+
* there is none because git did not answer in time. Asks only when the drain can run: curation
|
|
485
|
+
* is off, in a git repository, and the manifest holding the switch was written. With curation
|
|
486
|
+
* on there is nothing to drain, and with no manifest written the drain does not run, so a
|
|
487
|
+
* line that says it was not drained for want of the root would name the wrong cause.
|
|
488
|
+
*/
|
|
489
|
+
export async function lookupDrainLedgerRoot(opts, lookup = lookupLedgerRoot) {
|
|
490
|
+
if (opts.curationEnabled || opts.gitRoot === null || !opts.manifestWritten)
|
|
491
|
+
return { ledgerRoot: null, warning: null };
|
|
492
|
+
const own = await lookupOwnLedgerRoot(opts.gitRoot, lookup);
|
|
493
|
+
const warning = own.timedOut
|
|
494
|
+
? `The curation queue was not drained for ${describeMoveText(opts.gitRoot)}: git did not answer in time. The next init tries again.`
|
|
495
|
+
: null;
|
|
496
|
+
return { ledgerRoot: own.root, warning };
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Drain this repo's memory and curation queues for each feature init switched
|
|
431
500
|
* off, so stale turns are not processed on a future re-enable — the same drains
|
|
432
|
-
* `devflow memory --disable` and `devflow
|
|
501
|
+
* `devflow memory --disable` and `devflow curation --disable` perform (the curation
|
|
502
|
+
* drain also removes the pending rows of the backups a move left). Other
|
|
433
503
|
* repos' queues are inert: every gate reads the machine-wide switch, so nothing
|
|
434
504
|
* appends to or processes them while the feature is off.
|
|
435
505
|
*
|
|
@@ -442,13 +512,18 @@ export async function persistManifestThenConvergeTracker(opts) {
|
|
|
442
512
|
* everywhere, so its queue is live and is left alone.
|
|
443
513
|
*
|
|
444
514
|
* D-LEDGER-MAIN-WORKTREE: each queue drains where the hooks write it — memory at
|
|
445
|
-
* this checkout's toplevel (`gitRoot`),
|
|
446
|
-
*
|
|
515
|
+
* this checkout's toplevel (`gitRoot`), curation at the ledger root (`ledgerRoot`,
|
|
516
|
+
* lookupOwnLedgerRoot), which in a linked worktree is the main checkout.
|
|
447
517
|
*
|
|
448
518
|
* Returns one warning for each drain refused because a folder on the way to its
|
|
449
|
-
* queue is a symbolic link (D-CLI-NO-SYMLINK)
|
|
519
|
+
* queue is a symbolic link (D-CLI-NO-SYMLINK), one for a curation drain that
|
|
520
|
+
* could not list the backups (the live queue is drained then), and one for a curation
|
|
521
|
+
* drain that left the project alone because git tracks a data folder or did not answer
|
|
522
|
+
* (the fixed notice of a skipped move); the caller prints them. When the move step has
|
|
523
|
+
* printed that notice for the ledger root (`shownSkip`, its reason), the drain's line
|
|
524
|
+
* does not repeat it.
|
|
450
525
|
*/
|
|
451
|
-
export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue,
|
|
526
|
+
export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue, drainCurationQueue: drainDisabledCurationQueue }) {
|
|
452
527
|
if (!opts.manifestWritten)
|
|
453
528
|
return [];
|
|
454
529
|
const refused = [];
|
|
@@ -457,10 +532,12 @@ export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue,
|
|
|
457
532
|
if (!memory.drained)
|
|
458
533
|
refused.push(formatRefusedDrain('memory', memory.linkedFolder));
|
|
459
534
|
}
|
|
460
|
-
if (!opts.
|
|
461
|
-
const
|
|
462
|
-
if (!
|
|
463
|
-
refused.push(
|
|
535
|
+
if (!opts.curationEnabled && opts.ledgerRoot !== null) {
|
|
536
|
+
const curation = await io.drainCurationQueue(opts.ledgerRoot);
|
|
537
|
+
if (!curation.drained)
|
|
538
|
+
refused.push(formatCurationNotDrained(curation, opts.shownSkip ?? null));
|
|
539
|
+
else if (curation.warning !== undefined)
|
|
540
|
+
refused.push(curation.warning);
|
|
464
541
|
}
|
|
465
542
|
return refused;
|
|
466
543
|
}
|
|
@@ -471,18 +548,23 @@ export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue,
|
|
|
471
548
|
* nothing else, so the manifest keeps every recorded value — plugins, version,
|
|
472
549
|
* installedAt and every feature — and only `features.hud` turns on. The
|
|
473
550
|
* earlier shape rewrote the whole record as a HUD-only fresh install (ambient,
|
|
474
|
-
* memory,
|
|
551
|
+
* memory, curation, knowledge, rules and proxy all `false`, plugins `[]`) while
|
|
475
552
|
* leaving those features' artifacts on disk: the record stopped describing the
|
|
476
553
|
* machine, the next re-init seeded every feature off, and with
|
|
477
|
-
* memory/
|
|
554
|
+
* memory/curation/knowledge switched by the manifest alone
|
|
478
555
|
* (D-FEATURES-NARROW-ONLY) a HUD install would have really disabled them
|
|
479
556
|
* everywhere. `version` is kept too: --hud-only reinstalls no plugin, and a
|
|
480
557
|
* bumped version would make the next init skip the upgrade it still owes.
|
|
481
558
|
*
|
|
482
559
|
* With no prior manifest the result is the fresh HUD-only record: every other
|
|
483
|
-
* feature off, which is what is installed.
|
|
560
|
+
* feature off, which is what is installed. `readManifest()` also reads a manifest it
|
|
561
|
+
* refuses as no manifest, so `refusedGateLevel` carries the machine gate level found in
|
|
562
|
+
* the raw file (`readStoredGateLevel`): the fresh record keeps it exactly as stored,
|
|
563
|
+
* malformed and `null` included, and holds no key when it is `undefined`. Init never
|
|
564
|
+
* writes a gate level the user did not choose. It is ignored over a prior install,
|
|
565
|
+
* whose own level is already kept.
|
|
484
566
|
*/
|
|
485
|
-
export function buildHudOnlyManifest(existing, version, now) {
|
|
567
|
+
export function buildHudOnlyManifest(existing, version, now, refusedGateLevel) {
|
|
486
568
|
if (existing !== null) {
|
|
487
569
|
return {
|
|
488
570
|
...existing,
|
|
@@ -496,17 +578,35 @@ export function buildHudOnlyManifest(existing, version, now) {
|
|
|
496
578
|
scope: 'user',
|
|
497
579
|
features: {
|
|
498
580
|
ambient: false, memory: false, hud: true, knowledge: false,
|
|
499
|
-
|
|
581
|
+
curation: false, rules: false, flags: {}, proxy: false,
|
|
500
582
|
behavior: false,
|
|
501
583
|
compliance: { enabled: false, frameworks: [] },
|
|
502
584
|
// The exported default, not a literal — the one constant every other
|
|
503
585
|
// module reads, so a moved default moves here too.
|
|
504
586
|
tracker: { provider: DEFAULT_TRACKER_PROVIDER },
|
|
587
|
+
...(refusedGateLevel !== undefined ? { gateLevel: refusedGateLevel } : {}),
|
|
505
588
|
},
|
|
506
589
|
installedAt: now,
|
|
507
590
|
updatedAt: now,
|
|
508
591
|
};
|
|
509
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* The line `init --hud-only` prints when the version the manifest records is not the
|
|
595
|
+
* running version, or null when there is nothing to say. Pure.
|
|
596
|
+
*
|
|
597
|
+
* `--hud-only` refreshes the scripts under ~/.devflow and leaves the installed
|
|
598
|
+
* commands and agents alone, so after an upgrade or a downgrade the prompts and the
|
|
599
|
+
* scripts they call can disagree until a full init. The window is accepted; this
|
|
600
|
+
* line names it, in either direction, and ends with the remedy. A machine with no
|
|
601
|
+
* readable manifest records no version, so there is nothing to compare. The recorded
|
|
602
|
+
* version is hand-editable file content, so it prints through `describeEchoedValue`.
|
|
603
|
+
*/
|
|
604
|
+
export function formatHudOnlySkew(recordedVersion, runningVersion) {
|
|
605
|
+
if (recordedVersion === null || installedVersionMatches(recordedVersion, runningVersion))
|
|
606
|
+
return null;
|
|
607
|
+
return `The installed prompts come from devflow ${describeEchoedValue(recordedVersion)} and this CLI is ${runningVersion ?? 'of unknown version'}. ` +
|
|
608
|
+
'--hud-only refreshed only the scripts, so run `devflow init` to update the prompts.';
|
|
609
|
+
}
|
|
510
610
|
/**
|
|
511
611
|
* Decide what `init --scope <value>` does now that there is one install scope.
|
|
512
612
|
* Pure — the action performs the exit.
|
|
@@ -595,8 +695,8 @@ export const initCommand = new Command('init')
|
|
|
595
695
|
.option('--no-hud', 'Disable HUD status line')
|
|
596
696
|
.option('--knowledge', 'Enable feature knowledge bases')
|
|
597
697
|
.option('--no-knowledge', 'Disable feature knowledge bases')
|
|
598
|
-
.option('--
|
|
599
|
-
.option('--no-
|
|
698
|
+
.option('--curation', 'Enable curation (decision/pitfall tracking)')
|
|
699
|
+
.option('--no-curation', 'Disable curation (decision/pitfall tracking)')
|
|
600
700
|
.option('--rules', 'Enable rules (always-on engineering principles)')
|
|
601
701
|
.option('--no-rules', 'Disable rules')
|
|
602
702
|
.option('--proxy', 'Enable external model routing (GPT models via your OpenAI/Codex subscription)')
|
|
@@ -606,6 +706,7 @@ export const initCommand = new Command('init')
|
|
|
606
706
|
.option('--compliance <list>', 'Enable compliance with comma-separated framework IDs (e.g., gdpr,hipaa)')
|
|
607
707
|
.option('--no-compliance', 'Disable compliance (removes the rule; the skill and framework references stay installed; frameworks remembered for re-enable)')
|
|
608
708
|
.option('--tracker <id>', 'The machine\'s default issue tracker provider: github, jira, or linear')
|
|
709
|
+
.option('--gate-level <full|standard|lean>', 'The machine\'s default gate level: full, standard, or lean (skips the gate level prompt)')
|
|
609
710
|
.option('--security <mode>', 'Security deny list location: user, managed, or none', /^(user|managed|none)$/i)
|
|
610
711
|
.option('--hud-only', 'Install only the HUD (no plugins, hooks, or extras)')
|
|
611
712
|
.option('--recommended', 'Apply recommended defaults after plugin selection (skip advanced prompts)')
|
|
@@ -631,6 +732,19 @@ export const initCommand = new Command('init')
|
|
|
631
732
|
p.log.error('--reset and --plugin are mutually exclusive. Use --reset alone to restore defaults, or --plugin to update a specific plugin.');
|
|
632
733
|
process.exit(1);
|
|
633
734
|
}
|
|
735
|
+
// Parse --gate-level at the boundary, before anything is written (--hud-only writes early).
|
|
736
|
+
// Strict — reject, never repair — so a typo'd level exits here rather than being ignored.
|
|
737
|
+
let cliGateLevel;
|
|
738
|
+
{
|
|
739
|
+
const gateLevelResult = resolveGateLevelInitState(options.gateLevel, !!options.hudOnly);
|
|
740
|
+
if (gateLevelResult !== undefined) {
|
|
741
|
+
if (!gateLevelResult.ok) {
|
|
742
|
+
p.log.error(gateLevelResult.error);
|
|
743
|
+
process.exit(1);
|
|
744
|
+
}
|
|
745
|
+
cliGateLevel = gateLevelResult.value;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
634
748
|
// D-SCOPE-RETIRED: refuse a retired --scope value before anything is written.
|
|
635
749
|
const scopeDecision = resolveRetiredScopeOption(options.scope);
|
|
636
750
|
if (scopeDecision.kind === 'refuse') {
|
|
@@ -682,10 +796,28 @@ export const initCommand = new Command('init')
|
|
|
682
796
|
existingHudManifest = await readManifest(devflowDir);
|
|
683
797
|
}
|
|
684
798
|
catch { /* absent on fresh install — existingHudManifest stays null */ }
|
|
799
|
+
// A manifest readManifest() refuses reads as none, but its machine gate level is still
|
|
800
|
+
// stored there: --hud-only keeps it and says so, as the full init does. --reset does not
|
|
801
|
+
// apply here, so nothing asks to forget it.
|
|
802
|
+
const hudStoredGate = await readStoredGateLevel(devflowDir, existingHudManifest, false);
|
|
803
|
+
let hudManifestWritten = false;
|
|
685
804
|
try {
|
|
686
|
-
await writeManifest(devflowDir, buildHudOnlyManifest(existingHudManifest, version, new Date().toISOString()));
|
|
805
|
+
await writeManifest(devflowDir, buildHudOnlyManifest(existingHudManifest, version, new Date().toISOString(), hudStoredGate.value));
|
|
806
|
+
hudManifestWritten = true;
|
|
687
807
|
}
|
|
688
808
|
catch { /* non-fatal */ }
|
|
809
|
+
if (hudStoredGate.origin !== 'manifest') {
|
|
810
|
+
logSummaryLines(formatGateLevelSummary({
|
|
811
|
+
level: hudStoredGate.value,
|
|
812
|
+
priorLevel: hudStoredGate.value,
|
|
813
|
+
priorOrigin: hudStoredGate.origin,
|
|
814
|
+
kept: true,
|
|
815
|
+
manifestWritten: hudManifestWritten,
|
|
816
|
+
}));
|
|
817
|
+
}
|
|
818
|
+
const hudOnlySkew = formatHudOnlySkew(existingHudManifest?.version ?? null, await readRunningVersion());
|
|
819
|
+
if (hudOnlySkew !== null)
|
|
820
|
+
p.log.warn(hudOnlySkew);
|
|
689
821
|
p.log.success('HUD installed');
|
|
690
822
|
p.log.info(`Configure later: ${color.cyan('devflow hud --status')}`);
|
|
691
823
|
p.outro(color.green('HUD-only install complete.'));
|
|
@@ -697,6 +829,15 @@ export const initCommand = new Command('init')
|
|
|
697
829
|
existingManifest = await readManifest(devflowDir, { onWarning: (message) => p.log.warn(message) });
|
|
698
830
|
}
|
|
699
831
|
catch { /* unreadable manifest — seeded as a fresh install */ }
|
|
832
|
+
// The stored machine gate level, and where it was found. readManifest() reads a manifest it
|
|
833
|
+
// refuses (a missing required field, a byte-order mark, unparseable JSON) as no manifest, so
|
|
834
|
+
// the seed holds no level for it, yet `devflow gates` and the settings resolver still read
|
|
835
|
+
// that file's level. Init never writes a level the user did not choose, so when readManifest()
|
|
836
|
+
// returned none the raw file decides: a level found in it is the level the run replaces, and
|
|
837
|
+
// is kept as stored unless --reset asks to forget it. A file that cannot be read at all is
|
|
838
|
+
// reported as "unreadable". This is an init-side override; the seed itself still comes from
|
|
839
|
+
// the manifest alone.
|
|
840
|
+
const storedGate = await readStoredGateLevel(devflowDir, existingManifest, !!options.reset);
|
|
700
841
|
// D-INIT-NOT-HOME (same-location.ts): a repository rooted at HOME is no
|
|
701
842
|
// project, so init treats it as no repository — no .devflow/config.json (that
|
|
702
843
|
// would be the machine root's), no .claudeignore, no .gitignore block, no
|
|
@@ -914,7 +1055,7 @@ export const initCommand = new Command('init')
|
|
|
914
1055
|
let memoryEnabled = seed.features.memory;
|
|
915
1056
|
let hudEnabled = seed.features.hud;
|
|
916
1057
|
let knowledgeEnabled = seed.features.knowledge;
|
|
917
|
-
let
|
|
1058
|
+
let curationEnabled = seed.features.curation;
|
|
918
1059
|
let rulesEnabled = seed.features.rules;
|
|
919
1060
|
// proxy: Advanced-only; Recommended path carries seed value unchanged.
|
|
920
1061
|
// Fresh installs → false (FEATURE_DEFAULTS.proxy). Re-inits → prior manifest value.
|
|
@@ -935,6 +1076,21 @@ export const initCommand = new Command('init')
|
|
|
935
1076
|
// alone, so init carries the prior value through untouched: it stamps the installed agents
|
|
936
1077
|
// with it and writes it back to the manifest. Absent stays absent (the default list).
|
|
937
1078
|
const trackerServers = seed.features.tracker.servers;
|
|
1079
|
+
// The machine gate level (features.gateLevel), shared with `devflow gates --set`. The seed holds
|
|
1080
|
+
// it raw: `undefined` only for an absent key or a --reset seed, a malformed value or `null` as
|
|
1081
|
+
// stored. When readManifest() returned none, the raw level (`storedGate`, read above) stands in
|
|
1082
|
+
// for the seed, as `carriedGateLevel` decides. It stays raw unless --gate-level or the wizard
|
|
1083
|
+
// step sets it, so a plain init never defaults, narrows or heals it; the full manifest write
|
|
1084
|
+
// below then leaves an absent key out. Each wizard path assigns the decision `runGateLevelStepAt`
|
|
1085
|
+
// makes; that is the only precedence site (--gate-level, then the wizard answer, then this
|
|
1086
|
+
// carried level), and `carried` says whether the level is still the carried one.
|
|
1087
|
+
const carriedGate = carriedGateLevel({
|
|
1088
|
+
manifest: existingManifest,
|
|
1089
|
+
reset: !!options.reset,
|
|
1090
|
+
stored: storedGate,
|
|
1091
|
+
seedLevel: seed.features.gateLevel,
|
|
1092
|
+
});
|
|
1093
|
+
let gateDecision = { level: carriedGate.value, carried: true };
|
|
938
1094
|
let enabledFlags = { ...seed.flags };
|
|
939
1095
|
// viewModeExplicit: true when --reset is passed; signals resolveFinalViewMode to let the
|
|
940
1096
|
// seed-time view-mode win over an externally-set value in settings.json.
|
|
@@ -992,6 +1148,31 @@ export const initCommand = new Command('init')
|
|
|
992
1148
|
}
|
|
993
1149
|
return trackerStep.state;
|
|
994
1150
|
};
|
|
1151
|
+
/**
|
|
1152
|
+
* Decide the machine gate level for one wizard path: --gate-level, else the wizard step
|
|
1153
|
+
* (gated on modePromptShown), else the stored value untouched. The step's outcome line is
|
|
1154
|
+
* the post-install gate level summary row, on both paths.
|
|
1155
|
+
*
|
|
1156
|
+
* Returns the decision, the level raw as `resolveGateLevelForInit` returns it and whether it
|
|
1157
|
+
* is the carried stored level. The caller assigns it to `gateDecision`: that value is the
|
|
1158
|
+
* only precedence decision, and nothing after the call revises it.
|
|
1159
|
+
*/
|
|
1160
|
+
const runGateLevelStepAt = async (mode) => {
|
|
1161
|
+
const outcome = await resolveGateLevelForInit({
|
|
1162
|
+
cliLevel: cliGateLevel,
|
|
1163
|
+
stored: gateDecision.level,
|
|
1164
|
+
storedOrigin: carriedGate.origin,
|
|
1165
|
+
mode,
|
|
1166
|
+
modePromptShown,
|
|
1167
|
+
isTTY: process.stdin.isTTY,
|
|
1168
|
+
prompts: buildClackGateLevelPrompts(),
|
|
1169
|
+
});
|
|
1170
|
+
if (outcome.kind === 'cancelled') {
|
|
1171
|
+
p.cancel('Installation cancelled.');
|
|
1172
|
+
process.exit(0);
|
|
1173
|
+
}
|
|
1174
|
+
return { level: outcome.level, carried: outcome.carried };
|
|
1175
|
+
};
|
|
995
1176
|
// Safe-delete detection (both paths need this)
|
|
996
1177
|
const platform = detectPlatform();
|
|
997
1178
|
const shell = detectShell();
|
|
@@ -1031,6 +1212,9 @@ export const initCommand = new Command('init')
|
|
|
1031
1212
|
// Tracker line via formatTrackerSummary, so the step's own outcome line
|
|
1032
1213
|
// would be a duplicate.
|
|
1033
1214
|
const wizardTracker = await runTrackerStepAt('recommended', seed.features.tracker.provider, false);
|
|
1215
|
+
// Gate level wizard step — same gate; --recommended and !isTTY keep the stored value.
|
|
1216
|
+
// The decision is final here: applyCliToggles below carries no gate level.
|
|
1217
|
+
gateDecision = await runGateLevelStepAt('recommended');
|
|
1034
1218
|
// No attribution step here: the suppress-attribution question is Advanced-only (D27).
|
|
1035
1219
|
// Recommended silently carries the seeded value in enabledFlags — fresh installs get
|
|
1036
1220
|
// the registry default (off), re-inits get prior state. See shouldRunAttributionStep.
|
|
@@ -1042,7 +1226,7 @@ export const initCommand = new Command('init')
|
|
|
1042
1226
|
memory: options.memory,
|
|
1043
1227
|
hud: options.hud,
|
|
1044
1228
|
knowledge: options.knowledge,
|
|
1045
|
-
|
|
1229
|
+
curation: options.curation,
|
|
1046
1230
|
rules: options.rules,
|
|
1047
1231
|
proxy: options.proxy,
|
|
1048
1232
|
behavior: options.behavior,
|
|
@@ -1054,7 +1238,7 @@ export const initCommand = new Command('init')
|
|
|
1054
1238
|
memoryEnabled = effectiveFeatures.memory;
|
|
1055
1239
|
hudEnabled = effectiveFeatures.hud;
|
|
1056
1240
|
knowledgeEnabled = effectiveFeatures.knowledge;
|
|
1057
|
-
|
|
1241
|
+
curationEnabled = effectiveFeatures.curation;
|
|
1058
1242
|
rulesEnabled = effectiveFeatures.rules;
|
|
1059
1243
|
proxyEnabled = effectiveFeatures.proxy;
|
|
1060
1244
|
behaviorEnabled = effectiveFeatures.behavior;
|
|
@@ -1093,7 +1277,7 @@ export const initCommand = new Command('init')
|
|
|
1093
1277
|
const summaryLines = [
|
|
1094
1278
|
`Ambient mode: ${ambientEnabled ? 'enabled' : 'disabled'}`,
|
|
1095
1279
|
`Working memory: ${memoryEnabled ? 'enabled' : 'disabled'}`,
|
|
1096
|
-
`
|
|
1280
|
+
`Curation: ${curationEnabled ? 'enabled' : 'disabled'}`,
|
|
1097
1281
|
`Rules: ${rulesEnabled ? 'enabled' : 'disabled'}`,
|
|
1098
1282
|
`HUD: ${hudEnabled ? 'enabled' : 'disabled'}`,
|
|
1099
1283
|
`Knowledge bases: ${knowledgeEnabled ? 'enabled' : 'disabled'}`,
|
|
@@ -1104,6 +1288,9 @@ export const initCommand = new Command('init')
|
|
|
1104
1288
|
// tracker step's ONLY surface on this path — both surfaces or it is
|
|
1105
1289
|
// invisible on one path.
|
|
1106
1290
|
`Tracker: ${formatTrackerSummary(trackerProvider)}`,
|
|
1291
|
+
// The level this run will record, or the stored one it keeps; the summary after the
|
|
1292
|
+
// install reports what the run really recorded.
|
|
1293
|
+
`Gate level: ${formatGateLevelRow(gateDecision.level, carriedGate.origin)}`,
|
|
1107
1294
|
`View mode: ${readViewMode(enabledFlags)}`,
|
|
1108
1295
|
`Claude Code flags: ${defaultFlagCount} configured`,
|
|
1109
1296
|
`.claudeignore: ${resolveClaudeignoreOutcome(claudeignorePresent)}`,
|
|
@@ -1145,12 +1332,7 @@ export const initCommand = new Command('init')
|
|
|
1145
1332
|
memoryEnabled = options.memory;
|
|
1146
1333
|
}
|
|
1147
1334
|
else {
|
|
1148
|
-
p.note(
|
|
1149
|
-
'compaction. Clear your session at any point and resume right\n' +
|
|
1150
|
-
'where you left off.\n\n' +
|
|
1151
|
-
'Runs a background agent on session stop that consumes additional\n' +
|
|
1152
|
-
'tokens. Consider skipping if token usage is a concern.\n' +
|
|
1153
|
-
'Applies to every project.', 'Working Memory');
|
|
1335
|
+
p.note(workingMemoryNote(), 'Working Memory');
|
|
1154
1336
|
const memoryChoice = await p.confirm({
|
|
1155
1337
|
message: 'Enable working memory? (Recommended)',
|
|
1156
1338
|
initialValue: seed.features.memory,
|
|
@@ -1195,22 +1377,22 @@ export const initCommand = new Command('init')
|
|
|
1195
1377
|
}
|
|
1196
1378
|
knowledgeEnabled = knowledgeChoice;
|
|
1197
1379
|
}
|
|
1198
|
-
if (options.
|
|
1199
|
-
|
|
1380
|
+
if (options.curation !== undefined) {
|
|
1381
|
+
curationEnabled = options.curation;
|
|
1200
1382
|
}
|
|
1201
1383
|
else {
|
|
1202
1384
|
p.note('Detects architectural decisions and pitfalls from your session\n' +
|
|
1203
1385
|
'dialogs. Runs a background agent on session stop that consumes\n' +
|
|
1204
|
-
'additional tokens. Applies to every project.', '
|
|
1205
|
-
const
|
|
1206
|
-
message: 'Enable
|
|
1207
|
-
initialValue: seed.features.
|
|
1386
|
+
'additional tokens. Applies to every project.', 'Curation (Decision/Pitfall Tracking)');
|
|
1387
|
+
const curationChoice = await p.confirm({
|
|
1388
|
+
message: 'Enable curation? (Recommended)',
|
|
1389
|
+
initialValue: seed.features.curation,
|
|
1208
1390
|
});
|
|
1209
|
-
if (p.isCancel(
|
|
1391
|
+
if (p.isCancel(curationChoice)) {
|
|
1210
1392
|
p.cancel('Installation cancelled.');
|
|
1211
1393
|
process.exit(0);
|
|
1212
1394
|
}
|
|
1213
|
-
|
|
1395
|
+
curationEnabled = curationChoice;
|
|
1214
1396
|
}
|
|
1215
1397
|
if (options.rules !== undefined) {
|
|
1216
1398
|
rulesEnabled = options.rules;
|
|
@@ -1340,6 +1522,8 @@ export const initCommand = new Command('init')
|
|
|
1340
1522
|
else
|
|
1341
1523
|
p.log.info(overrideLine.text);
|
|
1342
1524
|
}
|
|
1525
|
+
// Gate level wizard step — same gate as the Recommended path; re-init lands here.
|
|
1526
|
+
gateDecision = await runGateLevelStepAt('advanced');
|
|
1343
1527
|
// Attribution feature (after compliance, before flags). This is the ONLY call site —
|
|
1344
1528
|
// the attribution question is Advanced-only (D27); the Recommended path never asks and
|
|
1345
1529
|
// silently carries the seeded value. The gate stays an explicit predicate call so the
|
|
@@ -1624,7 +1808,7 @@ export const initCommand = new Command('init')
|
|
|
1624
1808
|
// The SETTLED switch, after the flag, the prompt and the seed have all had
|
|
1625
1809
|
// their say (D-LEARNING-VARIANT-INSTALL): the files installed here and the
|
|
1626
1810
|
// manifest written at the end of init cannot disagree about it.
|
|
1627
|
-
|
|
1811
|
+
curation: curationEnabled,
|
|
1628
1812
|
trackerServers,
|
|
1629
1813
|
// Non-fatal install notices with no other channel (skipped symlinks in the
|
|
1630
1814
|
// generated reference tree, mode-normalisation failures) reach the user rather
|
|
@@ -1637,7 +1821,7 @@ export const initCommand = new Command('init')
|
|
|
1637
1821
|
p.log.error(`${error}`);
|
|
1638
1822
|
process.exit(1);
|
|
1639
1823
|
}
|
|
1640
|
-
// D-LEARNING-VARIANT-INSTALL: converge the
|
|
1824
|
+
// D-LEARNING-VARIANT-INSTALL: converge the curation variants. The file copy above
|
|
1641
1825
|
// installed the variant of everything IT installed; on a `--plugin` install the
|
|
1642
1826
|
// other plugins' files, already on disk, may still be the other variant (this run
|
|
1643
1827
|
// may have flipped the switch), and the apply-decisions skill is removed here when
|
|
@@ -1645,21 +1829,21 @@ export const initCommand = new Command('init')
|
|
|
1645
1829
|
// Warn-not-abort: either mismatch is safe (an on variant is still gated at run
|
|
1646
1830
|
// time, an off variant loads no decisions). The agent mapping is reapplied below.
|
|
1647
1831
|
try {
|
|
1648
|
-
const
|
|
1832
|
+
const curationConverge = await convergeCurationVariants({
|
|
1649
1833
|
claudeDir,
|
|
1650
1834
|
devflowDir,
|
|
1651
|
-
|
|
1835
|
+
curation: curationEnabled,
|
|
1652
1836
|
plugins: effectivePlugins,
|
|
1653
1837
|
warn: (msg) => installWarnings.push(msg),
|
|
1654
1838
|
});
|
|
1655
|
-
if (verbose &&
|
|
1656
|
-
p.log.info(`
|
|
1657
|
-
`${
|
|
1658
|
-
`rewritten, ${
|
|
1839
|
+
if (verbose && curationConverge.converged) {
|
|
1840
|
+
p.log.info(`Curation ${curationEnabled ? 'on' : 'off'} variants: ` +
|
|
1841
|
+
`${curationConverge.commandsRewritten.length} command(s) and ${curationConverge.agentsRewritten.length} agent(s) ` +
|
|
1842
|
+
`rewritten, ${curationConverge.unchanged} already current`);
|
|
1659
1843
|
}
|
|
1660
1844
|
}
|
|
1661
1845
|
catch (err) {
|
|
1662
|
-
installWarnings.push(`
|
|
1846
|
+
installWarnings.push(`Curation variant convergence failed — ${err instanceof Error ? err.message : String(err)}`);
|
|
1663
1847
|
}
|
|
1664
1848
|
// Converge compliance artifacts (always converge, never short-circuit).
|
|
1665
1849
|
// Called unconditionally so that enabling/disabling compliance during init
|
|
@@ -1805,6 +1989,8 @@ export const initCommand = new Command('init')
|
|
|
1805
1989
|
'lib/staleness.cjs',
|
|
1806
1990
|
'dream-lock',
|
|
1807
1991
|
'decisions-usage-scan.cjs',
|
|
1992
|
+
'learning-lock',
|
|
1993
|
+
'lib/learning-store.cjs',
|
|
1808
1994
|
];
|
|
1809
1995
|
const hooksDir = path.join(devflowDir, 'scripts', 'hooks');
|
|
1810
1996
|
for (const legacy of LEGACY_HOOK_FILES) {
|
|
@@ -1814,6 +2000,10 @@ export const initCommand = new Command('init')
|
|
|
1814
2000
|
}
|
|
1815
2001
|
catch { /* doesn't exist */ }
|
|
1816
2002
|
}
|
|
2003
|
+
// The curation move (#529): after the legacy sweep, before the manifest persist and
|
|
2004
|
+
// the disabled-feature drain below, so the drain sees the moved folder. Its lines
|
|
2005
|
+
// and its one notice print with the install warnings, after the spinner stops.
|
|
2006
|
+
const curationMove = await moveCurationData({ discoveredProjects, gitRoot, homeDir, devflowDir });
|
|
1817
2007
|
// === Settings & hooks (all automatic based on collected choices) ===
|
|
1818
2008
|
s.message('Configuring settings');
|
|
1819
2009
|
await installSettings(claudeDir, rootDir, devflowDir, verbose);
|
|
@@ -2040,7 +2230,7 @@ export const initCommand = new Command('init')
|
|
|
2040
2230
|
'Manifest records intended state; run devflow init again to retry.');
|
|
2041
2231
|
}
|
|
2042
2232
|
// Write .devflow/config.json — facts about this repo. The machine switches for
|
|
2043
|
-
// memory/
|
|
2233
|
+
// memory/curation/knowledge are the manifest's; a hand-written `features`
|
|
2044
2234
|
// object here only narrows them and is carried, never written
|
|
2045
2235
|
// (D-FEATURES-NARROW-ONLY; the managed write drops the retired top-level
|
|
2046
2236
|
// per-repo keys). A managed
|
|
@@ -2257,6 +2447,10 @@ export const initCommand = new Command('init')
|
|
|
2257
2447
|
logSummaryLines(formatSkillScopeSummary(installReport, pluginListUnchanged));
|
|
2258
2448
|
for (const warning of installWarnings)
|
|
2259
2449
|
p.log.warn(warning);
|
|
2450
|
+
for (const line of curationMove.lines)
|
|
2451
|
+
p.log.warn(describeMoveText(line));
|
|
2452
|
+
if (curationMove.notice !== null)
|
|
2453
|
+
p.log.info(curationMove.notice);
|
|
2260
2454
|
const installedSet = new Set(pluginsToInstall.flatMap(p => p.commands).filter(c => c.length > 0));
|
|
2261
2455
|
const orderedCommands = WORKFLOW_ORDER.filter(cmd => installedSet.has(cmd));
|
|
2262
2456
|
if (orderedCommands.length > 0) {
|
|
@@ -2304,7 +2498,7 @@ export const initCommand = new Command('init')
|
|
|
2304
2498
|
memory: memoryEnabled,
|
|
2305
2499
|
hud: hudEnabled,
|
|
2306
2500
|
knowledge: knowledgeEnabled,
|
|
2307
|
-
|
|
2501
|
+
curation: curationEnabled,
|
|
2308
2502
|
rules: rulesEnabled,
|
|
2309
2503
|
// FlagsRecord written directly — key-presence encodes "known".
|
|
2310
2504
|
// view-mode is encoded as flags['view-mode'] (the resolved final value).
|
|
@@ -2324,6 +2518,9 @@ export const initCommand = new Command('init')
|
|
|
2324
2518
|
// through parseTrackerId (CLI), the typed wizard select, or the seed, which
|
|
2325
2519
|
// itself came through normalizeTrackerFeature on read.
|
|
2326
2520
|
tracker: { provider: trackerProvider, ...(trackerServers !== undefined ? { servers: trackerServers } : {}) },
|
|
2521
|
+
// The machine gate level: --gate-level, the wizard answer, or the prior value raw
|
|
2522
|
+
// (see where `gateDecision` is read above).
|
|
2523
|
+
...(gateDecision.level !== undefined ? { gateLevel: gateDecision.level } : {}),
|
|
2327
2524
|
},
|
|
2328
2525
|
installedAt: existingManifest?.installedAt ?? now,
|
|
2329
2526
|
updatedAt: now,
|
|
@@ -2345,12 +2542,16 @@ export const initCommand = new Command('init')
|
|
|
2345
2542
|
p.log.info(msg.text);
|
|
2346
2543
|
}
|
|
2347
2544
|
// Only now that the machine-wide switch is on disk (D-INIT-DRAIN-AFTER-SWITCH).
|
|
2545
|
+
const drainRoot = await lookupDrainLedgerRoot({ gitRoot, curationEnabled, manifestWritten: trackerLifecycle.manifestWritten });
|
|
2546
|
+
if (drainRoot.warning !== null)
|
|
2547
|
+
p.log.warn(drainRoot.warning);
|
|
2348
2548
|
const refusedDrains = await drainDisabledFeatureQueues({
|
|
2349
2549
|
gitRoot,
|
|
2350
|
-
ledgerRoot:
|
|
2550
|
+
ledgerRoot: drainRoot.ledgerRoot,
|
|
2351
2551
|
memoryEnabled,
|
|
2352
|
-
|
|
2552
|
+
curationEnabled,
|
|
2353
2553
|
manifestWritten: trackerLifecycle.manifestWritten,
|
|
2554
|
+
shownSkip: shownSkipAt(curationMove, drainRoot.ledgerRoot),
|
|
2354
2555
|
});
|
|
2355
2556
|
for (const line of refusedDrains)
|
|
2356
2557
|
p.log.warn(line);
|
|
@@ -2378,6 +2579,14 @@ export const initCommand = new Command('init')
|
|
|
2378
2579
|
agent: trackerLifecycle.agent,
|
|
2379
2580
|
});
|
|
2380
2581
|
logSummaryLines(trackerLines);
|
|
2582
|
+
// The gate level this run recorded, from the manifest the run wrote (D-INIT-REAL-OUTCOME).
|
|
2583
|
+
logSummaryLines(formatGateLevelSummary({
|
|
2584
|
+
level: gateDecision.level,
|
|
2585
|
+
priorLevel: storedGate.value,
|
|
2586
|
+
priorOrigin: storedGate.origin,
|
|
2587
|
+
kept: gateDecision.carried && carriedGate.origin === 'rejected',
|
|
2588
|
+
manifestWritten: trackerLifecycle.manifestWritten,
|
|
2589
|
+
}));
|
|
2381
2590
|
// CLAUDE.md import audit (D-CLAUDE-MD-IMPORT-AUDIT, D-AUDIT-STAMP): after the install, on
|
|
2382
2591
|
// both paths, through claudeMdAuditStep. Global roots always; the project's roots when init
|
|
2383
2592
|
// runs inside a git repository that is not HOME, at its toplevel. The stamp is written here,
|