devflow-kit 3.0.1 → 3.2.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 +49 -0
- package/README.md +1 -1
- package/dist/agents/git.md +2 -2
- package/dist/cli/agents-view/index.js +1 -1
- package/dist/cli/agents-view/render.js +71 -17
- package/dist/cli/agents-view/state.js +42 -16
- package/dist/cli/agents-view/terminal.js +5 -5
- package/dist/cli/commands/agents.js +142 -51
- package/dist/cli/commands/ambient.js +1 -1
- package/dist/cli/commands/attribution-prompts.js +8 -8
- package/dist/cli/commands/capture.js +1 -1
- package/dist/cli/commands/compliance-prompts.js +8 -8
- package/dist/cli/commands/compliance.js +8 -7
- package/dist/cli/commands/flags.js +33 -31
- package/dist/cli/commands/hud.js +1 -1
- package/dist/cli/commands/init-seed.js +9 -9
- package/dist/cli/commands/init.js +162 -85
- package/dist/cli/commands/install-report.js +10 -10
- package/dist/cli/commands/learning.js +302 -136
- package/dist/cli/commands/memory.js +36 -15
- package/dist/cli/commands/proxy.js +23 -23
- package/dist/cli/commands/rules.js +6 -5
- package/dist/cli/commands/tracker-prompts.js +6 -6
- package/dist/cli/commands/tracker.js +9 -9
- package/dist/cli/commands/uninstall.js +183 -59
- package/dist/cli/flags-view/render.js +5 -5
- package/dist/cli/flags-view/state.js +9 -9
- package/dist/cli/flags-view/terminal.js +4 -4
- package/dist/cli/tui/cells.js +1 -1
- package/dist/cli/tui/terminal.js +6 -6
- package/dist/commands/code-review.md +0 -2
- package/dist/commands/debug.md +14 -11
- package/dist/commands/dynamic-build.md +51 -47
- package/dist/commands/dynamic-plan.md +27 -7
- package/dist/commands/dynamic-profile.md +17 -3
- package/dist/commands/dynamic-tickets.md +18 -4
- package/dist/commands/explore.md +9 -3
- package/dist/commands/implement.md +20 -16
- package/dist/commands/plan.md +13 -9
- package/dist/commands/release.md +23 -3
- package/dist/commands/research.md +9 -3
- package/dist/commands/resolve.md +9 -12
- package/dist/commands/self-review.md +0 -2
- package/dist/core/agent-frontmatter.js +28 -3
- package/dist/core/agent-models.js +204 -42
- package/dist/core/agent-state.js +28 -6
- package/dist/core/ansi.js +2 -2
- package/dist/core/assets.js +1 -1
- package/dist/core/cache.js +7 -8
- package/dist/core/codex-auth-inspect.js +4 -4
- package/dist/core/compliance-compose.js +3 -3
- package/dist/core/compliance.js +3 -4
- package/dist/core/evidence-policy.js +14 -13
- package/dist/core/external-models.js +1 -1
- package/dist/core/feature-config.js +71 -13
- package/dist/core/feature-switch.js +3 -3
- package/dist/core/flags.js +49 -25
- package/dist/core/fs-atomic.js +6 -7
- package/dist/core/learning-queue-cleanup.js +16 -81
- package/dist/core/learning-store.js +61 -0
- package/dist/core/linked-path.js +46 -0
- package/dist/core/manifest.js +5 -5
- package/dist/core/mds-variants.js +13 -13
- package/dist/core/model-discovery.js +8 -8
- package/dist/core/observations.js +17 -101
- package/dist/core/orphan-sweep.js +4 -4
- package/dist/core/plugins.js +13 -8
- package/dist/core/project-paths.js +9 -13
- package/dist/core/proxy-log.js +8 -8
- package/dist/core/proxy-state.js +3 -3
- package/dist/core/queue-drain.js +31 -0
- package/dist/core/reference-sweep.js +6 -6
- package/dist/core/teammate-mode-cleanup.js +1 -1
- package/dist/core/tracker.js +14 -14
- package/dist/hud/colors.js +2 -2
- package/dist/hud/components/learning-counts.js +54 -22
- package/dist/hud/components/version-badge.js +1 -1
- package/dist/skills/git/references/pr/resolve-review-threads.md +2 -2
- package/dist/skills/git/references/tracker/github/create-release.md +2 -2
- package/dist/skills/git/references/tracker/jira/create-release.md +2 -2
- package/dist/skills/git/references/tracker/linear/create-release.md +2 -2
- package/dist/targets/claude-code/compliance-install.js +17 -15
- package/dist/targets/claude-code/hooks.js +2 -2
- package/dist/targets/claude-code/installer.js +59 -32
- package/dist/targets/claude-code/legacy.js +1 -1
- package/dist/targets/claude-code/post-install.js +135 -45
- package/dist/targets/claude-code/tracker-install.js +2 -2
- package/package.json +1 -1
- package/src/assets/agents/code.md +15 -21
- package/src/assets/agents/design.md +4 -2
- package/src/assets/agents/diagnose.md +3 -1
- package/src/assets/agents/evaluate.md +4 -0
- package/src/assets/agents/git.mds +2 -2
- package/src/assets/agents/knowledge.md +5 -3
- package/src/assets/agents/learning.md +281 -196
- package/src/assets/agents/research.md +3 -1
- package/src/assets/agents/review.md +5 -3
- package/src/assets/agents/scrutinize.md +5 -1
- package/src/assets/agents/simplify.md +4 -0
- package/src/assets/agents/skim.md +4 -2
- package/src/assets/agents/synthesize.md +6 -0
- package/src/assets/agents/test.md +18 -10
- package/src/assets/agents/triage.md +11 -9
- package/src/assets/agents/validate.md +14 -10
- package/src/assets/commands/_partials/_decisions.mds +8 -3
- package/src/assets/commands/_partials/_docs_root.mds +3 -3
- package/src/assets/commands/_partials/_engine.mds +16 -32
- package/src/assets/commands/_partials/_knowledge.mds +0 -2
- package/src/assets/commands/_partials/_preamble.mds +6 -2
- package/src/assets/commands/_partials/_settings.mds +2 -2
- package/src/assets/commands/_partials/_tracker.mds +1 -1
- package/src/assets/commands/code-review.mds +0 -2
- package/src/assets/commands/debug.mds +13 -8
- package/src/assets/commands/dynamic-build.mds +18 -12
- package/src/assets/commands/dynamic-plan.mds +10 -4
- package/src/assets/commands/dynamic-profile.mds +1 -1
- package/src/assets/commands/dynamic-tickets.mds +2 -2
- package/src/assets/commands/explore.mds +9 -1
- package/src/assets/commands/implement.mds +19 -13
- package/src/assets/commands/plan.mds +12 -8
- package/src/assets/commands/release.md +23 -3
- package/src/assets/commands/research.mds +9 -3
- package/src/assets/commands/resolve.mds +9 -10
- package/src/assets/mds/git/_pr.mds +3 -3
- package/src/assets/mds/tracker/_common.mds +1 -1
- package/src/assets/mds/tracker/_github.mds +3 -3
- package/src/assets/mds/tracker/_jira.mds +3 -3
- package/src/assets/mds/tracker/_linear.mds +3 -3
- package/src/assets/mds/tracker/_mcp.mds +6 -5
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +4 -2
- package/src/assets/scripts/hooks/background-memory-update +97 -33
- package/src/assets/scripts/hooks/capture-prompt +4 -3
- package/src/assets/scripts/hooks/capture-question +4 -3
- package/src/assets/scripts/hooks/capture-turn +5 -20
- package/src/assets/scripts/hooks/ensure-devflow-init +14 -2
- package/src/assets/scripts/hooks/ensure-proxy +5 -6
- package/src/assets/scripts/hooks/ensure-root-gitignore +123 -11
- package/src/assets/scripts/hooks/git-marker +71 -0
- package/src/assets/scripts/hooks/is-hex-sha +1 -1
- package/src/assets/scripts/hooks/json-helper.cjs +345 -944
- package/src/assets/scripts/hooks/json-parse +25 -129
- package/src/assets/scripts/hooks/lib/decisions-format.cjs +205 -156
- package/src/assets/scripts/hooks/lib/learning-store.cjs +3207 -0
- package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +7 -5
- package/src/assets/scripts/hooks/lib/project-paths.cjs +13 -19
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +253 -226
- package/src/assets/scripts/hooks/memory-worker +10 -0
- package/src/assets/scripts/hooks/pre-compact-memory +66 -14
- package/src/assets/scripts/hooks/preamble +9 -1
- package/src/assets/scripts/hooks/queue-append +55 -23
- package/src/assets/scripts/hooks/resolve-project-root +3 -4
- package/src/assets/scripts/hooks/session-start-context +146 -45
- package/src/assets/scripts/hooks/session-start-memory +33 -11
- package/src/assets/scripts/lib/project-config.cjs +2 -2
- package/src/assets/scripts/pr-evidence.cjs +3 -3
- package/src/assets/scripts/redact-secrets.cjs +20 -20
- package/src/assets/scripts/release-trace.cjs +1 -1
- package/src/assets/scripts/resolve-evidence-policy.cjs +3 -3
- package/src/assets/scripts/resolve-settings.cjs +3 -3
- package/src/assets/scripts/verify-evidence.cjs +2 -2
- package/src/assets/skills/apply-decisions/SKILL.md +37 -17
- package/src/assets/skills/docs-framework/SKILL.md +2 -2
- package/src/assets/skills/feature-knowledge/SKILL.md +6 -5
- package/src/assets/skills/test-driven-development/SKILL.md +6 -4
- package/dist/core/observation-io.js +0 -50
- package/src/assets/scripts/hooks/decisions-usage-scan.cjs +0 -131
|
@@ -12,7 +12,7 @@ import { getLedgerRoot } from '../../core/ledger-root.js';
|
|
|
12
12
|
import { installViaFileCopy, composeScripts } from '../../targets/claude-code/installer.js';
|
|
13
13
|
import { formatOverlaySummary, formatSkillScopeSummary, formatTrackerAssetSummary, isPluginListUnchanged } from './install-report.js';
|
|
14
14
|
import { convergeTrackerArtifacts } from '../../targets/claude-code/tracker-install.js';
|
|
15
|
-
import { installSettings, installManagedSettings, installClaudeignore, discoverProjectGitRoots, ensureDevflowGitignore, applyUserSecurityDenyList, detectDenyState, resolveSecurityAction, assertHistoricalDenySuperset, loadTemplateDenyEntries, stripUserSecurityDenyList, } from '../../targets/claude-code/post-install.js';
|
|
15
|
+
import { installSettings, installManagedSettings, installClaudeignore, hasClaudeignore, discoverProjectGitRoots, ensureDevflowGitignore, applyUserSecurityDenyList, detectDenyState, resolveSecurityAction, assertHistoricalDenySuperset, loadTemplateDenyEntries, stripUserSecurityDenyList, } from '../../targets/claude-code/post-install.js';
|
|
16
16
|
import { DEVFLOW_PLUGINS, LEGACY_COMMAND_NAMES, LEGACY_RULE_NAMES, buildAssetMaps, buildScopedSkillsMap, buildRulesMap, partitionSelectablePlugins, WORKFLOW_ORDER, parsePluginSelection, resolveFeatureRedirect } from '../../core/plugins.js';
|
|
17
17
|
import { LEGACY_SKILL_NAMES } from '../../targets/claude-code/legacy.js';
|
|
18
18
|
import { detectPlatform, detectShell, getProfilePath, getSafeDeleteInfo, hasSafeDelete } from '../../core/safe-delete.js';
|
|
@@ -22,7 +22,7 @@ import { convergeMemoryHooks, drainMemoryQueue } from './memory.js';
|
|
|
22
22
|
import { addCaptureHooks, removeCaptureHooks } from './capture.js';
|
|
23
23
|
import { removeDreamHook } from './legacy-hooks.js';
|
|
24
24
|
import { addProxyHooks, removeProxyHooks, applyProxyEnv, stripProxyEnv, runProxyPreflight, buildRealPreflightDeps } from './proxy.js';
|
|
25
|
-
import { reapplyAgentMapping, readAgentMapping } from '../../core/agent-models.js';
|
|
25
|
+
import { reapplyAgentMapping, readAgentMapping, hasAgentMappingEntries } from '../../core/agent-models.js';
|
|
26
26
|
import { readProxyState, writeProxyState, buildProxyState, buildRoutingConfigJson, DEFAULT_PROXY_PORT, proxyJsonExists } from '../../core/proxy-state.js';
|
|
27
27
|
import { stripDevflowTeammateModeFromJson } from '../../core/teammate-mode-cleanup.js';
|
|
28
28
|
// Settings/HookMatcher types used by hook utilities — each in their own module
|
|
@@ -34,6 +34,7 @@ import { addContextHook, removeContextHook, hasContextHook } from './context.js'
|
|
|
34
34
|
import { writeSettingsFileAtomic } from '../../core/fs-atomic.js';
|
|
35
35
|
import { writeManagedConfig, readConfigIfPresent, DEFAULT_CONFIG } from '../../core/feature-config.js';
|
|
36
36
|
import { drainLearningQueue } from '../../core/learning-queue-cleanup.js';
|
|
37
|
+
import { formatRefusedDrain } from '../../core/queue-drain.js';
|
|
37
38
|
import { removeManagedDenyList, describeManagedDenyRemoval } from './security.js';
|
|
38
39
|
import { resolveInitSeed, applyCliToggles, resolveResetGatedInputs, resolvePluginsToInstall } from './init-seed.js';
|
|
39
40
|
import { parseFrameworkList, normalizeFrameworks } from '../../core/compliance.js';
|
|
@@ -55,7 +56,7 @@ import { getPackageRoot } from '../../core/paths.js';
|
|
|
55
56
|
/**
|
|
56
57
|
* D32/D35: Orchestrates the init-level migration-runner seam.
|
|
57
58
|
*
|
|
58
|
-
* Computes the project list with the D37 fallback rule:
|
|
59
|
+
* Computes the project list with the D37 fallback rule ({@link projectRoots}):
|
|
59
60
|
* 1. Use discoveredProjects when non-empty.
|
|
60
61
|
* 2. Fall back to [gitRoot] when discoveredProjects is empty and gitRoot is set.
|
|
61
62
|
* 3. Run with no per-project targets when both are absent (global-only; per-project
|
|
@@ -70,8 +71,7 @@ import { getPackageRoot } from '../../core/paths.js';
|
|
|
70
71
|
* this helper testable without real filesystem migration state.
|
|
71
72
|
*/
|
|
72
73
|
export async function runMigrationsWithFallback(discoveredProjects, gitRoot, devflowDir, logger, verbose, runner) {
|
|
73
|
-
const
|
|
74
|
-
const migrationResult = await runner({ devflowDir }, projectsForMigration);
|
|
74
|
+
const migrationResult = await runner({ devflowDir }, projectRoots(discoveredProjects, gitRoot));
|
|
75
75
|
reportMigrationResult(migrationResult, logger, verbose);
|
|
76
76
|
return migrationResult;
|
|
77
77
|
}
|
|
@@ -84,7 +84,7 @@ export async function runMigrationsWithFallback(discoveredProjects, gitRoot, dev
|
|
|
84
84
|
* or command still loading in Claude Code with no diagnostic at all. Both outcomes
|
|
85
85
|
* have to reach the summary.
|
|
86
86
|
*
|
|
87
|
-
* Pure function — returns lines, logs nothing
|
|
87
|
+
* Pure function — returns lines, logs nothing.
|
|
88
88
|
*/
|
|
89
89
|
export function formatSweepSummary(report) {
|
|
90
90
|
const lines = [];
|
|
@@ -119,6 +119,9 @@ export function formatSweepSummary(report) {
|
|
|
119
119
|
function logSummaryLines(lines) {
|
|
120
120
|
for (const line of lines) {
|
|
121
121
|
switch (line.level) {
|
|
122
|
+
case 'success':
|
|
123
|
+
p.log.success(line.message);
|
|
124
|
+
break;
|
|
122
125
|
case 'info':
|
|
123
126
|
p.log.info(line.message);
|
|
124
127
|
break;
|
|
@@ -144,6 +147,81 @@ export function classifySafeDeleteState(installedVersion, currentVersion) {
|
|
|
144
147
|
return 'outdated';
|
|
145
148
|
return 'missing';
|
|
146
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* The project roots init writes per-project files into and migrates (D37): every
|
|
152
|
+
* project discovered from Claude's history, else the current repository, else
|
|
153
|
+
* none. Pure.
|
|
154
|
+
*/
|
|
155
|
+
export function projectRoots(discoveredProjects, gitRoot) {
|
|
156
|
+
if (discoveredProjects.length > 0)
|
|
157
|
+
return [...discoveredProjects];
|
|
158
|
+
return gitRoot ? [gitRoot] : [];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The Recommended summary's `.claudeignore` row.
|
|
162
|
+
*
|
|
163
|
+
* D-INIT-REAL-OUTCOME: init's summary and status lines state what this run does,
|
|
164
|
+
* decided from the state it acts on rather than printed whatever happens, and
|
|
165
|
+
* point only at a step the user can still take. The summary prints before the
|
|
166
|
+
* install runs, so this row is decided from whether each targeted project
|
|
167
|
+
* already holds a `.claudeignore` (hasClaudeignore, which matches the install's
|
|
168
|
+
* exclusive create): one without it gets one, so the row says `created`; when
|
|
169
|
+
* all have one, `already present`; when the run targets no project, `skipped`.
|
|
170
|
+
* formatSafeDeleteStatus applies the same rule to safe-delete.
|
|
171
|
+
*
|
|
172
|
+
* Pure function.
|
|
173
|
+
*
|
|
174
|
+
* @param present - for each project the run targets, whether it already holds a
|
|
175
|
+
* `.claudeignore`; empty when the run targets none.
|
|
176
|
+
*/
|
|
177
|
+
export function resolveClaudeignoreOutcome(present) {
|
|
178
|
+
if (present.length === 0)
|
|
179
|
+
return 'skipped';
|
|
180
|
+
return present.every(Boolean) ? 'already present' : 'created';
|
|
181
|
+
}
|
|
182
|
+
/** The Recommended summary's safe-delete row for what the run does to the block; a run that leaves it alone adds none. */
|
|
183
|
+
const SAFE_DELETE_SUMMARY_ROW = {
|
|
184
|
+
install: 'Safe delete: installed',
|
|
185
|
+
upgrade: 'Safe delete: upgraded',
|
|
186
|
+
skip: '',
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* What init says about safe-delete once the install has run (D-INIT-REAL-OUTCOME).
|
|
190
|
+
*
|
|
191
|
+
* The outcome comes first: the block this run installed or upgraded, or the
|
|
192
|
+
* current block it found in place. Failing an outcome, the one step left to the
|
|
193
|
+
* user is installing the platform's trash command, which init cannot do for
|
|
194
|
+
* them. A non-interactive run always takes the Recommended path, which installs
|
|
195
|
+
* or upgrades the block itself, so its outcome is all there is to report.
|
|
196
|
+
*
|
|
197
|
+
* Pure function — returns lines, logs nothing.
|
|
198
|
+
*
|
|
199
|
+
* @param state - The profile's block before this run, or null when no profile was
|
|
200
|
+
* checked (the trash command is missing, or the shell has no profile init writes).
|
|
201
|
+
*/
|
|
202
|
+
export function formatSafeDeleteStatus(input) {
|
|
203
|
+
const { profilePath, info } = input;
|
|
204
|
+
if (profilePath !== null) {
|
|
205
|
+
const restart = { level: 'info', message: 'Restart your shell or run: ' + color.cyan(`source ${profilePath}`) };
|
|
206
|
+
if (input.action === 'install')
|
|
207
|
+
return [{ level: 'success', message: `Safe-delete installed to ${color.dim(profilePath)}` }, restart];
|
|
208
|
+
if (input.action === 'upgrade')
|
|
209
|
+
return [{ level: 'success', message: `Safe-delete upgraded in ${color.dim(profilePath)}` }, restart];
|
|
210
|
+
if (input.state === 'current')
|
|
211
|
+
return [{ level: 'info', message: `Safe-delete already configured in ${color.dim(profilePath)}` }];
|
|
212
|
+
}
|
|
213
|
+
if (input.available || info.installHint === null)
|
|
214
|
+
return [];
|
|
215
|
+
if (!input.interactive) {
|
|
216
|
+
return [{ level: 'info', message: `Protect against accidental ${color.red('rm -rf')}: ${color.cyan(info.installHint)}` }];
|
|
217
|
+
}
|
|
218
|
+
if (profilePath === null)
|
|
219
|
+
return [];
|
|
220
|
+
return [
|
|
221
|
+
{ level: 'info', message: `Install ${color.cyan(info.command ?? 'trash')} first: ${color.dim(info.installHint)}` },
|
|
222
|
+
{ level: 'info', message: `Then re-run ${color.cyan('devflow init')} to auto-configure safe-delete.` },
|
|
223
|
+
];
|
|
224
|
+
}
|
|
147
225
|
export { addContextHook, removeContextHook, hasContextHook };
|
|
148
226
|
/**
|
|
149
227
|
* Combine workflow and language selections into a single plugin list.
|
|
@@ -194,7 +272,8 @@ export function resolveComplianceInitState(complianceOption, seedFrameworks) {
|
|
|
194
272
|
return undefined;
|
|
195
273
|
}
|
|
196
274
|
// Re-export formatComplianceSummary from compliance-prompts.ts so existing test imports
|
|
197
|
-
// (tests/init-logic.test.ts
|
|
275
|
+
// (the 'formatComplianceSummary' suite in tests/init-logic.test.ts imports it from
|
|
276
|
+
// '../src/cli/commands/init.js') keep resolving.
|
|
198
277
|
export { formatComplianceSummary } from './compliance-prompts.js';
|
|
199
278
|
/**
|
|
200
279
|
* Parse the --tracker CLI option into a tracker override.
|
|
@@ -222,7 +301,7 @@ export function resolveTrackerInitState(trackerOption) {
|
|
|
222
301
|
/**
|
|
223
302
|
* The outcome line for a provider that arrived as `--tracker <id>`.
|
|
224
303
|
*
|
|
225
|
-
* D-TRACKER-CLI-SURFACE
|
|
304
|
+
* D-TRACKER-CLI-SURFACE: the Advanced path prints no end-of-wizard
|
|
226
305
|
* summary, and `--tracker` suppresses the wizard step that would otherwise
|
|
227
306
|
* print one, so the CLI-override arm is the only place the selection can
|
|
228
307
|
* surface there. Without this line `devflow init --advanced --tracker jira`
|
|
@@ -255,7 +334,7 @@ export function buildTrackerLifecycleIO() {
|
|
|
255
334
|
*
|
|
256
335
|
* D-TRACKER-CONVERGE: the manifest write and the tracker file-lifecycle owners
|
|
257
336
|
* are ONE unit because their relative order is the invariant, not an
|
|
258
|
-
* implementation detail
|
|
337
|
+
* implementation detail. The manifest write is explicitly failable —
|
|
259
338
|
* init must not abort on it — so converging the sentinel ahead of it leaves the
|
|
260
339
|
* two disagreeing in both directions: github→jira writes a sentinel naming a
|
|
261
340
|
* provider the manifest never records, and jira→github removes the sentinel while
|
|
@@ -268,7 +347,7 @@ export function buildTrackerLifecycleIO() {
|
|
|
268
347
|
* taken as a separate argument, so there is exactly one binding and the sentinel
|
|
269
348
|
* cannot name a value other than the one on disk.
|
|
270
349
|
*
|
|
271
|
-
* Every step reports rather than aborts
|
|
350
|
+
* Every step reports rather than aborts: a
|
|
272
351
|
* feature-state change must never fail `devflow init`.
|
|
273
352
|
*/
|
|
274
353
|
export async function persistManifestThenConvergeTracker(opts) {
|
|
@@ -302,7 +381,7 @@ export async function persistManifestThenConvergeTracker(opts) {
|
|
|
302
381
|
messages.push({ level: 'warn', text });
|
|
303
382
|
// D-TRACKER-PARALLEL: the two owners touch disjoint files — the attempt
|
|
304
383
|
// counters and the sentinel — depend on nothing the other writes, and both
|
|
305
|
-
// report through TrackerResult instead of throwing
|
|
384
|
+
// report through TrackerResult instead of throwing, so they run
|
|
306
385
|
// concurrently and their warnings are pushed in a fixed order regardless of
|
|
307
386
|
// which settles first.
|
|
308
387
|
const [rearm, sentinel] = await Promise.all([
|
|
@@ -343,14 +422,25 @@ export async function persistManifestThenConvergeTracker(opts) {
|
|
|
343
422
|
* D-LEDGER-MAIN-WORKTREE: each queue drains where the hooks write it — memory at
|
|
344
423
|
* this checkout's toplevel (`gitRoot`), learning at the ledger root (`ledgerRoot`,
|
|
345
424
|
* getLedgerRoot), which in a linked worktree is the main checkout.
|
|
425
|
+
*
|
|
426
|
+
* Returns one warning for each drain refused because a folder on the way to its
|
|
427
|
+
* queue is a symbolic link (D-CLI-NO-SYMLINK); the caller prints them.
|
|
346
428
|
*/
|
|
347
429
|
export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue, drainLearningQueue }) {
|
|
348
430
|
if (!opts.manifestWritten)
|
|
349
|
-
return;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
431
|
+
return [];
|
|
432
|
+
const refused = [];
|
|
433
|
+
if (!opts.memoryEnabled && opts.gitRoot !== null) {
|
|
434
|
+
const memory = await io.drainMemoryQueue(opts.gitRoot);
|
|
435
|
+
if (!memory.drained)
|
|
436
|
+
refused.push(formatRefusedDrain('memory', memory.linkedFolder));
|
|
437
|
+
}
|
|
438
|
+
if (!opts.learningEnabled && opts.ledgerRoot !== null) {
|
|
439
|
+
const learning = await io.drainLearningQueue(opts.ledgerRoot);
|
|
440
|
+
if (!learning.drained)
|
|
441
|
+
refused.push(formatRefusedDrain('learning', learning.linkedFolder));
|
|
442
|
+
}
|
|
443
|
+
return refused;
|
|
354
444
|
}
|
|
355
445
|
/**
|
|
356
446
|
* The manifest `devflow init --hud-only` writes. Pure — never mutates `existing`.
|
|
@@ -361,7 +451,7 @@ export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue,
|
|
|
361
451
|
* earlier shape rewrote the whole record as a HUD-only fresh install (ambient,
|
|
362
452
|
* memory, learning, knowledge, rules and proxy all `false`, plugins `[]`) while
|
|
363
453
|
* leaving those features' artifacts on disk: the record stopped describing the
|
|
364
|
-
* machine, the next re-init seeded every feature off
|
|
454
|
+
* machine, the next re-init seeded every feature off, and with
|
|
365
455
|
* memory/learning/knowledge switched by the manifest alone
|
|
366
456
|
* (D-FEATURES-NARROW-ONLY) a HUD install would have really disabled them
|
|
367
457
|
* everywhere. `version` is kept too: --hud-only reinstalls no plugin, and a
|
|
@@ -759,7 +849,7 @@ export const initCommand = new Command('init')
|
|
|
759
849
|
// modePromptShown: true only when the Setup-mode p.select actually ran (i.e. the user
|
|
760
850
|
// was shown a live interactive prompt and made an active choice). Used by
|
|
761
851
|
// shouldRunComplianceStep to preserve the promptless contracts of --recommended and !isTTY
|
|
762
|
-
// without re-checking the mode name
|
|
852
|
+
// without re-checking the mode name.
|
|
763
853
|
let modePromptShown = false;
|
|
764
854
|
let useRecommended;
|
|
765
855
|
if (options.recommended) {
|
|
@@ -821,6 +911,8 @@ export const initCommand = new Command('init')
|
|
|
821
911
|
let claudeignoreEnabled = !!gitRoot;
|
|
822
912
|
let discoveredProjects = [];
|
|
823
913
|
let safeDeleteAction = 'skip';
|
|
914
|
+
// The profile's block before this run; null when no profile was checked.
|
|
915
|
+
let safeDeleteState = null;
|
|
824
916
|
let safeDeleteBlock = null;
|
|
825
917
|
// Security mode is resolved from flag + manifest + detected reality via resolveSecurityAction.
|
|
826
918
|
// The final value is written to the manifest and consumed by the dedicated security step.
|
|
@@ -877,7 +969,7 @@ export const initCommand = new Command('init')
|
|
|
877
969
|
// ── Recommended path ──
|
|
878
970
|
// B4: compliance wizard step — runs only when the Setup-mode prompt actually ran
|
|
879
971
|
// (modePromptShown=true), preserving the promptless contracts of --recommended and !isTTY.
|
|
880
|
-
// shouldRunComplianceStep gates on modePromptShown rather than the mode name
|
|
972
|
+
// shouldRunComplianceStep gates on modePromptShown rather than the mode name.
|
|
881
973
|
// Re-init never routes here (seedManifest !== null → banner path → Advanced), so this path
|
|
882
974
|
// always sees fresh-install defaults.
|
|
883
975
|
let wizardCompliance;
|
|
@@ -949,14 +1041,17 @@ export const initCommand = new Command('init')
|
|
|
949
1041
|
]);
|
|
950
1042
|
discoveredProjects = discoveredResult;
|
|
951
1043
|
if (needsVersionCheck) {
|
|
952
|
-
|
|
953
|
-
if (
|
|
1044
|
+
safeDeleteState = classifySafeDeleteState(installedVersionResult, SAFE_DELETE_BLOCK_VERSION);
|
|
1045
|
+
if (safeDeleteState === 'current')
|
|
954
1046
|
safeDeleteAction = 'skip';
|
|
955
|
-
else if (
|
|
1047
|
+
else if (safeDeleteState === 'outdated')
|
|
956
1048
|
safeDeleteAction = 'upgrade';
|
|
957
1049
|
else
|
|
958
1050
|
safeDeleteAction = 'install';
|
|
959
1051
|
}
|
|
1052
|
+
// D-INIT-REAL-OUTCOME: the summary prints before the install runs, so its
|
|
1053
|
+
// .claudeignore row comes from what each project the install targets holds.
|
|
1054
|
+
const claudeignorePresent = await Promise.all((claudeignoreEnabled ? projectRoots(discoveredProjects, gitRoot) : []).map(hasClaudeignore));
|
|
960
1055
|
// Print summary
|
|
961
1056
|
const defaultFlagCount = countActiveFlags(enabledFlags);
|
|
962
1057
|
const complianceSummary = formatComplianceSummary(complianceEnabled, complianceFrameworks);
|
|
@@ -975,8 +1070,8 @@ export const initCommand = new Command('init')
|
|
|
975
1070
|
`Tracker: ${formatTrackerSummary(trackerProvider)}`,
|
|
976
1071
|
`View mode: ${readViewMode(enabledFlags)}`,
|
|
977
1072
|
`Claude Code flags: ${defaultFlagCount} configured`,
|
|
978
|
-
|
|
979
|
-
|
|
1073
|
+
`.claudeignore: ${resolveClaudeignoreOutcome(claudeignorePresent)}`,
|
|
1074
|
+
SAFE_DELETE_SUMMARY_ROW[safeDeleteAction],
|
|
980
1075
|
].filter(l => l.trim()).join('\n');
|
|
981
1076
|
p.note(summaryLines + `\n\nCustomize later: ${color.cyan('devflow init --advanced')}`, 'Recommended settings applied');
|
|
982
1077
|
}
|
|
@@ -1131,9 +1226,9 @@ export const initCommand = new Command('init')
|
|
|
1131
1226
|
isTTY: process.stdin.isTTY,
|
|
1132
1227
|
hasCliOverride: cliComplianceOverride !== undefined,
|
|
1133
1228
|
})) {
|
|
1134
|
-
// runComplianceStep: note with "Current setting:" header (legible on re-init
|
|
1229
|
+
// runComplianceStep: note with "Current setting:" header (legible on re-init),
|
|
1135
1230
|
// labeled Yes/No select (immune to Enter-through muscle memory), and framework multiselect.
|
|
1136
|
-
// Returns {kind:'cancelled'} on Escape — caller owns the cancel idiom
|
|
1231
|
+
// Returns {kind:'cancelled'} on Escape — caller owns the cancel idiom.
|
|
1137
1232
|
const complianceStep = await runComplianceStep({
|
|
1138
1233
|
seed: { enabled: complianceEnabled, frameworks: complianceFrameworks },
|
|
1139
1234
|
prompts: buildClackCompliancePrompts(),
|
|
@@ -1187,19 +1282,19 @@ export const initCommand = new Command('init')
|
|
|
1187
1282
|
// silently carries the seeded value. The gate stays an explicit predicate call so the
|
|
1188
1283
|
// documented gate table in attribution-prompts.ts remains the single authority.
|
|
1189
1284
|
// isTTY is guaranteed true here (the non-TTY guard above exit-1'd); passing it keeps
|
|
1190
|
-
// the promptless contract enforced at the predicate rather than by position
|
|
1285
|
+
// the promptless contract enforced at the predicate rather than by position.
|
|
1191
1286
|
if (shouldRunAttributionStep({
|
|
1192
1287
|
// D27-GATE: bind to the resolved mode so all four documented gate-table rows
|
|
1193
1288
|
// are reachable and the predicate — not lexical placement — enforces Advanced-only.
|
|
1194
1289
|
// Using useRecommended ? 'recommended' : 'advanced' makes the gate testable from
|
|
1195
1290
|
// both sides and prevents the Recommended path from accidentally running the step
|
|
1196
|
-
// if this block is ever repositioned
|
|
1291
|
+
// if this block is ever repositioned.
|
|
1197
1292
|
mode: useRecommended ? 'recommended' : 'advanced',
|
|
1198
1293
|
isTTY: process.stdin.isTTY,
|
|
1199
1294
|
})) {
|
|
1200
1295
|
const attributionStep = await runAttributionStep({
|
|
1201
1296
|
// attributionSeedFrom keeps the seed a real boolean regardless of the stored
|
|
1202
|
-
// FlagsRecord value type — undefined/null/false all map to false
|
|
1297
|
+
// FlagsRecord value type — undefined/null/false all map to false.
|
|
1203
1298
|
seed: attributionSeedFrom(enabledFlags),
|
|
1204
1299
|
prompts: buildClackAttributionPrompts(),
|
|
1205
1300
|
});
|
|
@@ -1220,7 +1315,7 @@ export const initCommand = new Command('init')
|
|
|
1220
1315
|
/**
|
|
1221
1316
|
* D40: init applies seeded flag defaults non-interactively. Flags are customized
|
|
1222
1317
|
* exclusively via `devflow flags`; re-init preserves existing values and adopts
|
|
1223
|
-
* registry defaults only for absent flags
|
|
1318
|
+
* registry defaults only for absent flags. No TUI is opened during init.
|
|
1224
1319
|
*/
|
|
1225
1320
|
{
|
|
1226
1321
|
const activeCount = countActiveFlags(enabledFlags);
|
|
@@ -1270,11 +1365,11 @@ export const initCommand = new Command('init')
|
|
|
1270
1365
|
safeDeleteBlock = generateSafeDeleteBlock(shell, process.platform, trashCmd);
|
|
1271
1366
|
if (safeDeleteBlock) {
|
|
1272
1367
|
const installedVersion = await getInstalledVersion(profilePath);
|
|
1273
|
-
|
|
1274
|
-
if (
|
|
1368
|
+
safeDeleteState = classifySafeDeleteState(installedVersion, SAFE_DELETE_BLOCK_VERSION);
|
|
1369
|
+
if (safeDeleteState === 'current') {
|
|
1275
1370
|
safeDeleteAction = 'skip';
|
|
1276
1371
|
}
|
|
1277
|
-
else if (
|
|
1372
|
+
else if (safeDeleteState === 'outdated') {
|
|
1278
1373
|
safeDeleteAction = 'upgrade';
|
|
1279
1374
|
}
|
|
1280
1375
|
else {
|
|
@@ -1474,10 +1569,10 @@ export const initCommand = new Command('init')
|
|
|
1474
1569
|
p.log.error(`${error}`);
|
|
1475
1570
|
process.exit(1);
|
|
1476
1571
|
}
|
|
1477
|
-
// Converge compliance artifacts (
|
|
1572
|
+
// Converge compliance artifacts (always converge, never short-circuit).
|
|
1478
1573
|
// Called unconditionally so that enabling/disabling compliance during init
|
|
1479
1574
|
// is reflected in the installed artifacts without a separate devflow compliance run.
|
|
1480
|
-
// Wrapped in its own try/catch (
|
|
1575
|
+
// Wrapped in its own try/catch (warn-not-abort).
|
|
1481
1576
|
let convergeResult = null;
|
|
1482
1577
|
try {
|
|
1483
1578
|
convergeResult = await convergeFromManifest({
|
|
@@ -1525,7 +1620,7 @@ export const initCommand = new Command('init')
|
|
|
1525
1620
|
}
|
|
1526
1621
|
// Sweep orphaned *-teams.md workflow command variants left by the Agent Teams
|
|
1527
1622
|
// refactor. None are ever re-installed, so a blanket sweep is safe on any
|
|
1528
|
-
// install type (the full-install dir wipe only covers full installs).
|
|
1623
|
+
// install type (the full-install dir wipe only covers full installs).
|
|
1529
1624
|
try {
|
|
1530
1625
|
for (const f of await fs.readdir(commandsDir)) {
|
|
1531
1626
|
if (f.endsWith('-teams.md')) {
|
|
@@ -1598,6 +1693,7 @@ export const initCommand = new Command('init')
|
|
|
1598
1693
|
'dream-procedure.md',
|
|
1599
1694
|
'lib/staleness.cjs',
|
|
1600
1695
|
'dream-lock',
|
|
1696
|
+
'decisions-usage-scan.cjs',
|
|
1601
1697
|
];
|
|
1602
1698
|
const hooksDir = path.join(devflowDir, 'scripts', 'hooks');
|
|
1603
1699
|
for (const legacy of LEGACY_HOOK_FILES) {
|
|
@@ -1613,7 +1709,7 @@ export const initCommand = new Command('init')
|
|
|
1613
1709
|
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
1614
1710
|
// === Proxy preflight (when enabled) ===
|
|
1615
1711
|
// Runs before the settings mutation pass so that proxyEnabled reflects reality
|
|
1616
|
-
// (preflight failure forces it off without aborting init
|
|
1712
|
+
// (preflight failure forces it off without aborting init).
|
|
1617
1713
|
//
|
|
1618
1714
|
// Read existing proxy state once to recover the remembered port.
|
|
1619
1715
|
// Init has no --port option; the remembered port from proxy.json always
|
|
@@ -1660,7 +1756,7 @@ export const initCommand = new Command('init')
|
|
|
1660
1756
|
'Routing disabled for this init — run `devflow proxy --enable` after signing in.');
|
|
1661
1757
|
proxyEnabled = false;
|
|
1662
1758
|
// Write proxy.json disabled so runtime authority converges with the
|
|
1663
|
-
// failed preflight outcome
|
|
1759
|
+
// failed preflight outcome. Without this write,
|
|
1664
1760
|
// proxy.json stays enabled:true from the prior run, causing
|
|
1665
1761
|
// reapplyAgentMapping to write GPT model IDs into agent frontmatter
|
|
1666
1762
|
// with no relay — a dormancy inversion.
|
|
@@ -1695,7 +1791,7 @@ export const initCommand = new Command('init')
|
|
|
1695
1791
|
}
|
|
1696
1792
|
else {
|
|
1697
1793
|
// Proxy disabled at entry: if proxy.json exists and is enabled, mark it disabled.
|
|
1698
|
-
//
|
|
1794
|
+
// Runtime authority (proxy.json) must converge with proxyEnabled=false.
|
|
1699
1795
|
// Check the Result — write errors are surfaced rather than silently ignored
|
|
1700
1796
|
// (consistent with the enable branch above).
|
|
1701
1797
|
if (priorProxyStateResult.ok && priorProxyStateResult.value.enabled) {
|
|
@@ -1716,7 +1812,7 @@ export const initCommand = new Command('init')
|
|
|
1716
1812
|
// MUST run AFTER the proxy preflight block above: preflight can force proxyEnabled=false
|
|
1717
1813
|
// on failure, and reapply's dormancy (GPT models materialize only while proxy enabled)
|
|
1718
1814
|
// depends on the FINAL proxyEnabled value — running earlier would leave GPT model lines
|
|
1719
|
-
// in agent frontmatter after a preflight failure. Per-item failures are non-fatal
|
|
1815
|
+
// in agent frontmatter after a preflight failure. Per-item failures are non-fatal.
|
|
1720
1816
|
//
|
|
1721
1817
|
// Init-only optimization: skip reapply when mapping is empty AND proxy is off.
|
|
1722
1818
|
// An empty mapping means every agent uses its shipped default; the file copy already
|
|
@@ -1725,7 +1821,9 @@ export const initCommand = new Command('init')
|
|
|
1725
1821
|
{
|
|
1726
1822
|
const agentInstallDir = path.join(claudeDir, 'agents', 'devflow');
|
|
1727
1823
|
const preCheckMapping = await readAgentMapping(devflowDir);
|
|
1728
|
-
|
|
1824
|
+
// hasAgentMappingEntries counts agent entries only: an agents.memory-only
|
|
1825
|
+
// mapping names no installed file, so it keeps this gate closed (D-WORKER-AGENTS).
|
|
1826
|
+
const hasMappingEntries = preCheckMapping.ok && hasAgentMappingEntries(preCheckMapping.value);
|
|
1729
1827
|
if (hasMappingEntries || proxyEnabled) {
|
|
1730
1828
|
const reapplyResult = await reapplyAgentMapping({
|
|
1731
1829
|
proxyEnabled,
|
|
@@ -1776,7 +1874,7 @@ export const initCommand = new Command('init')
|
|
|
1776
1874
|
// Strip Devflow-managed teammateMode ("auto"). User-set values (e.g. "tmux") are preserved.
|
|
1777
1875
|
content = stripDevflowTeammateModeFromJson(content);
|
|
1778
1876
|
// Claude Code flags — convergeFlagsIntoSettings is the single pipeline entry point
|
|
1779
|
-
// (ARCH-H1
|
|
1877
|
+
// (ARCH-H1): fold valued flags and view-mode from
|
|
1780
1878
|
// existing settings before strip, then strip all managed keys and apply the folded
|
|
1781
1879
|
// record. ownedRecord=existingManifest?.features.flags??null distinguishes keys
|
|
1782
1880
|
// devflow previously wrote (must not be overridden by fold) from keys newly adopted
|
|
@@ -1806,7 +1904,7 @@ export const initCommand = new Command('init')
|
|
|
1806
1904
|
// would silently delete both their URL and the window-enforcement var.
|
|
1807
1905
|
// Invariant: proxy.json always reflects the final settled state after the
|
|
1808
1906
|
// preflight block above — all paths that force proxyEnabled=false also write
|
|
1809
|
-
// proxy.json enabled:false
|
|
1907
|
+
// proxy.json enabled:false, so managedPort == effectivePort.
|
|
1810
1908
|
if (await proxyJsonExists(devflowDir)) {
|
|
1811
1909
|
const proxyStateForStrip = await readProxyState(devflowDir);
|
|
1812
1910
|
const managedPort = proxyStateForStrip.ok ? proxyStateForStrip.value.port : DEFAULT_PROXY_PORT;
|
|
@@ -1838,14 +1936,14 @@ export const initCommand = new Command('init')
|
|
|
1838
1936
|
// read-modify-write, not a whole-file write: init owns only reviewPublication,
|
|
1839
1937
|
// and every other key in the file — the hand-written per-repo `tracker`
|
|
1840
1938
|
// override first among them — is carried from disk, under --reset too
|
|
1841
|
-
// (D-CONFIG-PRESERVE-UNMANAGED in feature-config.ts
|
|
1939
|
+
// (D-CONFIG-PRESERVE-UNMANAGED in feature-config.ts).
|
|
1842
1940
|
// A malformed or unreadable file is left untouched and named (D-CONFIG-NO-REPAIR).
|
|
1843
1941
|
if (gitRoot) {
|
|
1844
1942
|
const configWrite = await writeManagedConfig(gitRoot, {
|
|
1845
1943
|
// reviewPublication has no prompt, so it is carried over from the
|
|
1846
1944
|
// reset-gated snapshot rather than re-read from disk: seedConfig is null
|
|
1847
1945
|
// under --reset, which is what collapses the field back to 'auto' with
|
|
1848
|
-
// every other feature (
|
|
1946
|
+
// every other feature (read the post-gate binding, not the file).
|
|
1849
1947
|
reviewPublication: seedConfig?.reviewPublication ?? DEFAULT_CONFIG.reviewPublication,
|
|
1850
1948
|
});
|
|
1851
1949
|
if (!configWrite.ok)
|
|
@@ -1854,10 +1952,10 @@ export const initCommand = new Command('init')
|
|
|
1854
1952
|
// Configure HUD
|
|
1855
1953
|
const existingHud = loadHudConfig();
|
|
1856
1954
|
saveHudConfig({ enabled: hudEnabled, detail: existingHud.detail });
|
|
1857
|
-
// File extras
|
|
1955
|
+
// File extras — into the projects the Recommended summary's .claudeignore row checked.
|
|
1858
1956
|
if (claudeignoreEnabled) {
|
|
1957
|
+
const results = await Promise.all(projectRoots(discoveredProjects, gitRoot).map(root => installClaudeignore(root, rootDir, verbose)));
|
|
1859
1958
|
if (discoveredProjects.length > 0) {
|
|
1860
|
-
const results = await Promise.all(discoveredProjects.map(root => installClaudeignore(root, rootDir, verbose)));
|
|
1861
1959
|
const created = results.filter(Boolean).length;
|
|
1862
1960
|
if (created > 0) {
|
|
1863
1961
|
p.log.success(`.claudeignore created in ${created} project(s)`);
|
|
@@ -1866,14 +1964,11 @@ export const initCommand = new Command('init')
|
|
|
1866
1964
|
p.log.info(`.claudeignore already exists in all ${discoveredProjects.length} project(s)`);
|
|
1867
1965
|
}
|
|
1868
1966
|
}
|
|
1869
|
-
else if (gitRoot) {
|
|
1870
|
-
await installClaudeignore(gitRoot, rootDir, verbose);
|
|
1871
|
-
}
|
|
1872
1967
|
}
|
|
1873
1968
|
// Deterministically ensure .devflow/ is gitignored at the repo root — independent
|
|
1874
1969
|
// of every feature toggle. The always-on ensure-root-gitignore
|
|
1875
1970
|
// hook covers projects that never re-run init; this covers the init-time path so a
|
|
1876
|
-
// fresh install never tracks .devflow/. Decoupled from memory
|
|
1971
|
+
// fresh install never tracks .devflow/. Decoupled from memory.
|
|
1877
1972
|
if (gitRoot) {
|
|
1878
1973
|
await ensureDevflowGitignore(gitRoot, verbose);
|
|
1879
1974
|
}
|
|
@@ -1932,7 +2027,7 @@ export const initCommand = new Command('init')
|
|
|
1932
2027
|
s.start('Finalizing installation...');
|
|
1933
2028
|
}
|
|
1934
2029
|
else {
|
|
1935
|
-
//
|
|
2030
|
+
// User declined sudo and chose the settings.json fallback.
|
|
1936
2031
|
// securityMode is 'managed' (from resolveSecurityAction or interactive choice) but
|
|
1937
2032
|
// managedSettingsConfirmed is false — honor the "fall back to settings.json" label
|
|
1938
2033
|
// by writing to user settings. Manifest will record 'user' to match reality.
|
|
@@ -1987,7 +2082,7 @@ export const initCommand = new Command('init')
|
|
|
1987
2082
|
}
|
|
1988
2083
|
else {
|
|
1989
2084
|
// Exhaustive guard — if TypeScript reaches here, a new SecurityMode variant was added
|
|
1990
|
-
// without a matching branch
|
|
2085
|
+
// without a matching branch — guards against stale references after rename/refactor.
|
|
1991
2086
|
const _exhaustive = securityMode;
|
|
1992
2087
|
void _exhaustive;
|
|
1993
2088
|
}
|
|
@@ -2038,7 +2133,7 @@ export const initCommand = new Command('init')
|
|
|
2038
2133
|
// devflow:compliance is never swept here — no suppression predicate is needed.
|
|
2039
2134
|
logSummaryLines(formatSweepSummary(installReport));
|
|
2040
2135
|
// Reference-overlay reporting: the overlay rewrites files inside an installed skill
|
|
2041
|
-
// the user may have shadowed, and reports any unit it had to leave alone
|
|
2136
|
+
// the user may have shadowed, and reports any unit it had to leave alone.
|
|
2042
2137
|
logSummaryLines(formatOverlaySummary(installReport));
|
|
2043
2138
|
// Skill-scoping reporting: a deselected skill is deleted and a dormant shadow
|
|
2044
2139
|
// is inert, and neither is distinguishable from "never installed" on disk.
|
|
@@ -2060,34 +2155,14 @@ export const initCommand = new Command('init')
|
|
|
2060
2155
|
p.note(commandsNote, 'Available commands');
|
|
2061
2156
|
}
|
|
2062
2157
|
// Safe-delete status messages (after spinner)
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
}
|
|
2072
|
-
else if (safeDeleteAvailable && safeDeleteBlock) {
|
|
2073
|
-
const installedVersion = await getInstalledVersion(profilePath);
|
|
2074
|
-
if (classifySafeDeleteState(installedVersion, SAFE_DELETE_BLOCK_VERSION) === 'current') {
|
|
2075
|
-
p.log.info(`Safe-delete already configured in ${color.dim(profilePath)}`);
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
else if (!safeDeleteAvailable && safeDeleteInfo.installHint) {
|
|
2079
|
-
p.log.info(`Install ${color.cyan(safeDeleteInfo.command ?? 'trash')} first: ${color.dim(safeDeleteInfo.installHint)}`);
|
|
2080
|
-
p.log.info(`Then re-run ${color.cyan('devflow init')} to auto-configure safe-delete.`);
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2083
|
-
else if (!process.stdin.isTTY) {
|
|
2084
|
-
if (safeDeleteAvailable && safeDeleteInfo.command) {
|
|
2085
|
-
p.log.info(`Safe-delete available (${safeDeleteInfo.command}). Run interactively to auto-install.`);
|
|
2086
|
-
}
|
|
2087
|
-
else if (safeDeleteInfo.installHint) {
|
|
2088
|
-
p.log.info(`Protect against accidental ${color.red('rm -rf')}: ${color.cyan(safeDeleteInfo.installHint)}`);
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2158
|
+
logSummaryLines(formatSafeDeleteStatus({
|
|
2159
|
+
interactive: process.stdin.isTTY === true,
|
|
2160
|
+
action: safeDeleteAction,
|
|
2161
|
+
state: safeDeleteState,
|
|
2162
|
+
available: safeDeleteAvailable,
|
|
2163
|
+
profilePath,
|
|
2164
|
+
info: safeDeleteInfo,
|
|
2165
|
+
}));
|
|
2091
2166
|
// Verbose mode: show details
|
|
2092
2167
|
if (verbose) {
|
|
2093
2168
|
const pluginsList = pluginsToInstall
|
|
@@ -2120,7 +2195,7 @@ export const initCommand = new Command('init')
|
|
|
2120
2195
|
knowledge: knowledgeEnabled,
|
|
2121
2196
|
learning: learningEnabled,
|
|
2122
2197
|
rules: rulesEnabled,
|
|
2123
|
-
// FlagsRecord written directly — key-presence encodes "known"
|
|
2198
|
+
// FlagsRecord written directly — key-presence encodes "known".
|
|
2124
2199
|
// view-mode is encoded as flags['view-mode'] (the resolved final value).
|
|
2125
2200
|
flags: enabledFlags,
|
|
2126
2201
|
security: securityMode,
|
|
@@ -2141,7 +2216,7 @@ export const initCommand = new Command('init')
|
|
|
2141
2216
|
// ── Manifest write + tracker selection lifecycle (the ONE call site) ──────
|
|
2142
2217
|
// persistManifestThenConvergeTracker owns the ordering invariant: the
|
|
2143
2218
|
// tracker file-lifecycle owners in src/core/tracker.ts converge only against
|
|
2144
|
-
// a provider the manifest actually persisted (D-TRACKER-CONVERGE
|
|
2219
|
+
// a provider the manifest actually persisted (D-TRACKER-CONVERGE).
|
|
2145
2220
|
const trackerLifecycle = await persistManifestThenConvergeTracker({
|
|
2146
2221
|
devflowDir,
|
|
2147
2222
|
claudeDir,
|
|
@@ -2155,13 +2230,15 @@ export const initCommand = new Command('init')
|
|
|
2155
2230
|
p.log.info(msg.text);
|
|
2156
2231
|
}
|
|
2157
2232
|
// Only now that the machine-wide switch is on disk (D-INIT-DRAIN-AFTER-SWITCH).
|
|
2158
|
-
await drainDisabledFeatureQueues({
|
|
2233
|
+
const refusedDrains = await drainDisabledFeatureQueues({
|
|
2159
2234
|
gitRoot,
|
|
2160
2235
|
ledgerRoot: learningEnabled || gitRoot === null ? null : await getLedgerRoot(),
|
|
2161
2236
|
memoryEnabled,
|
|
2162
2237
|
learningEnabled,
|
|
2163
2238
|
manifestWritten: trackerLifecycle.manifestWritten,
|
|
2164
2239
|
});
|
|
2240
|
+
for (const line of refusedDrains)
|
|
2241
|
+
p.log.warn(line);
|
|
2165
2242
|
// The hooks' per-directory log folders, capped (D-LOG-DIR-CAP): one pass
|
|
2166
2243
|
// clears every folder it scans beyond the cap; only a backlog beyond the
|
|
2167
2244
|
// scan bound (MAX_LOG_DIRS_SCANNED, 100,000) waits for the next init.
|