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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The skill files a compliance-off install removes by exact name, and the restore that
|
|
3
|
+
* puts them back (AC-41).
|
|
4
|
+
*
|
|
5
|
+
* The list is {@link COMPLIANCE_OFF_REMOVED_SKILL_FILES}: one constant, read by the install
|
|
6
|
+
* and by the compliance converge. This module does the file work and holds no policy:
|
|
7
|
+
*
|
|
8
|
+
* - {@link complianceOffCopyExclusions} runs with compliance off, BEFORE the skill copy, and
|
|
9
|
+
* names the files the copy leaves out, so a posting file never lands on the machine;
|
|
10
|
+
* - {@link removeComplianceOffFiles} runs with compliance off, AFTER the skill copy, and
|
|
11
|
+
* removes the files that an earlier install left in a skill the copy only overwrites;
|
|
12
|
+
* - {@link restoreComplianceOffFiles} runs with compliance on and writes back the files
|
|
13
|
+
* that a skill copy puts in place, from the same source the install would copy.
|
|
14
|
+
*
|
|
15
|
+
* Both are Results, never throws: an entry that cannot be handled is `refused` with its
|
|
16
|
+
* reason, the next entry still runs, and the caller decides what a refusal costs.
|
|
17
|
+
*
|
|
18
|
+
* Link and directory rules, shared by both halves (the same posture as the converged-root
|
|
19
|
+
* check, D-CONVERGED-ROOT-REAL):
|
|
20
|
+
* - every path is checked with `lstat`, never `stat`, so a link is seen as a link;
|
|
21
|
+
* - a link AT the entry is unlinked (the link, never its target) by a removal and replaced
|
|
22
|
+
* by a restore, so nothing is read or written through it;
|
|
23
|
+
* - a directory AT the entry is refused and reported, never recursed into;
|
|
24
|
+
* - a link or a non-directory ABOVE the entry, below the skills directory, is refused: the
|
|
25
|
+
* operation would otherwise reach a file through someone else's directory.
|
|
26
|
+
*
|
|
27
|
+
* Bound: the loops run over the entries of the list (three) and the segments of one entry.
|
|
28
|
+
*/
|
|
29
|
+
import { constants as fsConstants, promises as fs } from 'fs';
|
|
30
|
+
import * as path from 'path';
|
|
31
|
+
import { COMPLIANCE_OFF_REMOVED_SKILL_FILES } from '../../core/compliance-off-files.js';
|
|
32
|
+
import { unprefixSkillName } from '../../core/plugins.js';
|
|
33
|
+
const errno = (err) => err.code;
|
|
34
|
+
/** The entry as segments, or why it is not a plain relative path below the skills directory. */
|
|
35
|
+
function entrySegments(entry) {
|
|
36
|
+
const segments = entry.split('/');
|
|
37
|
+
const plain = segments.length >= 2 && segments.every(segment => segment !== '' && segment !== '.' && segment !== '..' && !segment.includes('\\'));
|
|
38
|
+
return plain ? { ok: true, segments } : { ok: false, reason: 'the entry is not a plain relative path' };
|
|
39
|
+
}
|
|
40
|
+
/** Walk the directories above the entry's last segment with `lstat`. */
|
|
41
|
+
async function inspectAncestors(skillsDir, segments) {
|
|
42
|
+
let current = skillsDir;
|
|
43
|
+
for (let index = 0; index < segments.length - 1; index++) {
|
|
44
|
+
current = path.join(current, segments[index]);
|
|
45
|
+
let stat;
|
|
46
|
+
try {
|
|
47
|
+
stat = await fs.lstat(current);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (errno(err) === 'ENOENT')
|
|
51
|
+
return { kind: 'absent', index };
|
|
52
|
+
return { kind: 'refused', reason: `cannot inspect ${current} (${errno(err) ?? String(err)})` };
|
|
53
|
+
}
|
|
54
|
+
if (stat.isSymbolicLink())
|
|
55
|
+
return { kind: 'refused', reason: `${current} is a link, so nothing is changed through it` };
|
|
56
|
+
if (!stat.isDirectory())
|
|
57
|
+
return { kind: 'refused', reason: `${current} is not a directory` };
|
|
58
|
+
}
|
|
59
|
+
return { kind: 'ok' };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The files of the list that belong to one skill, as absolute paths below the directory the
|
|
63
|
+
* skill copy reads (the valid shadow, else the shipped source). The install passes the result
|
|
64
|
+
* as the `exclude` set of `copyDirectory`, so with compliance off a posting file is never
|
|
65
|
+
* written to the machine and no later step has to remove it.
|
|
66
|
+
*
|
|
67
|
+
* A path the source does not hold (the generated gate is built, not shipped) excludes nothing.
|
|
68
|
+
* An entry that is not a plain relative path is left out here, and {@link removeComplianceOffFiles}
|
|
69
|
+
* reports it.
|
|
70
|
+
*
|
|
71
|
+
* @param skillName - The registry name of the skill, without the `devflow:` prefix.
|
|
72
|
+
* @param sourceDir - The directory the skill is copied from.
|
|
73
|
+
* @param entries - Defaults to {@link COMPLIANCE_OFF_REMOVED_SKILL_FILES}.
|
|
74
|
+
*/
|
|
75
|
+
export function complianceOffCopyExclusions(skillName, sourceDir, entries = COMPLIANCE_OFF_REMOVED_SKILL_FILES) {
|
|
76
|
+
const excluded = new Set();
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const parsed = entrySegments(entry);
|
|
79
|
+
if (!parsed.ok)
|
|
80
|
+
continue;
|
|
81
|
+
const [skill, ...below] = parsed.segments;
|
|
82
|
+
if (unprefixSkillName(skill) === skillName)
|
|
83
|
+
excluded.add(path.join(sourceDir, ...below));
|
|
84
|
+
}
|
|
85
|
+
return excluded;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Remove each entry of the list under `skillsDir`. An entry that is absent is a no-op.
|
|
89
|
+
*
|
|
90
|
+
* @param skillsDir - `{claudeDir}/skills`. Injected, so a test never touches the real one.
|
|
91
|
+
* @param entries - Defaults to {@link COMPLIANCE_OFF_REMOVED_SKILL_FILES}; injectable so a
|
|
92
|
+
* known-bad list is provable.
|
|
93
|
+
*/
|
|
94
|
+
export async function removeComplianceOffFiles(skillsDir, entries = COMPLIANCE_OFF_REMOVED_SKILL_FILES) {
|
|
95
|
+
const removed = [];
|
|
96
|
+
const refused = [];
|
|
97
|
+
for (const entry of entries) {
|
|
98
|
+
const parsed = entrySegments(entry);
|
|
99
|
+
if (!parsed.ok) {
|
|
100
|
+
refused.push({ entry, reason: parsed.reason });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const ancestors = await inspectAncestors(skillsDir, parsed.segments);
|
|
104
|
+
if (ancestors.kind === 'absent')
|
|
105
|
+
continue;
|
|
106
|
+
if (ancestors.kind === 'refused') {
|
|
107
|
+
refused.push({ entry, reason: ancestors.reason });
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const target = path.join(skillsDir, ...parsed.segments);
|
|
111
|
+
let stat;
|
|
112
|
+
try {
|
|
113
|
+
stat = await fs.lstat(target);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
if (errno(err) === 'ENOENT')
|
|
117
|
+
continue;
|
|
118
|
+
refused.push({ entry, reason: `cannot inspect ${target} (${errno(err) ?? String(err)})` });
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (stat.isDirectory()) {
|
|
122
|
+
refused.push({ entry, reason: `${target} is a directory, so it is not removed. Delete it by hand.` });
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (!stat.isFile() && !stat.isSymbolicLink()) {
|
|
126
|
+
refused.push({ entry, reason: `${target} is not a regular file or a link, so it is not removed. Delete it by hand.` });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
await fs.unlink(target);
|
|
131
|
+
removed.push(entry);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (errno(err) === 'ENOENT')
|
|
135
|
+
continue;
|
|
136
|
+
refused.push({ entry, reason: `could not remove ${target} (${errno(err) ?? String(err)})` });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { removed, refused };
|
|
140
|
+
}
|
|
141
|
+
/** Whether `a` and `b` hold the same bytes; any read error answers false. */
|
|
142
|
+
async function sameBytes(a, b) {
|
|
143
|
+
try {
|
|
144
|
+
const [x, y] = await Promise.all([fs.readFile(a), fs.readFile(b)]);
|
|
145
|
+
return x.equals(y);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** Copy `source` over `target` through a sibling temp file and a rename, so no reader sees a partial file. */
|
|
152
|
+
async function copyIntoPlace(source, target) {
|
|
153
|
+
const temp = `${target}.tmp.${process.pid}`;
|
|
154
|
+
try {
|
|
155
|
+
await fs.unlink(temp).catch((err) => {
|
|
156
|
+
if (errno(err) !== 'ENOENT')
|
|
157
|
+
throw err;
|
|
158
|
+
});
|
|
159
|
+
await fs.copyFile(source, temp, fsConstants.COPYFILE_EXCL);
|
|
160
|
+
// A reference is read-only instruction text; the overlay normalises the same mode.
|
|
161
|
+
await fs.chmod(temp, 0o644);
|
|
162
|
+
await fs.rename(temp, target);
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
await fs.unlink(temp).catch(() => undefined);
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Write back each entry whose file exists in its skill's source and is missing from, or
|
|
171
|
+
* differs in, the installed skill.
|
|
172
|
+
*
|
|
173
|
+
* It acts on a skill that is ALREADY installed and never installs one: an entry whose skill
|
|
174
|
+
* directory is absent is left alone, as a partial install of another plugin must be. An entry
|
|
175
|
+
* whose source has no such file is left alone too (a shadow that ships none installs none, and a
|
|
176
|
+
* fresh install of the same shadow agrees). Rename replaces a link at the entry, never writes
|
|
177
|
+
* through it.
|
|
178
|
+
*/
|
|
179
|
+
export async function restoreComplianceOffFiles(opts) {
|
|
180
|
+
const { skillsDir, sourceDirOf } = opts;
|
|
181
|
+
const entries = opts.entries ?? COMPLIANCE_OFF_REMOVED_SKILL_FILES;
|
|
182
|
+
const restored = [];
|
|
183
|
+
const unchanged = [];
|
|
184
|
+
const refused = [];
|
|
185
|
+
for (const entry of entries) {
|
|
186
|
+
if (opts.ownedElsewhere?.has(entry) === true)
|
|
187
|
+
continue;
|
|
188
|
+
const parsed = entrySegments(entry);
|
|
189
|
+
if (!parsed.ok) {
|
|
190
|
+
refused.push({ entry, reason: parsed.reason });
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const { segments } = parsed;
|
|
194
|
+
const ancestors = await inspectAncestors(skillsDir, segments);
|
|
195
|
+
if (ancestors.kind === 'refused') {
|
|
196
|
+
refused.push({ entry, reason: ancestors.reason });
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
// The skill directory is the first ancestor: a skill that is not installed is not ours to create.
|
|
200
|
+
if (ancestors.kind === 'absent' && ancestors.index === 0)
|
|
201
|
+
continue;
|
|
202
|
+
let sourceFile;
|
|
203
|
+
try {
|
|
204
|
+
sourceFile = path.join(await sourceDirOf(unprefixSkillName(segments[0])), ...segments.slice(1));
|
|
205
|
+
if (!(await fs.stat(sourceFile)).isFile())
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
if (errno(err) === 'ENOENT')
|
|
210
|
+
continue;
|
|
211
|
+
refused.push({ entry, reason: `cannot read the source of ${entry} (${errno(err) ?? String(err)})` });
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const target = path.join(skillsDir, ...segments);
|
|
215
|
+
let existing = 'absent';
|
|
216
|
+
try {
|
|
217
|
+
const stat = await fs.lstat(target);
|
|
218
|
+
existing = stat.isSymbolicLink() ? 'link' : stat.isFile() ? 'file' : stat.isDirectory() ? 'directory' : 'other';
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
if (errno(err) !== 'ENOENT') {
|
|
222
|
+
refused.push({ entry, reason: `cannot inspect ${target} (${errno(err) ?? String(err)})` });
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (existing === 'directory' || existing === 'other') {
|
|
227
|
+
refused.push({ entry, reason: `${target} is ${existing === 'directory' ? 'a directory' : 'not a regular file'}, so it is not replaced. Delete it by hand.` });
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (existing === 'file' && (await sameBytes(sourceFile, target))) {
|
|
231
|
+
unchanged.push(entry);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
236
|
+
await copyIntoPlace(sourceFile, target);
|
|
237
|
+
restored.push(entry);
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
refused.push({ entry, reason: `could not write ${target} (${errno(err) ?? String(err)})` });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return { restored, unchanged, refused };
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=compliance-off-files.js.map
|