devflow-kit 2.4.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +229 -0
- package/README.md +111 -18
- package/dist/agents/git.md +822 -0
- package/dist/cli/commands/agents.js +6 -1
- package/dist/cli/commands/ambient.js +160 -145
- package/dist/cli/commands/attribution-prompts.js +1 -1
- package/dist/cli/commands/capture.js +29 -55
- package/dist/cli/commands/compliance-prompts.js +1 -1
- package/dist/cli/commands/compliance.js +48 -55
- package/dist/cli/commands/context.js +17 -32
- package/dist/cli/commands/debug.js +65 -26
- package/dist/cli/commands/flags.js +3 -3
- package/dist/cli/commands/hud.js +34 -10
- package/dist/cli/commands/init-seed.js +61 -27
- package/dist/cli/commands/init.js +649 -240
- package/dist/cli/commands/install-report.js +200 -0
- package/dist/cli/commands/knowledge/index.js +2 -2
- package/dist/cli/commands/knowledge/toggle.js +35 -37
- package/dist/cli/commands/learning.js +79 -57
- package/dist/cli/commands/legacy-hooks.js +11 -14
- package/dist/cli/commands/memory.js +134 -135
- package/dist/cli/commands/prompt-io.js +4 -4
- package/dist/cli/commands/proxy.js +23 -41
- package/dist/cli/commands/security.js +81 -29
- package/dist/cli/commands/skills.js +71 -7
- package/dist/cli/commands/tracker-prompts.js +145 -0
- package/dist/cli/commands/tracker.js +277 -0
- package/dist/cli/commands/uninstall.js +520 -169
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +58 -14
- package/dist/commands/code-review.md +110 -32
- package/dist/commands/debug.md +55 -11
- package/dist/commands/dynamic-build.md +344 -73
- package/dist/commands/dynamic-plan.md +77 -27
- package/dist/commands/dynamic-profile.md +25 -11
- package/dist/commands/dynamic-tickets.md +76 -15
- package/dist/commands/explore.md +37 -7
- package/dist/commands/implement.md +314 -62
- package/dist/commands/plan.md +146 -32
- package/dist/commands/release.md +64 -17
- package/dist/commands/research.md +34 -8
- package/dist/commands/resolve.md +196 -68
- package/dist/commands/self-review.md +45 -9
- package/dist/core/agent-models.js +55 -12
- package/dist/core/assets.js +58 -2
- package/dist/core/compliance-compose.js +27 -27
- package/dist/core/evidence-policy.js +363 -0
- package/dist/core/feature-config.js +200 -65
- package/dist/core/feature-switch.js +112 -0
- package/dist/core/flags.js +34 -6
- package/dist/core/fs-atomic.js +27 -0
- package/dist/core/hook-log-dirs.js +104 -0
- package/dist/core/learning-tuning-config.js +5 -3
- package/dist/core/ledger-root.js +102 -0
- package/dist/core/manifest.js +38 -10
- package/dist/core/mds-variants.js +798 -0
- package/dist/core/migrations.js +49 -23
- package/dist/core/model-discovery.js +12 -1
- package/dist/core/plugins.js +361 -12
- package/dist/core/project-paths.js +1 -18
- package/dist/core/proxy-log.js +8 -6
- package/dist/core/proxy-state.js +11 -8
- package/dist/core/reference-sweep.js +136 -0
- package/dist/core/same-location.js +25 -0
- package/dist/core/tracker.js +494 -0
- package/dist/hud/components/config-counts.js +15 -4
- package/dist/hud/components/learning-counts.js +14 -0
- package/dist/hud/config.js +2 -1
- package/dist/hud/cost-history.js +2 -4
- package/dist/hud/git.js +52 -7
- package/dist/hud/index.js +7 -9
- package/dist/skills/git/references/decision-markers.md +19 -0
- package/dist/skills/git/references/learn-conventions.md +56 -0
- package/dist/skills/git/references/pr/check-ci-status.md +14 -0
- package/dist/skills/git/references/pr/check-merge-readiness.md +28 -0
- package/dist/skills/git/references/pr/ensure-pr-ready.md +24 -0
- package/dist/skills/git/references/pr/fetch-review-threads.md +22 -0
- package/dist/skills/git/references/pr/post-resolution-summary.md +40 -0
- package/dist/skills/git/references/pr/post-review-summary.md +42 -0
- package/dist/skills/git/references/pr/resolve-review-threads.md +35 -0
- package/dist/skills/git/references/pr/update-pr-evidence.md +14 -0
- package/dist/skills/git/references/pr/validate-branch.md +18 -0
- package/dist/skills/git/references/publication-gate.md +13 -0
- package/dist/skills/git/references/tracker/_mcp.md +153 -0
- package/dist/skills/git/references/tracker/github/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +40 -0
- package/dist/skills/git/references/tracker/github/create-release.md +11 -0
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +16 -0
- package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +69 -0
- package/dist/skills/git/references/tracker/github/fetch-issue.md +32 -0
- package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +17 -0
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +19 -0
- package/dist/skills/git/references/tracker/github/manage-debt.md +101 -0
- package/dist/skills/git/references/tracker/github/post-wave-report.md +28 -0
- package/dist/skills/git/references/tracker/github/setup-task.md +26 -0
- package/dist/skills/git/references/tracker/jira/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +49 -0
- package/dist/skills/git/references/tracker/jira/create-release.md +17 -0
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +22 -0
- package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +53 -0
- package/dist/skills/git/references/tracker/jira/fetch-issue.md +14 -0
- package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +15 -0
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +18 -0
- package/dist/skills/git/references/tracker/jira/manage-debt.md +37 -0
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +33 -0
- package/dist/skills/git/references/tracker/jira/setup-task.md +31 -0
- package/dist/skills/git/references/tracker/linear/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +53 -0
- package/dist/skills/git/references/tracker/linear/create-release.md +17 -0
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +22 -0
- package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +53 -0
- package/dist/skills/git/references/tracker/linear/fetch-issue.md +14 -0
- package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +15 -0
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +18 -0
- package/dist/skills/git/references/tracker/linear/manage-debt.md +37 -0
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +33 -0
- package/dist/skills/git/references/tracker/linear/setup-task.md +32 -0
- package/dist/skills/git/references/trust-rule.md +7 -0
- package/dist/targets/claude-code/claude-paths.js +59 -57
- package/dist/targets/claude-code/compliance-install.js +49 -65
- package/dist/targets/claude-code/hooks.js +108 -3
- package/dist/targets/claude-code/installer.js +1187 -32
- package/dist/targets/claude-code/legacy.js +5 -0
- package/dist/targets/claude-code/post-install.js +366 -151
- package/dist/targets/claude-code/tracker-install.js +134 -0
- package/package.json +8 -6
- package/src/assets/agents/code.md +45 -6
- package/src/assets/agents/design.md +2 -1
- package/src/assets/agents/git.mds +825 -0
- package/src/assets/agents/knowledge.md +3 -3
- package/src/assets/agents/learning.md +11 -0
- package/src/assets/agents/review.md +3 -1
- package/src/assets/agents/synthesize.md +1 -1
- package/src/assets/agents/test.md +16 -5
- package/src/assets/agents/tracker.md +474 -0
- package/src/assets/agents/validate.md +7 -5
- package/src/assets/commands/_partials/_compliance.mds +19 -1
- package/src/assets/commands/_partials/_decisions.mds +15 -3
- package/src/assets/commands/_partials/_docs_root.mds +35 -0
- package/src/assets/commands/_partials/_engine.mds +13 -11
- package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
- package/src/assets/commands/_partials/_factory.mds +1 -1
- package/src/assets/commands/_partials/_knowledge.mds +27 -9
- package/src/assets/commands/_partials/_plan_contract.mds +22 -7
- package/src/assets/commands/_partials/_preamble.mds +2 -2
- package/src/assets/commands/_partials/_publication.mds +8 -2
- package/src/assets/commands/_partials/_settings.mds +28 -0
- package/src/assets/commands/_partials/_ticket_template.mds +3 -2
- package/src/assets/commands/_partials/_tracker.mds +18 -0
- package/src/assets/commands/_partials/_wave.mds +16 -10
- package/src/assets/commands/bug-analysis.mds +31 -19
- package/src/assets/commands/code-review.mds +67 -41
- package/src/assets/commands/debug.mds +13 -7
- package/src/assets/commands/dynamic-build.mds +274 -66
- package/src/assets/commands/dynamic-plan.mds +50 -23
- package/src/assets/commands/dynamic-profile.mds +24 -11
- package/src/assets/commands/dynamic-tickets.mds +63 -16
- package/src/assets/commands/explore.mds +4 -5
- package/src/assets/commands/implement.mds +234 -67
- package/src/assets/commands/plan.mds +91 -33
- package/src/assets/commands/release.md +64 -17
- package/src/assets/commands/research.mds +11 -9
- package/src/assets/commands/resolve.mds +150 -78
- package/src/assets/commands/self-review.mds +24 -25
- package/src/assets/mds/git/_pr.mds +331 -0
- package/src/assets/mds/git/_references.mds +135 -0
- package/src/assets/mds/tracker/_common.mds +156 -0
- package/src/assets/mds/tracker/_github.mds +472 -0
- package/src/assets/mds/tracker/_jira.mds +407 -0
- package/src/assets/mds/tracker/_linear.mds +449 -0
- package/src/assets/mds/tracker/_mcp.mds +305 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
- package/src/assets/scripts/hooks/background-memory-update +40 -19
- package/src/assets/scripts/hooks/capture-prompt +18 -8
- package/src/assets/scripts/hooks/capture-question +18 -8
- package/src/assets/scripts/hooks/capture-turn +27 -13
- package/src/assets/scripts/hooks/debug-trace +11 -6
- package/src/assets/scripts/hooks/ensure-devflow-init +33 -6
- package/src/assets/scripts/hooks/ensure-proxy +9 -8
- package/src/assets/scripts/hooks/ensure-root-gitignore +236 -60
- package/src/assets/scripts/hooks/git-marker +48 -0
- package/src/assets/scripts/hooks/hook-log-init +3 -1
- package/src/assets/scripts/hooks/json-helper.cjs +228 -5
- package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -20
- package/src/assets/scripts/hooks/log-paths +80 -0
- package/src/assets/scripts/hooks/memory-worker +22 -13
- package/src/assets/scripts/hooks/pre-compact-memory +44 -15
- package/src/assets/scripts/hooks/preamble +1 -4
- package/src/assets/scripts/hooks/queue-append +146 -28
- package/src/assets/scripts/hooks/resolve-project-root +101 -7
- package/src/assets/scripts/hooks/session-start-context +534 -20
- package/src/assets/scripts/hooks/session-start-memory +38 -15
- package/src/assets/scripts/lib/project-config.cjs +633 -0
- package/src/assets/scripts/pr-evidence.cjs +1961 -0
- package/src/assets/scripts/redact-secrets.cjs +490 -62
- package/src/assets/scripts/release-trace.cjs +1143 -0
- package/src/assets/scripts/resolve-evidence-policy.cjs +1145 -0
- package/src/assets/scripts/resolve-settings.cjs +1054 -0
- package/src/assets/scripts/verify-evidence.cjs +1822 -0
- package/src/assets/skills/compliance/SKILL.md +4 -2
- package/src/assets/skills/docs-framework/SKILL.md +11 -10
- package/src/assets/skills/docs-framework/references/patterns.md +10 -17
- package/src/assets/skills/gap-analysis/SKILL.md +2 -2
- package/src/assets/skills/git/SKILL.md +8 -78
- package/src/assets/skills/git/references/github-api.md +179 -141
- package/src/assets/skills/git/references/patterns.md +11 -6
- package/src/assets/skills/review-methodology/SKILL.md +1 -1
- package/src/assets/skills/review-methodology/references/patterns.md +6 -61
- package/src/assets/skills/review-methodology/references/violations.md +14 -22
- package/src/assets/skills/worktree-support/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +29 -0
- package/src/targets/claude-code/templates/managed-settings.json +25 -9
- package/src/assets/agents/git.md +0 -938
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering for the post-install summary — pure functions that turn an
|
|
3
|
+
* {@link InstallReport} into lines, logging nothing (applies ADR-013).
|
|
4
|
+
*
|
|
5
|
+
* Its own module rather than a section of init.ts because `devflow tracker --set`
|
|
6
|
+
* renders the same overlay outcomes from a different command. A CLI command
|
|
7
|
+
* importing a renderer out of a 2,450-line sibling couples two commands through
|
|
8
|
+
* a file neither of them owns; both import from here instead (design review M5).
|
|
9
|
+
*/
|
|
10
|
+
import color from 'picocolors';
|
|
11
|
+
import { overlayUnitLabel } from '../../targets/claude-code/installer.js';
|
|
12
|
+
import { SKILL_REFS_SKILL_NAME } from '../../core/mds-variants.js';
|
|
13
|
+
import { prefixSkillName } from '../../core/plugins.js';
|
|
14
|
+
/**
|
|
15
|
+
* Turn the reference-overlay half of an InstallReport into summary lines.
|
|
16
|
+
*
|
|
17
|
+
* The overlay rewrites files inside an installed skill directory the user may have
|
|
18
|
+
* shadowed, and a unit it could not refresh is left in one of the states
|
|
19
|
+
* {@link OverlayFailureState} enumerates — running on the previous install, half
|
|
20
|
+
* replaced, absent, or recoverable only from a backup path. None of that is visible from
|
|
21
|
+
* the filesystem at a glance, so all of it reaches the summary — PF-015: a report field
|
|
22
|
+
* with no render site is not a report, and a render site that flattens four states into
|
|
23
|
+
* one sentence is the same defect one layer up.
|
|
24
|
+
*
|
|
25
|
+
* Pure function — returns lines, logs nothing (applies ADR-013).
|
|
26
|
+
*
|
|
27
|
+
* @param skillName - Bare name of the skill hosting the generated references,
|
|
28
|
+
* rendered `devflow:`-prefixed. Defaults to the core constant the build path and
|
|
29
|
+
* the installer's overlay trigger both read, so the renderer is never a third
|
|
30
|
+
* independent statement of which skill owns them — the divergence PF-013
|
|
31
|
+
* describes, where changing the answer means finding every retyped spelling and
|
|
32
|
+
* nothing fails if one is missed.
|
|
33
|
+
*/
|
|
34
|
+
export function formatOverlaySummary(report, skillName = SKILL_REFS_SKILL_NAME) {
|
|
35
|
+
const lines = [];
|
|
36
|
+
if (report.overlaidRefs.length > 0) {
|
|
37
|
+
lines.push({
|
|
38
|
+
level: 'info',
|
|
39
|
+
message: `Installed ${report.overlaidRefs.length} generated skill reference(s) for ` +
|
|
40
|
+
prefixSkillName(skillName),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
for (const failure of report.overlayFailures) {
|
|
44
|
+
lines.push({
|
|
45
|
+
level: 'warn',
|
|
46
|
+
message: `Could not refresh the generated references for ${overlayUnitLabel(failure.unit)} ` +
|
|
47
|
+
`(${failure.error}) — ${describeOverlayFailureState(failure.state)}`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The half of an overlay warning that describes what is actually on disk.
|
|
54
|
+
*
|
|
55
|
+
* One sentence per state, each true of that state and of no other. A single shared
|
|
56
|
+
* sentence — "the previously installed files were left unchanged" — is true of the first
|
|
57
|
+
* arm only, and would read loudest over the arms it fits worst: a set left
|
|
58
|
+
* half-refreshed, and a unit whose only surviving copy is a backup path the user has to
|
|
59
|
+
* be told about.
|
|
60
|
+
*
|
|
61
|
+
* Exported because `devflow tracker --set` renders the same states when it aborts
|
|
62
|
+
* on an overlay failure (applies PF-013 — one sentence per state, in one place,
|
|
63
|
+
* rather than a second wording that drifts).
|
|
64
|
+
*
|
|
65
|
+
* Exhaustive over {@link OverlayFailureState} — a new state added to the union without a
|
|
66
|
+
* sentence here is a compile error, not a state that silently prints nothing.
|
|
67
|
+
*/
|
|
68
|
+
export function describeOverlayFailureState(state) {
|
|
69
|
+
switch (state.kind) {
|
|
70
|
+
case 'installed-unchanged':
|
|
71
|
+
return 'the previously installed files were left unchanged';
|
|
72
|
+
case 'not-installed':
|
|
73
|
+
return (`nothing is installed in their place, so ${state.absent.length} reference(s) the ` +
|
|
74
|
+
`agent is told to load are absent: ${state.absent.join(', ')}`);
|
|
75
|
+
case 'partially-refreshed':
|
|
76
|
+
return (`${state.refreshed.length} of ${state.refreshed.length + state.stale.length} ` +
|
|
77
|
+
`document(s) had already been replaced, so the set is part new and part old — ` +
|
|
78
|
+
`still on the previous install: ${state.stale.join(', ') || 'none'}`);
|
|
79
|
+
case 'restore-failed':
|
|
80
|
+
return (`the displaced copy could NOT be put back (${state.restoreError}), so nothing is ` +
|
|
81
|
+
`installed there now — the only surviving copy is "${state.recoveryPath}", which ` +
|
|
82
|
+
`this run's stale-reference prune was skipped to preserve`);
|
|
83
|
+
default: {
|
|
84
|
+
const _exhaustive = state;
|
|
85
|
+
void _exhaustive;
|
|
86
|
+
return 'the state it was left in is unknown';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The install summary's tracker rows.
|
|
92
|
+
*
|
|
93
|
+
* Two facts the install has no other visible trace of:
|
|
94
|
+
*
|
|
95
|
+
* - WHICH provider is the machine default. The sentinel is a dotfile and every
|
|
96
|
+
* provider's mechanics are installed (D-INSTALL-ALL-PROVIDERS), so nothing on
|
|
97
|
+
* disk says which one the machine selected. `(default)` distinguishes "github
|
|
98
|
+
* because I chose it" from "github because nothing was chosen"; `(was jira)`
|
|
99
|
+
* is what makes a self-heal or a `--reset` collapse legible rather than silent.
|
|
100
|
+
* - WHAT this run moved: references written or pruned, and the Tracker agent
|
|
101
|
+
* when this run wrote it.
|
|
102
|
+
*
|
|
103
|
+
* The delta line is emitted only when something moved: on a steady-state re-init
|
|
104
|
+
* the counts are noise.
|
|
105
|
+
*
|
|
106
|
+
* Pure function — returns lines, logs nothing (applies ADR-013).
|
|
107
|
+
*
|
|
108
|
+
* @param previous - The provider recorded by the PRIOR manifest, or undefined on
|
|
109
|
+
* a first install. Rendered only when it differs from `provider`.
|
|
110
|
+
* @param isDefault - Whether `provider` is the registry default.
|
|
111
|
+
*/
|
|
112
|
+
export function formatTrackerAssetSummary(input) {
|
|
113
|
+
const lines = [];
|
|
114
|
+
const changed = input.previous !== undefined && input.previous !== input.provider;
|
|
115
|
+
const qualifier = changed
|
|
116
|
+
? ` ${color.dim(`(was ${input.previous})`)}`
|
|
117
|
+
: input.isDefault ? ` ${color.dim('(default)')}` : '';
|
|
118
|
+
lines.push({ level: 'info', message: `Tracker: ${input.provider}${qualifier}` });
|
|
119
|
+
const agentNote = input.agent === 'unchanged' ? '' : `, tracker agent ${input.agent}`;
|
|
120
|
+
if (input.installedRefs > 0 || input.removedRefs > 0 || agentNote !== '') {
|
|
121
|
+
lines.push({
|
|
122
|
+
level: 'info',
|
|
123
|
+
message: `Tracker assets: +${input.installedRefs} reference(s), ` +
|
|
124
|
+
`−${input.removedRefs} reference(s)${agentNote}`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return lines;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Did this run's plugin selection match the one already on disk?
|
|
131
|
+
*
|
|
132
|
+
* The question {@link formatSkillScopeSummary}'s `pluginListUnchanged` asks, and
|
|
133
|
+
* a separate function because the two halves fail differently and only one of
|
|
134
|
+
* them had executed evidence (design review L2): the renderer's behaviour given
|
|
135
|
+
* an answer, and the answer itself.
|
|
136
|
+
*
|
|
137
|
+
* Two clauses, and the first one is the one a set comparison alone would lose:
|
|
138
|
+
*
|
|
139
|
+
* - **A prior manifest must EXIST.** `null` is a first install. Nothing was
|
|
140
|
+
* removed from a user who had nothing, so there is no upgrade to explain,
|
|
141
|
+
* and comparing "no previous selection" against this run's would otherwise
|
|
142
|
+
* read as a match whenever both are empty.
|
|
143
|
+
* - **The plugin SETS must be equal**, not the arrays: order is an artifact of
|
|
144
|
+
* how the selection was assembled, and a duplicate name in either list is a
|
|
145
|
+
* manifest detail rather than a different selection.
|
|
146
|
+
*
|
|
147
|
+
* Pure function (applies ADR-013).
|
|
148
|
+
*
|
|
149
|
+
* @param previousPlugins - `manifest.plugins` as it stands before this run, or
|
|
150
|
+
* `null` when there is no prior manifest.
|
|
151
|
+
* @param effectivePluginNames - What this run installs.
|
|
152
|
+
*/
|
|
153
|
+
export function isPluginListUnchanged(previousPlugins, effectivePluginNames) {
|
|
154
|
+
if (previousPlugins === null)
|
|
155
|
+
return false;
|
|
156
|
+
const previous = new Set(previousPlugins);
|
|
157
|
+
const effective = new Set(effectivePluginNames);
|
|
158
|
+
return previous.size === effective.size && [...effective].every(name => previous.has(name));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Turn the skill-scoping half of an InstallReport into summary lines.
|
|
162
|
+
*
|
|
163
|
+
* Two facts the filesystem cannot tell the user apart from an install that never
|
|
164
|
+
* happened:
|
|
165
|
+
*
|
|
166
|
+
* - a REMOVED skill. Scoping the install means a re-init after deselecting a
|
|
167
|
+
* plugin silently deletes skills the previous install carried. The line is
|
|
168
|
+
* emitted only when the plugin list is UNCHANGED, because that is the case
|
|
169
|
+
* the user did not ask for: they re-ran init expecting nothing to move, and
|
|
170
|
+
* the scoping change is what moved it. When they deselected a plugin
|
|
171
|
+
* themselves, the removal is the thing they asked for and needs no notice.
|
|
172
|
+
* - a DORMANT shadow. `~/.devflow/skills/{name}/` is never deleted, so an
|
|
173
|
+
* inactive shadow and an applied one look identical from disk.
|
|
174
|
+
*
|
|
175
|
+
* `pluginListUnchanged` is the caller's answer to "did the selection move?", and
|
|
176
|
+
* it is FALSE when there is no prior manifest: a first install removed nothing a
|
|
177
|
+
* user had, so there is no upgrade to explain (design review L2).
|
|
178
|
+
*
|
|
179
|
+
* Pure function — returns lines, logs nothing (applies ADR-013).
|
|
180
|
+
*/
|
|
181
|
+
export function formatSkillScopeSummary(report, pluginListUnchanged) {
|
|
182
|
+
const lines = [];
|
|
183
|
+
if (pluginListUnchanged && report.removedSkills.length > 0) {
|
|
184
|
+
const names = [...report.removedSkills].sort().join(', ');
|
|
185
|
+
lines.push({
|
|
186
|
+
level: 'info',
|
|
187
|
+
message: `Removed ${report.removedSkills.length} skill(s) no selected plugin requires: ${names}. ` +
|
|
188
|
+
`Re-run devflow init and select the plugin that provides them to keep them.`,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
for (const name of report.dormantShadows) {
|
|
192
|
+
lines.push({
|
|
193
|
+
level: 'info',
|
|
194
|
+
message: `Shadow for ${name} is inactive — the plugin that uses it is not selected ` +
|
|
195
|
+
`(${color.dim('kept in ~/.devflow/skills/')})`,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return lines;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=install-report.js.map
|
|
@@ -10,8 +10,8 @@ import { handleToggle } from './toggle.js';
|
|
|
10
10
|
import { handleList } from './list.js';
|
|
11
11
|
export const knowledgeCommand = new Command('knowledge')
|
|
12
12
|
.description('Manage per-feature knowledge bases')
|
|
13
|
-
.option('--enable', 'Enable per-feature knowledge bases')
|
|
14
|
-
.option('--disable', 'Disable per-feature knowledge bases')
|
|
13
|
+
.option('--enable', 'Enable per-feature knowledge bases in every project (a repository can opt out)')
|
|
14
|
+
.option('--disable', 'Disable per-feature knowledge bases in every project')
|
|
15
15
|
.option('--status', 'Show knowledge base feature status')
|
|
16
16
|
.action(async (options) => {
|
|
17
17
|
await handleToggle(options);
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Handle the enable/disable/status toggle actions for `devflow knowledge`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* D-FEATURES-NARROW-ONLY (src/core/feature-switch.ts): knowledge write-back is
|
|
5
|
+
* switched for the whole machine by `features.knowledge` in
|
|
6
|
+
* ~/.devflow/manifest.json. `--enable`/`--disable` write that value — the same
|
|
7
|
+
* one `devflow init --knowledge / --no-knowledge` writes — and `--status`
|
|
8
|
+
* reports it, plus the repository's narrowing when a repo layer narrows it.
|
|
5
9
|
*/
|
|
6
10
|
import { promises as fs } from 'fs';
|
|
7
11
|
import * as path from 'path';
|
|
@@ -9,8 +13,8 @@ import * as p from '@clack/prompts';
|
|
|
9
13
|
import color from 'picocolors';
|
|
10
14
|
import { getGitRoot } from '../../../core/git.js';
|
|
11
15
|
import { getDevFlowDirectory } from '../../../targets/claude-code/claude-paths.js';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
16
|
+
import { readMachineFeature, writeMachineFeature } from '../../../core/feature-switch.js';
|
|
17
|
+
import { loadSettingsModule, narrowedSwitchLabel, personalConfigTrackedWarning } from '../../../core/evidence-policy.js';
|
|
14
18
|
import { getFeaturesDir } from '../../../core/project-paths.js';
|
|
15
19
|
async function getWorktreePath() {
|
|
16
20
|
return (await getGitRoot()) ?? process.cwd();
|
|
@@ -39,46 +43,40 @@ async function countKnowledgeBases(worktreePath) {
|
|
|
39
43
|
export async function handleToggle(options) {
|
|
40
44
|
if (!options.enable && !options.disable && !options.status)
|
|
41
45
|
return;
|
|
42
|
-
const worktreePath = await getWorktreePath();
|
|
43
46
|
const devflowDir = getDevFlowDirectory();
|
|
44
|
-
if (options.
|
|
45
|
-
p.intro(color.cyan('
|
|
46
|
-
|
|
47
|
-
await
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
p.log.info(
|
|
47
|
+
if (options.status) {
|
|
48
|
+
p.intro(color.cyan('Feature Knowledge Status'));
|
|
49
|
+
const enabled = await readMachineFeature(devflowDir, 'knowledge');
|
|
50
|
+
const kbCount = await countKnowledgeBases(await getWorktreePath());
|
|
51
|
+
p.log.info(`Status: ${enabled ? color.green('enabled') : color.yellow('disabled')}`);
|
|
52
|
+
const settingsModule = loadSettingsModule();
|
|
53
|
+
const narrowed = enabled ? narrowedSwitchLabel(settingsModule, { dir: process.cwd() }, 'knowledge') : null;
|
|
54
|
+
if (narrowed !== null)
|
|
55
|
+
p.log.info(`Effective here: ${color.yellow(narrowed)}`);
|
|
56
|
+
const trackedWarning = personalConfigTrackedWarning(settingsModule, { dir: process.cwd() });
|
|
57
|
+
if (trackedWarning !== null)
|
|
58
|
+
p.log.warn(trackedWarning);
|
|
59
|
+
p.log.info(`Knowledge bases: ${kbCount}`);
|
|
57
60
|
p.outro('');
|
|
61
|
+
return;
|
|
58
62
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (manifest) {
|
|
66
|
-
manifest.features.knowledge = false;
|
|
67
|
-
manifest.updatedAt = new Date().toISOString();
|
|
68
|
-
await writeManifest(devflowDir, manifest);
|
|
69
|
-
}
|
|
70
|
-
p.log.success('Feature knowledge bases disabled');
|
|
71
|
-
p.log.info('Existing knowledge bases preserved. Write-back skipped while disabled.');
|
|
63
|
+
const enabled = options.enable === true;
|
|
64
|
+
p.intro(color.cyan(`${enabled ? 'Enable' : 'Disable'} Feature Knowledge Bases`));
|
|
65
|
+
const recorded = await writeMachineFeature(devflowDir, 'knowledge', enabled);
|
|
66
|
+
if (!recorded.ok) {
|
|
67
|
+
p.log.error(`Devflow is not installed on this machine — run ${color.cyan('devflow init')} first`);
|
|
68
|
+
process.exitCode = 1;
|
|
72
69
|
p.outro('');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (enabled) {
|
|
73
|
+
p.log.success('Feature knowledge bases enabled in every project (a repository can opt out)');
|
|
74
|
+
p.log.info('Knowledge bases are created automatically when workflows detect documented area changes.');
|
|
73
75
|
}
|
|
74
76
|
else {
|
|
75
|
-
|
|
76
|
-
p.
|
|
77
|
-
const enabled = await isFeatureEnabled(worktreePath, 'knowledge');
|
|
78
|
-
const kbCount = await countKnowledgeBases(worktreePath);
|
|
79
|
-
p.log.info(`Status: ${enabled ? color.green('enabled') : color.yellow('disabled')}`);
|
|
80
|
-
p.log.info(`Knowledge bases: ${kbCount}`);
|
|
81
|
-
p.outro('');
|
|
77
|
+
p.log.success('Feature knowledge bases disabled in every project');
|
|
78
|
+
p.log.info('Existing knowledge bases preserved. Write-back skipped while disabled.');
|
|
82
79
|
}
|
|
80
|
+
p.outro('');
|
|
83
81
|
}
|
|
84
82
|
//# sourceMappingURL=toggle.js.map
|
|
@@ -4,10 +4,10 @@ import * as path from 'path';
|
|
|
4
4
|
import * as p from '@clack/prompts';
|
|
5
5
|
import color from 'picocolors';
|
|
6
6
|
import { getLearningDir, getLearningTuningConfigPath, getDecisionsLogPath, getDecisionsLockDir, } from '../../core/project-paths.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { readMachineFeature, writeMachineFeature } from '../../core/feature-switch.js';
|
|
8
|
+
import { loadSettingsModule, narrowedSwitchLabel, personalConfigTrackedWarning } from '../../core/evidence-policy.js';
|
|
9
9
|
import { getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
|
|
10
|
-
import {
|
|
10
|
+
import { getLedgerRoot } from '../../core/ledger-root.js';
|
|
11
11
|
import { sweepLegacyDreamMarkers, drainLearningQueue } from '../../core/learning-queue-cleanup.js';
|
|
12
12
|
import { readObservations, warnIfInvalid, } from '../../core/observation-io.js';
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
@@ -15,8 +15,8 @@ import { readObservations, warnIfInvalid, } from '../../core/observation-io.js';
|
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
function printUsage() {
|
|
17
17
|
p.intro(color.bgCyan(color.black(' Learning ')));
|
|
18
|
-
p.note(`${color.cyan('devflow learning --enable')} Enable learning (
|
|
19
|
-
`${color.cyan('devflow learning --disable')} Disable learning (drains queue)\n` +
|
|
18
|
+
p.note(`${color.cyan('devflow learning --enable')} Enable learning in every project (a repository can opt out)\n` +
|
|
19
|
+
`${color.cyan('devflow learning --disable')} Disable learning in every project (drains this project's queue)\n` +
|
|
20
20
|
`${color.cyan('devflow learning --status')} Show learning status\n` +
|
|
21
21
|
`${color.cyan('devflow learning --list')} Show all observations\n` +
|
|
22
22
|
`${color.cyan('devflow learning --configure')} Configuration wizard\n` +
|
|
@@ -25,25 +25,39 @@ function printUsage() {
|
|
|
25
25
|
p.outro(color.dim('Detects architectural decisions and known pitfalls from your sessions'));
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Resolve the
|
|
28
|
+
* Resolve the ledger root for a state-mutating subcommand, warning and
|
|
29
29
|
* returning null if the caller isn't inside a git project. `actionSuffix`
|
|
30
30
|
* completes "Could not resolve git root — {actionSuffix}".
|
|
31
|
+
*
|
|
32
|
+
* D-LEDGER-MAIN-WORKTREE: every subcommand here resolves the ledger with
|
|
33
|
+
* getLedgerRoot — the hooks' DF_LEDGER_ROOT rule — so in a linked worktree it
|
|
34
|
+
* reads, clears and drains the main checkout's ledger the hooks write.
|
|
31
35
|
*/
|
|
32
|
-
async function
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
36
|
+
async function requireLedgerRoot(actionSuffix) {
|
|
37
|
+
const ledgerRoot = await getLedgerRoot();
|
|
38
|
+
if (!ledgerRoot) {
|
|
35
39
|
p.log.warn(`Could not resolve git root — ${actionSuffix}`);
|
|
36
40
|
}
|
|
37
|
-
return
|
|
41
|
+
return ledgerRoot;
|
|
38
42
|
}
|
|
39
43
|
async function handleStatus() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
// D-FEATURES-NARROW-ONLY: the machine switch is the manifest's and reads the
|
|
45
|
+
// same from every directory; a repository layer can only narrow it, and adds a
|
|
46
|
+
// line only when it does. The observation counts are per-project.
|
|
47
|
+
const enabled = await readMachineFeature(getDevFlowDirectory(), 'learning');
|
|
48
|
+
const settingsModule = loadSettingsModule();
|
|
49
|
+
const narrowed = enabled ? narrowedSwitchLabel(settingsModule, { dir: process.cwd() }, 'learning') : null;
|
|
50
|
+
const stateLine = `Learning: ${enabled ? 'enabled' : 'disabled'}`
|
|
51
|
+
+ (narrowed === null ? '' : `\nEffective here: ${narrowed}`);
|
|
52
|
+
const trackedWarning = personalConfigTrackedWarning(settingsModule, { dir: process.cwd() });
|
|
53
|
+
if (trackedWarning !== null)
|
|
54
|
+
p.log.warn(trackedWarning);
|
|
55
|
+
const ledgerRoot = await getLedgerRoot();
|
|
56
|
+
if (!ledgerRoot) {
|
|
57
|
+
p.log.info(`${stateLine}\nObservations: not in a git project`);
|
|
43
58
|
return;
|
|
44
59
|
}
|
|
45
|
-
const logPath = getDecisionsLogPath(
|
|
46
|
-
const enabled = await isFeatureEnabled(gitRoot, 'learning');
|
|
60
|
+
const logPath = getDecisionsLogPath(ledgerRoot);
|
|
47
61
|
const { observations, invalidCount } = await readObservations(logPath);
|
|
48
62
|
const decisionObs = observations.filter(o => o.type === 'decision' || o.type === 'pitfall');
|
|
49
63
|
const decisions = observations.filter(o => o.type === 'decision');
|
|
@@ -52,7 +66,7 @@ async function handleStatus() {
|
|
|
52
66
|
const ready = decisionObs.filter(o => o.status === 'ready');
|
|
53
67
|
const observing = decisionObs.filter(o => o.status === 'observing');
|
|
54
68
|
const deprecated = decisionObs.filter(o => o.status === 'deprecated');
|
|
55
|
-
const lines = [
|
|
69
|
+
const lines = [stateLine];
|
|
56
70
|
if (decisionObs.length === 0) {
|
|
57
71
|
lines.push('Observations: none');
|
|
58
72
|
}
|
|
@@ -65,12 +79,12 @@ async function handleStatus() {
|
|
|
65
79
|
warnIfInvalid(invalidCount);
|
|
66
80
|
}
|
|
67
81
|
async function handleList() {
|
|
68
|
-
// Resolve the log from the
|
|
69
|
-
// --disable) so `--list` run from a subdirectory
|
|
70
|
-
// instead of a nonexistent one under process.cwd(). Falls back
|
|
71
|
-
// when not in a git project, preserving the prior behavior for that case.
|
|
72
|
-
const
|
|
73
|
-
const logPath = getDecisionsLogPath(
|
|
82
|
+
// Resolve the log from the ledger root (matches --status, --clear, --reset,
|
|
83
|
+
// --disable) so `--list` run from a subdirectory or a linked worktree finds
|
|
84
|
+
// the real log instead of a nonexistent one under process.cwd(). Falls back
|
|
85
|
+
// to cwd when not in a git project, preserving the prior behavior for that case.
|
|
86
|
+
const ledgerRoot = await getLedgerRoot();
|
|
87
|
+
const logPath = getDecisionsLogPath(ledgerRoot ?? process.cwd());
|
|
74
88
|
let logExists = true;
|
|
75
89
|
try {
|
|
76
90
|
await fs.access(logPath);
|
|
@@ -148,19 +162,22 @@ async function handleConfigure() {
|
|
|
148
162
|
p.log.success(`Global config written to ${color.dim(path.join(globalDir, 'learning.json'))}`);
|
|
149
163
|
}
|
|
150
164
|
else {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
165
|
+
// D-LEDGER-MAIN-WORKTREE: session-start-context reads the project tuning config
|
|
166
|
+
// from the ledger ($LEDGER_ROOT/.devflow/learning/), so write it there — the
|
|
167
|
+
// main checkout in a linked worktree; the current directory outside git.
|
|
168
|
+
const projectRoot = (await getLedgerRoot()) ?? process.cwd();
|
|
169
|
+
await fs.mkdir(getLearningDir(projectRoot), { recursive: true });
|
|
170
|
+
const projectConfigPath = getLearningTuningConfigPath(projectRoot);
|
|
154
171
|
await fs.writeFile(projectConfigPath, configJson, 'utf-8');
|
|
155
172
|
p.log.success(`Project config written to ${color.dim(projectConfigPath)}`);
|
|
156
173
|
}
|
|
157
174
|
p.outro(color.green('Configuration saved.'));
|
|
158
175
|
}
|
|
159
176
|
async function handleReset() {
|
|
160
|
-
const
|
|
161
|
-
if (!
|
|
177
|
+
const ledgerRoot = await requireLedgerRoot('reset not performed');
|
|
178
|
+
if (!ledgerRoot)
|
|
162
179
|
return;
|
|
163
|
-
const lockDir = getDecisionsLockDir(
|
|
180
|
+
const lockDir = getDecisionsLockDir(ledgerRoot);
|
|
164
181
|
// Ensure the parent directory exists so a second reset (after .devflow/learning/
|
|
165
182
|
// was already removed) does not fail with ENOENT and emit a false contention error.
|
|
166
183
|
await fs.mkdir(path.dirname(lockDir), { recursive: true });
|
|
@@ -187,13 +204,13 @@ async function handleReset() {
|
|
|
187
204
|
// Remove the entire learning directory (contains queue files, content files,
|
|
188
205
|
// ledger, and tuning config). Single-dir semantics: all learning state lives here.
|
|
189
206
|
try {
|
|
190
|
-
await fs.rm(getLearningDir(
|
|
207
|
+
await fs.rm(getLearningDir(ledgerRoot), { recursive: true, force: true });
|
|
191
208
|
}
|
|
192
209
|
catch { /* best effort */ }
|
|
193
210
|
// Clean legacy dream marker-pipeline stamps from old installs.
|
|
194
211
|
// Best-effort: sweeps the now-absent dir silently (ENOENT-tolerant).
|
|
195
212
|
try {
|
|
196
|
-
await sweepLegacyDreamMarkers(getLearningDir(
|
|
213
|
+
await sweepLegacyDreamMarkers(getLearningDir(ledgerRoot));
|
|
197
214
|
}
|
|
198
215
|
catch { /* best effort */ }
|
|
199
216
|
p.log.success('Reset complete — removed .devflow/learning/ state.');
|
|
@@ -206,10 +223,10 @@ async function handleReset() {
|
|
|
206
223
|
}
|
|
207
224
|
}
|
|
208
225
|
async function handleClear() {
|
|
209
|
-
const
|
|
210
|
-
if (!
|
|
226
|
+
const ledgerRoot = await requireLedgerRoot('clear not performed');
|
|
227
|
+
if (!ledgerRoot)
|
|
211
228
|
return;
|
|
212
|
-
const decisionsLogPath = getDecisionsLogPath(
|
|
229
|
+
const decisionsLogPath = getDecisionsLogPath(ledgerRoot);
|
|
213
230
|
try {
|
|
214
231
|
await fs.access(decisionsLogPath);
|
|
215
232
|
}
|
|
@@ -232,35 +249,40 @@ async function handleClear() {
|
|
|
232
249
|
// on the next session — mirrors memory.ts's drain-on-disable behavior for
|
|
233
250
|
// the sibling memory queue. A mid-run Learning agent whose claimed batch
|
|
234
251
|
// vanishes aborts without changes — the desired outcome of clearing.
|
|
235
|
-
await drainLearningQueue(
|
|
252
|
+
await drainLearningQueue(ledgerRoot);
|
|
236
253
|
p.log.success('Decisions log cleared.');
|
|
237
254
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
255
|
+
/**
|
|
256
|
+
* `--enable` / `--disable`: the machine-wide switch (D-FEATURES-NARROW-ONLY),
|
|
257
|
+
* converged exactly as `devflow init --learning / --no-learning` converges it —
|
|
258
|
+
* the manifest value, and on disable a drained queue in the current project.
|
|
259
|
+
* Never requires a git root: the switch is not a per-project setting.
|
|
260
|
+
*/
|
|
261
|
+
async function handleToggle(enabled) {
|
|
262
|
+
const recorded = await writeMachineFeature(getDevFlowDirectory(), 'learning', enabled);
|
|
263
|
+
if (!recorded.ok) {
|
|
264
|
+
p.log.error(`Devflow is not installed on this machine — run ${color.cyan('devflow init')} first`);
|
|
265
|
+
process.exitCode = 1;
|
|
241
266
|
return;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
async function handleDisable() {
|
|
248
|
-
const gitRoot = await requireGitRoot('configuration not updated');
|
|
249
|
-
if (!gitRoot)
|
|
267
|
+
}
|
|
268
|
+
if (enabled) {
|
|
269
|
+
p.log.success('Learning enabled in every project (a repository can opt out)');
|
|
270
|
+
p.log.info(color.dim('Architectural decisions and pitfalls will be detected from your sessions'));
|
|
250
271
|
return;
|
|
251
|
-
|
|
252
|
-
// Drain the learning (decisions-detection) queue so stale
|
|
253
|
-
// on re-enable
|
|
254
|
-
// sibling memory queue. Unconditional: a mid-run Learning agent whose claimed
|
|
272
|
+
}
|
|
273
|
+
// Drain the current project's learning (decisions-detection) queue so stale
|
|
274
|
+
// turns don't process on re-enable. A mid-run Learning agent whose claimed
|
|
255
275
|
// batch vanishes aborts without changes — the desired outcome of disabling.
|
|
256
|
-
await
|
|
257
|
-
|
|
258
|
-
|
|
276
|
+
const ledgerRoot = await getLedgerRoot();
|
|
277
|
+
if (ledgerRoot) {
|
|
278
|
+
await drainLearningQueue(ledgerRoot);
|
|
279
|
+
}
|
|
280
|
+
p.log.success('Learning disabled in every project');
|
|
259
281
|
}
|
|
260
282
|
export const learningCommand = new Command('learning')
|
|
261
|
-
.description('Enable or disable learning (decision/pitfall detection
|
|
262
|
-
.option('--enable', 'Enable learning')
|
|
263
|
-
.option('--disable', 'Disable learning')
|
|
283
|
+
.description('Enable or disable learning (decision/pitfall detection) in every project')
|
|
284
|
+
.option('--enable', 'Enable learning in every project (a repository can opt out)')
|
|
285
|
+
.option('--disable', 'Disable learning in every project')
|
|
264
286
|
.option('--status', 'Show learning status and observation counts')
|
|
265
287
|
.option('--list', 'Show all decision/pitfall observations sorted by confidence')
|
|
266
288
|
.option('--configure', 'Interactive configuration wizard for learning.json')
|
|
@@ -300,11 +322,11 @@ export const learningCommand = new Command('learning')
|
|
|
300
322
|
return;
|
|
301
323
|
}
|
|
302
324
|
if (options.enable) {
|
|
303
|
-
await
|
|
325
|
+
await handleToggle(true);
|
|
304
326
|
return;
|
|
305
327
|
}
|
|
306
328
|
if (options.disable) {
|
|
307
|
-
await
|
|
329
|
+
await handleToggle(false);
|
|
308
330
|
return;
|
|
309
331
|
}
|
|
310
332
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { devflowHookOwner, hasHook, removeHooks, } from '../../targets/claude-code/hooks.js';
|
|
1
2
|
// ─── Dream worker hook cleanup ──────────────────────────────────────────────
|
|
2
3
|
//
|
|
3
4
|
// The spawn-dream-worker SessionStart hook belonged to the retired detached
|
|
@@ -6,27 +7,23 @@
|
|
|
6
7
|
// own. remove/has exist for upgrade cleanup: init and uninstall strip any
|
|
7
8
|
// stale entry left in settings.json by a prior install.
|
|
8
9
|
const SPAWN_DREAM_WORKER_MARKER = 'spawn-dream-worker';
|
|
10
|
+
/**
|
|
11
|
+
* D-EXACT-HOOK-OWNER: the dream-worker hook is devflow's only when its command
|
|
12
|
+
* ends in `/scripts/hooks/run-hook spawn-dream-worker` (hooks.ts), under any
|
|
13
|
+
* directory — the one form it was ever registered in.
|
|
14
|
+
*/
|
|
15
|
+
const isDreamHook = devflowHookOwner([SPAWN_DREAM_WORKER_MARKER]);
|
|
9
16
|
/**
|
|
10
17
|
* Remove the spawn-dream-worker hook from settings JSON.
|
|
11
18
|
* Idempotent — returns unchanged JSON if hook not present.
|
|
12
|
-
*
|
|
19
|
+
* Removes the single hook, so the other hooks of its matcher group and every
|
|
20
|
+
* other SessionStart group (session-start-memory, session-start-context) stay in place.
|
|
13
21
|
*/
|
|
14
22
|
export function removeDreamHook(settingsJson) {
|
|
15
23
|
const settings = JSON.parse(settingsJson);
|
|
16
|
-
if (!settings
|
|
17
|
-
return settingsJson;
|
|
18
|
-
}
|
|
19
|
-
const before = settings.hooks.SessionStart.length;
|
|
20
|
-
settings.hooks.SessionStart = settings.hooks.SessionStart.filter((matcher) => !matcher.hooks.some((h) => h.command.includes(SPAWN_DREAM_WORKER_MARKER)));
|
|
21
|
-
if (settings.hooks.SessionStart.length === before) {
|
|
24
|
+
if (!removeHooks(settings, 'SessionStart', isDreamHook)) {
|
|
22
25
|
return settingsJson;
|
|
23
26
|
}
|
|
24
|
-
if (settings.hooks.SessionStart.length === 0) {
|
|
25
|
-
delete settings.hooks.SessionStart;
|
|
26
|
-
}
|
|
27
|
-
if (Object.keys(settings.hooks).length === 0) {
|
|
28
|
-
delete settings.hooks;
|
|
29
|
-
}
|
|
30
27
|
return JSON.stringify(settings, null, 2) + '\n';
|
|
31
28
|
}
|
|
32
29
|
/**
|
|
@@ -35,6 +32,6 @@ export function removeDreamHook(settingsJson) {
|
|
|
35
32
|
*/
|
|
36
33
|
export function hasDreamHook(input) {
|
|
37
34
|
const settings = typeof input === 'string' ? JSON.parse(input) : input;
|
|
38
|
-
return settings
|
|
35
|
+
return hasHook(settings, 'SessionStart', isDreamHook);
|
|
39
36
|
}
|
|
40
37
|
//# sourceMappingURL=legacy-hooks.js.map
|