devflow-kit 3.8.0 → 3.10.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 +231 -0
- package/README.md +6 -6
- package/dist/agents/code.md +10 -10
- package/dist/agents/fix.md +7 -7
- package/dist/agents/git.md +2 -2
- package/dist/agents/harden.md +7 -6
- package/dist/agents/refactor.md +6 -6
- package/dist/agents/skim.md +1 -1
- package/dist/agents/triage.md +7 -0
- package/dist/cli/commands/ambient.js +10 -2
- package/dist/cli/commands/compliance-prompts.js +9 -4
- package/dist/cli/commands/compliance.js +171 -24
- package/dist/cli/commands/curation.js +79 -23
- package/dist/cli/commands/gate-level-prompts.js +39 -23
- package/dist/cli/commands/gates.js +6 -7
- package/dist/cli/commands/init.js +150 -145
- package/dist/cli/commands/install-report.js +34 -0
- package/dist/cli/commands/legacy-sweep.js +64 -0
- package/dist/cli/commands/memory.js +59 -12
- package/dist/cli/commands/proxy.js +6 -1
- package/dist/cli/commands/rules.js +29 -12
- package/dist/cli/commands/skills.js +42 -9
- package/dist/cli/commands/tracker.js +9 -3
- package/dist/cli/commands/uninstall.js +354 -60
- package/dist/commands/bug-analysis.md +71 -122
- package/dist/commands/code-review.md +11 -11
- package/dist/commands/debug.md +2 -2
- package/dist/commands/dynamic-build.md +25 -25
- package/dist/commands/dynamic-plan.md +2 -2
- package/dist/commands/dynamic-profile.md +2 -2
- package/dist/commands/dynamic-tickets.md +2 -2
- package/dist/commands/explore.md +2 -2
- package/dist/commands/implement.md +48 -34
- package/dist/commands/plan.md +2 -2
- package/dist/commands/release.md +3 -3
- package/dist/commands/research.md +2 -2
- package/dist/commands/resolve.md +50 -46
- package/dist/commands/self-review.md +10 -9
- package/dist/compliance-off/agents/git.md +120 -0
- package/dist/compliance-off/agents/triage.md +180 -0
- package/dist/compliance-off/commands/bug-analysis.md +398 -0
- package/dist/compliance-off/commands/code-review.md +520 -0
- package/dist/compliance-off/commands/dynamic-build.md +1251 -0
- package/dist/compliance-off/commands/dynamic-tickets.md +676 -0
- package/dist/compliance-off/commands/implement.md +736 -0
- package/dist/compliance-off/commands/plan.md +671 -0
- package/dist/compliance-off/commands/release.md +309 -0
- package/dist/compliance-off/commands/resolve.md +742 -0
- package/dist/compliance-off/skills/git/references/decision-markers.md +17 -0
- package/dist/compliance-off/skills/git/references/learn-conventions.md +80 -0
- package/dist/compliance-off/skills/git/references/pr/check-ci-status.md +33 -0
- package/dist/compliance-off/skills/git/references/pr/check-merge-readiness.md +49 -0
- package/dist/compliance-off/skills/git/references/pr/fetch-review-threads.md +52 -0
- package/dist/compliance-off/skills/git/references/pr/validate-branch.md +47 -0
- package/dist/compliance-off/skills/git/references/tracker/_contract.md +33 -0
- package/dist/compliance-off/skills/git/references/tracker/_mcp.md +153 -0
- package/dist/compliance-off/skills/git/references/tracker/github/create-release.md +35 -0
- package/dist/compliance-off/skills/git/references/tracker/github/ensure-pr-ready.md +100 -0
- package/dist/compliance-off/skills/git/references/tracker/github/ensure-traceable-issue.md +74 -0
- package/dist/compliance-off/skills/git/references/tracker/github/fetch-issue.md +69 -0
- package/dist/compliance-off/skills/git/references/tracker/github/fetch-issues-batch.md +65 -0
- package/dist/compliance-off/skills/git/references/tracker/github/gather-release-evidence.md +48 -0
- package/dist/compliance-off/skills/git/references/tracker/github/setup-task.md +77 -0
- package/dist/compliance-off/skills/git/references/tracker/jira/create-release.md +35 -0
- package/dist/compliance-off/skills/git/references/tracker/jira/ensure-pr-ready.md +100 -0
- package/dist/compliance-off/skills/git/references/tracker/jira/ensure-traceable-issue.md +61 -0
- package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issue.md +51 -0
- package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issues-batch.md +63 -0
- package/dist/compliance-off/skills/git/references/tracker/jira/gather-release-evidence.md +47 -0
- package/dist/compliance-off/skills/git/references/tracker/jira/setup-task.md +82 -0
- package/dist/compliance-off/skills/git/references/tracker/linear/create-release.md +35 -0
- package/dist/compliance-off/skills/git/references/tracker/linear/ensure-pr-ready.md +100 -0
- package/dist/compliance-off/skills/git/references/tracker/linear/ensure-traceable-issue.md +61 -0
- package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issue.md +51 -0
- package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issues-batch.md +63 -0
- package/dist/compliance-off/skills/git/references/tracker/linear/gather-release-evidence.md +47 -0
- package/dist/compliance-off/skills/git/references/tracker/linear/setup-task.md +83 -0
- package/dist/compliance-off/skills/git/references/trust-rule.md +7 -0
- package/dist/core/agent-models.js +19 -4
- package/dist/core/area-pause-notice.js +44 -0
- package/dist/core/assets.js +126 -17
- package/dist/core/compliance-off-files.js +27 -0
- package/dist/core/curation-move-skip.js +4 -5
- package/dist/core/curation-queue-cleanup.js +6 -5
- package/dist/core/curation-store.js +1 -0
- package/dist/core/curation-variants.js +460 -112
- package/dist/core/evidence-policy.js +10 -8
- package/dist/core/flags.js +2 -2
- package/dist/core/installed-variants.js +63 -0
- package/dist/core/manifest.js +48 -5
- package/dist/core/mds-variants.js +700 -3
- package/dist/core/orphan-sweep.js +11 -2
- package/dist/core/plugins.js +69 -12
- package/dist/core/tracked-areas.js +138 -14
- package/dist/curation-compliance-off/agents/triage.md +176 -0
- package/dist/curation-compliance-off/commands/bug-analysis.md +349 -0
- package/dist/curation-compliance-off/commands/code-review.md +481 -0
- package/dist/curation-compliance-off/commands/dynamic-build.md +1211 -0
- package/dist/curation-compliance-off/commands/dynamic-tickets.md +634 -0
- package/dist/curation-compliance-off/commands/implement.md +689 -0
- package/dist/curation-compliance-off/commands/plan.md +631 -0
- package/dist/curation-compliance-off/commands/release.md +279 -0
- package/dist/curation-compliance-off/commands/resolve.md +692 -0
- package/dist/curation-off/agents/code.md +10 -10
- package/dist/curation-off/agents/fix.md +7 -7
- package/dist/curation-off/agents/harden.md +7 -6
- package/dist/curation-off/agents/refactor.md +6 -6
- package/dist/curation-off/agents/triage.md +7 -0
- package/dist/curation-off/commands/bug-analysis.md +69 -119
- package/dist/curation-off/commands/code-review.md +11 -11
- package/dist/curation-off/commands/debug.md +2 -2
- package/dist/curation-off/commands/dynamic-build.md +25 -25
- package/dist/curation-off/commands/explore.md +2 -2
- package/dist/curation-off/commands/implement.md +48 -34
- package/dist/curation-off/commands/plan.md +2 -2
- package/dist/curation-off/commands/release.md +1 -1
- package/dist/curation-off/commands/resolve.md +50 -46
- package/dist/curation-off/commands/self-review.md +10 -9
- package/dist/hud/components/curation-counts.js +27 -0
- package/dist/skills/git/references/pr/post-resolution-summary.md +12 -11
- package/dist/skills/git/references/pr/post-review-summary.md +12 -11
- package/dist/skills/git/references/pr/resolve-review-threads.md +1 -1
- package/dist/skills/git/references/pr/update-pr-evidence.md +2 -2
- package/dist/skills/git/references/publication-gate.md +6 -7
- package/dist/skills/git/references/tracker/github/manage-debt.md +6 -2
- package/dist/skills/git/references/tracker/jira/manage-debt.md +6 -2
- package/dist/skills/git/references/tracker/linear/manage-debt.md +6 -2
- package/dist/targets/claude-code/behavior-install.js +23 -9
- package/dist/targets/claude-code/charter-render.js +374 -0
- package/dist/targets/claude-code/compliance-install.js +32 -12
- package/dist/targets/claude-code/compliance-off-files.js +245 -0
- package/dist/targets/claude-code/curation-install.js +570 -100
- package/dist/targets/claude-code/installer.js +467 -62
- package/dist/targets/claude-code/language-stamp.js +6 -0
- package/dist/targets/claude-code/linked-folder.js +182 -0
- package/dist/targets/claude-code/reference-converge.js +118 -0
- package/dist/targets/claude-code/tracker-install.js +21 -8
- package/dist/targets/claude-code/tracker-servers.js +20 -6
- package/dist/targets/claude-code/workflow-folder-install.js +231 -0
- package/dist/variant-axes.json +109 -0
- package/package.json +1 -1
- package/src/assets/agents/_partials/_code.mds +6 -6
- package/src/assets/agents/code.mds +4 -4
- package/src/assets/agents/evaluate.md +7 -13
- package/src/assets/agents/fix.mds +1 -1
- package/src/assets/agents/git.mds +34 -2
- package/src/assets/agents/harden.mds +7 -6
- package/src/assets/agents/skim.mds +1 -1
- package/src/assets/agents/test.md +21 -12
- package/src/assets/agents/triage.mds +9 -0
- package/src/assets/agents/validate.md +27 -34
- package/src/assets/commands/_partials/_compliance.mds +5 -3
- package/src/assets/commands/_partials/_decisions.mds +10 -8
- package/src/assets/commands/_partials/_engine.mds +9 -8
- package/src/assets/commands/_partials/_publication.mds +10 -1
- package/src/assets/commands/_partials/_settings.mds +2 -2
- package/src/assets/commands/bug-analysis.mds +80 -126
- package/src/assets/commands/code-review.mds +52 -9
- package/src/assets/commands/dynamic-build.mds +109 -15
- package/src/assets/commands/dynamic-tickets.mds +13 -0
- package/src/assets/commands/implement.mds +145 -33
- package/src/assets/commands/plan.mds +19 -0
- package/src/assets/commands/release.mds +50 -1
- package/src/assets/commands/resolve.mds +229 -44
- package/src/assets/commands/self-review.mds +8 -7
- package/src/assets/mds/git/_ops.mds +29 -1
- package/src/assets/mds/git/_ops_pr.mds +20 -10
- package/src/assets/mds/git/_ops_steps.mds +27 -0
- package/src/assets/mds/git/_pr.mds +15 -15
- package/src/assets/mds/git/_references.mds +12 -7
- package/src/assets/mds/tracker/_common.mds +4 -0
- package/src/assets/mds/tracker/_github.mds +43 -4
- package/src/assets/mds/tracker/_jira.mds +44 -4
- package/src/assets/mds/tracker/_linear.mds +44 -4
- package/src/assets/rules/quality.md +2 -0
- package/src/assets/scripts/ci-wait.cjs +493 -118
- package/src/assets/scripts/hooks/assets/implement-installed +1 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +13 -11
- package/src/assets/scripts/hooks/background-memory-update +79 -1
- package/src/assets/scripts/hooks/json-helper.cjs +44 -2
- package/src/assets/scripts/hooks/lib/curation-store.cjs +82 -20
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +9 -1
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +226 -33
- package/src/assets/scripts/hooks/memory-permission +10 -2
- package/src/assets/scripts/hooks/memory-worker +11 -0
- package/src/assets/scripts/hooks/pre-compact-memory +11 -0
- package/src/assets/scripts/hooks/preamble +18 -8
- package/src/assets/scripts/hooks/queue-append +218 -1
- package/src/assets/scripts/hooks/session-start-context +89 -37
- package/src/assets/scripts/hooks/session-start-memory +39 -0
- package/src/assets/scripts/hooks/session-start-orchestrator +4 -2
- package/src/assets/scripts/pr-evidence.cjs +46 -4
- package/src/assets/scripts/resolve-settings.cjs +244 -30
- package/src/assets/scripts/verify-evidence.cjs +708 -124
- package/src/assets/skills/agent-references/SKILL.md +7 -1
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +6 -4
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +3 -3
- package/src/assets/skills/agent-references/references/commands/plan-levels.md +1 -1
- package/src/assets/skills/agent-references/references/commands/resolve-levels.md +3 -2
- package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +6 -2
- package/src/assets/skills/agent-references/references/synthesize/review.md +4 -4
- package/src/assets/skills/agent-references/references/triage/severity-tiers.md +43 -0
- package/src/assets/skills/agent-references/references/validate/passed-alone.md +14 -0
- package/src/assets/skills/docs-framework/SKILL.md +5 -2
- package/src/assets/skills/git/SKILL.md +3 -3
- package/src/assets/skills/git/references/github-api.md +2 -139
- package/src/assets/skills/git/references/github-posting.md +158 -0
- package/src/assets/skills/review-methodology/SKILL.md +1 -1
- package/src/assets/skills/review-methodology/references/patterns.md +1 -1
- package/src/assets/skills/review-methodology/references/report-template.md +1 -2
- package/src/assets/skills/testing/SKILL.md +2 -4
|
@@ -8,8 +8,11 @@ import { writeSettingsFileAtomic } from '../../core/fs-atomic.js';
|
|
|
8
8
|
import { discoverProjectGitRoots } from '../../targets/claude-code/post-install.js';
|
|
9
9
|
import { getGitRoot } from '../../core/git.js';
|
|
10
10
|
import { firstSymbolicLink } from '../../core/linked-path.js';
|
|
11
|
+
import { pooled } from '../../core/pool.js';
|
|
11
12
|
import { getMemoryDir, getPendingTurnsPath, getPendingTurnsProcessingPath, } from '../../core/project-paths.js';
|
|
12
13
|
import { drainQueueFiles, formatRefusedDrain } from '../../core/queue-drain.js';
|
|
14
|
+
import { AREA_PAUSE_NOTICE } from '../../core/area-pause-notice.js';
|
|
15
|
+
import { probeTrackedArea } from '../../core/tracked-areas.js';
|
|
13
16
|
import { HOOKS_DIR_SUFFIX, devflowHookOwner, endsWithAny, ensureHook, hasHook, removeHooks, runHookCommand, runHookSuffix, } from '../../targets/claude-code/hooks.js';
|
|
14
17
|
import { readMachineFeature, writeMachineFeature } from '../../core/feature-switch.js';
|
|
15
18
|
import { loadSettingsModule, narrowedSwitchLabel, personalConfigTrackedWarning } from '../../core/evidence-policy.js';
|
|
@@ -159,21 +162,42 @@ export function convergeMemoryHooks(settingsJson, enabled, devflowDir) {
|
|
|
159
162
|
const cleaned = removeMemoryHooks(settingsJson);
|
|
160
163
|
return enabled ? addMemoryHooks(cleaned, devflowDir) : cleaned;
|
|
161
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Why the memory area at `projectRoot` is paused, or null when git tracks nothing in the
|
|
167
|
+
* memory folder there. The area is paused when git tracks files in the memory folder, or
|
|
168
|
+
* a `.git` entry stands and git did not answer (`unknown`); a root with no `.git` entry
|
|
169
|
+
* makes no git call. The root is the checkout's toplevel, where the memory hooks work.
|
|
170
|
+
*/
|
|
171
|
+
async function memoryPauseReason(projectRoot) {
|
|
172
|
+
const { answer } = await probeTrackedArea(projectRoot, 'memory');
|
|
173
|
+
return answer === 'untracked' ? null : answer;
|
|
174
|
+
}
|
|
162
175
|
/**
|
|
163
176
|
* Drain a project's pending memory queue (and a claimed batch) so stale turns
|
|
164
177
|
* are not processed when memory is next switched on. Shared by `devflow init
|
|
165
|
-
* --no-memory` and `devflow memory --disable`.
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
178
|
+
* --no-memory` and `devflow memory --disable`. Deletes nothing, and answers
|
|
179
|
+
* `skipped`, when git tracks files in the memory folder or a `.git` entry stands and
|
|
180
|
+
* git does not answer: a folder that git tracks is the repository's content. Refused,
|
|
181
|
+
* deleting nothing, when `.devflow` or `.devflow/memory` under `projectRoot` is a
|
|
182
|
+
* symbolic link (D-CLI-NO-SYMLINK). ENOENT-tolerant; any other error propagates to the
|
|
183
|
+
* command boundary, like drainCurationQueue.
|
|
169
184
|
*/
|
|
170
185
|
export async function drainMemoryQueue(projectRoot) {
|
|
186
|
+
const skipped = await memoryPauseReason(projectRoot);
|
|
187
|
+
if (skipped !== null)
|
|
188
|
+
return { drained: false, skipped };
|
|
171
189
|
const memoryDir = getMemoryDir(projectRoot);
|
|
172
190
|
return drainQueueFiles([path.dirname(memoryDir), memoryDir], [
|
|
173
191
|
getPendingTurnsPath(projectRoot),
|
|
174
192
|
getPendingTurnsProcessingPath(projectRoot),
|
|
175
193
|
]);
|
|
176
194
|
}
|
|
195
|
+
/** The line a caller prints for a memory drain that deleted nothing: the fixed notice of a paused area, or the refusal at a link. */
|
|
196
|
+
export function formatMemoryNotDrained(drain) {
|
|
197
|
+
if (!('skipped' in drain))
|
|
198
|
+
return formatRefusedDrain('memory', drain.linkedFolder);
|
|
199
|
+
return `The memory queue was not drained. ${AREA_PAUSE_NOTICE.memory[drain.skipped]}`;
|
|
200
|
+
}
|
|
177
201
|
/**
|
|
178
202
|
* Returns true if the given project root contains a `.devflow/memory/` directory.
|
|
179
203
|
* Treats unexpected errors (e.g. EACCES) as absent to avoid false positives.
|
|
@@ -200,15 +224,23 @@ export async function filterProjectsWithMemory(gitRoots) {
|
|
|
200
224
|
const checks = await Promise.all(gitRoots.map(async (root) => ({ root, has: await hasMemoryDir(root) })));
|
|
201
225
|
return checks.filter((c) => c.has).map((c) => c.root);
|
|
202
226
|
}
|
|
227
|
+
/** At most this many projects are checked and cleaned at once, so `memory --clear` starts few git processes. */
|
|
228
|
+
export const CLEAN_QUEUE_POOL_LIMIT = 8;
|
|
203
229
|
/**
|
|
204
230
|
* Clean up memory queue files from the given project paths.
|
|
205
231
|
* Skips projects where the background updater lock is held to avoid data loss,
|
|
206
|
-
*
|
|
207
|
-
* `.
|
|
208
|
-
*
|
|
232
|
+
* leaves a project whose memory area is paused alone (git tracks files in the memory
|
|
233
|
+
* folder, or a `.git` entry stands and git did not answer; `paused` names each such
|
|
234
|
+
* project with the reason), and refuses, deleting nothing there, a project whose
|
|
235
|
+
* `.devflow` or `.devflow/memory` is a symbolic link (D-CLI-NO-SYMLINK); `refused` names
|
|
236
|
+
* each such link. At most {@link CLEAN_QUEUE_POOL_LIMIT} projects are handled at once.
|
|
237
|
+
* Returns the count of projects from which at least one file was removed.
|
|
209
238
|
*/
|
|
210
239
|
export async function cleanQueueFiles(projectPaths) {
|
|
211
|
-
const
|
|
240
|
+
const settled = await pooled(projectPaths, CLEAN_QUEUE_POOL_LIMIT, async (project) => {
|
|
241
|
+
const reason = await memoryPauseReason(project);
|
|
242
|
+
if (reason !== null)
|
|
243
|
+
return { paused: reason, project };
|
|
212
244
|
const memDir = getMemoryDir(project);
|
|
213
245
|
let linkedFolder;
|
|
214
246
|
try {
|
|
@@ -234,10 +266,16 @@ export async function cleanQueueFiles(projectPaths) {
|
|
|
234
266
|
fs.unlink(getPendingTurnsProcessingPath(project)).then(() => true).catch(() => false),
|
|
235
267
|
]);
|
|
236
268
|
return (q || pr) ? { cleaned: project } : null;
|
|
237
|
-
})
|
|
269
|
+
});
|
|
270
|
+
const results = settled.map((entry) => {
|
|
271
|
+
if (entry.status === 'rejected')
|
|
272
|
+
throw entry.reason;
|
|
273
|
+
return entry.value;
|
|
274
|
+
});
|
|
238
275
|
const cleanedProjects = results.flatMap((r) => (r !== null && 'cleaned' in r ? [r.cleaned] : []));
|
|
239
276
|
const refused = results.flatMap((r) => (r !== null && 'refused' in r ? [r.refused] : []));
|
|
240
|
-
|
|
277
|
+
const paused = results.flatMap((r) => (r !== null && 'paused' in r ? [{ project: r.project, reason: r.paused }] : []));
|
|
278
|
+
return { cleaned: cleanedProjects.length, projects: cleanedProjects, refused, paused };
|
|
241
279
|
}
|
|
242
280
|
export const memoryCommand = new Command('memory')
|
|
243
281
|
.description('Enable, disable, or clean up working memory (session context preservation)')
|
|
@@ -297,10 +335,13 @@ export const memoryCommand = new Command('memory')
|
|
|
297
335
|
}
|
|
298
336
|
targets = scope === 'local' && currentProject ? [currentProject] : allProjects;
|
|
299
337
|
}
|
|
300
|
-
const { cleaned, projects: cleanedProjects, refused } = await cleanQueueFiles(targets);
|
|
338
|
+
const { cleaned, projects: cleanedProjects, refused, paused } = await cleanQueueFiles(targets);
|
|
301
339
|
for (const project of cleanedProjects) {
|
|
302
340
|
p.log.info(color.dim(`Cleaned: ${project}`));
|
|
303
341
|
}
|
|
342
|
+
for (const { project, reason } of paused) {
|
|
343
|
+
p.log.warn(`The memory queue of ${project} was not cleaned. ${AREA_PAUSE_NOTICE.memory[reason]}`);
|
|
344
|
+
}
|
|
304
345
|
for (const linkedFolder of refused) {
|
|
305
346
|
p.log.warn(formatRefusedDrain('memory', linkedFolder));
|
|
306
347
|
}
|
|
@@ -340,6 +381,12 @@ export const memoryCommand = new Command('memory')
|
|
|
340
381
|
const narrowed = enabled ? narrowedSwitchLabel(settingsModule, { dir: process.cwd() }, 'memory') : null;
|
|
341
382
|
if (narrowed !== null)
|
|
342
383
|
p.log.info(`Effective here: ${color.yellow(narrowed)}`);
|
|
384
|
+
// A paused memory area (git tracks files in the memory folder, or did not answer):
|
|
385
|
+
// the fixed notice, from the checkout's toplevel, where the memory hooks work. The
|
|
386
|
+
// current directory stands in outside a git work tree, as it does for the settings line.
|
|
387
|
+
const paused = await memoryPauseReason((await getGitRoot()) ?? process.cwd());
|
|
388
|
+
if (paused !== null)
|
|
389
|
+
p.log.info(AREA_PAUSE_NOTICE.memory[paused]);
|
|
343
390
|
const trackedWarning = personalConfigTrackedWarning(settingsModule, { dir: process.cwd() });
|
|
344
391
|
if (trackedWarning !== null)
|
|
345
392
|
p.log.warn(trackedWarning);
|
|
@@ -380,7 +427,7 @@ export const memoryCommand = new Command('memory')
|
|
|
380
427
|
if (gitRoot) {
|
|
381
428
|
const drain = await drainMemoryQueue(gitRoot);
|
|
382
429
|
if (!drain.drained)
|
|
383
|
-
p.log.warn(
|
|
430
|
+
p.log.warn(formatMemoryNotDrained(drain));
|
|
384
431
|
}
|
|
385
432
|
p.log.success('Working memory disabled in every project');
|
|
386
433
|
});
|
|
@@ -31,6 +31,7 @@ import { stripAnsi } from '../../hud/colors.js';
|
|
|
31
31
|
import { reapplyAgentMapping, revertExternalAgents, countExternalMappedAgents, readAgentMapping, } from '../../core/agent-models.js';
|
|
32
32
|
import { getExternalModelsCached, discoverExternalModels, } from '../../core/model-discovery.js';
|
|
33
33
|
import { modelCacheDir } from '../../core/cache.js';
|
|
34
|
+
import { formatLinksSummary } from './install-report.js';
|
|
34
35
|
import { getClaudeDirectory, getDevFlowDirectory, getHomeDirectory, } from '../../targets/claude-code/claude-paths.js';
|
|
35
36
|
import { devflowHookOwner, ensureHook, hasHook, removeHooks, runHookCommand, } from '../../targets/claude-code/hooks.js';
|
|
36
37
|
function Ok(value) {
|
|
@@ -1389,6 +1390,8 @@ async function runEnable(portOption) {
|
|
|
1389
1390
|
s.message('Warming model cache...');
|
|
1390
1391
|
await discoverExternalModels(cacheDir, logPath).catch(() => { });
|
|
1391
1392
|
s.stop(color.green('External model routing enabled'));
|
|
1393
|
+
for (const { message } of formatLinksSummary(reapplyResult))
|
|
1394
|
+
p.log.warn(message);
|
|
1392
1395
|
// D-P4-1: applies-on-restart — env var takes effect only for new sessions
|
|
1393
1396
|
p.log.info(color.dim('Context-window enforcement disabled for relay-routed models — applies to new Claude Code sessions'));
|
|
1394
1397
|
if (adopted) {
|
|
@@ -1472,7 +1475,9 @@ async function runDisable() {
|
|
|
1472
1475
|
// Step 3: Sync manifest
|
|
1473
1476
|
await syncManifestFeature(devflowDir, 'proxy', false);
|
|
1474
1477
|
// Step 4: Revert external agents to shipped defaults
|
|
1475
|
-
await revertExternalAgents({ installDir, devflowDir });
|
|
1478
|
+
const revertResult = await revertExternalAgents({ installDir, devflowDir });
|
|
1479
|
+
for (const { message } of formatLinksSummary(revertResult))
|
|
1480
|
+
p.log.warn(message);
|
|
1476
1481
|
p.log.success('External model routing disabled');
|
|
1477
1482
|
// Step 5: Terminate the relay process.
|
|
1478
1483
|
// Identity is confirmed via TCP + health before signalling — a recycled PID
|
|
@@ -10,6 +10,8 @@ import { rulesDir } from '../../core/assets.js';
|
|
|
10
10
|
import { installAllRules, validateRuleShadow } from '../../targets/claude-code/installer.js';
|
|
11
11
|
import { convergeFromManifest } from '../../targets/claude-code/compliance-install.js';
|
|
12
12
|
import { convergeBehaviorFromManifest } from '../../targets/claude-code/behavior-install.js';
|
|
13
|
+
import { newLinkReport, refuseUnrealFolder, refusedFolderMessage, removeReportingLink } from '../../targets/claude-code/linked-folder.js';
|
|
14
|
+
import { formatLinksSummary } from './install-report.js';
|
|
13
15
|
/**
|
|
14
16
|
* Check whether a rule has a user shadow file at ~/.devflow/rules/{name}.md.
|
|
15
17
|
*/
|
|
@@ -213,17 +215,24 @@ export async function runRulesEnable(claudeDir, devflowDir) {
|
|
|
213
215
|
}
|
|
214
216
|
const installedPlugins = DEVFLOW_PLUGINS.filter(pl => manifest.plugins.includes(pl.name));
|
|
215
217
|
const rulesMap = buildRulesMap(installedPlugins);
|
|
216
|
-
// Wipe stale rules from previously uninstalled plugins before re-installing.
|
|
217
|
-
|
|
218
|
-
|
|
218
|
+
// Wipe stale rules from previously uninstalled plugins before re-installing. A rules folder that is
|
|
219
|
+
// a link is unlinked and replaced by a real folder, never emptied through, and the link is reported.
|
|
220
|
+
const links = newLinkReport();
|
|
221
|
+
links.replacedLinks.push(...await removeReportingLink(rulesTarget));
|
|
222
|
+
// A link that could not be removed is still there, and `mkdir` and every rule write would follow it.
|
|
223
|
+
// Prove the folder first; a refused folder gets no rule, no manifest write and one refusal line.
|
|
224
|
+
const rulesFolderRefused = await refuseUnrealFolder(links, rulesTarget);
|
|
219
225
|
let outcomes = [];
|
|
220
|
-
let installFailed =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
let installFailed = rulesFolderRefused;
|
|
227
|
+
if (!rulesFolderRefused) {
|
|
228
|
+
await fs.mkdir(rulesTarget, { recursive: true });
|
|
229
|
+
try {
|
|
230
|
+
outcomes = await installAllRules(rulesMap, devflowDir, rulesTarget, links);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
p.log.error(`Rules installation failed — rules directory has been cleared: ${err instanceof Error ? err.message : String(err)}`);
|
|
234
|
+
installFailed = true;
|
|
235
|
+
}
|
|
227
236
|
}
|
|
228
237
|
if (!installFailed) {
|
|
229
238
|
const shadowedCount = outcomes.filter(o => o.outcome === 'shadow').length;
|
|
@@ -239,6 +248,14 @@ export async function runRulesEnable(claudeDir, devflowDir) {
|
|
|
239
248
|
await writeManifest(devflowDir, manifest);
|
|
240
249
|
p.log.success(`Installed ${rulesMap.size} rule(s) to ${color.dim(rulesTarget)}${shadowSuffix}`);
|
|
241
250
|
}
|
|
251
|
+
for (const { message } of formatLinksSummary(links))
|
|
252
|
+
p.log.warn(message);
|
|
253
|
+
// Each converge below proves the rules folder again and names a refused one itself; the line above
|
|
254
|
+
// has already named it, so the repeat is dropped.
|
|
255
|
+
const warnConverge = (msg) => {
|
|
256
|
+
if (!(rulesFolderRefused && msg === refusedFolderMessage(rulesTarget)))
|
|
257
|
+
p.log.warn(msg);
|
|
258
|
+
};
|
|
242
259
|
// Converge compliance artifacts unconditionally — convergeFromManifest handles the
|
|
243
260
|
// disabled case itself (removes stale artifacts). Running this regardless of installFailed
|
|
244
261
|
// prevents a rules-install failure from leaving the compliance rule in a stale state.
|
|
@@ -249,7 +266,7 @@ export async function runRulesEnable(claudeDir, devflowDir) {
|
|
|
249
266
|
claudeDir,
|
|
250
267
|
devflowDir,
|
|
251
268
|
manifest,
|
|
252
|
-
warn:
|
|
269
|
+
warn: warnConverge,
|
|
253
270
|
rulesEnabledOverride: !installFailed,
|
|
254
271
|
});
|
|
255
272
|
}
|
|
@@ -265,7 +282,7 @@ export async function runRulesEnable(claudeDir, devflowDir) {
|
|
|
265
282
|
claudeDir,
|
|
266
283
|
devflowDir,
|
|
267
284
|
manifest,
|
|
268
|
-
warn:
|
|
285
|
+
warn: warnConverge,
|
|
269
286
|
rulesEnabledOverride: !installFailed,
|
|
270
287
|
});
|
|
271
288
|
}
|
|
@@ -4,7 +4,7 @@ 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 { getAllSkillNames, prefixSkillName, unprefixSkillName, skillOwners, FEATURE_OWNED_SKILLS } from '../../core/plugins.js';
|
|
7
|
+
import { getAllSkillNames, prefixSkillName, unprefixSkillName, skillOwners, FEATURE_OWNED_SKILLS, WORKFLOW_FOLDER_NAMES } from '../../core/plugins.js';
|
|
8
8
|
import { skillsDir } from '../../core/assets.js';
|
|
9
9
|
import { copyDirectory, validateSkillShadow } from '../../targets/claude-code/installer.js';
|
|
10
10
|
/**
|
|
@@ -42,6 +42,22 @@ export function shadowSeedDir(bareName, installedDir) {
|
|
|
42
42
|
return sourceDir;
|
|
43
43
|
return installedDir ?? sourceDir;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* D-WORKFLOW-SHADOW-REFUSAL: why `devflow skills shadow <name>` refuses a workflow that ships as a
|
|
47
|
+
* skill folder, or null for any other name.
|
|
48
|
+
*
|
|
49
|
+
* A folder installs from the build tree of the machine's exact variant key, with no source
|
|
50
|
+
* directory to copy and no shadow to overlay, so a shadow of its name could never apply. Refusing
|
|
51
|
+
* at the command says so before the user edits a copy that does nothing; the install ignores a
|
|
52
|
+
* shadow that is already there, with a warning, and keeps it.
|
|
53
|
+
*
|
|
54
|
+
* @param folderWorkflows - The target-layer set; the default is the real set.
|
|
55
|
+
*/
|
|
56
|
+
export function workflowFolderShadowRefusal(bareName, folderWorkflows = WORKFLOW_FOLDER_NAMES) {
|
|
57
|
+
if (!folderWorkflows.has(bareName))
|
|
58
|
+
return null;
|
|
59
|
+
return `${bareName} ships as a skill folder built for the machine's variant, so a shadow cannot replace it. Nothing was shadowed.`;
|
|
60
|
+
}
|
|
45
61
|
/**
|
|
46
62
|
* Check if a skill has a shadow (personal override).
|
|
47
63
|
*/
|
|
@@ -85,16 +101,19 @@ function buildSkillShadowTag(shadowState) {
|
|
|
85
101
|
}
|
|
86
102
|
}
|
|
87
103
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Run one `devflow skills` action: shadow, unshadow or list.
|
|
106
|
+
*
|
|
107
|
+
* @param folderWorkflows - The workflows that ship as skill folders; the default is the real
|
|
108
|
+
* set. They count as known skills, `shadow` refuses them, and `list` tags a leftover shadow
|
|
109
|
+
* of one as ignored. Tests pass a fixture set, because the real set is empty.
|
|
110
|
+
*/
|
|
111
|
+
export async function runSkillsAction(action, name, folderWorkflows = WORKFLOW_FOLDER_NAMES) {
|
|
93
112
|
const devflowDir = getDevFlowDirectory();
|
|
94
113
|
const claudeDir = getClaudeDirectory();
|
|
95
114
|
// Union FEATURE_OWNED_SKILLS (compliance) so shadow/unshadow/list cover feature-managed
|
|
96
115
|
// skills that left the plugin registry but still live in src/assets/skills/ (step 1.5).
|
|
97
|
-
const allSkills = [...getAllSkillNames(), ...FEATURE_OWNED_SKILLS];
|
|
116
|
+
const allSkills = [...getAllSkillNames(folderWorkflows), ...FEATURE_OWNED_SKILLS];
|
|
98
117
|
if (action === 'shadow') {
|
|
99
118
|
if (!name) {
|
|
100
119
|
p.log.error('Skill name required. Usage: devflow skills shadow <name>');
|
|
@@ -103,6 +122,11 @@ export const skillsCommand = new Command('skills')
|
|
|
103
122
|
}
|
|
104
123
|
// Accept both bare and prefixed input
|
|
105
124
|
const bareName = unprefixSkillName(name);
|
|
125
|
+
const refusal = workflowFolderShadowRefusal(bareName, folderWorkflows);
|
|
126
|
+
if (refusal !== null) {
|
|
127
|
+
p.log.error(refusal);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
106
130
|
if (!allSkills.includes(bareName)) {
|
|
107
131
|
p.log.error(`Unknown skill: ${bareName}`);
|
|
108
132
|
p.log.info(`Available skills: ${allSkills.join(', ')}`);
|
|
@@ -183,7 +207,11 @@ export const skillsCommand = new Command('skills')
|
|
|
183
207
|
const provenance = installed
|
|
184
208
|
? color.dim(`installed because: ${describeOwners(skill, ownersBySkill.get(skill))}`)
|
|
185
209
|
: color.dim(`not installed — provided by: ${describeOwners(skill, ownersBySkill.get(skill))}`);
|
|
186
|
-
|
|
210
|
+
// A shadow of a skill folder is kept but never applied (D-WORKFLOW-SHADOW-REFUSAL).
|
|
211
|
+
const tag = workflowFolderShadowRefusal(skill, folderWorkflows) !== null && shadowState !== 'none'
|
|
212
|
+
? color.yellow('shadow ignored — skill folder')
|
|
213
|
+
: buildSkillShadowTag(shadowState);
|
|
214
|
+
return ` ${color.cyan(skill.padEnd(28))} ${tag.padEnd(20)} ${provenance}`;
|
|
187
215
|
});
|
|
188
216
|
// Orphan shadows: in ~/.devflow/skills/ but not a known skill
|
|
189
217
|
for (const dirName of shadowDirNames) {
|
|
@@ -200,5 +228,10 @@ export const skillsCommand = new Command('skills')
|
|
|
200
228
|
p.log.info('Usage: devflow skills <shadow|unshadow|list> [name]');
|
|
201
229
|
process.exit(1);
|
|
202
230
|
}
|
|
203
|
-
}
|
|
231
|
+
}
|
|
232
|
+
export const skillsCommand = new Command('skills')
|
|
233
|
+
.description('Manage skill overrides (shadow/unshadow/list)')
|
|
234
|
+
.argument('<action>', 'Action: shadow, unshadow, or list')
|
|
235
|
+
.argument('[name]', 'Skill name (required for shadow/unshadow)')
|
|
236
|
+
.action((action, name) => runSkillsAction(action, name));
|
|
204
237
|
//# sourceMappingURL=skills.js.map
|
|
@@ -56,12 +56,18 @@ export async function readTrackerProvenance(devflowDir, provider) {
|
|
|
56
56
|
* every install carries them all (D-INSTALL-ALL-PROVIDERS) — rather than listing
|
|
57
57
|
* the directory: the question a user asks `--status` is "can the agent load what
|
|
58
58
|
* it is told to load?", and a stray file in the tree is not an answer to it.
|
|
59
|
+
*
|
|
60
|
+
* `compliance` is the machine's compliance switch, read from the install manifest by the
|
|
61
|
+
* caller and never from a repository file: a compliance-off machine installs the compliance-off
|
|
62
|
+
* manifest, which holds fewer references, so counting against the compliance-on one would read
|
|
63
|
+
* every such machine as damaged.
|
|
64
|
+
*
|
|
59
65
|
* Never throws.
|
|
60
66
|
*/
|
|
61
|
-
export async function readTrackerMechanics(claudeDir) {
|
|
67
|
+
export async function readTrackerMechanics(claudeDir, compliance) {
|
|
62
68
|
const root = path.join(claudeDir, 'skills', prefixSkillName(SKILL_REFS_SKILL_NAME), 'references');
|
|
63
69
|
let present = 0;
|
|
64
|
-
for (const rel of installedReferenceManifest()) {
|
|
70
|
+
for (const rel of installedReferenceManifest({ compliance })) {
|
|
65
71
|
try {
|
|
66
72
|
await fs.access(path.join(root, ...rel.split('/')));
|
|
67
73
|
present++;
|
|
@@ -292,7 +298,7 @@ export const trackerCommand = new Command('tracker')
|
|
|
292
298
|
const selection = repoTrackerSelection(settingsModule, { dir: process.cwd() });
|
|
293
299
|
const effective = selection?.provider ?? current.provider;
|
|
294
300
|
const conventions = await readTrackerStatusConventions(devflowDir, effective);
|
|
295
|
-
const mechanics = await readTrackerMechanics(getClaudeDirectory());
|
|
301
|
+
const mechanics = await readTrackerMechanics(getClaudeDirectory(), manifest.features.compliance.enabled);
|
|
296
302
|
// [D-F] Inspecting the status re-arms the attempt counters. --status is
|
|
297
303
|
// the command a capped user reaches for to find out why nothing is being
|
|
298
304
|
// learned, so it is the command that has to hand back another five
|