devflow-kit 2.4.0 → 2.5.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 +156 -0
- package/README.md +86 -18
- package/dist/agents/git.md +824 -0
- package/dist/cli/commands/agents.js +6 -1
- package/dist/cli/commands/attribution-prompts.js +1 -1
- package/dist/cli/commands/compliance-prompts.js +1 -1
- package/dist/cli/commands/compliance.js +23 -1
- package/dist/cli/commands/init-seed.js +24 -26
- package/dist/cli/commands/init.js +502 -71
- package/dist/cli/commands/install-report.js +205 -0
- package/dist/cli/commands/knowledge/index.js +2 -2
- package/dist/cli/commands/knowledge/toggle.js +27 -37
- package/dist/cli/commands/learning.js +37 -30
- package/dist/cli/commands/memory.js +79 -69
- package/dist/cli/commands/prompt-io.js +4 -4
- package/dist/cli/commands/security.js +76 -16
- package/dist/cli/commands/skills.js +53 -7
- package/dist/cli/commands/tracker-prompts.js +145 -0
- package/dist/cli/commands/tracker.js +405 -0
- package/dist/cli/commands/uninstall.js +211 -65
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +22 -4
- package/dist/commands/code-review.md +44 -15
- package/dist/commands/debug.md +20 -6
- package/dist/commands/dynamic-build.md +289 -67
- package/dist/commands/dynamic-plan.md +60 -21
- package/dist/commands/dynamic-profile.md +1 -1
- package/dist/commands/dynamic-tickets.md +58 -8
- package/dist/commands/explore.md +2 -2
- package/dist/commands/implement.md +241 -53
- package/dist/commands/plan.md +88 -17
- package/dist/commands/release.md +64 -17
- package/dist/commands/resolve.md +138 -58
- package/dist/commands/self-review.md +2 -2
- package/dist/core/agent-models.js +55 -12
- package/dist/core/assets.js +58 -2
- package/dist/core/evidence-policy.js +147 -0
- package/dist/core/feature-config.js +130 -64
- package/dist/core/feature-switch.js +112 -0
- package/dist/core/flags.js +4 -4
- package/dist/core/manifest.js +33 -7
- package/dist/core/mds-variants.js +861 -0
- package/dist/core/model-discovery.js +12 -1
- package/dist/core/plugins.js +357 -9
- package/dist/core/project-paths.js +1 -1
- 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/tracker.js +407 -0
- 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/installer.js +1213 -31
- package/dist/targets/claude-code/legacy.js +5 -0
- package/dist/targets/claude-code/post-install.js +196 -74
- package/dist/targets/claude-code/tracker-install.js +161 -0
- package/package.json +4 -3
- package/src/assets/agents/code.md +42 -4
- package/src/assets/agents/design.md +1 -1
- package/src/assets/agents/git.mds +827 -0
- package/src/assets/agents/knowledge.md +1 -1
- package/src/assets/agents/learning.md +11 -0
- package/src/assets/agents/synthesize.md +1 -1
- package/src/assets/agents/test.md +16 -5
- package/src/assets/agents/tracker.md +467 -0
- package/src/assets/agents/validate.md +7 -5
- package/src/assets/commands/_partials/_engine.mds +11 -9
- package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
- package/src/assets/commands/_partials/_knowledge.mds +2 -2
- package/src/assets/commands/_partials/_plan_contract.mds +22 -7
- package/src/assets/commands/_partials/_preamble.mds +1 -1
- package/src/assets/commands/_partials/_publication.mds +3 -1
- 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 +15 -5
- package/src/assets/commands/code-review.mds +34 -14
- package/src/assets/commands/debug.mds +11 -4
- package/src/assets/commands/dynamic-build.mds +227 -41
- package/src/assets/commands/dynamic-plan.mds +35 -13
- package/src/assets/commands/dynamic-tickets.mds +47 -5
- package/src/assets/commands/implement.mds +206 -52
- package/src/assets/commands/plan.mds +70 -17
- package/src/assets/commands/release.md +64 -17
- package/src/assets/commands/resolve.mds +126 -56
- 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 +299 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
- package/src/assets/scripts/hooks/background-memory-update +14 -9
- package/src/assets/scripts/hooks/capture-prompt +6 -2
- package/src/assets/scripts/hooks/capture-question +6 -2
- package/src/assets/scripts/hooks/capture-turn +6 -2
- package/src/assets/scripts/hooks/ensure-devflow-init +1 -1
- package/src/assets/scripts/hooks/ensure-root-gitignore +161 -60
- package/src/assets/scripts/hooks/hook-log-init +3 -1
- package/src/assets/scripts/hooks/json-helper.cjs +223 -5
- package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -1
- package/src/assets/scripts/hooks/memory-worker +15 -8
- package/src/assets/scripts/hooks/pre-compact-memory +12 -8
- package/src/assets/scripts/hooks/preamble +1 -4
- package/src/assets/scripts/hooks/queue-append +68 -24
- package/src/assets/scripts/hooks/session-start-context +355 -8
- package/src/assets/scripts/hooks/session-start-memory +12 -8
- 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 +1065 -0
- package/src/assets/scripts/verify-evidence.cjs +1822 -0
- package/src/assets/skills/compliance/SKILL.md +2 -0
- package/src/assets/skills/docs-framework/SKILL.md +5 -3
- 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/agents/git.md +0 -938
|
@@ -0,0 +1,205 @@
|
|
|
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 provider - The resolved tracker provider the overlay converged to. The
|
|
28
|
+
* count alone cannot say WHICH mechanics are installed, and after the install
|
|
29
|
+
* became selection-scoped that is the number's whole meaning.
|
|
30
|
+
* @param skillName - Bare name of the skill hosting the generated references,
|
|
31
|
+
* rendered `devflow:`-prefixed. Defaults to the core constant the build path and
|
|
32
|
+
* the installer's overlay trigger both read, so the renderer is never a third
|
|
33
|
+
* independent statement of which skill owns them — the divergence PF-013
|
|
34
|
+
* describes, where changing the answer means finding every retyped spelling and
|
|
35
|
+
* nothing fails if one is missed.
|
|
36
|
+
*/
|
|
37
|
+
export function formatOverlaySummary(report, provider, skillName = SKILL_REFS_SKILL_NAME) {
|
|
38
|
+
const lines = [];
|
|
39
|
+
if (report.overlaidRefs.length > 0) {
|
|
40
|
+
const scope = provider === undefined ? '' : ` (${provider} tracker mechanics)`;
|
|
41
|
+
lines.push({
|
|
42
|
+
level: 'info',
|
|
43
|
+
message: `Installed ${report.overlaidRefs.length} generated skill reference(s) for ` +
|
|
44
|
+
prefixSkillName(skillName) + scope,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
for (const failure of report.overlayFailures) {
|
|
48
|
+
lines.push({
|
|
49
|
+
level: 'warn',
|
|
50
|
+
message: `Could not refresh the generated references for ${overlayUnitLabel(failure.unit)} ` +
|
|
51
|
+
`(${failure.error}) — ${describeOverlayFailureState(failure.state)}`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return lines;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The half of an overlay warning that describes what is actually on disk.
|
|
58
|
+
*
|
|
59
|
+
* One sentence per state, each true of that state and of no other. A single shared
|
|
60
|
+
* sentence — "the previously installed files were left unchanged" — is true of the first
|
|
61
|
+
* arm only, and would read loudest over the arms it fits worst: a set left
|
|
62
|
+
* half-refreshed, and a unit whose only surviving copy is a backup path the user has to
|
|
63
|
+
* be told about.
|
|
64
|
+
*
|
|
65
|
+
* Exported because `devflow tracker --set` renders the same states when it aborts
|
|
66
|
+
* on an overlay failure (applies PF-013 — one sentence per state, in one place,
|
|
67
|
+
* rather than a second wording that drifts).
|
|
68
|
+
*
|
|
69
|
+
* Exhaustive over {@link OverlayFailureState} — a new state added to the union without a
|
|
70
|
+
* sentence here is a compile error, not a state that silently prints nothing.
|
|
71
|
+
*/
|
|
72
|
+
export function describeOverlayFailureState(state) {
|
|
73
|
+
switch (state.kind) {
|
|
74
|
+
case 'installed-unchanged':
|
|
75
|
+
return 'the previously installed files were left unchanged';
|
|
76
|
+
case 'not-installed':
|
|
77
|
+
return (`nothing is installed in their place, so ${state.absent.length} reference(s) the ` +
|
|
78
|
+
`agent is told to load are absent: ${state.absent.join(', ')}`);
|
|
79
|
+
case 'partially-refreshed':
|
|
80
|
+
return (`${state.refreshed.length} of ${state.refreshed.length + state.stale.length} ` +
|
|
81
|
+
`document(s) had already been replaced, so the set is part new and part old — ` +
|
|
82
|
+
`still on the previous install: ${state.stale.join(', ') || 'none'}`);
|
|
83
|
+
case 'restore-failed':
|
|
84
|
+
return (`the displaced copy could NOT be put back (${state.restoreError}), so nothing is ` +
|
|
85
|
+
`installed there now — the only surviving copy is "${state.recoveryPath}", which ` +
|
|
86
|
+
`this run's stale-reference prune was skipped to preserve`);
|
|
87
|
+
default: {
|
|
88
|
+
const _exhaustive = state;
|
|
89
|
+
void _exhaustive;
|
|
90
|
+
return 'the state it was left in is unknown';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The install summary's tracker rows.
|
|
96
|
+
*
|
|
97
|
+
* Two facts the previous summary never stated, and after the install became
|
|
98
|
+
* selection-scoped both of them decide what the user actually has:
|
|
99
|
+
*
|
|
100
|
+
* - WHICH provider is active. The install has no other visible trace of it —
|
|
101
|
+
* the sentinel is a zero-byte dotfile and the mechanics are a directory the
|
|
102
|
+
* user has no reason to list. `(default)` distinguishes "github because I
|
|
103
|
+
* chose it" from "github because nothing was chosen"; `(was jira)` is what
|
|
104
|
+
* makes a self-heal or a `--reset` collapse legible rather than silent.
|
|
105
|
+
* - WHAT the provider change moved. A provider swap installs one tree and
|
|
106
|
+
* prunes another, and the agent file appears or disappears with it.
|
|
107
|
+
*
|
|
108
|
+
* The delta line is emitted only when something moved: on a steady-state re-init
|
|
109
|
+
* the counts are noise.
|
|
110
|
+
*
|
|
111
|
+
* Pure function — returns lines, logs nothing (applies ADR-013).
|
|
112
|
+
*
|
|
113
|
+
* @param previous - The provider recorded by the PRIOR manifest, or undefined on
|
|
114
|
+
* a first install. Rendered only when it differs from `provider`.
|
|
115
|
+
* @param isDefault - Whether `provider` is the registry default.
|
|
116
|
+
*/
|
|
117
|
+
export function formatTrackerAssetSummary(input) {
|
|
118
|
+
const lines = [];
|
|
119
|
+
const changed = input.previous !== undefined && input.previous !== input.provider;
|
|
120
|
+
const qualifier = changed
|
|
121
|
+
? ` ${color.dim(`(was ${input.previous})`)}`
|
|
122
|
+
: input.isDefault ? ` ${color.dim('(default)')}` : '';
|
|
123
|
+
lines.push({ level: 'info', message: `Tracker: ${input.provider}${qualifier}` });
|
|
124
|
+
const agentNote = input.agent === 'unchanged' ? '' : `, tracker agent ${input.agent}`;
|
|
125
|
+
if (input.installedRefs > 0 || input.removedRefs > 0 || agentNote !== '') {
|
|
126
|
+
lines.push({
|
|
127
|
+
level: 'info',
|
|
128
|
+
message: `Tracker assets: +${input.installedRefs} reference(s), ` +
|
|
129
|
+
`−${input.removedRefs} reference(s)${agentNote}`,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return lines;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Did this run's plugin selection match the one already on disk?
|
|
136
|
+
*
|
|
137
|
+
* The question {@link formatSkillScopeSummary}'s `pluginListUnchanged` asks, and
|
|
138
|
+
* a separate function because the two halves fail differently and only one of
|
|
139
|
+
* them had executed evidence (design review L2): the renderer's behaviour given
|
|
140
|
+
* an answer, and the answer itself.
|
|
141
|
+
*
|
|
142
|
+
* Two clauses, and the first one is the one a set comparison alone would lose:
|
|
143
|
+
*
|
|
144
|
+
* - **A prior manifest must EXIST.** `null` is a first install. Nothing was
|
|
145
|
+
* removed from a user who had nothing, so there is no upgrade to explain,
|
|
146
|
+
* and comparing "no previous selection" against this run's would otherwise
|
|
147
|
+
* read as a match whenever both are empty.
|
|
148
|
+
* - **The plugin SETS must be equal**, not the arrays: order is an artifact of
|
|
149
|
+
* how the selection was assembled, and a duplicate name in either list is a
|
|
150
|
+
* manifest detail rather than a different selection.
|
|
151
|
+
*
|
|
152
|
+
* Pure function (applies ADR-013).
|
|
153
|
+
*
|
|
154
|
+
* @param previousPlugins - `manifest.plugins` as it stands before this run, or
|
|
155
|
+
* `null` when there is no prior manifest.
|
|
156
|
+
* @param effectivePluginNames - What this run installs.
|
|
157
|
+
*/
|
|
158
|
+
export function isPluginListUnchanged(previousPlugins, effectivePluginNames) {
|
|
159
|
+
if (previousPlugins === null)
|
|
160
|
+
return false;
|
|
161
|
+
const previous = new Set(previousPlugins);
|
|
162
|
+
const effective = new Set(effectivePluginNames);
|
|
163
|
+
return previous.size === effective.size && [...effective].every(name => previous.has(name));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Turn the skill-scoping half of an InstallReport into summary lines.
|
|
167
|
+
*
|
|
168
|
+
* Two facts the filesystem cannot tell the user apart from an install that never
|
|
169
|
+
* happened:
|
|
170
|
+
*
|
|
171
|
+
* - a REMOVED skill. Scoping the install means a re-init after deselecting a
|
|
172
|
+
* plugin silently deletes skills the previous install carried. The line is
|
|
173
|
+
* emitted only when the plugin list is UNCHANGED, because that is the case
|
|
174
|
+
* the user did not ask for: they re-ran init expecting nothing to move, and
|
|
175
|
+
* the scoping change is what moved it. When they deselected a plugin
|
|
176
|
+
* themselves, the removal is the thing they asked for and needs no notice.
|
|
177
|
+
* - a DORMANT shadow. `~/.devflow/skills/{name}/` is never deleted, so an
|
|
178
|
+
* inactive shadow and an applied one look identical from disk.
|
|
179
|
+
*
|
|
180
|
+
* `pluginListUnchanged` is the caller's answer to "did the selection move?", and
|
|
181
|
+
* it is FALSE when there is no prior manifest: a first install removed nothing a
|
|
182
|
+
* user had, so there is no upgrade to explain (design review L2).
|
|
183
|
+
*
|
|
184
|
+
* Pure function — returns lines, logs nothing (applies ADR-013).
|
|
185
|
+
*/
|
|
186
|
+
export function formatSkillScopeSummary(report, pluginListUnchanged) {
|
|
187
|
+
const lines = [];
|
|
188
|
+
if (pluginListUnchanged && report.removedSkills.length > 0) {
|
|
189
|
+
const names = [...report.removedSkills].sort().join(', ');
|
|
190
|
+
lines.push({
|
|
191
|
+
level: 'info',
|
|
192
|
+
message: `Removed ${report.removedSkills.length} skill(s) no selected plugin requires: ${names}. ` +
|
|
193
|
+
`Re-run devflow init and select the plugin that provides them to keep them.`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
for (const name of report.dormantShadows) {
|
|
197
|
+
lines.push({
|
|
198
|
+
level: 'info',
|
|
199
|
+
message: `Shadow for ${name} is inactive — the plugin that uses it is not selected ` +
|
|
200
|
+
`(${color.dim('kept in ~/.devflow/skills/')})`,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return lines;
|
|
204
|
+
}
|
|
205
|
+
//# 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')
|
|
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-MACHINE-WIDE (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.
|
|
5
9
|
*/
|
|
6
10
|
import { promises as fs } from 'fs';
|
|
7
11
|
import * as path from 'path';
|
|
@@ -9,8 +13,7 @@ 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 { updateFeature, isFeatureEnabled } from '../../../core/feature-config.js';
|
|
16
|
+
import { readMachineFeature, writeMachineFeature } from '../../../core/feature-switch.js';
|
|
14
17
|
import { getFeaturesDir } from '../../../core/project-paths.js';
|
|
15
18
|
async function getWorktreePath() {
|
|
16
19
|
return (await getGitRoot()) ?? process.cwd();
|
|
@@ -39,46 +42,33 @@ async function countKnowledgeBases(worktreePath) {
|
|
|
39
42
|
export async function handleToggle(options) {
|
|
40
43
|
if (!options.enable && !options.disable && !options.status)
|
|
41
44
|
return;
|
|
42
|
-
const worktreePath = await getWorktreePath();
|
|
43
45
|
const devflowDir = getDevFlowDirectory();
|
|
44
|
-
if (options.
|
|
45
|
-
p.intro(color.cyan('
|
|
46
|
-
|
|
47
|
-
await
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (manifest) {
|
|
51
|
-
manifest.features.knowledge = true;
|
|
52
|
-
manifest.updatedAt = new Date().toISOString();
|
|
53
|
-
await writeManifest(devflowDir, manifest);
|
|
54
|
-
}
|
|
55
|
-
p.log.success('Feature knowledge bases enabled');
|
|
56
|
-
p.log.info('Knowledge bases are created automatically when workflows detect documented area changes.');
|
|
46
|
+
if (options.status) {
|
|
47
|
+
p.intro(color.cyan('Feature Knowledge Status'));
|
|
48
|
+
const enabled = await readMachineFeature(devflowDir, 'knowledge');
|
|
49
|
+
const kbCount = await countKnowledgeBases(await getWorktreePath());
|
|
50
|
+
p.log.info(`Status: ${enabled ? color.green('enabled') : color.yellow('disabled')}`);
|
|
51
|
+
p.log.info(`Knowledge bases: ${kbCount}`);
|
|
57
52
|
p.outro('');
|
|
53
|
+
return;
|
|
58
54
|
}
|
|
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.');
|
|
55
|
+
const enabled = options.enable === true;
|
|
56
|
+
p.intro(color.cyan(`${enabled ? 'Enable' : 'Disable'} Feature Knowledge Bases`));
|
|
57
|
+
const recorded = await writeMachineFeature(devflowDir, 'knowledge', enabled);
|
|
58
|
+
if (!recorded.ok) {
|
|
59
|
+
p.log.error(`Devflow is not installed on this machine — run ${color.cyan('devflow init')} first`);
|
|
60
|
+
process.exitCode = 1;
|
|
72
61
|
p.outro('');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (enabled) {
|
|
65
|
+
p.log.success('Feature knowledge bases enabled in every project');
|
|
66
|
+
p.log.info('Knowledge bases are created automatically when workflows detect documented area changes.');
|
|
73
67
|
}
|
|
74
68
|
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('');
|
|
69
|
+
p.log.success('Feature knowledge bases disabled in every project');
|
|
70
|
+
p.log.info('Existing knowledge bases preserved. Write-back skipped while disabled.');
|
|
82
71
|
}
|
|
72
|
+
p.outro('');
|
|
83
73
|
}
|
|
84
74
|
//# sourceMappingURL=toggle.js.map
|
|
@@ -4,8 +4,7 @@ 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 { syncManifestFeature } from '../../core/manifest.js';
|
|
7
|
+
import { readMachineFeature, writeMachineFeature } from '../../core/feature-switch.js';
|
|
9
8
|
import { getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
|
|
10
9
|
import { getGitRoot } from '../../core/git.js';
|
|
11
10
|
import { sweepLegacyDreamMarkers, drainLearningQueue } from '../../core/learning-queue-cleanup.js';
|
|
@@ -15,8 +14,8 @@ import { readObservations, warnIfInvalid, } from '../../core/observation-io.js';
|
|
|
15
14
|
// ---------------------------------------------------------------------------
|
|
16
15
|
function printUsage() {
|
|
17
16
|
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` +
|
|
17
|
+
p.note(`${color.cyan('devflow learning --enable')} Enable learning in every project\n` +
|
|
18
|
+
`${color.cyan('devflow learning --disable')} Disable learning in every project (drains this project's queue)\n` +
|
|
20
19
|
`${color.cyan('devflow learning --status')} Show learning status\n` +
|
|
21
20
|
`${color.cyan('devflow learning --list')} Show all observations\n` +
|
|
22
21
|
`${color.cyan('devflow learning --configure')} Configuration wizard\n` +
|
|
@@ -37,13 +36,16 @@ async function requireGitRoot(actionSuffix) {
|
|
|
37
36
|
return gitRoot;
|
|
38
37
|
}
|
|
39
38
|
async function handleStatus() {
|
|
39
|
+
// D-FEATURES-MACHINE-WIDE: the one switch is the manifest's, so the state is
|
|
40
|
+
// the same from every directory; only the observation counts are per-project.
|
|
41
|
+
const enabled = await readMachineFeature(getDevFlowDirectory(), 'learning');
|
|
42
|
+
const stateLine = `Learning: ${enabled ? 'enabled' : 'disabled'}`;
|
|
40
43
|
const gitRoot = await getGitRoot();
|
|
41
44
|
if (!gitRoot) {
|
|
42
|
-
p.log.info(
|
|
45
|
+
p.log.info(`${stateLine}\nObservations: not in a git project`);
|
|
43
46
|
return;
|
|
44
47
|
}
|
|
45
48
|
const logPath = getDecisionsLogPath(gitRoot);
|
|
46
|
-
const enabled = await isFeatureEnabled(gitRoot, 'learning');
|
|
47
49
|
const { observations, invalidCount } = await readObservations(logPath);
|
|
48
50
|
const decisionObs = observations.filter(o => o.type === 'decision' || o.type === 'pitfall');
|
|
49
51
|
const decisions = observations.filter(o => o.type === 'decision');
|
|
@@ -52,7 +54,7 @@ async function handleStatus() {
|
|
|
52
54
|
const ready = decisionObs.filter(o => o.status === 'ready');
|
|
53
55
|
const observing = decisionObs.filter(o => o.status === 'observing');
|
|
54
56
|
const deprecated = decisionObs.filter(o => o.status === 'deprecated');
|
|
55
|
-
const lines = [
|
|
57
|
+
const lines = [stateLine];
|
|
56
58
|
if (decisionObs.length === 0) {
|
|
57
59
|
lines.push('Observations: none');
|
|
58
60
|
}
|
|
@@ -235,32 +237,37 @@ async function handleClear() {
|
|
|
235
237
|
await drainLearningQueue(gitRoot);
|
|
236
238
|
p.log.success('Decisions log cleared.');
|
|
237
239
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
240
|
+
/**
|
|
241
|
+
* `--enable` / `--disable`: the machine-wide switch (D-FEATURES-MACHINE-WIDE),
|
|
242
|
+
* converged exactly as `devflow init --learning / --no-learning` converges it —
|
|
243
|
+
* the manifest value, and on disable a drained queue in the current project.
|
|
244
|
+
* Never requires a git root: the switch is not a per-project setting.
|
|
245
|
+
*/
|
|
246
|
+
async function handleToggle(enabled) {
|
|
247
|
+
const recorded = await writeMachineFeature(getDevFlowDirectory(), 'learning', enabled);
|
|
248
|
+
if (!recorded.ok) {
|
|
249
|
+
p.log.error(`Devflow is not installed on this machine — run ${color.cyan('devflow init')} first`);
|
|
250
|
+
process.exitCode = 1;
|
|
241
251
|
return;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
async function handleDisable() {
|
|
248
|
-
const gitRoot = await requireGitRoot('configuration not updated');
|
|
249
|
-
if (!gitRoot)
|
|
252
|
+
}
|
|
253
|
+
if (enabled) {
|
|
254
|
+
p.log.success('Learning enabled in every project');
|
|
255
|
+
p.log.info(color.dim('Architectural decisions and pitfalls will be detected from your sessions'));
|
|
250
256
|
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
|
|
257
|
+
}
|
|
258
|
+
// Drain the current project's learning (decisions-detection) queue so stale
|
|
259
|
+
// turns don't process on re-enable. A mid-run Learning agent whose claimed
|
|
255
260
|
// batch vanishes aborts without changes — the desired outcome of disabling.
|
|
256
|
-
await
|
|
257
|
-
|
|
258
|
-
|
|
261
|
+
const gitRoot = await getGitRoot();
|
|
262
|
+
if (gitRoot) {
|
|
263
|
+
await drainLearningQueue(gitRoot);
|
|
264
|
+
}
|
|
265
|
+
p.log.success('Learning disabled in every project');
|
|
259
266
|
}
|
|
260
267
|
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')
|
|
268
|
+
.description('Enable or disable learning (decision/pitfall detection) in every project')
|
|
269
|
+
.option('--enable', 'Enable learning in every project')
|
|
270
|
+
.option('--disable', 'Disable learning in every project')
|
|
264
271
|
.option('--status', 'Show learning status and observation counts')
|
|
265
272
|
.option('--list', 'Show all decision/pitfall observations sorted by confidence')
|
|
266
273
|
.option('--configure', 'Interactive configuration wizard for learning.json')
|
|
@@ -300,11 +307,11 @@ export const learningCommand = new Command('learning')
|
|
|
300
307
|
return;
|
|
301
308
|
}
|
|
302
309
|
if (options.enable) {
|
|
303
|
-
await
|
|
310
|
+
await handleToggle(true);
|
|
304
311
|
return;
|
|
305
312
|
}
|
|
306
313
|
if (options.disable) {
|
|
307
|
-
await
|
|
314
|
+
await handleToggle(false);
|
|
308
315
|
return;
|
|
309
316
|
}
|
|
310
317
|
});
|
|
@@ -4,12 +4,11 @@ import * as path from 'path';
|
|
|
4
4
|
import * as p from '@clack/prompts';
|
|
5
5
|
import color from 'picocolors';
|
|
6
6
|
import { getClaudeDirectory, getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
|
|
7
|
-
import { syncManifestFeature } from '../../core/manifest.js';
|
|
8
7
|
import { writeFileAtomicExclusive } from '../../core/fs-atomic.js';
|
|
9
8
|
import { discoverProjectGitRoots } from '../../targets/claude-code/post-install.js';
|
|
10
9
|
import { getGitRoot } from '../../core/git.js';
|
|
11
10
|
import { getMemoryDir, getPendingTurnsPath, getPendingTurnsProcessingPath, } from '../../core/project-paths.js';
|
|
12
|
-
import {
|
|
11
|
+
import { readMachineFeature, writeMachineFeature } from '../../core/feature-switch.js';
|
|
13
12
|
/**
|
|
14
13
|
* Map of hook event type → filename marker for the memory hooks.
|
|
15
14
|
* Three hooks total: Stop, SessionStart, PreCompact.
|
|
@@ -141,6 +140,39 @@ export function countMemoryHooks(input) {
|
|
|
141
140
|
}
|
|
142
141
|
return count;
|
|
143
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Converge the memory hooks in a settings JSON string to `enabled`. Pure.
|
|
145
|
+
*
|
|
146
|
+
* D-FEATURES-MACHINE-WIDE: the ONE settings transform for the memory feature,
|
|
147
|
+
* shared by `devflow init` (inside its single settings read-modify-write pass)
|
|
148
|
+
* and `devflow memory --enable/--disable`, so the two controls of the same
|
|
149
|
+
* machine-wide switch leave settings.json byte-for-byte alike. Always
|
|
150
|
+
* remove-then-add, which also upgrades an older hook format (e.g. `.sh` →
|
|
151
|
+
* `run-hook`) in place.
|
|
152
|
+
*
|
|
153
|
+
* Stop-array ordering (AC-C2): memory-worker is appended after whatever the
|
|
154
|
+
* Stop array already holds, so it lands after capture-turn as long as the
|
|
155
|
+
* capture hooks are registered first — init registers them earlier in the same
|
|
156
|
+
* pass, and on a standalone toggle they are already present.
|
|
157
|
+
*/
|
|
158
|
+
export function convergeMemoryHooks(settingsJson, enabled, devflowDir) {
|
|
159
|
+
const cleaned = removeMemoryHooks(settingsJson);
|
|
160
|
+
return enabled ? addMemoryHooks(cleaned, devflowDir) : cleaned;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Drain a project's pending memory queue (and a claimed batch) so stale turns
|
|
164
|
+
* are not processed when memory is next switched on. Shared by `devflow init
|
|
165
|
+
* --no-memory` and `devflow memory --disable`. ENOENT-tolerant; any other
|
|
166
|
+
* error propagates to the command boundary, like drainLearningQueue.
|
|
167
|
+
*/
|
|
168
|
+
export async function drainMemoryQueue(projectRoot) {
|
|
169
|
+
const ignoreMissing = (e) => { if (e.code !== 'ENOENT')
|
|
170
|
+
throw e; };
|
|
171
|
+
await Promise.all([
|
|
172
|
+
fs.unlink(getPendingTurnsPath(projectRoot)).catch(ignoreMissing),
|
|
173
|
+
fs.unlink(getPendingTurnsProcessingPath(projectRoot)).catch(ignoreMissing),
|
|
174
|
+
]);
|
|
175
|
+
}
|
|
144
176
|
/**
|
|
145
177
|
* Returns true if the given project root contains a `.devflow/memory/` directory.
|
|
146
178
|
* Treats unexpected errors (e.g. EACCES) as absent to avoid false positives.
|
|
@@ -195,16 +227,16 @@ export async function cleanQueueFiles(projectPaths) {
|
|
|
195
227
|
}
|
|
196
228
|
export const memoryCommand = new Command('memory')
|
|
197
229
|
.description('Enable, disable, or clean up working memory (session context preservation)')
|
|
198
|
-
.option('--enable', 'Enable working memory')
|
|
199
|
-
.option('--disable', 'Disable working memory')
|
|
230
|
+
.option('--enable', 'Enable working memory in every project')
|
|
231
|
+
.option('--disable', 'Disable working memory in every project')
|
|
200
232
|
.option('--status', 'Show current state')
|
|
201
233
|
.option('--clear', 'Clean up queue files from projects')
|
|
202
234
|
.action(async (options) => {
|
|
203
235
|
const hasFlag = options.enable || options.disable || options.status || options.clear;
|
|
204
236
|
if (!hasFlag) {
|
|
205
237
|
p.intro(color.bgCyan(color.white(' Working Memory ')));
|
|
206
|
-
p.note(`${color.cyan('devflow memory --enable')}
|
|
207
|
-
`${color.cyan('devflow memory --disable')}
|
|
238
|
+
p.note(`${color.cyan('devflow memory --enable')} Enable working memory (every project)\n` +
|
|
239
|
+
`${color.cyan('devflow memory --disable')} Disable working memory (every project)\n` +
|
|
208
240
|
`${color.cyan('devflow memory --status')} Check current state\n` +
|
|
209
241
|
`${color.cyan('devflow memory --clear')} Clean up queue files`, 'Usage');
|
|
210
242
|
p.outro(color.dim('Memory hooks provide automatic session context preservation'));
|
|
@@ -260,90 +292,68 @@ export const memoryCommand = new Command('memory')
|
|
|
260
292
|
: 'No queue files found to clean');
|
|
261
293
|
return;
|
|
262
294
|
}
|
|
263
|
-
const
|
|
264
|
-
const
|
|
295
|
+
const settingsPath = path.join(getClaudeDirectory(), 'settings.json');
|
|
296
|
+
const devflowDir = getDevFlowDirectory();
|
|
265
297
|
let settingsContent;
|
|
266
298
|
try {
|
|
267
299
|
settingsContent = await fs.readFile(settingsPath, 'utf-8');
|
|
268
300
|
}
|
|
269
301
|
catch {
|
|
270
|
-
if (options.status) {
|
|
271
|
-
p.log.info('Working memory: disabled (no settings.json found)');
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
// Create minimal settings.json
|
|
275
302
|
settingsContent = '{}';
|
|
276
303
|
}
|
|
277
|
-
// Resolve current project root for feature config
|
|
278
|
-
const gitRoot = await getGitRoot();
|
|
279
304
|
if (options.status) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
305
|
+
// D-FEATURES-MACHINE-WIDE: one switch, the manifest's. The hook count is
|
|
306
|
+
// reported beside it because the hooks are how that switch takes effect.
|
|
307
|
+
const enabled = await readMachineFeature(devflowDir, 'memory');
|
|
284
308
|
const count = countMemoryHooks(settingsContent);
|
|
285
309
|
const total = Object.keys(MEMORY_HOOK_CONFIG).length;
|
|
286
|
-
|
|
287
|
-
const featureEnabled = await isFeatureEnabled(gitRoot, 'memory');
|
|
288
|
-
if (count === total && featureEnabled) {
|
|
310
|
+
if (enabled && count === total) {
|
|
289
311
|
p.log.info(`Working memory: ${color.green('enabled')} (${total}/${total} hooks)`);
|
|
290
312
|
}
|
|
291
|
-
else if (
|
|
313
|
+
else if (!enabled) {
|
|
292
314
|
p.log.info(`Working memory: ${color.dim('disabled')}`);
|
|
293
315
|
}
|
|
294
316
|
else {
|
|
295
|
-
p.log.info(`Working memory: ${color.yellow(`
|
|
317
|
+
p.log.info(`Working memory: ${color.yellow(`enabled, but ${count}/${total} hooks registered`)} — ` +
|
|
318
|
+
`run ${color.cyan('devflow memory --enable')} to fix`);
|
|
296
319
|
}
|
|
297
320
|
return;
|
|
298
321
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
p.log.success('Working memory enabled — configuration updated');
|
|
313
|
-
p.log.info(color.dim('Session context will be automatically preserved across conversations'));
|
|
314
|
-
}
|
|
315
|
-
else {
|
|
316
|
-
const updated = addMemoryHooks(settingsContent, devflowDir);
|
|
317
|
-
await writeFileAtomicExclusive(settingsPath, updated);
|
|
318
|
-
p.log.success('Working memory enabled — hooks registered');
|
|
319
|
-
p.log.info(color.dim('Session context will be automatically preserved across conversations'));
|
|
320
|
-
}
|
|
321
|
-
// Update config to enable memory feature
|
|
322
|
-
if (gitRoot) {
|
|
323
|
-
await updateFeature(gitRoot, 'memory', true);
|
|
324
|
-
}
|
|
325
|
-
await syncManifestFeature(getDevFlowDirectory(), 'memory', true);
|
|
322
|
+
// --enable / --disable: the machine-wide switch, converged exactly as
|
|
323
|
+
// `devflow init --memory / --no-memory` converges it (D-FEATURES-MACHINE-WIDE).
|
|
324
|
+
// The settings transform runs FIRST: it is the step that can reject its
|
|
325
|
+
// input (malformed JSON), and the switch must not be recorded unless the
|
|
326
|
+
// hooks that enact it can follow.
|
|
327
|
+
const enabled = options.enable === true;
|
|
328
|
+
let converged;
|
|
329
|
+
try {
|
|
330
|
+
converged = convergeMemoryHooks(settingsContent, enabled, devflowDir);
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
p.log.error(`Could not update ${settingsPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
334
|
+
process.exitCode = 1;
|
|
326
335
|
return;
|
|
327
336
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
await updateFeature(gitRoot, 'memory', false);
|
|
333
|
-
// Drain orphaned queue files so stale turns don't process on re-enable
|
|
334
|
-
await Promise.all([
|
|
335
|
-
fs.unlink(getPendingTurnsPath(gitRoot)).catch((e) => { if (e.code !== 'ENOENT')
|
|
336
|
-
throw e; }),
|
|
337
|
-
fs.unlink(getPendingTurnsProcessingPath(gitRoot)).catch((e) => { if (e.code !== 'ENOENT')
|
|
338
|
-
throw e; }),
|
|
339
|
-
]);
|
|
340
|
-
await syncManifestFeature(getDevFlowDirectory(), 'memory', false);
|
|
341
|
-
p.log.success('Working memory disabled — configuration updated');
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
p.log.warn('Could not resolve git root — configuration not updated');
|
|
345
|
-
}
|
|
337
|
+
const recorded = await writeMachineFeature(devflowDir, 'memory', enabled);
|
|
338
|
+
if (!recorded.ok) {
|
|
339
|
+
p.log.error(`Devflow is not installed on this machine — run ${color.cyan('devflow init')} first`);
|
|
340
|
+
process.exitCode = 1;
|
|
346
341
|
return;
|
|
347
342
|
}
|
|
343
|
+
if (converged !== settingsContent) {
|
|
344
|
+
await writeFileAtomicExclusive(settingsPath, converged);
|
|
345
|
+
}
|
|
346
|
+
if (enabled) {
|
|
347
|
+
p.log.success('Working memory enabled in every project');
|
|
348
|
+
p.log.info(color.dim('Session context will be automatically preserved across conversations'));
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// Drain the current project's queue, as init does. Outside a git project
|
|
352
|
+
// there is no project queue to drain, and the switch itself still applies.
|
|
353
|
+
const gitRoot = await getGitRoot();
|
|
354
|
+
if (gitRoot) {
|
|
355
|
+
await drainMemoryQueue(gitRoot);
|
|
356
|
+
}
|
|
357
|
+
p.log.success('Working memory disabled in every project');
|
|
348
358
|
});
|
|
349
359
|
//# sourceMappingURL=memory.js.map
|