devflow-kit 3.9.0 → 3.11.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 +142 -0
- package/README.md +1 -1
- package/dist/agents/code.md +6 -6
- package/dist/agents/fix.md +6 -6
- package/dist/agents/harden.md +7 -6
- package/dist/agents/refactor.md +6 -6
- package/dist/cli/commands/ambient.js +10 -2
- package/dist/cli/commands/backup-session.js +131 -0
- package/dist/cli/commands/compliance-prompts.js +9 -4
- package/dist/cli/commands/compliance.js +184 -24
- package/dist/cli/commands/curation.js +23 -10
- package/dist/cli/commands/gate-level-prompts.js +39 -25
- package/dist/cli/commands/gates.js +5 -7
- package/dist/cli/commands/init.js +169 -184
- package/dist/cli/commands/install-report.js +129 -0
- package/dist/cli/commands/legacy-sweep.js +159 -0
- package/dist/cli/commands/proxy.js +6 -1
- package/dist/cli/commands/rules.js +75 -21
- package/dist/cli/commands/skills.js +42 -9
- package/dist/cli/commands/tracker.js +9 -3
- package/dist/cli/commands/uninstall.js +339 -75
- package/dist/commands/bug-analysis.md +69 -120
- package/dist/commands/dynamic-build.md +23 -23
- package/dist/commands/implement.md +46 -32
- package/dist/commands/release.md +1 -1
- package/dist/commands/resolve.md +35 -31
- package/dist/commands/self-review.md +8 -7
- 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/assets.js +126 -17
- package/dist/core/compliance-off-files.js +27 -0
- package/dist/core/compliance.js +14 -0
- package/dist/core/curation-variants.js +460 -112
- package/dist/core/flags.js +2 -2
- package/dist/core/fs-atomic.js +5 -1
- 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 +29 -2
- package/dist/core/path-set.js +15 -0
- package/dist/core/plugins.js +68 -10
- package/dist/core/reference-sweep.js +42 -25
- package/dist/core/retired-shipped-names.js +278 -0
- 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 +6 -6
- package/dist/curation-off/agents/fix.md +6 -6
- package/dist/curation-off/agents/harden.md +7 -6
- package/dist/curation-off/agents/refactor.md +6 -6
- package/dist/curation-off/commands/bug-analysis.md +69 -119
- package/dist/curation-off/commands/dynamic-build.md +23 -23
- package/dist/curation-off/commands/implement.md +46 -32
- package/dist/curation-off/commands/release.md +1 -1
- package/dist/curation-off/commands/resolve.md +35 -31
- package/dist/curation-off/commands/self-review.md +8 -7
- package/dist/skills/git/references/pr/resolve-review-threads.md +1 -1
- 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 +62 -17
- package/dist/targets/claude-code/compliance-off-files.js +249 -0
- package/dist/targets/claude-code/curation-install.js +632 -117
- package/dist/targets/claude-code/folder-swap.js +142 -0
- package/dist/targets/claude-code/install-backup.js +780 -0
- package/dist/targets/claude-code/installer.js +795 -199
- package/dist/targets/claude-code/language-stamp.js +6 -0
- package/dist/targets/claude-code/legacy-skill-hashes.js +170 -0
- package/dist/targets/claude-code/linked-folder.js +176 -0
- package/dist/targets/claude-code/reference-converge.js +131 -0
- package/dist/targets/claude-code/rules-folder.js +34 -0
- package/dist/targets/claude-code/shipped-set.js +160 -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 +251 -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/evaluate.md +1 -1
- package/src/assets/agents/git.mds +32 -0
- package/src/assets/agents/harden.mds +7 -6
- package/src/assets/agents/test.md +21 -12
- package/src/assets/agents/triage.mds +2 -0
- package/src/assets/agents/validate.md +10 -10
- package/src/assets/commands/_partials/_compliance.mds +5 -3
- package/src/assets/commands/_partials/_engine.mds +9 -8
- package/src/assets/commands/_partials/_publication.mds +10 -1
- package/src/assets/commands/bug-analysis.mds +80 -126
- package/src/assets/commands/code-review.mds +43 -0
- 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 +217 -32
- package/src/assets/commands/self-review.mds +8 -7
- package/src/assets/mds/git/_ops.mds +23 -0
- package/src/assets/mds/git/_ops_steps.mds +17 -0
- package/src/assets/mds/git/_pr.mds +1 -1
- package/src/assets/mds/git/_references.mds +6 -0
- package/src/assets/mds/tracker/_common.mds +4 -0
- package/src/assets/mds/tracker/_github.mds +42 -0
- package/src/assets/mds/tracker/_jira.mds +43 -0
- package/src/assets/mds/tracker/_linear.mds +43 -0
- package/src/assets/rules/quality.md +2 -0
- package/src/assets/scripts/ci-wait.cjs +204 -126
- 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/preamble +18 -8
- package/src/assets/scripts/hooks/session-start-orchestrator +4 -2
- package/src/assets/scripts/pr-evidence.cjs +41 -2
- package/src/assets/scripts/verify-evidence.cjs +408 -56
- 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/synthesize/bug-analysis.md +6 -2
- package/src/assets/skills/agent-references/references/validate/passed-alone.md +7 -7
- 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
|
@@ -53,6 +53,16 @@ export function mdEntryName(entry) {
|
|
|
53
53
|
* For agents and commands: use {@link mdEntryName}.
|
|
54
54
|
* For skills: strip the devflow: prefix.
|
|
55
55
|
*
|
|
56
|
+
* @param mayRemove - Optional check made on the full path of each orphan before
|
|
57
|
+
* it is removed. An orphan it refuses stays where it is and is neither
|
|
58
|
+
* removed nor failed; the check reports the refusal itself. The caller proves
|
|
59
|
+
* that `dir` is a real folder before the call, because the sweep reads and
|
|
60
|
+
* removes inside whatever `dir` names.
|
|
61
|
+
*
|
|
62
|
+
* @param removal - How an admitted orphan is removed ({@link SweepRemoval}). Defaults to
|
|
63
|
+
* `recursive`. With `unlink`, an entry that has become a folder since the check stays and is
|
|
64
|
+
* recorded in `failed`; an entry that has vanished since the check counts as removed.
|
|
65
|
+
*
|
|
56
66
|
* @returns A {@link SweepResult} reporting how many entries were scanned, which
|
|
57
67
|
* registry names were removed, and which removals failed.
|
|
58
68
|
* `scanned` is the count matched by the predicate (not the removed count) —
|
|
@@ -64,7 +74,7 @@ export function mdEntryName(entry) {
|
|
|
64
74
|
* individual removal is recorded in `failed` without aborting the sweep.
|
|
65
75
|
* Never writes, only removes — no delete-then-write window.
|
|
66
76
|
*/
|
|
67
|
-
export async function sweepOrphanedAssets(dir, knownNames, extractRegistryName) {
|
|
77
|
+
export async function sweepOrphanedAssets(dir, knownNames, extractRegistryName, mayRemove, removal = 'recursive') {
|
|
68
78
|
const removed = [];
|
|
69
79
|
const failed = [];
|
|
70
80
|
let scanned = 0;
|
|
@@ -76,8 +86,11 @@ export async function sweepOrphanedAssets(dir, knownNames, extractRegistryName)
|
|
|
76
86
|
continue;
|
|
77
87
|
scanned++;
|
|
78
88
|
if (!knownNames.has(registryName)) {
|
|
89
|
+
const entryPath = path.join(dir, entry);
|
|
79
90
|
try {
|
|
80
|
-
|
|
91
|
+
if (mayRemove !== undefined && !(await mayRemove(entryPath)))
|
|
92
|
+
continue;
|
|
93
|
+
await removeEntry(entryPath, removal);
|
|
81
94
|
removed.push(registryName);
|
|
82
95
|
}
|
|
83
96
|
catch (err) {
|
|
@@ -89,4 +102,18 @@ export async function sweepOrphanedAssets(dir, knownNames, extractRegistryName)
|
|
|
89
102
|
catch { /* directory absent or unreadable — not an error */ }
|
|
90
103
|
return { scanned, removed, failed };
|
|
91
104
|
}
|
|
105
|
+
/** Remove one orphan as `removal` says. An absent entry is already removed, as `fs.rm` with `force` treats it. */
|
|
106
|
+
async function removeEntry(entryPath, removal) {
|
|
107
|
+
if (removal === 'recursive') {
|
|
108
|
+
await fs.rm(entryPath, { recursive: true, force: true });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
await fs.unlink(entryPath);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (err.code !== 'ENOENT')
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
92
119
|
//# sourceMappingURL=orphan-sweep.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Build a {@link PathSet} from file paths: the folder prefixes are computed once. */
|
|
2
|
+
export function createPathSet(files) {
|
|
3
|
+
const all = new Set(files);
|
|
4
|
+
const folders = new Set();
|
|
5
|
+
for (const file of all) {
|
|
6
|
+
for (let cut = file.indexOf('/'); cut !== -1; cut = file.indexOf('/', cut + 1)) {
|
|
7
|
+
folders.add(file.slice(0, cut));
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return Object.freeze({
|
|
11
|
+
files: all,
|
|
12
|
+
holds: (folder) => all.has(folder) || folders.has(folder),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=path-set.js.map
|
package/dist/core/plugins.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared plugin registry — single source of truth for all CLI commands.
|
|
3
3
|
*/
|
|
4
|
+
import { WORKFLOW_FOLDERS } from './mds-variants.js';
|
|
4
5
|
/**
|
|
5
6
|
* Namespace prefix for Devflow skills installed to ~/.claude/skills/.
|
|
6
7
|
* Skills are installed as `devflow:{skill-name}` to avoid collisions with
|
|
@@ -431,6 +432,18 @@ export const LEGACY_PLUGIN_NAMES = {
|
|
|
431
432
|
'devflow-frontend-design': 'devflow-ui-design',
|
|
432
433
|
'devflow-specify': 'devflow-plan',
|
|
433
434
|
};
|
|
435
|
+
/**
|
|
436
|
+
* The registered plugins that `names` select: each legacy name mapped to its current plugin, and every name
|
|
437
|
+
* the registry does not have dropped. Order and repeats are kept.
|
|
438
|
+
*
|
|
439
|
+
* `names` is often file content (a manifest record), so only a name that LEGACY_PLUGIN_NAMES owns maps; a
|
|
440
|
+
* key the table inherits from Object, such as `constructor`, is an unknown name like any other.
|
|
441
|
+
*/
|
|
442
|
+
export function registeredPluginNames(names) {
|
|
443
|
+
return names
|
|
444
|
+
.map(name => (Object.hasOwn(LEGACY_PLUGIN_NAMES, name) ? LEGACY_PLUGIN_NAMES[name] : name))
|
|
445
|
+
.filter(name => DEVFLOW_PLUGINS.some(plugin => plugin.name === name));
|
|
446
|
+
}
|
|
434
447
|
/**
|
|
435
448
|
* Plugin names that have been deleted from the registry.
|
|
436
449
|
* Used during init to prune stale entries from users' manifests on partial
|
|
@@ -489,7 +502,7 @@ export const FEATURE_OWNED_RULES = ['compliance', 'behavior'];
|
|
|
489
502
|
* stays OWNED and REQUIRED by its plugins in the registry — the closure guard
|
|
490
503
|
* reasons over the curation-on variant, the superset — and the install drops it
|
|
491
504
|
* on top of the selection: `installViaFileCopy` skips it when `curation` is false
|
|
492
|
-
* and `
|
|
505
|
+
* and `convergeVariantAxes` installs or removes it when the switch flips.
|
|
493
506
|
*
|
|
494
507
|
* This is a machine-switch condition, not a deselection: the plan
|
|
495
508
|
* ({@link resolveSkillInstallPlan}) is untouched, so a curation-off machine never
|
|
@@ -498,7 +511,7 @@ export const FEATURE_OWNED_RULES = ['compliance', 'behavior'];
|
|
|
498
511
|
*
|
|
499
512
|
* Used by:
|
|
500
513
|
* - installer.ts installViaFileCopy: omits these from the install loop when curation is off
|
|
501
|
-
* - curation-install.ts
|
|
514
|
+
* - curation-install.ts convergeVariantAxes: installs or removes the directory
|
|
502
515
|
* - tests: independent literal ['apply-decisions'] (avoids the EXCLUDED-as-oracle trap)
|
|
503
516
|
*/
|
|
504
517
|
export const CURATION_GATED_SKILLS = ['apply-decisions'];
|
|
@@ -627,35 +640,71 @@ export function parsePluginSelection(input, validPlugins) {
|
|
|
627
640
|
/**
|
|
628
641
|
* Deprecated command names from old installations.
|
|
629
642
|
* Used during init to clean up stale command files on upgrade.
|
|
643
|
+
*
|
|
644
|
+
* Each name is a command file that a release installed. The cleanup removes such a file with no
|
|
645
|
+
* backup, so a name that no release shipped does not belong here.
|
|
630
646
|
*/
|
|
631
647
|
export const LEGACY_COMMAND_NAMES = [
|
|
632
|
-
'review',
|
|
633
648
|
'specify',
|
|
634
|
-
'specify-teams',
|
|
635
649
|
];
|
|
650
|
+
/** A command entry of a plugin without its leading `/`. */
|
|
651
|
+
function commandName(cmd) {
|
|
652
|
+
return cmd.startsWith('/') ? cmd.slice(1) : cmd;
|
|
653
|
+
}
|
|
654
|
+
/** The set of the folders in a registry; tests pass a fixture registry to get a fixture set. */
|
|
655
|
+
export function workflowFolderNames(folders) {
|
|
656
|
+
return new Set(folders.map(folder => folder.name));
|
|
657
|
+
}
|
|
658
|
+
/** The real set: empty until a workflow is registered as a folder. */
|
|
659
|
+
export const WORKFLOW_FOLDER_NAMES = workflowFolderNames(WORKFLOW_FOLDERS);
|
|
660
|
+
/**
|
|
661
|
+
* The folder workflows that some plugin of `plugins` owns as a command. A plugin that
|
|
662
|
+
* declares `/name` for a name in the set owns the folder `devflow:name`.
|
|
663
|
+
*/
|
|
664
|
+
export function selectedWorkflowFolders(plugins, folderWorkflows = WORKFLOW_FOLDER_NAMES) {
|
|
665
|
+
const selected = new Set();
|
|
666
|
+
for (const plugin of plugins) {
|
|
667
|
+
for (const cmd of plugin.commands) {
|
|
668
|
+
const name = commandName(cmd);
|
|
669
|
+
if (folderWorkflows.has(name))
|
|
670
|
+
selected.add(name);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return selected;
|
|
674
|
+
}
|
|
636
675
|
/**
|
|
637
676
|
* Derive unique command names (without leading /) from all plugins.
|
|
638
677
|
* Returns one entry per distinct command, preserving DEVFLOW_PLUGINS declaration order.
|
|
678
|
+
*
|
|
679
|
+
* A workflow in `folderWorkflows` is not a command file, so it is left out: the
|
|
680
|
+
* command sweeps then treat a stale command file of a folder workflow as an orphan.
|
|
639
681
|
*/
|
|
640
|
-
export function getAllCommandNames() {
|
|
682
|
+
export function getAllCommandNames(folderWorkflows = WORKFLOW_FOLDER_NAMES) {
|
|
641
683
|
const commands = new Set();
|
|
642
684
|
for (const plugin of DEVFLOW_PLUGINS) {
|
|
643
685
|
for (const cmd of plugin.commands) {
|
|
644
|
-
|
|
686
|
+
const name = commandName(cmd);
|
|
687
|
+
if (!folderWorkflows.has(name))
|
|
688
|
+
commands.add(name);
|
|
645
689
|
}
|
|
646
690
|
}
|
|
647
691
|
return [...commands];
|
|
648
692
|
}
|
|
649
693
|
/**
|
|
650
694
|
* Derive unique skill names from all plugins.
|
|
695
|
+
*
|
|
696
|
+
* The folders of `folderWorkflows` install as `devflow:<name>`, so they join the names
|
|
697
|
+
* the skill sweeps treat as known.
|
|
651
698
|
*/
|
|
652
|
-
export function getAllSkillNames() {
|
|
699
|
+
export function getAllSkillNames(folderWorkflows = WORKFLOW_FOLDER_NAMES) {
|
|
653
700
|
const skills = new Set();
|
|
654
701
|
for (const plugin of DEVFLOW_PLUGINS) {
|
|
655
702
|
for (const skill of plugin.skills) {
|
|
656
703
|
skills.add(skill);
|
|
657
704
|
}
|
|
658
705
|
}
|
|
706
|
+
for (const name of folderWorkflows)
|
|
707
|
+
skills.add(name);
|
|
659
708
|
return [...skills];
|
|
660
709
|
}
|
|
661
710
|
/**
|
|
@@ -804,8 +853,10 @@ export function buildFullSkillsMap() {
|
|
|
804
853
|
* nothing, and is reported so the user can tell "inert" from "ignored".
|
|
805
854
|
*/
|
|
806
855
|
export function resolveSkillInstallPlan(input) {
|
|
856
|
+
const folderWorkflows = input.folderWorkflows ?? WORKFLOW_FOLDER_NAMES;
|
|
807
857
|
const install = skillsOf(input.effectivePlugins);
|
|
808
858
|
const remove = new Set();
|
|
859
|
+
const removeFolders = new Set();
|
|
809
860
|
if (!input.isPartialInstall) {
|
|
810
861
|
for (const skill of skillsOf(DEVFLOW_PLUGINS)) {
|
|
811
862
|
if (install.has(skill))
|
|
@@ -814,11 +865,18 @@ export function resolveSkillInstallPlan(input) {
|
|
|
814
865
|
continue;
|
|
815
866
|
remove.add(skill);
|
|
816
867
|
}
|
|
868
|
+
const ownedFolders = selectedWorkflowFolders(input.effectivePlugins, folderWorkflows);
|
|
869
|
+
for (const name of folderWorkflows) {
|
|
870
|
+
if (!ownedFolders.has(name))
|
|
871
|
+
removeFolders.add(name);
|
|
872
|
+
}
|
|
817
873
|
}
|
|
818
|
-
const
|
|
819
|
-
|
|
874
|
+
const shadowed = [...new Set(input.shadowedSkills)];
|
|
875
|
+
const dormantShadows = shadowed
|
|
876
|
+
.filter(skill => !install.has(skill) && !folderWorkflows.has(skill))
|
|
820
877
|
.sort();
|
|
821
|
-
|
|
878
|
+
const ignoredFolderShadows = shadowed.filter(skill => folderWorkflows.has(skill)).sort();
|
|
879
|
+
return { install, remove, dormantShadows, removeFolders, ignoredFolderShadows };
|
|
822
880
|
}
|
|
823
881
|
/**
|
|
824
882
|
* Derive unique rule names from all plugins.
|
|
@@ -14,7 +14,9 @@ import * as path from 'path';
|
|
|
14
14
|
* two providers may legitimately both carry a `comment.md`, so the registry name has to
|
|
15
15
|
* be the relative PATH, and the walk has to descend.
|
|
16
16
|
*
|
|
17
|
-
* Never writes
|
|
17
|
+
* Never writes. The sweep names what stays and what goes; a {@link ReferenceSweepDelegate}
|
|
18
|
+
* acts on every entry the manifest does not name, and the sweep itself never removes a
|
|
19
|
+
* folder recursively. No delete-then-write window.
|
|
18
20
|
*/
|
|
19
21
|
/**
|
|
20
22
|
* Descent bound for every walk over the generated reference tree — this sweep and the
|
|
@@ -42,24 +44,27 @@ export const MAX_REFERENCE_SWEEP_DEPTH = 8;
|
|
|
42
44
|
* @param root - Directory to converge (the installed `references/tracker/` tree).
|
|
43
45
|
* @param knownRelPaths - POSIX paths relative to `root` that must survive. Derived from
|
|
44
46
|
* the build's own module registries by the caller — never hand-listed.
|
|
47
|
+
* @param delegate - Acts on every entry the manifest does not name. Required: a sweep with
|
|
48
|
+
* no delegate would have to delete what it cannot name.
|
|
45
49
|
*
|
|
46
50
|
* @returns A {@link SweepResult} whose `removed` entries are POSIX relative paths. A
|
|
47
|
-
* directory into which no manifest path descends is
|
|
48
|
-
* own relative path
|
|
49
|
-
* short, and an empty provider directory is
|
|
50
|
-
* references failed to install. A subtree left
|
|
51
|
+
* directory into which no manifest path descends is handed to the delegate WHOLE and
|
|
52
|
+
* reported by its own relative path once the delegate removed it — leaving it empty
|
|
53
|
+
* would be a convergence that stops one step short, and an empty provider directory is
|
|
54
|
+
* indistinguishable from a provider whose references failed to install. A subtree left
|
|
55
|
+
* unswept because it breached
|
|
51
56
|
* {@link MAX_REFERENCE_SWEEP_DEPTH} is reported in `failed` under its own relative
|
|
52
57
|
* path, for the same reason: everything this sweep did not converge is named.
|
|
53
58
|
*
|
|
54
59
|
* A missing or unreadable `root` is a no-op, not an error: the overlay creates the tree
|
|
55
60
|
* it converges, so an absent one simply means there is nothing to prune yet.
|
|
56
61
|
*/
|
|
57
|
-
export async function sweepOrphanedReferences(root, knownRelPaths) {
|
|
62
|
+
export async function sweepOrphanedReferences(root, knownRelPaths, delegate) {
|
|
58
63
|
const acc = { scanned: 0, removed: [], failed: [] };
|
|
59
|
-
await sweepDirectory(root, '', 0, knownRelPaths, directoryPrefixes(knownRelPaths), acc);
|
|
64
|
+
await sweepDirectory(root, '', 0, knownRelPaths, directoryPrefixes(knownRelPaths), delegate, acc);
|
|
60
65
|
return { scanned: acc.scanned, removed: acc.removed, failed: acc.failed };
|
|
61
66
|
}
|
|
62
|
-
async function sweepDirectory(dir, prefix, depth, known, knownDirPrefixes, acc) {
|
|
67
|
+
async function sweepDirectory(dir, prefix, depth, known, knownDirPrefixes, delegate, acc) {
|
|
63
68
|
if (depth > MAX_REFERENCE_SWEEP_DEPTH) {
|
|
64
69
|
// A breached bound means this subtree is never visited, so any orphan inside it
|
|
65
70
|
// survives. Report it through the same channel as a failed removal: a silent return
|
|
@@ -82,35 +87,47 @@ async function sweepDirectory(dir, prefix, depth, known, knownDirPrefixes, acc)
|
|
|
82
87
|
const relPath = prefix === '' ? entry.name : `${prefix}/${entry.name}`;
|
|
83
88
|
const fullPath = path.join(dir, entry.name);
|
|
84
89
|
// A real subdirectory is either an ancestor of something the manifest names — in
|
|
85
|
-
// which case descend — or dead weight, in which case
|
|
86
|
-
// isDirectory() is false for a symlink-to-dir, so a planted link is treated
|
|
87
|
-
// leaf and
|
|
90
|
+
// which case descend — or dead weight, in which case the delegate takes the whole
|
|
91
|
+
// subtree. isDirectory() is false for a symlink-to-dir, so a planted link is treated
|
|
92
|
+
// as a leaf and handed over rather than followed.
|
|
88
93
|
if (entry.isDirectory()) {
|
|
89
94
|
if (knownDirPrefixes.has(`${relPath}/`)) {
|
|
90
|
-
await sweepDirectory(fullPath, relPath, depth + 1, known, knownDirPrefixes, acc);
|
|
95
|
+
await sweepDirectory(fullPath, relPath, depth + 1, known, knownDirPrefixes, delegate, acc);
|
|
91
96
|
continue;
|
|
92
97
|
}
|
|
93
98
|
acc.scanned++;
|
|
94
|
-
|
|
95
|
-
await fs.rm(fullPath, { recursive: true, force: true });
|
|
96
|
-
acc.removed.push(relPath);
|
|
97
|
-
}
|
|
98
|
-
catch (err) {
|
|
99
|
-
acc.failed.push({ name: relPath, error: err }); /* per-item isolation */
|
|
100
|
-
}
|
|
99
|
+
await dispose(fullPath, relPath, true, delegate, acc);
|
|
101
100
|
continue;
|
|
102
101
|
}
|
|
103
102
|
acc.scanned++;
|
|
104
103
|
if (known.has(relPath))
|
|
105
104
|
continue;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
await dispose(fullPath, relPath, false, delegate, acc);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Hand one entry to the delegate and record the outcome; a throw from the delegate is a per-item failure. */
|
|
109
|
+
async function dispose(fullPath, relPath, isDirectory, delegate, acc) {
|
|
110
|
+
try {
|
|
111
|
+
const disposition = await delegate.dispose(fullPath, isDirectory);
|
|
112
|
+
switch (disposition.outcome) {
|
|
113
|
+
case 'removed':
|
|
114
|
+
acc.removed.push(relPath);
|
|
115
|
+
break;
|
|
116
|
+
case 'failed':
|
|
117
|
+
acc.failed.push({ name: relPath, error: disposition.error });
|
|
118
|
+
break;
|
|
119
|
+
case 'moved':
|
|
120
|
+
case 'kept':
|
|
121
|
+
break;
|
|
122
|
+
default: {
|
|
123
|
+
const _exhaustive = disposition;
|
|
124
|
+
void _exhaustive;
|
|
125
|
+
}
|
|
112
126
|
}
|
|
113
127
|
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
acc.failed.push({ name: relPath, error: err }); /* per-item isolation */
|
|
130
|
+
}
|
|
114
131
|
}
|
|
115
132
|
/**
|
|
116
133
|
* Every directory prefix some manifest path sits under, trailing slash included:
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { createPathSet } from './path-set.js';
|
|
2
|
+
const names = (list) => Object.freeze([...list]);
|
|
3
|
+
export const RETIRED_SHIPPED = Object.freeze({
|
|
4
|
+
commands: names([
|
|
5
|
+
'ambient',
|
|
6
|
+
'audit-claude',
|
|
7
|
+
'brainstorm',
|
|
8
|
+
'breakdown',
|
|
9
|
+
'catch-up',
|
|
10
|
+
'commit',
|
|
11
|
+
'design',
|
|
12
|
+
'devlog',
|
|
13
|
+
'get-issue',
|
|
14
|
+
'plan-next-steps',
|
|
15
|
+
'pre-commit',
|
|
16
|
+
'pre-pr',
|
|
17
|
+
'pull-request',
|
|
18
|
+
'resolve-comments',
|
|
19
|
+
'specify',
|
|
20
|
+
]),
|
|
21
|
+
agents: names([
|
|
22
|
+
'audit-architecture',
|
|
23
|
+
'audit-complexity',
|
|
24
|
+
'audit-database',
|
|
25
|
+
'audit-dependencies',
|
|
26
|
+
'audit-documentation',
|
|
27
|
+
'audit-performance',
|
|
28
|
+
'audit-security',
|
|
29
|
+
'audit-tests',
|
|
30
|
+
'audit-typescript',
|
|
31
|
+
'brainstorm',
|
|
32
|
+
'catch-up',
|
|
33
|
+
'claude-md-auditor',
|
|
34
|
+
'code-edit',
|
|
35
|
+
'code-fix',
|
|
36
|
+
'code-review',
|
|
37
|
+
'coder',
|
|
38
|
+
'commit',
|
|
39
|
+
'diagnose',
|
|
40
|
+
'get-issue',
|
|
41
|
+
'learning',
|
|
42
|
+
'pr-comments',
|
|
43
|
+
'project-state',
|
|
44
|
+
'pull-request',
|
|
45
|
+
'release',
|
|
46
|
+
'resolver',
|
|
47
|
+
'reviewer',
|
|
48
|
+
'scrutinize',
|
|
49
|
+
'scrutinizer',
|
|
50
|
+
'shepherd',
|
|
51
|
+
'simplifier',
|
|
52
|
+
'simplify',
|
|
53
|
+
'skimmer',
|
|
54
|
+
'synthesizer',
|
|
55
|
+
'tech-debt',
|
|
56
|
+
'validator',
|
|
57
|
+
]),
|
|
58
|
+
rules: names([]),
|
|
59
|
+
skills: names([
|
|
60
|
+
'accessibility',
|
|
61
|
+
'agent-teams',
|
|
62
|
+
'ambient-router',
|
|
63
|
+
'apply-feature-kb',
|
|
64
|
+
'apply-knowledge',
|
|
65
|
+
'architecture-patterns',
|
|
66
|
+
'code-smell',
|
|
67
|
+
'codebase-navigation',
|
|
68
|
+
'commit',
|
|
69
|
+
'complexity-patterns',
|
|
70
|
+
'consistency-patterns',
|
|
71
|
+
'core-patterns',
|
|
72
|
+
'database-patterns',
|
|
73
|
+
'debug',
|
|
74
|
+
'debug-orchestration',
|
|
75
|
+
'debug:guided',
|
|
76
|
+
'debug:orch',
|
|
77
|
+
'debug:triage',
|
|
78
|
+
'decisions-format',
|
|
79
|
+
'dependencies-patterns',
|
|
80
|
+
'devflow',
|
|
81
|
+
'devflow-accessibility',
|
|
82
|
+
'devflow-agent-teams',
|
|
83
|
+
'devflow-architecture-patterns',
|
|
84
|
+
'devflow-code-smell',
|
|
85
|
+
'devflow-codebase-navigation',
|
|
86
|
+
'devflow-commit',
|
|
87
|
+
'devflow-complexity-patterns',
|
|
88
|
+
'devflow-consistency-patterns',
|
|
89
|
+
'devflow-core-patterns',
|
|
90
|
+
'devflow-database-patterns',
|
|
91
|
+
'devflow-debug',
|
|
92
|
+
'devflow-dependencies-patterns',
|
|
93
|
+
'devflow-docs-framework',
|
|
94
|
+
'devflow-documentation-patterns',
|
|
95
|
+
'devflow-error-handling',
|
|
96
|
+
'devflow-frontend-design',
|
|
97
|
+
'devflow-git-safety',
|
|
98
|
+
'devflow-git-workflow',
|
|
99
|
+
'devflow-github-patterns',
|
|
100
|
+
'devflow-implementation-patterns',
|
|
101
|
+
'devflow-input-validation',
|
|
102
|
+
'devflow-pattern-check',
|
|
103
|
+
'devflow-performance-patterns',
|
|
104
|
+
'devflow-pull-request',
|
|
105
|
+
'devflow-react',
|
|
106
|
+
'devflow-regression-patterns',
|
|
107
|
+
'devflow-review-methodology',
|
|
108
|
+
'devflow-security-patterns',
|
|
109
|
+
'devflow-self-review',
|
|
110
|
+
'devflow-test-design',
|
|
111
|
+
'devflow-tests-patterns',
|
|
112
|
+
'devflow-typescript',
|
|
113
|
+
'devflow:agent-teams',
|
|
114
|
+
'devflow:ambient-router',
|
|
115
|
+
'devflow:apply-feature-kb',
|
|
116
|
+
'devflow:apply-knowledge',
|
|
117
|
+
'devflow:architecture-patterns',
|
|
118
|
+
'devflow:complexity-patterns',
|
|
119
|
+
'devflow:consistency-patterns',
|
|
120
|
+
'devflow:core-patterns',
|
|
121
|
+
'devflow:database-patterns',
|
|
122
|
+
'devflow:debug',
|
|
123
|
+
'devflow:debug-orchestration',
|
|
124
|
+
'devflow:debug:guided',
|
|
125
|
+
'devflow:debug:orch',
|
|
126
|
+
'devflow:debug:triage',
|
|
127
|
+
'devflow:dependencies-patterns',
|
|
128
|
+
'devflow:documentation-patterns',
|
|
129
|
+
'devflow:dream-curation',
|
|
130
|
+
'devflow:dream-decisions',
|
|
131
|
+
'devflow:dream-knowledge',
|
|
132
|
+
'devflow:dream-memory',
|
|
133
|
+
'devflow:explore',
|
|
134
|
+
'devflow:explore:guided',
|
|
135
|
+
'devflow:explore:orch',
|
|
136
|
+
'devflow:explore:triage',
|
|
137
|
+
'devflow:feature-kb',
|
|
138
|
+
'devflow:frontend-design',
|
|
139
|
+
'devflow:git-safety',
|
|
140
|
+
'devflow:git-workflow',
|
|
141
|
+
'devflow:github-patterns',
|
|
142
|
+
'devflow:implement',
|
|
143
|
+
'devflow:implement:guided',
|
|
144
|
+
'devflow:implement:orch',
|
|
145
|
+
'devflow:implement:triage',
|
|
146
|
+
'devflow:implementation-orchestration',
|
|
147
|
+
'devflow:implementation-patterns',
|
|
148
|
+
'devflow:input-validation',
|
|
149
|
+
'devflow:knowledge-persistence',
|
|
150
|
+
'devflow:performance-patterns',
|
|
151
|
+
'devflow:pipeline',
|
|
152
|
+
'devflow:pipeline-orchestration',
|
|
153
|
+
'devflow:pipeline:orch',
|
|
154
|
+
'devflow:plan',
|
|
155
|
+
'devflow:plan-orchestration',
|
|
156
|
+
'devflow:plan:guided',
|
|
157
|
+
'devflow:plan:orch',
|
|
158
|
+
'devflow:plan:triage',
|
|
159
|
+
'devflow:regression-patterns',
|
|
160
|
+
'devflow:release:guided',
|
|
161
|
+
'devflow:release:orch',
|
|
162
|
+
'devflow:release:triage',
|
|
163
|
+
'devflow:research',
|
|
164
|
+
'devflow:research:guided',
|
|
165
|
+
'devflow:research:orch',
|
|
166
|
+
'devflow:research:triage',
|
|
167
|
+
'devflow:resolve',
|
|
168
|
+
'devflow:resolve-orchestration',
|
|
169
|
+
'devflow:resolve:orch',
|
|
170
|
+
'devflow:review',
|
|
171
|
+
'devflow:review-orchestration',
|
|
172
|
+
'devflow:review:guided',
|
|
173
|
+
'devflow:review:orch',
|
|
174
|
+
'devflow:review:triage',
|
|
175
|
+
'devflow:router',
|
|
176
|
+
'devflow:search-first',
|
|
177
|
+
'devflow:security-patterns',
|
|
178
|
+
'devflow:self-review',
|
|
179
|
+
'devflow:sidecar',
|
|
180
|
+
'devflow:test-patterns',
|
|
181
|
+
'docs-framework',
|
|
182
|
+
'documentation-patterns',
|
|
183
|
+
'dream-curation',
|
|
184
|
+
'dream-decisions',
|
|
185
|
+
'dream-knowledge',
|
|
186
|
+
'dream-memory',
|
|
187
|
+
'error-handling',
|
|
188
|
+
'explore',
|
|
189
|
+
'explore:guided',
|
|
190
|
+
'explore:orch',
|
|
191
|
+
'explore:triage',
|
|
192
|
+
'feature-kb',
|
|
193
|
+
'frontend-design',
|
|
194
|
+
'git-safety',
|
|
195
|
+
'git-workflow',
|
|
196
|
+
'github-patterns',
|
|
197
|
+
'go',
|
|
198
|
+
'implement',
|
|
199
|
+
'implement:guided',
|
|
200
|
+
'implement:orch',
|
|
201
|
+
'implement:triage',
|
|
202
|
+
'implementation-orchestration',
|
|
203
|
+
'implementation-patterns',
|
|
204
|
+
'input-validation',
|
|
205
|
+
'java',
|
|
206
|
+
'knowledge-persistence',
|
|
207
|
+
'pattern-check',
|
|
208
|
+
'performance-patterns',
|
|
209
|
+
'pipeline',
|
|
210
|
+
'pipeline-orchestration',
|
|
211
|
+
'pipeline:orch',
|
|
212
|
+
'plan',
|
|
213
|
+
'plan-orchestration',
|
|
214
|
+
'plan:guided',
|
|
215
|
+
'plan:orch',
|
|
216
|
+
'plan:triage',
|
|
217
|
+
'pull-request',
|
|
218
|
+
'python',
|
|
219
|
+
'react',
|
|
220
|
+
'regression-patterns',
|
|
221
|
+
'release:guided',
|
|
222
|
+
'release:orch',
|
|
223
|
+
'release:triage',
|
|
224
|
+
'research',
|
|
225
|
+
'research:guided',
|
|
226
|
+
'research:orch',
|
|
227
|
+
'research:triage',
|
|
228
|
+
'resolve',
|
|
229
|
+
'resolve-orchestration',
|
|
230
|
+
'resolve:orch',
|
|
231
|
+
'review',
|
|
232
|
+
'review-methodology',
|
|
233
|
+
'review-orchestration',
|
|
234
|
+
'review:guided',
|
|
235
|
+
'review:orch',
|
|
236
|
+
'review:triage',
|
|
237
|
+
'router',
|
|
238
|
+
'rust',
|
|
239
|
+
'search-first',
|
|
240
|
+
'security-patterns',
|
|
241
|
+
'self-review',
|
|
242
|
+
'sidecar',
|
|
243
|
+
'test-design',
|
|
244
|
+
'test-driven-development',
|
|
245
|
+
'test-patterns',
|
|
246
|
+
'tests-patterns',
|
|
247
|
+
'typescript',
|
|
248
|
+
'worktree-support',
|
|
249
|
+
]),
|
|
250
|
+
skillFiles: Object.freeze({
|
|
251
|
+
'devflow:agent-references': names([
|
|
252
|
+
'references/learning/maintain.md',
|
|
253
|
+
]),
|
|
254
|
+
'devflow:git': names([
|
|
255
|
+
'references/pr/ensure-pr-ready.md',
|
|
256
|
+
]),
|
|
257
|
+
}),
|
|
258
|
+
});
|
|
259
|
+
/**
|
|
260
|
+
* The files of {@link RETIRED_SHIPPED} as paths relative to the Claude dir: the command, agent and
|
|
261
|
+
* rule files, and the listed files of a skill folder. A skill folder of `skills` is not a path
|
|
262
|
+
* here, since the install moves it whole.
|
|
263
|
+
*/
|
|
264
|
+
export function retiredPathSet() {
|
|
265
|
+
const paths = [];
|
|
266
|
+
for (const name of RETIRED_SHIPPED.commands)
|
|
267
|
+
paths.push(`commands/devflow/${name}.md`);
|
|
268
|
+
for (const name of RETIRED_SHIPPED.agents)
|
|
269
|
+
paths.push(`agents/devflow/${name}.md`);
|
|
270
|
+
for (const name of RETIRED_SHIPPED.rules)
|
|
271
|
+
paths.push(`rules/devflow/${name}.md`);
|
|
272
|
+
for (const [skill, files] of Object.entries(RETIRED_SHIPPED.skillFiles)) {
|
|
273
|
+
for (const file of files)
|
|
274
|
+
paths.push(`skills/${skill}/${file}`);
|
|
275
|
+
}
|
|
276
|
+
return createPathSet(paths);
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=retired-shipped-names.js.map
|