@oevery/rsp 3.3.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -17
- package/README.zh-CN.md +25 -18
- package/dist/{chunk-TSUDEX5Y.mjs → chunk-HFSTLCCK.mjs} +1261 -319
- package/dist/cli.mjs +1129 -2350
- package/dist/{entry-GIDNR7CJ.mjs → entry-RIJQ2X46.mjs} +122 -36
- package/package.json +32 -24
- package/skills/rsp/SKILL.md +43 -38
- package/skills/rsp/references/closeout.md +38 -0
- package/skills/rsp/references/context-migration.md +22 -0
- package/skills/rsp/references/control-outcome.md +3 -29
- package/skills/rsp/references/coordination-recovery.md +25 -0
- package/skills/rsp/references/coordination-review.md +15 -0
- package/skills/rsp/references/coordination.md +59 -0
- package/skills/rsp/references/durable-review.md +60 -12
- package/skills/rsp/references/focus-continuation.md +8 -4
- package/skills/rsp/references/groups-dependencies.md +6 -4
- package/skills/rsp/references/response-language.md +2 -0
- package/skills/rsp/references/setup-repair.md +37 -7
- package/skills/rsp/references/work-model.md +27 -0
- package/skills/rsp-commit/SKILL.md +76 -31
- package/skills/rsp-commit/references/message.md +34 -0
- package/skills/rsp-doc/SKILL.md +65 -0
- package/skills/rsp-doc/agents/openai.yaml +3 -0
- package/skills/rsp-doc/references/artifact-lenses.md +43 -0
- package/skills/rsp-doc/references/release-communication.md +39 -0
- package/skills/rsp-doc/references/skill-authoring.md +36 -0
- package/skills/rsp-implement/SKILL.md +56 -25
- package/skills/rsp-implement/references/diagnosis.md +10 -0
- package/skills/rsp-implement/references/finding-resolution.md +21 -0
- package/skills/rsp-implement/references/test-first.md +7 -0
- package/skills/rsp-review/SKILL.md +46 -45
- package/skills/rsp-review/references/code-review.md +23 -8
- package/skills/rsp-review/references/document-review.md +25 -8
- package/skills/rsp-review/references/release-communication.md +21 -0
- package/skills/{rsp-structural-audit/references/structural-lenses.md → rsp-review/references/structural-review.md} +5 -5
- package/skills/rsp-shape/SKILL.md +24 -45
- package/skills/rsp-shape/references/complex-shaping.md +3 -1
- package/skills/rsp-shape/references/deep-clarification.md +4 -4
- package/skills/rsp-shape/references/domain-modeling.md +13 -0
- package/skills/rsp-shape/references/module-seams.md +13 -0
- package/skills/rsp-shape/references/planning.md +42 -0
- package/skills/{rsp-design → rsp-shape}/references/reversible-exploration.md +2 -2
- package/skills/rsp-verify/SKILL.md +44 -12
- package/dist/chunk-7HXQNBIG.mjs +0 -871
- package/dist/chunk-JOMFFQRG.mjs +0 -99
- package/dist/chunk-QM2UVPQK.mjs +0 -375
- package/dist/entry-YAW3PYFA.mjs +0 -174
- package/rules/rsp-rules.md +0 -48
- package/skills/rsp/references/implementation-evidence.md +0 -12
- package/skills/rsp/references/managed-routing.md +0 -45
- package/skills/rsp-design/SKILL.md +0 -53
- package/skills/rsp-design/references/domain-modeling.md +0 -11
- package/skills/rsp-design/references/module-seams.md +0 -11
- package/skills/rsp-diagnose/SKILL.md +0 -48
- package/skills/rsp-manage/SKILL.md +0 -101
- package/skills/rsp-manage/references/closeout.md +0 -13
- package/skills/rsp-manage/references/delegation.md +0 -47
- package/skills/rsp-manage/references/interruption-recovery.md +0 -19
- package/skills/rsp-manage/references/review-convergence.md +0 -9
- package/skills/rsp-release-docs/SKILL.md +0 -77
- package/skills/rsp-release-docs/references/convention-discovery.md +0 -65
- package/skills/rsp-release-docs/references/evidence-and-surfaces.md +0 -21
- package/skills/rsp-release-docs/references/output-contracts.md +0 -97
- package/skills/rsp-release-docs/references/publication-lifecycle.md +0 -28
- package/skills/rsp-resolve-findings/SKILL.md +0 -61
- package/skills/rsp-structural-audit/SKILL.md +0 -71
- package/skills/rsp-tdd/SKILL.md +0 -56
package/dist/chunk-JOMFFQRG.mjs
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/tui/terminal.ts
|
|
4
|
-
function openTerminalSession(stdout, options) {
|
|
5
|
-
let cleaned = false;
|
|
6
|
-
if (!options.screenReader)
|
|
7
|
-
stdout.write("\x1B[?1049h\x1B[?25l");
|
|
8
|
-
return {
|
|
9
|
-
cleanup() {
|
|
10
|
-
if (cleaned)
|
|
11
|
-
return;
|
|
12
|
-
cleaned = true;
|
|
13
|
-
if (!options.screenReader)
|
|
14
|
-
stdout.write("\x1B[?25h\x1B[?1049l");
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// src/tui/display.ts
|
|
20
|
-
var segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
21
|
-
function isWide(codePoint) {
|
|
22
|
-
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 42191 && codePoint !== 12351 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 131072 && codePoint <= 262141);
|
|
23
|
-
}
|
|
24
|
-
function displayWidth(value) {
|
|
25
|
-
let width = 0;
|
|
26
|
-
for (const { segment } of segmenter.segment(value)) {
|
|
27
|
-
const codePoint = segment.codePointAt(0) ?? 0;
|
|
28
|
-
if (/^\p{Mark}$/u.test(segment) || codePoint === 8205)
|
|
29
|
-
continue;
|
|
30
|
-
width += isWide(codePoint) ? 2 : 1;
|
|
31
|
-
}
|
|
32
|
-
return width;
|
|
33
|
-
}
|
|
34
|
-
function truncateDisplay(value, maxWidth) {
|
|
35
|
-
if (displayWidth(value) <= maxWidth)
|
|
36
|
-
return value;
|
|
37
|
-
if (maxWidth <= 1)
|
|
38
|
-
return "\u2026".slice(0, maxWidth);
|
|
39
|
-
let result = "";
|
|
40
|
-
for (const { segment } of segmenter.segment(value)) {
|
|
41
|
-
if (displayWidth(result + segment) > maxWidth - 1)
|
|
42
|
-
break;
|
|
43
|
-
result += segment;
|
|
44
|
-
}
|
|
45
|
-
return `${result}\u2026`;
|
|
46
|
-
}
|
|
47
|
-
function wrapDisplay(value, maxWidth, firstPrefix = "", continuationPrefix = firstPrefix) {
|
|
48
|
-
const width = Math.max(1, maxWidth);
|
|
49
|
-
if (displayWidth(firstPrefix) >= width)
|
|
50
|
-
return [truncateDisplay(`${firstPrefix}${value}`, width)];
|
|
51
|
-
const lines = [];
|
|
52
|
-
let prefix = firstPrefix;
|
|
53
|
-
let line = prefix;
|
|
54
|
-
const pushLine = () => {
|
|
55
|
-
lines.push(line);
|
|
56
|
-
prefix = continuationPrefix;
|
|
57
|
-
line = prefix;
|
|
58
|
-
};
|
|
59
|
-
const appendUnbroken = (token) => {
|
|
60
|
-
for (const { segment } of segmenter.segment(token)) {
|
|
61
|
-
if (displayWidth(line + segment) > width && line !== prefix)
|
|
62
|
-
pushLine();
|
|
63
|
-
if (displayWidth(line + segment) > width) {
|
|
64
|
-
lines.push(truncateDisplay(line + segment, width));
|
|
65
|
-
prefix = continuationPrefix;
|
|
66
|
-
line = prefix;
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
line += segment;
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
let pendingWhitespace = "";
|
|
73
|
-
for (const token of value.split(/(\s+)/u).filter(Boolean)) {
|
|
74
|
-
if (token.trim().length === 0) {
|
|
75
|
-
if (line !== prefix)
|
|
76
|
-
pendingWhitespace += token;
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
const separator = line === prefix ? "" : pendingWhitespace;
|
|
80
|
-
if (displayWidth(line + separator + token) <= width) {
|
|
81
|
-
line += `${separator}${token}`;
|
|
82
|
-
} else {
|
|
83
|
-
if (line !== prefix && separator)
|
|
84
|
-
pushLine();
|
|
85
|
-
appendUnbroken(token);
|
|
86
|
-
}
|
|
87
|
-
pendingWhitespace = "";
|
|
88
|
-
}
|
|
89
|
-
if (line !== prefix || lines.length === 0)
|
|
90
|
-
lines.push(line);
|
|
91
|
-
return lines;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export {
|
|
95
|
-
openTerminalSession,
|
|
96
|
-
displayWidth,
|
|
97
|
-
truncateDisplay,
|
|
98
|
-
wrapDisplay
|
|
99
|
-
};
|
package/dist/chunk-QM2UVPQK.mjs
DELETED
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
ManagedPathError,
|
|
4
|
-
PKG_ROOT,
|
|
5
|
-
emitJson,
|
|
6
|
-
inspectManagedDirectory,
|
|
7
|
-
inspectManagedFileTree,
|
|
8
|
-
resolveManagedDirectoryChain
|
|
9
|
-
} from "./chunk-7HXQNBIG.mjs";
|
|
10
|
-
|
|
11
|
-
// src/cli/presenters/skills.ts
|
|
12
|
-
function presentPackagedSkillInventory(inventory, json = false) {
|
|
13
|
-
if (json) {
|
|
14
|
-
emitJson(inventory);
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
console.log(` ${inventory.package.name}@${inventory.package.version}`);
|
|
18
|
-
console.log(` target: ${inventory.target}`);
|
|
19
|
-
for (const group of [
|
|
20
|
-
{ heading: "Default suite Skills", kind: "default" },
|
|
21
|
-
{ heading: "Optional project Skills", kind: "optional" }
|
|
22
|
-
]) {
|
|
23
|
-
console.log("");
|
|
24
|
-
console.log(` ${group.heading}`);
|
|
25
|
-
const skills = inventory.skills.filter((skill) => skill.kind === group.kind);
|
|
26
|
-
if (skills.length === 0)
|
|
27
|
-
console.log(" none");
|
|
28
|
-
for (const skill of skills)
|
|
29
|
-
console.log(` ${skill.name} ${skill.status}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function presentSkillInstallResult(result, dryRun = false) {
|
|
33
|
-
const prefix = dryRun ? "would be " : "";
|
|
34
|
-
for (const status of ["installed", "unchanged", "replaced", "removed"]) {
|
|
35
|
-
if (result[status].length > 0)
|
|
36
|
-
console.log(` ${prefix}${status}: ${result[status].join(", ")}`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// src/commands/skills.ts
|
|
41
|
-
import { randomUUID } from "crypto";
|
|
42
|
-
import { lstatSync, realpathSync } from "fs";
|
|
43
|
-
import { chmod, copyFile, mkdir, readdir, readFile, rename, rm } from "fs/promises";
|
|
44
|
-
import { dirname, join, relative } from "path";
|
|
45
|
-
var DEFAULT_PACKAGED_SKILL_NAMES = [
|
|
46
|
-
"rsp",
|
|
47
|
-
"rsp-commit",
|
|
48
|
-
"rsp-design",
|
|
49
|
-
"rsp-diagnose",
|
|
50
|
-
"rsp-implement",
|
|
51
|
-
"rsp-manage",
|
|
52
|
-
"rsp-release-docs",
|
|
53
|
-
"rsp-resolve-findings",
|
|
54
|
-
"rsp-review",
|
|
55
|
-
"rsp-shape",
|
|
56
|
-
"rsp-tdd",
|
|
57
|
-
"rsp-verify"
|
|
58
|
-
];
|
|
59
|
-
var OBSOLETE_PACKAGED_SKILL_RENAMES = {
|
|
60
|
-
"rsp-address-review": "rsp-resolve-findings",
|
|
61
|
-
"rsp-codebase-audit": "rsp-structural-audit"
|
|
62
|
-
};
|
|
63
|
-
function unsupportedEntry(path, label) {
|
|
64
|
-
return new ManagedPathError(path, `unsupported entry in the ${label} tree: ${path}`);
|
|
65
|
-
}
|
|
66
|
-
function inspectDirectoryIdentity(path, label) {
|
|
67
|
-
const inspection = inspectManagedDirectory(path, label);
|
|
68
|
-
if (inspection.issue)
|
|
69
|
-
throw inspection.issue;
|
|
70
|
-
const stats = lstatSync(path);
|
|
71
|
-
return { path, dev: stats.dev, ino: stats.ino };
|
|
72
|
-
}
|
|
73
|
-
function assertDirectoryIdentities(identities, label) {
|
|
74
|
-
for (const identity of identities) {
|
|
75
|
-
const current = inspectDirectoryIdentity(identity.path, label);
|
|
76
|
-
if (current.dev !== identity.dev || current.ino !== identity.ino)
|
|
77
|
-
throw new ManagedPathError(identity.path, `${label} changed after validation: ${identity.path}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
async function prepareTargetRoot(projectRoot, targetRoot) {
|
|
81
|
-
const projectIdentity = inspectDirectoryIdentity(projectRoot, "project Skills root");
|
|
82
|
-
const agentsRoot = join(projectRoot, ".agents");
|
|
83
|
-
const identities = [projectIdentity];
|
|
84
|
-
assertDirectoryIdentities(identities, "project Skills root");
|
|
85
|
-
await mkdir(agentsRoot).catch((error) => {
|
|
86
|
-
if (error.code !== "EEXIST")
|
|
87
|
-
throw error;
|
|
88
|
-
});
|
|
89
|
-
assertDirectoryIdentities(identities, "project Skills root");
|
|
90
|
-
identities.push(inspectDirectoryIdentity(agentsRoot, "project Skills root"));
|
|
91
|
-
assertDirectoryIdentities(identities, "project Skills root");
|
|
92
|
-
await mkdir(targetRoot).catch((error) => {
|
|
93
|
-
if (error.code !== "EEXIST")
|
|
94
|
-
throw error;
|
|
95
|
-
});
|
|
96
|
-
assertDirectoryIdentities(identities, "project Skills root");
|
|
97
|
-
identities.push(inspectDirectoryIdentity(targetRoot, "project Skills root"));
|
|
98
|
-
return identities;
|
|
99
|
-
}
|
|
100
|
-
async function inspectPackagedSkills(packageRoot) {
|
|
101
|
-
const skillsRoot = join(packageRoot, "skills");
|
|
102
|
-
const rootInspection = inspectManagedDirectory(skillsRoot, "packaged Skills root");
|
|
103
|
-
if (rootInspection.issue)
|
|
104
|
-
throw rootInspection.issue;
|
|
105
|
-
const entries = await readdir(skillsRoot, { withFileTypes: true });
|
|
106
|
-
const skills = [];
|
|
107
|
-
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
108
|
-
if (entry.name.startsWith("."))
|
|
109
|
-
continue;
|
|
110
|
-
const root = join(skillsRoot, entry.name);
|
|
111
|
-
if (!entry.isDirectory())
|
|
112
|
-
throw unsupportedEntry(root, "packaged Skills");
|
|
113
|
-
const inspection = await inspectManagedFileTree(root, `packaged Skill ${entry.name}`);
|
|
114
|
-
if (inspection.issues.length > 0)
|
|
115
|
-
throw inspection.issues[0];
|
|
116
|
-
skills.push({
|
|
117
|
-
name: entry.name,
|
|
118
|
-
root,
|
|
119
|
-
files: inspection.files.map((path) => relative(root, path))
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
return skills;
|
|
123
|
-
}
|
|
124
|
-
function inspectTarget(path, name) {
|
|
125
|
-
try {
|
|
126
|
-
const stats = lstatSync(path);
|
|
127
|
-
if (stats.isDirectory())
|
|
128
|
-
return "directory";
|
|
129
|
-
throw unsupportedEntry(path, `installed Skill ${name}`);
|
|
130
|
-
} catch (error) {
|
|
131
|
-
if (error.code === "ENOENT")
|
|
132
|
-
return "missing";
|
|
133
|
-
throw error;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function inspectInventoryTarget(path) {
|
|
137
|
-
try {
|
|
138
|
-
return lstatSync(path).isDirectory() ? "directory" : "divergent";
|
|
139
|
-
} catch (error) {
|
|
140
|
-
if (error.code === "ENOENT")
|
|
141
|
-
return "missing";
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
function isCanonicalSourceProjection(sourceRoot, targetRoot) {
|
|
146
|
-
try {
|
|
147
|
-
if (!lstatSync(targetRoot).isSymbolicLink())
|
|
148
|
-
return false;
|
|
149
|
-
return realpathSync(targetRoot) === realpathSync(sourceRoot);
|
|
150
|
-
} catch {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
async function treesEqual(source, targetRoot) {
|
|
155
|
-
const target = await inspectManagedFileTree(targetRoot, `installed Skill ${source.name}`);
|
|
156
|
-
if (target.issues.length > 0)
|
|
157
|
-
throw target.issues[0];
|
|
158
|
-
const targetFiles = target.files.map((path) => relative(targetRoot, path));
|
|
159
|
-
if (source.files.length !== targetFiles.length || source.files.some((path, index) => path !== targetFiles[index]))
|
|
160
|
-
return false;
|
|
161
|
-
for (const path of source.files) {
|
|
162
|
-
const sourcePath = join(source.root, path);
|
|
163
|
-
const targetPath = join(targetRoot, path);
|
|
164
|
-
const [sourceStats, targetStats] = [lstatSync(sourcePath), lstatSync(targetPath)];
|
|
165
|
-
if (!sourceStats.isFile() || !targetStats.isFile())
|
|
166
|
-
throw unsupportedEntry(!sourceStats.isFile() ? sourcePath : targetPath, `Skill ${source.name}`);
|
|
167
|
-
if ((sourceStats.mode & 511) !== (targetStats.mode & 511))
|
|
168
|
-
return false;
|
|
169
|
-
const [sourceContent, targetContent] = await Promise.all([readFile(sourcePath), readFile(targetPath)]);
|
|
170
|
-
if (!sourceContent.equals(targetContent))
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
return true;
|
|
174
|
-
}
|
|
175
|
-
async function inspectPackagedSkillInventory(options = {}) {
|
|
176
|
-
const packageRoot = options.packageRoot ?? PKG_ROOT;
|
|
177
|
-
const projectRoot = options.projectRoot ?? process.cwd();
|
|
178
|
-
const packagedSkills = await inspectPackagedSkills(packageRoot);
|
|
179
|
-
const packagedNames = new Set(packagedSkills.map((skill) => skill.name));
|
|
180
|
-
const missingDefaults = DEFAULT_PACKAGED_SKILL_NAMES.filter((name) => !packagedNames.has(name));
|
|
181
|
-
if (missingDefaults.length > 0)
|
|
182
|
-
throw new Error(`packaged Skills missing default Skills: ${missingDefaults.join(", ")}`);
|
|
183
|
-
const manifest = JSON.parse(await readFile(join(packageRoot, "package.json"), "utf8"));
|
|
184
|
-
if (typeof manifest.name !== "string" || typeof manifest.version !== "string")
|
|
185
|
-
throw new Error("package.json must contain string name and version fields");
|
|
186
|
-
const targetRoot = resolveManagedDirectoryChain(projectRoot, [".agents", "skills"], "project Skills root");
|
|
187
|
-
const defaults = new Set(DEFAULT_PACKAGED_SKILL_NAMES);
|
|
188
|
-
const skills = [];
|
|
189
|
-
for (const skill of packagedSkills) {
|
|
190
|
-
const target = join(targetRoot, skill.name);
|
|
191
|
-
const targetStatus = inspectInventoryTarget(target);
|
|
192
|
-
skills.push({
|
|
193
|
-
name: skill.name,
|
|
194
|
-
kind: defaults.has(skill.name) ? "default" : "optional",
|
|
195
|
-
status: targetStatus === "missing" ? "missing" : targetStatus === "divergent" ? isCanonicalSourceProjection(skill.root, target) ? "unchanged" : "divergent" : await treesEqual(skill, target) ? "unchanged" : "divergent"
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
package: { name: manifest.name, version: manifest.version },
|
|
200
|
-
target: relative(projectRoot, targetRoot) || ".",
|
|
201
|
-
skills
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
async function copySkillTree(source, targetRoot) {
|
|
205
|
-
await mkdir(targetRoot, { recursive: true });
|
|
206
|
-
for (const path of source.files) {
|
|
207
|
-
const sourcePath = join(source.root, path);
|
|
208
|
-
const targetPath = join(targetRoot, path);
|
|
209
|
-
const stats = lstatSync(sourcePath);
|
|
210
|
-
if (!stats.isFile())
|
|
211
|
-
throw unsupportedEntry(sourcePath, `packaged Skill ${source.name}`);
|
|
212
|
-
await mkdir(dirname(targetPath), { recursive: true });
|
|
213
|
-
await copyFile(sourcePath, targetPath);
|
|
214
|
-
await chmod(targetPath, stats.mode & 511);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
async function installPackagedSkills(args = {}, options = {}) {
|
|
218
|
-
const packageRoot = options.packageRoot ?? PKG_ROOT;
|
|
219
|
-
const projectRoot = options.projectRoot ?? process.cwd();
|
|
220
|
-
const renamePath = options.renamePath ?? rename;
|
|
221
|
-
const packagedSkills = await inspectPackagedSkills(packageRoot);
|
|
222
|
-
const requestedNames = args.names?.length ? [...new Set(args.names)] : [...DEFAULT_PACKAGED_SKILL_NAMES];
|
|
223
|
-
const packagedSkillsByName = new Map(packagedSkills.map((skill) => [skill.name, skill]));
|
|
224
|
-
for (const name of requestedNames) {
|
|
225
|
-
if (!packagedSkillsByName.has(name))
|
|
226
|
-
throw new Error(`unknown packaged Skill: ${name}`);
|
|
227
|
-
}
|
|
228
|
-
const skills = requestedNames.map((name) => packagedSkillsByName.get(name)).sort((a, b) => a.name.localeCompare(b.name));
|
|
229
|
-
const targetRoot = resolveManagedDirectoryChain(projectRoot, [".agents", "skills"], "project Skills root");
|
|
230
|
-
const result = { installed: [], removed: [], unchanged: [], replaced: [] };
|
|
231
|
-
const conflicts = [];
|
|
232
|
-
const obsoleteConflicts = [];
|
|
233
|
-
const targetTreeIdentities = /* @__PURE__ */ new Map();
|
|
234
|
-
for (const skill of skills) {
|
|
235
|
-
const target = join(targetRoot, skill.name);
|
|
236
|
-
if (inspectTarget(target, skill.name) === "missing") {
|
|
237
|
-
result.installed.push(skill.name);
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
const targetIdentity = inspectDirectoryIdentity(target, `installed Skill ${skill.name}`);
|
|
241
|
-
if (await treesEqual(skill, target)) {
|
|
242
|
-
result.unchanged.push(skill.name);
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
assertDirectoryIdentities([targetIdentity], `installed Skill ${skill.name}`);
|
|
246
|
-
if (args.force) {
|
|
247
|
-
result.replaced.push(skill.name);
|
|
248
|
-
targetTreeIdentities.set(skill.name, targetIdentity);
|
|
249
|
-
} else {
|
|
250
|
-
conflicts.push(skill.name);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
const selectedNames = new Set(skills.map((skill) => skill.name));
|
|
254
|
-
const obsoleteTreeIdentities = /* @__PURE__ */ new Map();
|
|
255
|
-
for (const [obsolete, replacement] of Object.entries(OBSOLETE_PACKAGED_SKILL_RENAMES)) {
|
|
256
|
-
if (!selectedNames.has(replacement))
|
|
257
|
-
continue;
|
|
258
|
-
const target = join(targetRoot, obsolete);
|
|
259
|
-
if (inspectTarget(target, obsolete) === "missing")
|
|
260
|
-
continue;
|
|
261
|
-
const targetIdentity = inspectDirectoryIdentity(target, `obsolete installed Skill ${obsolete}`);
|
|
262
|
-
assertDirectoryIdentities([targetIdentity], `obsolete installed Skill ${obsolete}`);
|
|
263
|
-
if (args.force) {
|
|
264
|
-
result.removed.push(obsolete);
|
|
265
|
-
obsoleteTreeIdentities.set(obsolete, targetIdentity);
|
|
266
|
-
} else {
|
|
267
|
-
obsoleteConflicts.push(`${obsolete} -> ${replacement}`);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
const conflictMessages = [];
|
|
271
|
-
if (conflicts.length > 0)
|
|
272
|
-
conflictMessages.push(`conflicting packaged Skills: ${conflicts.join(", ")}`);
|
|
273
|
-
if (obsoleteConflicts.length > 0)
|
|
274
|
-
conflictMessages.push(`obsolete packaged Skill renames: ${obsoleteConflicts.join(", ")}`);
|
|
275
|
-
if (conflictMessages.length > 0)
|
|
276
|
-
throw new Error(`${conflictMessages.join("; ")}; rerun with --force to replace or remove only these package-owned directories`);
|
|
277
|
-
if (args.dryRun)
|
|
278
|
-
return result;
|
|
279
|
-
await options.onMutationStep?.({ phase: "before-target-root-mutation", targetRoot });
|
|
280
|
-
const targetIdentities = await prepareTargetRoot(projectRoot, targetRoot);
|
|
281
|
-
const stagingRoot = join(targetRoot, `.rsp-skills-install-${randomUUID()}`);
|
|
282
|
-
const nextRoot = join(stagingRoot, "next");
|
|
283
|
-
const previousRoot = join(stagingRoot, "previous");
|
|
284
|
-
const failedRoot = join(stagingRoot, "failed");
|
|
285
|
-
const movedPrevious = [];
|
|
286
|
-
const activated = [];
|
|
287
|
-
let removeStaging = true;
|
|
288
|
-
try {
|
|
289
|
-
assertDirectoryIdentities(targetIdentities, "project Skills root");
|
|
290
|
-
await mkdir(nextRoot, { recursive: true });
|
|
291
|
-
await mkdir(previousRoot);
|
|
292
|
-
await mkdir(failedRoot);
|
|
293
|
-
for (const skill of skills) {
|
|
294
|
-
if (result.unchanged.includes(skill.name))
|
|
295
|
-
continue;
|
|
296
|
-
await copySkillTree(skill, join(nextRoot, skill.name));
|
|
297
|
-
}
|
|
298
|
-
for (const name of result.installed) {
|
|
299
|
-
const target = join(targetRoot, name);
|
|
300
|
-
if (inspectTarget(target, name) !== "missing")
|
|
301
|
-
throw new ManagedPathError(target, `installed Skill changed after preflight: ${target}`);
|
|
302
|
-
}
|
|
303
|
-
for (const name of result.replaced) {
|
|
304
|
-
assertDirectoryIdentities([targetTreeIdentities.get(name)], `installed Skill ${name}`);
|
|
305
|
-
}
|
|
306
|
-
for (const name of result.removed) {
|
|
307
|
-
assertDirectoryIdentities([obsoleteTreeIdentities.get(name)], `obsolete installed Skill ${name}`);
|
|
308
|
-
}
|
|
309
|
-
for (const name of result.removed) {
|
|
310
|
-
assertDirectoryIdentities(targetIdentities, "project Skills root");
|
|
311
|
-
assertDirectoryIdentities([obsoleteTreeIdentities.get(name)], `obsolete installed Skill ${name}`);
|
|
312
|
-
await renamePath(join(targetRoot, name), join(previousRoot, name));
|
|
313
|
-
movedPrevious.push(name);
|
|
314
|
-
}
|
|
315
|
-
for (const name of result.replaced) {
|
|
316
|
-
assertDirectoryIdentities(targetIdentities, "project Skills root");
|
|
317
|
-
assertDirectoryIdentities([targetTreeIdentities.get(name)], `installed Skill ${name}`);
|
|
318
|
-
await renamePath(join(targetRoot, name), join(previousRoot, name));
|
|
319
|
-
movedPrevious.push(name);
|
|
320
|
-
}
|
|
321
|
-
for (const name of [...result.installed, ...result.replaced].sort()) {
|
|
322
|
-
const target = join(targetRoot, name);
|
|
323
|
-
assertDirectoryIdentities(targetIdentities, "project Skills root");
|
|
324
|
-
await options.onMutationStep?.({ phase: "before-activate", name, target });
|
|
325
|
-
assertDirectoryIdentities(targetIdentities, "project Skills root");
|
|
326
|
-
if (inspectTarget(target, name) !== "missing")
|
|
327
|
-
throw new ManagedPathError(target, `installed Skill changed after preflight: ${target}`);
|
|
328
|
-
await renamePath(join(nextRoot, name), target);
|
|
329
|
-
activated.push(name);
|
|
330
|
-
}
|
|
331
|
-
} catch (error) {
|
|
332
|
-
const rollbackErrors = [];
|
|
333
|
-
for (const name of activated.reverse()) {
|
|
334
|
-
const target = join(targetRoot, name);
|
|
335
|
-
try {
|
|
336
|
-
assertDirectoryIdentities(targetIdentities, "project Skills root");
|
|
337
|
-
if (inspectTarget(target, name) === "directory")
|
|
338
|
-
await renamePath(target, join(failedRoot, name));
|
|
339
|
-
} catch (rollbackError) {
|
|
340
|
-
rollbackErrors.push(rollbackError instanceof Error ? rollbackError.message : String(rollbackError));
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
for (const name of movedPrevious.reverse()) {
|
|
344
|
-
const target = join(targetRoot, name);
|
|
345
|
-
const previous = join(previousRoot, name);
|
|
346
|
-
try {
|
|
347
|
-
assertDirectoryIdentities(targetIdentities, "project Skills root");
|
|
348
|
-
await options.onMutationStep?.({ phase: "before-rollback-restore", name, target, previous });
|
|
349
|
-
assertDirectoryIdentities(targetIdentities, "project Skills root");
|
|
350
|
-
if (inspectTarget(target, name) !== "missing")
|
|
351
|
-
throw new ManagedPathError(target, `cannot restore installed Skill because the target is occupied: ${target}`);
|
|
352
|
-
await renamePath(previous, target);
|
|
353
|
-
} catch (rollbackError) {
|
|
354
|
-
rollbackErrors.push(rollbackError instanceof Error ? rollbackError.message : String(rollbackError));
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
if (rollbackErrors.length > 0) {
|
|
358
|
-
removeStaging = false;
|
|
359
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
360
|
-
throw new Error(`${message}; rollback incomplete; recover original Skills from ${previousRoot}; ${rollbackErrors.join("; ")}`);
|
|
361
|
-
}
|
|
362
|
-
throw error;
|
|
363
|
-
} finally {
|
|
364
|
-
if (removeStaging)
|
|
365
|
-
await rm(stagingRoot, { recursive: true, force: true });
|
|
366
|
-
}
|
|
367
|
-
return result;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
export {
|
|
371
|
-
presentPackagedSkillInventory,
|
|
372
|
-
presentSkillInstallResult,
|
|
373
|
-
inspectPackagedSkillInventory,
|
|
374
|
-
installPackagedSkills
|
|
375
|
-
};
|
package/dist/entry-YAW3PYFA.mjs
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
inspectPackagedSkillInventory
|
|
4
|
-
} from "./chunk-QM2UVPQK.mjs";
|
|
5
|
-
import {
|
|
6
|
-
openTerminalSession,
|
|
7
|
-
truncateDisplay
|
|
8
|
-
} from "./chunk-JOMFFQRG.mjs";
|
|
9
|
-
import "./chunk-7HXQNBIG.mjs";
|
|
10
|
-
|
|
11
|
-
// src/skills-tui/entry.tsx
|
|
12
|
-
import process from "process";
|
|
13
|
-
import { render } from "ink";
|
|
14
|
-
import ReactRuntime from "react";
|
|
15
|
-
|
|
16
|
-
// src/skills-tui/app.tsx
|
|
17
|
-
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
18
|
-
import { useMemo, useState } from "react";
|
|
19
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
-
function SkillsApp({ initialWidth, inventory, messages, onComplete }) {
|
|
21
|
-
const { exit } = useApp();
|
|
22
|
-
const { stdout } = useStdout();
|
|
23
|
-
const width = Math.max(40, initialWidth ?? stdout.columns ?? 80);
|
|
24
|
-
const optional = useMemo(() => inventory.skills.filter((skill) => skill.kind === "optional"), [inventory]);
|
|
25
|
-
const defaults = useMemo(() => inventory.skills.filter((skill) => skill.kind === "default"), [inventory]);
|
|
26
|
-
const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
|
|
27
|
-
const [cursor, setCursor] = useState(0);
|
|
28
|
-
const [phase, setPhase] = useState("select");
|
|
29
|
-
const finish = (selection) => {
|
|
30
|
-
onComplete(selection);
|
|
31
|
-
exit();
|
|
32
|
-
};
|
|
33
|
-
const selectedNames = () => [...defaults.map((skill) => skill.name), ...optional.filter((skill) => selected.has(skill.name)).map((skill) => skill.name)].sort();
|
|
34
|
-
useInput((input, key) => {
|
|
35
|
-
if (phase === "replace") {
|
|
36
|
-
if (input.toLowerCase() === "y") {
|
|
37
|
-
finish({ kind: "confirmed", names: selectedNames(), force: true });
|
|
38
|
-
} else if (input.toLowerCase() === "n" || key.escape) {
|
|
39
|
-
finish({ kind: "cancelled" });
|
|
40
|
-
}
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
if (key.escape || input === "q" || key.ctrl && input === "c") {
|
|
44
|
-
finish({ kind: "cancelled" });
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
if ((key.upArrow || input === "k") && optional.length > 0) {
|
|
48
|
-
setCursor((value) => (value - 1 + optional.length) % optional.length);
|
|
49
|
-
} else if ((key.downArrow || input === "j") && optional.length > 0) {
|
|
50
|
-
setCursor((value) => (value + 1) % optional.length);
|
|
51
|
-
} else if (input === " " && optional[cursor]) {
|
|
52
|
-
const name = optional[cursor].name;
|
|
53
|
-
setSelected((current) => {
|
|
54
|
-
const next = new Set(current);
|
|
55
|
-
if (next.has(name))
|
|
56
|
-
next.delete(name);
|
|
57
|
-
else
|
|
58
|
-
next.add(name);
|
|
59
|
-
return next;
|
|
60
|
-
});
|
|
61
|
-
} else if (key.return) {
|
|
62
|
-
const names = selectedNames();
|
|
63
|
-
const divergent = inventory.skills.some((skill) => names.includes(skill.name) && skill.status === "divergent");
|
|
64
|
-
if (divergent)
|
|
65
|
-
setPhase("replace");
|
|
66
|
-
else
|
|
67
|
-
finish({ kind: "confirmed", names, force: false });
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
const status = (value) => messages[value];
|
|
71
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width, children: [
|
|
72
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: messages.title }),
|
|
73
|
-
/* @__PURE__ */ jsx(Text, { children: truncateDisplay(`${inventory.package.name}@${inventory.package.version}`, width) }),
|
|
74
|
-
/* @__PURE__ */ jsx(Text, { children: truncateDisplay(`${messages.target}: ${inventory.target}`, width) }),
|
|
75
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
76
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: messages.defaultHeading }),
|
|
77
|
-
defaults.map((skill) => /* @__PURE__ */ jsx(Text, { children: truncateDisplay(` [x] ${skill.name} ${messages.locked} \xB7 ${status(skill.status)}`, width) }, skill.name)),
|
|
78
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
79
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: messages.optionalHeading }),
|
|
80
|
-
optional.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${messages.none}` }),
|
|
81
|
-
optional.map((skill, index) => /* @__PURE__ */ jsx(Text, { children: truncateDisplay(`${index === cursor ? "\u203A" : " "} ${selected.has(skill.name) ? "[x]" : "[ ]"} ${skill.name} ${status(skill.status)}`, width) }, skill.name)),
|
|
82
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
83
|
-
/* @__PURE__ */ jsx(Text, { color: phase === "replace" ? "yellow" : void 0, children: phase === "replace" ? messages.replaceTitle : messages.selectHelp }),
|
|
84
|
-
phase === "replace" && /* @__PURE__ */ jsx(Text, { children: messages.replaceHelp })
|
|
85
|
-
] });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// src/skills-tui/messages.ts
|
|
89
|
-
var skillsCatalogs = {
|
|
90
|
-
"en": {
|
|
91
|
-
title: "RSP Skill manager",
|
|
92
|
-
target: "Target",
|
|
93
|
-
defaultHeading: "Default suite Skills",
|
|
94
|
-
optionalHeading: "Optional project Skills",
|
|
95
|
-
missing: "missing",
|
|
96
|
-
unchanged: "unchanged",
|
|
97
|
-
divergent: "divergent",
|
|
98
|
-
locked: "locked",
|
|
99
|
-
none: "none",
|
|
100
|
-
selectHelp: "\u2191/\u2193 move \xB7 Space select \xB7 Enter confirm \xB7 Esc cancel",
|
|
101
|
-
replaceTitle: "Replace divergent selected Skills?",
|
|
102
|
-
replaceHelp: "y replace \xB7 n cancel"
|
|
103
|
-
},
|
|
104
|
-
"zh-CN": {
|
|
105
|
-
title: "RSP Skill \u7BA1\u7406\u5668",
|
|
106
|
-
target: "\u76EE\u6807",
|
|
107
|
-
defaultHeading: "\u9ED8\u8BA4\u5957\u4EF6 Skills",
|
|
108
|
-
optionalHeading: "\u53EF\u9009\u9879\u76EE Skills",
|
|
109
|
-
missing: "\u672A\u5B89\u88C5",
|
|
110
|
-
unchanged: "\u4E00\u81F4",
|
|
111
|
-
divergent: "\u6709\u5DEE\u5F02",
|
|
112
|
-
locked: "\u9501\u5B9A",
|
|
113
|
-
none: "\u65E0",
|
|
114
|
-
selectHelp: "\u2191/\u2193 \u79FB\u52A8 \xB7 \u7A7A\u683C\u9009\u62E9 \xB7 \u56DE\u8F66\u786E\u8BA4 \xB7 Esc \u53D6\u6D88",
|
|
115
|
-
replaceTitle: "\u66FF\u6362\u6240\u9009\u7684\u5DEE\u5F02 Skill\uFF1F",
|
|
116
|
-
replaceHelp: "y \u66FF\u6362 \xB7 n \u53D6\u6D88"
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
// src/skills-tui/entry.tsx
|
|
121
|
-
var signalExitCodes = { SIGHUP: 129, SIGINT: 130, SIGTERM: 143 };
|
|
122
|
-
var defaultRuntime = {
|
|
123
|
-
inspect: inspectPackagedSkillInventory,
|
|
124
|
-
render,
|
|
125
|
-
openSession: openTerminalSession,
|
|
126
|
-
host: process,
|
|
127
|
-
reportError: (message) => console.error(message)
|
|
128
|
-
};
|
|
129
|
-
async function runSkillsTui(locale, runtime = defaultRuntime) {
|
|
130
|
-
const screenReader = runtime.host.env.INK_SCREEN_READER === "true" || runtime.host.env.INK_SCREEN_READER === "1";
|
|
131
|
-
const session = runtime.openSession(runtime.host.stdout, { screenReader });
|
|
132
|
-
let instance;
|
|
133
|
-
let selection = { kind: "cancelled" };
|
|
134
|
-
const listeners = /* @__PURE__ */ new Map();
|
|
135
|
-
let cleaned = false;
|
|
136
|
-
const cleanup = () => {
|
|
137
|
-
if (cleaned)
|
|
138
|
-
return;
|
|
139
|
-
cleaned = true;
|
|
140
|
-
for (const [signal, listener] of listeners)
|
|
141
|
-
runtime.host.off(signal, listener);
|
|
142
|
-
listeners.clear();
|
|
143
|
-
instance?.unmount();
|
|
144
|
-
session.cleanup();
|
|
145
|
-
};
|
|
146
|
-
try {
|
|
147
|
-
const inventory = await runtime.inspect();
|
|
148
|
-
instance = runtime.render(ReactRuntime.createElement(SkillsApp, {
|
|
149
|
-
inventory,
|
|
150
|
-
messages: skillsCatalogs[locale],
|
|
151
|
-
onComplete: (value) => {
|
|
152
|
-
selection = value;
|
|
153
|
-
}
|
|
154
|
-
}), { exitOnCtrlC: false, patchConsole: false });
|
|
155
|
-
for (const signal of Object.keys(signalExitCodes)) {
|
|
156
|
-
const listener = () => {
|
|
157
|
-
cleanup();
|
|
158
|
-
runtime.host.exit(signalExitCodes[signal]);
|
|
159
|
-
};
|
|
160
|
-
listeners.set(signal, listener);
|
|
161
|
-
runtime.host.once(signal, listener);
|
|
162
|
-
}
|
|
163
|
-
await instance.waitUntilExit();
|
|
164
|
-
cleanup();
|
|
165
|
-
return selection;
|
|
166
|
-
} catch (error) {
|
|
167
|
-
cleanup();
|
|
168
|
-
runtime.reportError(` Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
169
|
-
return { kind: "error" };
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
export {
|
|
173
|
-
runSkillsTui
|
|
174
|
-
};
|