@oevery/rsp 4.0.0 → 4.1.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/dist/chunk-JO6NDJM4.mjs +360 -0
- package/dist/chunk-LGDCIQYL.mjs +725 -0
- package/dist/chunk-P3KO3GZW.mjs +1023 -0
- package/dist/chunk-U56XMIOO.mjs +66 -0
- package/dist/{chunk-HFSTLCCK.mjs → chunk-VDRXFKBP.mjs} +133 -1800
- package/dist/cli.mjs +275 -290
- package/dist/client-EHRWH5Q7.mjs +108 -0
- package/dist/{entry-RIJQ2X46.mjs → entry-NDASTAI5.mjs} +13 -9
- package/dist/viewer/NOTICE.txt +299 -0
- package/dist/viewer/app.css +2 -0
- package/dist/viewer/app.js +670 -0
- package/dist/viewer-build.json +1 -0
- package/dist/viewer-service.mjs +551 -0
- package/package.json +21 -3
- package/skills/rsp/SKILL.md +7 -6
- package/skills/rsp/references/architecture.md +39 -0
- package/skills/rsp/references/durable-review.md +2 -2
- package/skills/rsp/references/focus-continuation.md +6 -2
- package/skills/rsp/references/setup-repair.md +11 -6
- package/skills/rsp/references/work-model.md +12 -2
- package/skills/rsp-doc/SKILL.md +13 -10
- package/skills/rsp-doc/references/skill-authoring.md +2 -2
- package/skills/rsp-implement/SKILL.md +15 -11
- package/skills/rsp-review/SKILL.md +8 -8
- package/skills/rsp-review/references/code-review.md +11 -5
- package/skills/rsp-review/references/document-review.md +7 -9
- package/skills/rsp-shape/SKILL.md +3 -1
- package/skills/rsp-shape/references/planning.md +3 -3
- package/skills/rsp-verify/SKILL.md +8 -13
- package/skills/rsp-doc/references/artifact-lenses.md +0 -43
- package/skills/rsp-review/references/structural-review.md +0 -41
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,78 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
ARCHITECTURE_FILE,
|
|
4
|
+
PROJECT_VERSION_PATH,
|
|
5
|
+
SOURCE_INVOCATION,
|
|
6
|
+
compareVersions,
|
|
7
|
+
exactInvocation,
|
|
8
|
+
guardProjectVersion,
|
|
9
|
+
isExactVersion,
|
|
10
|
+
queryArchitecture,
|
|
11
|
+
readProjectRuntime,
|
|
12
|
+
readProjectVersion,
|
|
13
|
+
selectProjectRuntime
|
|
14
|
+
} from "./chunk-JO6NDJM4.mjs";
|
|
15
|
+
import {
|
|
4
16
|
ArchiveHistoryError,
|
|
17
|
+
GROUP_BRIEF_FILENAME,
|
|
18
|
+
GROUP_REOPEN_COMPLETION_PREFIX,
|
|
19
|
+
HISTORY_DEFAULT_LIMIT,
|
|
20
|
+
HISTORY_MAX_LIMIT,
|
|
21
|
+
IssueRelationshipError,
|
|
22
|
+
WorkRefError,
|
|
23
|
+
buildDurableReviewGuidance,
|
|
24
|
+
collectArchiveReadiness,
|
|
25
|
+
createIssueRelationship,
|
|
26
|
+
generateGroupBriefContent,
|
|
27
|
+
getDurableReviewCandidateTargets,
|
|
28
|
+
getGroupReopenEvidence,
|
|
29
|
+
hasArchivedGroupBrief,
|
|
30
|
+
historyInspectionComplete,
|
|
31
|
+
inspectArchiveHistory,
|
|
32
|
+
inspectArchiveTree,
|
|
33
|
+
inspectChangeDependencies,
|
|
34
|
+
inspectChangeGroups,
|
|
35
|
+
inspectFocusTree,
|
|
36
|
+
inspectProjectStatus,
|
|
37
|
+
inspectWorkTree,
|
|
38
|
+
normalizeExecutableWorkRef,
|
|
39
|
+
normalizeWorkRefSegment,
|
|
40
|
+
parseIssueRelationships,
|
|
41
|
+
projectDependencyForest,
|
|
42
|
+
projectSpecsDetail,
|
|
43
|
+
projectSpecsSearch,
|
|
44
|
+
projectSpecsTree,
|
|
45
|
+
queryArchiveHistory,
|
|
46
|
+
readArchiveHistoryDetail,
|
|
47
|
+
resolveArchiveDirectory,
|
|
48
|
+
resolveExecutableChange,
|
|
49
|
+
resolveFocusMarkerPath,
|
|
50
|
+
resolveWorkRef,
|
|
51
|
+
selectArchiveHistoryRecord,
|
|
52
|
+
selectArchivedGroupBrief,
|
|
53
|
+
toArchiveReadinessOutput
|
|
54
|
+
} from "./chunk-VDRXFKBP.mjs";
|
|
55
|
+
import {
|
|
56
|
+
SPECS_DEFAULT_RESULT_LIMIT,
|
|
57
|
+
SPECS_DETAIL_CONTENT_CODE_POINTS,
|
|
58
|
+
SPECS_MAX_RESULT_LIMIT,
|
|
59
|
+
SpecsQueryError,
|
|
60
|
+
cleanupEmptyParentDirs,
|
|
61
|
+
detectProjectName,
|
|
62
|
+
emitJson,
|
|
63
|
+
guardRspInitialized,
|
|
64
|
+
inspectRspInitialization,
|
|
65
|
+
inspectSpecs,
|
|
66
|
+
inspectUnsupportedRules,
|
|
67
|
+
isRecognizedGeneratedSpecsIndex,
|
|
68
|
+
isValidSpecName,
|
|
69
|
+
normalizeLogicalPath,
|
|
70
|
+
specsInspectionComplete,
|
|
71
|
+
toErrorMessage,
|
|
72
|
+
walkMarkdownFiles
|
|
73
|
+
} from "./chunk-LGDCIQYL.mjs";
|
|
74
|
+
import {
|
|
75
|
+
ARCHIVES_DIR,
|
|
5
76
|
CHANGES_DIR,
|
|
6
77
|
CHANGE_DOCUMENT_SCHEMA,
|
|
7
78
|
CONFIG_PATH,
|
|
@@ -9,11 +80,6 @@ import {
|
|
|
9
80
|
DocumentSectionCardinalityError,
|
|
10
81
|
FOCUS_DIR,
|
|
11
82
|
GROUP_BRIEF_DOCUMENT_SCHEMA,
|
|
12
|
-
GROUP_BRIEF_FILENAME,
|
|
13
|
-
GROUP_REOPEN_COMPLETION_PREFIX,
|
|
14
|
-
HISTORY_DEFAULT_LIMIT,
|
|
15
|
-
HISTORY_MAX_LIMIT,
|
|
16
|
-
IssueRelationshipError,
|
|
17
83
|
LOCK_PATH,
|
|
18
84
|
MAX_FOCUS_CAPSULE_BYTES,
|
|
19
85
|
ManagedPathError,
|
|
@@ -21,94 +87,45 @@ import {
|
|
|
21
87
|
PKG_ROOT,
|
|
22
88
|
RSP_DIR,
|
|
23
89
|
RSP_RULES_PATH,
|
|
24
|
-
SPECS_DEFAULT_RESULT_LIMIT,
|
|
25
|
-
SPECS_DETAIL_CONTENT_CODE_POINTS,
|
|
26
|
-
SPECS_MAX_RESULT_LIMIT,
|
|
27
|
-
SpecsQueryError,
|
|
28
|
-
WorkRefError,
|
|
29
90
|
appendDocumentSectionItem,
|
|
30
|
-
buildDurableReviewGuidance,
|
|
31
|
-
cleanupEmptyParentDirs,
|
|
32
91
|
clearConfigCache,
|
|
33
|
-
collectArchiveReadiness,
|
|
34
92
|
countCheckboxes,
|
|
35
|
-
createIssueRelationship,
|
|
36
93
|
detectDeltaSections,
|
|
37
|
-
detectProjectName,
|
|
38
|
-
emitJson,
|
|
39
94
|
ensureManagedFile,
|
|
40
95
|
generateConfigTemplate,
|
|
41
|
-
generateGroupBriefContent,
|
|
42
96
|
getDocumentSectionDefinitionByHeading,
|
|
43
97
|
getDocumentSectionMarkdown,
|
|
44
98
|
getDocumentSections,
|
|
45
99
|
getDocumentTitles,
|
|
46
|
-
getDurableReviewCandidateTargets,
|
|
47
|
-
getGroupReopenEvidence,
|
|
48
100
|
getVersion,
|
|
49
101
|
getVisibleBodyLines,
|
|
50
|
-
guardRspInitialized,
|
|
51
|
-
hasArchivedGroupBrief,
|
|
52
102
|
hasMeaningfulBlockers,
|
|
53
|
-
historyInspectionComplete,
|
|
54
|
-
inspectArchiveHistory,
|
|
55
|
-
inspectArchiveTree,
|
|
56
|
-
inspectChangeDependencies,
|
|
57
|
-
inspectChangeGroups,
|
|
58
|
-
inspectFocusTree,
|
|
59
103
|
inspectManagedDirectory,
|
|
60
104
|
inspectManagedFile,
|
|
61
105
|
inspectManagedFileTree,
|
|
62
|
-
inspectProjectStatus,
|
|
63
106
|
inspectRspConfig,
|
|
64
|
-
inspectRspInitialization,
|
|
65
|
-
inspectSpecs,
|
|
66
|
-
inspectUnsupportedRules,
|
|
67
|
-
inspectWorkTree,
|
|
68
|
-
isRecognizedGeneratedSpecsIndex,
|
|
69
|
-
isValidSpecName,
|
|
70
107
|
loadRspConfig,
|
|
71
108
|
markdownProse,
|
|
72
109
|
markdownText,
|
|
73
110
|
markdownTopLevelComments,
|
|
74
|
-
normalizeExecutableWorkRef,
|
|
75
|
-
normalizeLogicalPath,
|
|
76
|
-
normalizeWorkRefSegment,
|
|
77
111
|
parseFrontmatter,
|
|
78
|
-
parseIssueRelationships,
|
|
79
112
|
parseMarkdown,
|
|
80
113
|
parseRspDocument,
|
|
81
114
|
parseScenarios,
|
|
82
115
|
pc,
|
|
83
|
-
projectDependencyForest,
|
|
84
|
-
projectSpecsDetail,
|
|
85
|
-
projectSpecsSearch,
|
|
86
|
-
projectSpecsTree,
|
|
87
|
-
queryArchiveHistory,
|
|
88
|
-
readArchiveHistoryDetail,
|
|
89
116
|
renderDocumentSectionHeading,
|
|
90
117
|
renderDocumentTitle,
|
|
91
118
|
requireManagedDirectory,
|
|
92
119
|
requireManagedFile,
|
|
93
|
-
resolveArchiveDirectory,
|
|
94
120
|
resolveDecisionRecordsPath,
|
|
95
|
-
resolveExecutableChange,
|
|
96
|
-
resolveFocusMarkerPath,
|
|
97
121
|
resolveKinds,
|
|
98
122
|
resolveLanguagePolicy,
|
|
99
123
|
resolveManagedDirectoryChain,
|
|
100
124
|
resolveRequiredSections,
|
|
101
|
-
resolveWorkRef,
|
|
102
125
|
resolveWorkflowPolicy,
|
|
103
|
-
selectArchiveHistoryRecord,
|
|
104
|
-
selectArchivedGroupBrief,
|
|
105
|
-
specsInspectionComplete,
|
|
106
|
-
toArchiveReadinessOutput,
|
|
107
|
-
toErrorMessage,
|
|
108
126
|
validateDecisionRecordsFilesystemPath,
|
|
109
|
-
walkMarkdownFiles,
|
|
110
127
|
writeManagedFile
|
|
111
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-P3KO3GZW.mjs";
|
|
112
129
|
|
|
113
130
|
// src/cli.ts
|
|
114
131
|
import { resolve as resolve2 } from "path";
|
|
@@ -117,6 +134,7 @@ import { runMain } from "citty";
|
|
|
117
134
|
|
|
118
135
|
// src/cli/capabilities.ts
|
|
119
136
|
var CLI_COMMAND_CAPABILITIES = {
|
|
137
|
+
"architecture": { json: true, compact: true },
|
|
120
138
|
"config": { json: true, compact: true },
|
|
121
139
|
"status": { json: true, compact: true },
|
|
122
140
|
"show": { json: true, compact: true },
|
|
@@ -150,10 +168,49 @@ function supportsCompact(command) {
|
|
|
150
168
|
}
|
|
151
169
|
|
|
152
170
|
// src/cli/registry.ts
|
|
153
|
-
import { defineCommand as
|
|
171
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
154
172
|
|
|
155
|
-
// src/cli/commands/
|
|
173
|
+
// src/cli/commands/architecture.ts
|
|
156
174
|
import { defineCommand } from "citty";
|
|
175
|
+
var architectureCommand = defineCommand({
|
|
176
|
+
meta: { name: "architecture", description: "Read full or selected architecture structure without document bodies" },
|
|
177
|
+
args: { node: { type: "string", description: "Node and descendants" }, view: { type: "string", description: "Explicit view selection" }, ...compactJsonArgs },
|
|
178
|
+
async run({ args }) {
|
|
179
|
+
const unknown = Object.keys(args).filter((key) => !["node", "view", "json", "compact", "_"].includes(key));
|
|
180
|
+
if (unknown.length || args._.length)
|
|
181
|
+
throw new Error(`Unsupported architecture options/arguments: ${[...unknown, ...args._].join(", ")}`);
|
|
182
|
+
const result = await queryArchitecture({ node: args.node, view: args.view });
|
|
183
|
+
if (args.json) {
|
|
184
|
+
console.log(JSON.stringify(result, null, args.compact ? void 0 : 2));
|
|
185
|
+
} else {
|
|
186
|
+
console.log(`Architecture: ${result.configured ? result.source.root : "not configured"}
|
|
187
|
+
Selection: ${result.selection.kind}${result.selection.id ? ` ${result.selection.id}` : ""}`);
|
|
188
|
+
for (const node of result.nodes) {
|
|
189
|
+
console.log(`${node.boundary ? "[boundary] " : ""}${node.id}: ${node.title} \u2014 ${node.description}${node.parent ? ` (parent: ${node.parent})` : ""}`);
|
|
190
|
+
for (const binding of node.bindings) console.log(` ${binding.file}${binding.headings ? ` > ${binding.headings.join(" > ")}` : ""} (scope: ${binding.scope}; ${binding.inherited ? `inherited from ${binding.origin}` : `direct on ${binding.origin}`})`);
|
|
191
|
+
}
|
|
192
|
+
for (const edge of result.relations) console.log(`Relation ${edge.id}: ${edge.source} -> ${edge.target} [${edge.type}]: ${edge.description}`);
|
|
193
|
+
for (const view of result.views) console.log(`View ${view.id}: ${view.title} (owner: ${view.node}; include: ${view.include.join(", ")})`);
|
|
194
|
+
for (const diagnostic of result.diagnostics) console.log(`${diagnostic.code}: ${diagnostic.message}`);
|
|
195
|
+
if (result.configured)
|
|
196
|
+
console.log(`Source: ${result.source.file} @ ${result.source.revision}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
var viewCommand = defineCommand({
|
|
201
|
+
meta: { name: "view", description: "Open the shared local architecture viewer, inspect or stop its service" },
|
|
202
|
+
args: { action: { type: "positional", required: false, description: "status or stop; omit to open" }, open: { type: "boolean", default: true, description: "Open browser (use --no-open to print URL only)" } },
|
|
203
|
+
async run({ args }) {
|
|
204
|
+
const unknown = Object.keys(args).filter((key) => !["action", "open", "_"].includes(key));
|
|
205
|
+
if (unknown.length || args._.length > 1 || args.action && !args.open)
|
|
206
|
+
throw new Error("rsp view accepts only an optional status/stop action or --no-open");
|
|
207
|
+
const { runView } = await import("./client-EHRWH5Q7.mjs");
|
|
208
|
+
await runView(args.action, args.open);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// src/cli/commands/inspection.ts
|
|
213
|
+
import { defineCommand as defineCommand2 } from "citty";
|
|
157
214
|
|
|
158
215
|
// src/commands/check.ts
|
|
159
216
|
import { readFile } from "fs/promises";
|
|
@@ -255,7 +312,6 @@ function inspectChangeDocument(content, options) {
|
|
|
255
312
|
}
|
|
256
313
|
|
|
257
314
|
// src/core/focus-capsule.ts
|
|
258
|
-
import { Buffer } from "buffer";
|
|
259
315
|
import { TextDecoder } from "util";
|
|
260
316
|
var FOCUS_CAPSULE_VERSION = "<!-- rsp-focus:v1 -->";
|
|
261
317
|
var LEGACY_WARNING = "legacy focus capsule has no structured recovery projection";
|
|
@@ -273,15 +329,6 @@ function inspectFocusCapsuleBytes(bytes) {
|
|
|
273
329
|
} catch {
|
|
274
330
|
return { kind: "invalid", code: "focus_capsule_invalid_utf8", message: "focus capsule must contain valid UTF-8" };
|
|
275
331
|
}
|
|
276
|
-
if (Buffer.byteLength(content, "utf-8") > MAX_FOCUS_CAPSULE_BYTES) {
|
|
277
|
-
return {
|
|
278
|
-
kind: "invalid",
|
|
279
|
-
code: "focus_capsule_too_large",
|
|
280
|
-
message: `focus capsule exceeds ${MAX_FOCUS_CAPSULE_BYTES} UTF-8 bytes`
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
if (content.length === 0)
|
|
284
|
-
return { kind: "empty", content: "" };
|
|
285
332
|
return parseFocusCapsule(content);
|
|
286
333
|
}
|
|
287
334
|
function parseFocusCapsule(content) {
|
|
@@ -637,109 +684,8 @@ async function showConfig() {
|
|
|
637
684
|
|
|
638
685
|
// src/commands/doctor.ts
|
|
639
686
|
import { existsSync } from "fs";
|
|
640
|
-
import { readdir as readdir2, readFile as
|
|
641
|
-
import { basename, join as
|
|
642
|
-
|
|
643
|
-
// src/core/project-version.ts
|
|
644
|
-
import { readFile as readFile2, realpath } from "fs/promises";
|
|
645
|
-
import { join } from "path";
|
|
646
|
-
var PROJECT_VERSION_PATH = ".rsp/version";
|
|
647
|
-
var PROJECT_SOURCE_PATH = ".rsp/source";
|
|
648
|
-
var SOURCE_INVOCATION = "mise exec -- node dist/cli.mjs";
|
|
649
|
-
function isExactVersion(value) {
|
|
650
|
-
return /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?$/i.test(value);
|
|
651
|
-
}
|
|
652
|
-
function exactInvocation(version) {
|
|
653
|
-
if (!isExactVersion(version))
|
|
654
|
-
throw new Error(`Invalid exact RSP version: ${version}`);
|
|
655
|
-
return `npx -y @oevery/rsp@${version}`;
|
|
656
|
-
}
|
|
657
|
-
async function readProjectVersion(projectRoot = ".") {
|
|
658
|
-
const directory = inspectManagedDirectory(join(projectRoot, RSP_DIR), "RSP root", { allowMissing: true });
|
|
659
|
-
if (directory.issue)
|
|
660
|
-
throw directory.issue;
|
|
661
|
-
if (!directory.exists)
|
|
662
|
-
return null;
|
|
663
|
-
const path = join(projectRoot, PROJECT_VERSION_PATH);
|
|
664
|
-
const inspection = inspectManagedFile(path, "project CLI version", { allowMissing: true });
|
|
665
|
-
if (inspection.issue)
|
|
666
|
-
throw inspection.issue;
|
|
667
|
-
if (!inspection.exists)
|
|
668
|
-
return null;
|
|
669
|
-
const version = (await readFile2(path, "utf8")).trim();
|
|
670
|
-
if (!isExactVersion(version))
|
|
671
|
-
throw new Error(`${PROJECT_VERSION_PATH} must contain one exact package version, not a tag or range`);
|
|
672
|
-
return version;
|
|
673
|
-
}
|
|
674
|
-
async function readProjectRuntime(projectRoot = ".") {
|
|
675
|
-
const version = await readProjectVersion(projectRoot);
|
|
676
|
-
const source = inspectManagedFile(join(projectRoot, PROJECT_SOURCE_PATH), "project source selection", { allowMissing: true });
|
|
677
|
-
if (source.issue)
|
|
678
|
-
throw source.issue;
|
|
679
|
-
if (!source.exists)
|
|
680
|
-
return { mode: "package", version };
|
|
681
|
-
if (version !== null)
|
|
682
|
-
throw new Error(`${PROJECT_SOURCE_PATH} and ${PROJECT_VERSION_PATH} are mutually exclusive; explicitly select one runtime.`);
|
|
683
|
-
if ((await readFile2(join(projectRoot, PROJECT_SOURCE_PATH), "utf8")).trim() !== ".")
|
|
684
|
-
throw new Error(`${PROJECT_SOURCE_PATH} must contain . to select this RSP source checkout.`);
|
|
685
|
-
if (await realpath(projectRoot) !== await realpath(PKG_ROOT))
|
|
686
|
-
throw new Error(`Source mode requires this checkout's local CLI. Build here, then use ${SOURCE_INVOCATION} <command>; a registry package or another checkout cannot substitute.`);
|
|
687
|
-
const sourceDirectory = inspectManagedDirectory(join(projectRoot, "src"), "RSP source directory");
|
|
688
|
-
const sourceEntry = inspectManagedFile(join(projectRoot, "src/cli.ts"), "RSP source entry");
|
|
689
|
-
if (sourceDirectory.issue || sourceEntry.issue)
|
|
690
|
-
throw sourceDirectory.issue ?? sourceEntry.issue;
|
|
691
|
-
return { mode: "source" };
|
|
692
|
-
}
|
|
693
|
-
async function selectProjectRuntime(operation = "ordinary") {
|
|
694
|
-
const version = await getVersion();
|
|
695
|
-
exactInvocation(version);
|
|
696
|
-
const selected = await readProjectRuntime();
|
|
697
|
-
if (selected.mode === "source")
|
|
698
|
-
return { mode: "source", version };
|
|
699
|
-
if (selected.version && selected.version !== version && operation !== "update") {
|
|
700
|
-
throw new Error(`Project pins RSP ${selected.version}; running CLI is ${version}. Use ${exactInvocation(selected.version)} <command>. To migrate deliberately, inspect the target version and run ${exactInvocation(version)} update.`);
|
|
701
|
-
}
|
|
702
|
-
if (selected.version && operation === "update" && compareVersions(version, selected.version) < 0)
|
|
703
|
-
throw new Error(`Downgrading RSP from ${selected.version} to ${version} is unsupported. Use ${exactInvocation(selected.version)} <command>.`);
|
|
704
|
-
return { mode: "package", version };
|
|
705
|
-
}
|
|
706
|
-
function compareVersions(left, right) {
|
|
707
|
-
const parts = (value) => {
|
|
708
|
-
const [core, ...pre] = value.split("+", 1)[0].split("-");
|
|
709
|
-
return { core: core.split(".").map(BigInt), pre: pre.length ? pre.join("-").split(".") : [] };
|
|
710
|
-
};
|
|
711
|
-
const a = parts(left);
|
|
712
|
-
const b = parts(right);
|
|
713
|
-
for (let i = 0; i < 3; i++) {
|
|
714
|
-
if (a.core[i] !== b.core[i])
|
|
715
|
-
return a.core[i] > b.core[i] ? 1 : -1;
|
|
716
|
-
}
|
|
717
|
-
if (!a.pre.length || !b.pre.length)
|
|
718
|
-
return Number(!a.pre.length) - Number(!b.pre.length);
|
|
719
|
-
for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {
|
|
720
|
-
const x = a.pre[i];
|
|
721
|
-
const y = b.pre[i];
|
|
722
|
-
if (x === y)
|
|
723
|
-
continue;
|
|
724
|
-
if (x === void 0 || y === void 0)
|
|
725
|
-
return x === void 0 ? -1 : 1;
|
|
726
|
-
const nx = /^\d+$/.test(x);
|
|
727
|
-
const ny = /^\d+$/.test(y);
|
|
728
|
-
if (nx && ny)
|
|
729
|
-
return BigInt(x) > BigInt(y) ? 1 : -1;
|
|
730
|
-
if (nx !== ny)
|
|
731
|
-
return nx ? -1 : 1;
|
|
732
|
-
return x > y ? 1 : -1;
|
|
733
|
-
}
|
|
734
|
-
return 0;
|
|
735
|
-
}
|
|
736
|
-
async function guardProjectVersion(rawArgs) {
|
|
737
|
-
if (rawArgs.some((arg) => ["--help", "-h"].includes(arg)) || rawArgs.length === 1 && ["--version", "-v"].includes(rawArgs[0]))
|
|
738
|
-
return;
|
|
739
|
-
if (["doctor", "config", "update"].includes(rawArgs[0] ?? ""))
|
|
740
|
-
return;
|
|
741
|
-
await selectProjectRuntime();
|
|
742
|
-
}
|
|
687
|
+
import { readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
|
|
688
|
+
import { basename, join as join2, relative as relative2 } from "path";
|
|
743
689
|
|
|
744
690
|
// src/core/artifacts.ts
|
|
745
691
|
var RSP_AGENTS_BEGIN = "<!-- rsp:begin -->";
|
|
@@ -895,9 +841,9 @@ function changeSectionHeading(sectionId) {
|
|
|
895
841
|
const guidance = {
|
|
896
842
|
proposal: "State the intended outcome, why it matters, scope, and non-goals; do not narrate execution.",
|
|
897
843
|
spec: "Describe the contract delta and acceptance conditions, referencing affected existing Specs; do not copy the full baseline or implementation steps.",
|
|
898
|
-
design: "Explain the approach,
|
|
899
|
-
tasks: "
|
|
900
|
-
verify: "
|
|
844
|
+
design: "Explain the current approach, boundaries, and necessary tradeoffs; replace obsolete plans while preserving decision reasons and useful outcome comparisons.",
|
|
845
|
+
tasks: "Keep current checkable work and actual completion; revise or reopen invalidated tasks within authorized scope. Do not append routine attempts, fixes, retries, or check runs.",
|
|
846
|
+
verify: "Keep required/optional checks and only details needed to judge acceptance, evidence applicability, or locate necessary evidence. Do not repeat check results or retain commands, counts, or review steps merely because they occurred. Preserve applicable coverage, gaps, and distinct review/human acceptance; no run history.",
|
|
901
847
|
blockers: "Name unresolved decisions or dependencies that prevent progress; use none when there are no blockers."
|
|
902
848
|
};
|
|
903
849
|
return `${renderDocumentSectionHeading(CHANGE_DOCUMENT_SCHEMA, sectionId)}
|
|
@@ -911,14 +857,13 @@ function renderRspAgentsBlock(runtime) {
|
|
|
911
857
|
|
|
912
858
|
RSP tracks current work, stable specs, and archives under \`.rsp/\`.
|
|
913
859
|
|
|
914
|
-
|
|
860
|
+
Use supplied or confirmed paths; discover only missing paths or applicable ancestor instructions, without overlapping whole-tree scans:
|
|
915
861
|
1. Nearest \`AGENTS.md\` for project or module instructions.
|
|
916
862
|
2. Root \`CONTEXT.md\` if present, then the relevant nearest \`CONTEXT.md\` for domain language, relationships, and navigation. If a legacy \`CONTEXT-MAP.md\` remains, retain its relevant context and use Core's context-migration branch; discovery alone does not authorize migration.
|
|
917
|
-
3.
|
|
918
|
-
4. \`.rsp/focus.d
|
|
919
|
-
5. Only the relevant Specs and Decision Records under the configured authoritative path.
|
|
863
|
+
3. Establish the request's scope, authority and existing owner. A clear bounded request selecting one capability with sufficient owner facts loads that project Skill directly from \`.agents/skills/\`, without a Core or Focus preflight. For tracked work read the selected Change; for grouped work read its sibling Group Brief first. Read only relevant Specs and Decision Records under the configured authoritative path.
|
|
864
|
+
4. Load \`rsp\` at \`.agents/skills/rsp/SKILL.md\` for unresolved routing or ownership, actual cross-capability coordination, or lifecycle operations. Read \`.rsp/focus.d/\` only when needed to resolve or recover the owner; marker paths are open-work candidates and optional bounded Markdown is recovery guidance, not authority. An explicit WorkRef or the user's mentioned Change selects first; otherwise use current status and dependency evidence.
|
|
920
865
|
|
|
921
|
-
|
|
866
|
+
Direct capability invocation does not make tracked work untracked. Preserve existing Changes, required tracking prerequisites, Group dependencies, independent review and permission boundaries; missing prerequisites stop dependent work. Skills are the only workflow: hosts may discover them or read them directly, but a missing or unusable required Skill stops dependent work and must be reported as an incomplete installation. Do not use retired Rules or install implicitly.
|
|
922
867
|
Do not treat \`.rsp/specs/\` or \`.rsp/changes/\` as replacements for nearest \`AGENTS.md\` or \`CONTEXT.md\`.
|
|
923
868
|
|
|
924
869
|
${selection}
|
|
@@ -1031,8 +976,8 @@ async function checkForUpdates(current) {
|
|
|
1031
976
|
|
|
1032
977
|
// src/commands/skills.ts
|
|
1033
978
|
import { lstatSync, realpathSync } from "fs";
|
|
1034
|
-
import { chmod, copyFile, mkdir, mkdtemp, readdir, readFile as
|
|
1035
|
-
import { dirname, join
|
|
979
|
+
import { chmod, copyFile, mkdir, mkdtemp, readdir, readFile as readFile2, rename, rm } from "fs/promises";
|
|
980
|
+
import { dirname, join, relative } from "path";
|
|
1036
981
|
var DEFAULT_PACKAGED_SKILL_NAMES = [
|
|
1037
982
|
"rsp",
|
|
1038
983
|
"rsp-shape",
|
|
@@ -1057,7 +1002,7 @@ function unsupportedEntry(path, label) {
|
|
|
1057
1002
|
return new ManagedPathError(path, `unsupported entry in the ${label} tree: ${path}`);
|
|
1058
1003
|
}
|
|
1059
1004
|
async function inspectPackagedSkills(packageRoot) {
|
|
1060
|
-
const skillsRoot =
|
|
1005
|
+
const skillsRoot = join(packageRoot, "skills");
|
|
1061
1006
|
const rootInspection = inspectManagedDirectory(skillsRoot, "packaged Skills root");
|
|
1062
1007
|
if (rootInspection.issue)
|
|
1063
1008
|
throw rootInspection.issue;
|
|
@@ -1066,7 +1011,7 @@ async function inspectPackagedSkills(packageRoot) {
|
|
|
1066
1011
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1067
1012
|
if (entry.name.startsWith("."))
|
|
1068
1013
|
continue;
|
|
1069
|
-
const root =
|
|
1014
|
+
const root = join(skillsRoot, entry.name);
|
|
1070
1015
|
if (!entry.isDirectory())
|
|
1071
1016
|
throw unsupportedEntry(root, "packaged Skills");
|
|
1072
1017
|
const inspection = await inspectManagedFileTree(root, `packaged Skill ${entry.name}`, { includeHidden: true });
|
|
@@ -1118,14 +1063,14 @@ async function treesEqual(source, targetRoot) {
|
|
|
1118
1063
|
if (source.files.length !== targetFiles.length || source.files.some((path, index) => path !== targetFiles[index]))
|
|
1119
1064
|
return false;
|
|
1120
1065
|
for (const path of source.files) {
|
|
1121
|
-
const sourcePath =
|
|
1122
|
-
const targetPath =
|
|
1066
|
+
const sourcePath = join(source.root, path);
|
|
1067
|
+
const targetPath = join(targetRoot, path);
|
|
1123
1068
|
const [sourceStats, targetStats] = [lstatSync(sourcePath), lstatSync(targetPath)];
|
|
1124
1069
|
if (!sourceStats.isFile() || !targetStats.isFile())
|
|
1125
1070
|
throw unsupportedEntry(!sourceStats.isFile() ? sourcePath : targetPath, `Skill ${source.name}`);
|
|
1126
1071
|
if ((sourceStats.mode & 511) !== (targetStats.mode & 511))
|
|
1127
1072
|
return false;
|
|
1128
|
-
const [sourceContent, targetContent] = await Promise.all([
|
|
1073
|
+
const [sourceContent, targetContent] = await Promise.all([readFile2(sourcePath), readFile2(targetPath)]);
|
|
1129
1074
|
if (!sourceContent.equals(targetContent))
|
|
1130
1075
|
return false;
|
|
1131
1076
|
}
|
|
@@ -1139,14 +1084,14 @@ async function inspectPackagedSkillInventory(options = {}) {
|
|
|
1139
1084
|
const missingDefaults = DEFAULT_PACKAGED_SKILL_NAMES.filter((name) => !packagedNames.has(name));
|
|
1140
1085
|
if (missingDefaults.length > 0)
|
|
1141
1086
|
throw new Error(`packaged Skills missing default Skills: ${missingDefaults.join(", ")}`);
|
|
1142
|
-
const manifest = JSON.parse(await
|
|
1087
|
+
const manifest = JSON.parse(await readFile2(join(packageRoot, "package.json"), "utf8"));
|
|
1143
1088
|
if (typeof manifest.name !== "string" || typeof manifest.version !== "string")
|
|
1144
1089
|
throw new Error("package.json must contain string name and version fields");
|
|
1145
1090
|
const targetRoot = resolveManagedDirectoryChain(projectRoot, [".agents", "skills"], "project Skills root");
|
|
1146
1091
|
const defaults = new Set(DEFAULT_PACKAGED_SKILL_NAMES);
|
|
1147
1092
|
const skills = [];
|
|
1148
1093
|
for (const skill of packagedSkills) {
|
|
1149
|
-
const target =
|
|
1094
|
+
const target = join(targetRoot, skill.name);
|
|
1150
1095
|
const targetStatus = inspectInventoryTarget(target);
|
|
1151
1096
|
skills.push({
|
|
1152
1097
|
name: skill.name,
|
|
@@ -1154,7 +1099,7 @@ async function inspectPackagedSkillInventory(options = {}) {
|
|
|
1154
1099
|
status: targetStatus === "missing" ? "missing" : targetStatus === "divergent" ? isCanonicalSourceProjection(skill.root, target) ? "unchanged" : "divergent" : await treesEqual(skill, target) ? "unchanged" : "divergent"
|
|
1155
1100
|
});
|
|
1156
1101
|
}
|
|
1157
|
-
const retired = RETIRED_PACKAGED_SKILL_NAMES.filter((name) => inspectInventoryTarget(
|
|
1102
|
+
const retired = RETIRED_PACKAGED_SKILL_NAMES.filter((name) => inspectInventoryTarget(join(targetRoot, name)) !== "missing");
|
|
1158
1103
|
return {
|
|
1159
1104
|
retired,
|
|
1160
1105
|
package: { name: manifest.name, version: manifest.version },
|
|
@@ -1165,8 +1110,8 @@ async function inspectPackagedSkillInventory(options = {}) {
|
|
|
1165
1110
|
async function copySkillTree(source, targetRoot) {
|
|
1166
1111
|
await mkdir(targetRoot, { recursive: true });
|
|
1167
1112
|
for (const path of source.files) {
|
|
1168
|
-
const sourcePath =
|
|
1169
|
-
const targetPath =
|
|
1113
|
+
const sourcePath = join(source.root, path);
|
|
1114
|
+
const targetPath = join(targetRoot, path);
|
|
1170
1115
|
const stats = lstatSync(sourcePath);
|
|
1171
1116
|
if (!stats.isFile())
|
|
1172
1117
|
throw unsupportedEntry(sourcePath, `packaged Skill ${source.name}`);
|
|
@@ -1189,7 +1134,7 @@ async function updatePackagedSkills(options = {}) {
|
|
|
1189
1134
|
const targetRoot = resolveManagedDirectoryChain(projectRoot, [".agents", "skills"], "project Skills root");
|
|
1190
1135
|
const result = { installed: [], removed: [], unchanged: [], replaced: [] };
|
|
1191
1136
|
for (const skill of skills) {
|
|
1192
|
-
const target =
|
|
1137
|
+
const target = join(targetRoot, skill.name);
|
|
1193
1138
|
if (isCanonicalSourceProjection(skill.root, target))
|
|
1194
1139
|
result.unchanged.push(skill.name);
|
|
1195
1140
|
else if (inspectTarget(target, skill.name) === "missing")
|
|
@@ -1200,35 +1145,35 @@ async function updatePackagedSkills(options = {}) {
|
|
|
1200
1145
|
result.replaced.push(skill.name);
|
|
1201
1146
|
}
|
|
1202
1147
|
for (const name of RETIRED_PACKAGED_SKILL_NAMES) {
|
|
1203
|
-
if (inspectTarget(
|
|
1148
|
+
if (inspectTarget(join(targetRoot, name), name) !== "missing")
|
|
1204
1149
|
result.removed.push(name);
|
|
1205
1150
|
}
|
|
1206
1151
|
if (options.dryRun || result.installed.length + result.replaced.length + result.removed.length === 0)
|
|
1207
1152
|
return result;
|
|
1208
1153
|
await mkdir(targetRoot, { recursive: true });
|
|
1209
1154
|
resolveManagedDirectoryChain(projectRoot, [".agents", "skills"], "project Skills root");
|
|
1210
|
-
const staging = await mkdtemp(
|
|
1155
|
+
const staging = await mkdtemp(join(targetRoot, ".rsp-skills-"));
|
|
1211
1156
|
const completed = [];
|
|
1212
1157
|
let failure2;
|
|
1213
1158
|
try {
|
|
1214
1159
|
for (const skill of skills) {
|
|
1215
1160
|
if (!result.unchanged.includes(skill.name))
|
|
1216
|
-
await copySkillTree(skill,
|
|
1161
|
+
await copySkillTree(skill, join(staging, skill.name));
|
|
1217
1162
|
}
|
|
1218
1163
|
for (const name of result.removed) {
|
|
1219
1164
|
resolveManagedDirectoryChain(projectRoot, [".agents", "skills"], "project Skills root");
|
|
1220
|
-
inspectTarget(
|
|
1221
|
-
await rm(
|
|
1165
|
+
inspectTarget(join(targetRoot, name), name);
|
|
1166
|
+
await rm(join(targetRoot, name), { recursive: true, force: true });
|
|
1222
1167
|
completed.push(`removed ${name}`);
|
|
1223
1168
|
}
|
|
1224
1169
|
for (const skill of skills) {
|
|
1225
1170
|
if (result.unchanged.includes(skill.name))
|
|
1226
1171
|
continue;
|
|
1227
1172
|
resolveManagedDirectoryChain(projectRoot, [".agents", "skills"], "project Skills root");
|
|
1228
|
-
const target =
|
|
1173
|
+
const target = join(targetRoot, skill.name);
|
|
1229
1174
|
inspectTarget(target, skill.name);
|
|
1230
1175
|
await rm(target, { recursive: true, force: true });
|
|
1231
|
-
await renamePath(
|
|
1176
|
+
await renamePath(join(staging, skill.name), target);
|
|
1232
1177
|
completed.push(`installed ${skill.name}`);
|
|
1233
1178
|
}
|
|
1234
1179
|
} catch (error) {
|
|
@@ -1275,14 +1220,16 @@ async function runDoctor(options = {}) {
|
|
|
1275
1220
|
hint: "Use Core context migration. Retain relevant legacy context until an authorized merge updates CONTEXT.md and active references; update does not migrate or delete it."
|
|
1276
1221
|
});
|
|
1277
1222
|
}
|
|
1278
|
-
await checkSpecsTree(checks, reportRuntime);
|
|
1279
1223
|
await checkArchiveNaming(checks);
|
|
1280
|
-
const
|
|
1281
|
-
if (
|
|
1224
|
+
const configValid = await checkConfigSemantics(checks, reportRuntime);
|
|
1225
|
+
if (configValid) {
|
|
1226
|
+
await checkSpecsTree(checks, reportRuntime);
|
|
1282
1227
|
await checkDecisionRecordsDirectory(checks, reportRuntime);
|
|
1283
1228
|
await checkInactiveDefaultDecisionRecords(checks, reportRuntime);
|
|
1284
1229
|
}
|
|
1285
1230
|
await checkActiveChangeConsistency(checks);
|
|
1231
|
+
if (configValid)
|
|
1232
|
+
await checkArchitecture(checks);
|
|
1286
1233
|
const result = {
|
|
1287
1234
|
command: "doctor",
|
|
1288
1235
|
ok: checks.every((check) => check.status !== "issue"),
|
|
@@ -1294,6 +1241,42 @@ async function runDoctor(options = {}) {
|
|
|
1294
1241
|
};
|
|
1295
1242
|
return result;
|
|
1296
1243
|
}
|
|
1244
|
+
async function checkArchitecture(checks) {
|
|
1245
|
+
try {
|
|
1246
|
+
const architecture = await queryArchitecture();
|
|
1247
|
+
if (!architecture.configured) {
|
|
1248
|
+
checks.push({
|
|
1249
|
+
status: "info",
|
|
1250
|
+
code: "architecture_not_configured",
|
|
1251
|
+
label: ARCHITECTURE_FILE,
|
|
1252
|
+
message: "Optional architecture model is not configured."
|
|
1253
|
+
});
|
|
1254
|
+
} else if (architecture.diagnostics.length) {
|
|
1255
|
+
for (const diagnostic of architecture.diagnostics) {
|
|
1256
|
+
checks.push({
|
|
1257
|
+
status: "issue",
|
|
1258
|
+
code: `architecture_${diagnostic.code}`,
|
|
1259
|
+
label: `architecture binding: ${diagnostic.file ?? ARCHITECTURE_FILE}`,
|
|
1260
|
+
message: `${diagnostic.file ?? ARCHITECTURE_FILE}: ${diagnostic.message}`
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
} else {
|
|
1264
|
+
checks.push({
|
|
1265
|
+
status: "ok",
|
|
1266
|
+
code: "architecture_valid",
|
|
1267
|
+
label: "architecture structure and document bindings",
|
|
1268
|
+
message: `${architecture.nodes.length} nodes, ${architecture.relations.length} relations, ${architecture.views.length} views. Structural and reference validity does not establish semantic agreement with implementation.`
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
} catch (error) {
|
|
1272
|
+
checks.push({
|
|
1273
|
+
status: "issue",
|
|
1274
|
+
code: "architecture_inspection_failed",
|
|
1275
|
+
label: ARCHITECTURE_FILE,
|
|
1276
|
+
message: `${ARCHITECTURE_FILE}: ${toErrorMessage(error)}`
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1297
1280
|
async function reportProjectRuntime(checks) {
|
|
1298
1281
|
let sourceMode = false;
|
|
1299
1282
|
try {
|
|
@@ -1389,7 +1372,7 @@ async function checkAgents(checks, reportRuntime) {
|
|
|
1389
1372
|
}
|
|
1390
1373
|
let agents;
|
|
1391
1374
|
try {
|
|
1392
|
-
agents = await
|
|
1375
|
+
agents = await readFile3("AGENTS.md", "utf-8");
|
|
1393
1376
|
} catch (error) {
|
|
1394
1377
|
reportRuntime({
|
|
1395
1378
|
code: "agents_read_failed",
|
|
@@ -1448,7 +1431,7 @@ async function checkSpecsTree(checks, reportRuntime) {
|
|
|
1448
1431
|
}
|
|
1449
1432
|
}
|
|
1450
1433
|
async function checkArchiveNaming(checks) {
|
|
1451
|
-
const archivesDir =
|
|
1434
|
+
const archivesDir = join2(RSP_DIR, "archives");
|
|
1452
1435
|
const inspection = await inspectArchiveTree({ archivesDir });
|
|
1453
1436
|
if (inspection.diagnostics.length > 0) {
|
|
1454
1437
|
checks.push({
|
|
@@ -1812,7 +1795,7 @@ function emptyCollectionSummary() {
|
|
|
1812
1795
|
}
|
|
1813
1796
|
|
|
1814
1797
|
// src/commands/ready.ts
|
|
1815
|
-
import { readFile as
|
|
1798
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1816
1799
|
function readyError(name, error, kind) {
|
|
1817
1800
|
return { command: "ready", ok: false, change: name || null, path: null, warnings: [], runtime: [], error, kind };
|
|
1818
1801
|
}
|
|
@@ -1832,7 +1815,7 @@ async function showReady(name) {
|
|
|
1832
1815
|
const runtime = [];
|
|
1833
1816
|
let content;
|
|
1834
1817
|
try {
|
|
1835
|
-
content = await
|
|
1818
|
+
content = await readFile4(srcPath, "utf-8");
|
|
1836
1819
|
} catch {
|
|
1837
1820
|
return readyError(name, { code: "change_read_failed", message: `unable to read .rsp/changes/${name}.md` }, "read");
|
|
1838
1821
|
}
|
|
@@ -1876,7 +1859,7 @@ async function showReady(name) {
|
|
|
1876
1859
|
}
|
|
1877
1860
|
|
|
1878
1861
|
// src/commands/show.ts
|
|
1879
|
-
import { readFile as
|
|
1862
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
1880
1863
|
function showError(error, runtime = []) {
|
|
1881
1864
|
const nextActions = error.code === "no_focused_change" ? ["Run: rsp status", "Run: rsp focus <name>", "Or run: rsp create <name>"] : [];
|
|
1882
1865
|
return { command: "show", ok: false, change: null, contextPaths: [], runtime, nextActions, error };
|
|
@@ -1920,7 +1903,7 @@ async function showChange(nameOrFocused, options = {}) {
|
|
|
1920
1903
|
const srcPath = workRef.path;
|
|
1921
1904
|
let content;
|
|
1922
1905
|
try {
|
|
1923
|
-
content = await
|
|
1906
|
+
content = await readFile5(srcPath, "utf-8");
|
|
1924
1907
|
} catch {
|
|
1925
1908
|
return showError({ code: "change_read_failed", message: `unable to read .rsp/changes/${name}.md` }, runtime);
|
|
1926
1909
|
}
|
|
@@ -1949,7 +1932,7 @@ async function showChange(nameOrFocused, options = {}) {
|
|
|
1949
1932
|
const markerPath = focusMarkers.get(name);
|
|
1950
1933
|
let markerContent;
|
|
1951
1934
|
try {
|
|
1952
|
-
markerContent = await
|
|
1935
|
+
markerContent = await readFile5(markerPath);
|
|
1953
1936
|
} catch (error) {
|
|
1954
1937
|
reportRuntime({
|
|
1955
1938
|
code: "focus_marker_read_failed",
|
|
@@ -2850,7 +2833,7 @@ function formatArchiveReady(value) {
|
|
|
2850
2833
|
}
|
|
2851
2834
|
|
|
2852
2835
|
// src/cli/commands/inspection.ts
|
|
2853
|
-
var configCommand =
|
|
2836
|
+
var configCommand = defineCommand2({
|
|
2854
2837
|
meta: {
|
|
2855
2838
|
name: "config",
|
|
2856
2839
|
description: "Inspect effective project configuration without scanning work state"
|
|
@@ -2866,7 +2849,7 @@ var configCommand = defineCommand({
|
|
|
2866
2849
|
}, args);
|
|
2867
2850
|
}
|
|
2868
2851
|
});
|
|
2869
|
-
var specsCommand =
|
|
2852
|
+
var specsCommand = defineCommand2({
|
|
2870
2853
|
meta: {
|
|
2871
2854
|
name: "specs",
|
|
2872
2855
|
description: "Inspect current Specs and Decision Records or run bounded literal search"
|
|
@@ -2905,7 +2888,7 @@ var specsCommand = defineCommand({
|
|
|
2905
2888
|
}, args);
|
|
2906
2889
|
}
|
|
2907
2890
|
});
|
|
2908
|
-
var statusCommand =
|
|
2891
|
+
var statusCommand = defineCommand2({
|
|
2909
2892
|
meta: {
|
|
2910
2893
|
name: "status",
|
|
2911
2894
|
description: "Show project status summary with current focus information"
|
|
@@ -2953,7 +2936,7 @@ var statusCommand = defineCommand({
|
|
|
2953
2936
|
}, args);
|
|
2954
2937
|
}
|
|
2955
2938
|
});
|
|
2956
|
-
var checkCommand =
|
|
2939
|
+
var checkCommand = defineCommand2({
|
|
2957
2940
|
meta: {
|
|
2958
2941
|
name: "check",
|
|
2959
2942
|
description: "Validate changes (frontmatter, sections, template text, deltas, scenarios)"
|
|
@@ -2979,7 +2962,7 @@ var checkCommand = defineCommand({
|
|
|
2979
2962
|
}, args);
|
|
2980
2963
|
}
|
|
2981
2964
|
});
|
|
2982
|
-
var doctorCommand =
|
|
2965
|
+
var doctorCommand = defineCommand2({
|
|
2983
2966
|
meta: {
|
|
2984
2967
|
name: "doctor",
|
|
2985
2968
|
description: "Check RSP setup health and common integration issues"
|
|
@@ -3007,7 +2990,7 @@ var doctorCommand = defineCommand({
|
|
|
3007
2990
|
}, args);
|
|
3008
2991
|
}
|
|
3009
2992
|
});
|
|
3010
|
-
var readyCommand =
|
|
2993
|
+
var readyCommand = defineCommand2({
|
|
3011
2994
|
meta: {
|
|
3012
2995
|
name: "ready",
|
|
3013
2996
|
description: "Preview archive readiness for a change without moving it"
|
|
@@ -3033,7 +3016,7 @@ var readyCommand = defineCommand({
|
|
|
3033
3016
|
}, args);
|
|
3034
3017
|
}
|
|
3035
3018
|
});
|
|
3036
|
-
var showCommand =
|
|
3019
|
+
var showCommand = defineCommand2({
|
|
3037
3020
|
meta: {
|
|
3038
3021
|
name: "show",
|
|
3039
3022
|
description: "Show change context with readiness signals and relevant paths"
|
|
@@ -3064,7 +3047,7 @@ var showCommand = defineCommand({
|
|
|
3064
3047
|
}, args);
|
|
3065
3048
|
}
|
|
3066
3049
|
});
|
|
3067
|
-
var historyCommand =
|
|
3050
|
+
var historyCommand = defineCommand2({
|
|
3068
3051
|
meta: {
|
|
3069
3052
|
name: "history",
|
|
3070
3053
|
description: "Query bounded archived Change summaries or one exact archived WorkRef"
|
|
@@ -3130,14 +3113,14 @@ var inspectionCommands = {
|
|
|
3130
3113
|
};
|
|
3131
3114
|
|
|
3132
3115
|
// src/cli/commands/setup.ts
|
|
3133
|
-
import { defineCommand as
|
|
3116
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
3134
3117
|
|
|
3135
3118
|
// src/commands/add-spec.ts
|
|
3136
3119
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
|
|
3137
|
-
import { join as
|
|
3120
|
+
import { join as join3 } from "path";
|
|
3138
3121
|
|
|
3139
3122
|
// src/core/lock.ts
|
|
3140
|
-
import { mkdir as mkdir2, readFile as
|
|
3123
|
+
import { mkdir as mkdir2, readFile as readFile6, unlink, writeFile } from "fs/promises";
|
|
3141
3124
|
async function withRspLock(operation, fn) {
|
|
3142
3125
|
requireManagedDirectory(RSP_DIR, "RSP root", { allowMissing: true });
|
|
3143
3126
|
await mkdir2(RSP_DIR, { recursive: true });
|
|
@@ -3162,7 +3145,7 @@ ${(/* @__PURE__ */ new Date()).toISOString()}`, { flag: "wx" });
|
|
|
3162
3145
|
}
|
|
3163
3146
|
async function handleStaleLock(operation) {
|
|
3164
3147
|
try {
|
|
3165
|
-
const content = await
|
|
3148
|
+
const content = await readFile6(LOCK_PATH, "utf-8");
|
|
3166
3149
|
const lines = content.trim().split("\n");
|
|
3167
3150
|
const pid = Number(lines[0]);
|
|
3168
3151
|
if (pid && Number.isFinite(pid)) {
|
|
@@ -3220,10 +3203,10 @@ async function addSpec(name, projectName = "project") {
|
|
|
3220
3203
|
return { ok: false, message: `.rsp/config.yaml could not be parsed: ${toErrorMessage(error)}` };
|
|
3221
3204
|
}
|
|
3222
3205
|
return withRspLock("add-spec", async () => {
|
|
3223
|
-
const specsDir =
|
|
3206
|
+
const specsDir = join3(RSP_DIR, "specs");
|
|
3224
3207
|
const segments = name.split("/");
|
|
3225
3208
|
const parent = resolveManagedDirectoryChain(specsDir, segments.slice(0, -1), "spec path");
|
|
3226
|
-
const path =
|
|
3209
|
+
const path = join3(parent, `${segments.at(-1)}.md`);
|
|
3227
3210
|
const inspection = await inspectManagedFileTree(specsDir, "Specs");
|
|
3228
3211
|
if (inspection.issues.length > 0)
|
|
3229
3212
|
throw inspection.issues[0];
|
|
@@ -3243,30 +3226,30 @@ async function addSpec(name, projectName = "project") {
|
|
|
3243
3226
|
|
|
3244
3227
|
// src/commands/init.ts
|
|
3245
3228
|
import { existsSync as existsSync2 } from "fs";
|
|
3246
|
-
import { mkdir as mkdir4, readdir as readdir3, readFile as
|
|
3247
|
-
import { join as
|
|
3229
|
+
import { mkdir as mkdir4, readdir as readdir3, readFile as readFile8 } from "fs/promises";
|
|
3230
|
+
import { join as join5 } from "path";
|
|
3248
3231
|
|
|
3249
3232
|
// src/core/setup-files.ts
|
|
3250
3233
|
import { constants } from "fs";
|
|
3251
|
-
import { access, chmod as chmod2, link, mkdtemp as mkdtemp2, readFile as
|
|
3252
|
-
import { dirname as dirname2, join as
|
|
3234
|
+
import { access, chmod as chmod2, link, mkdtemp as mkdtemp2, readFile as readFile7, rename as rename2, rm as rm2, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
3235
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
3253
3236
|
async function updateSetupFile(path, render) {
|
|
3254
3237
|
const exists = requireManagedFile(path, "setup file", { allowMissing: true });
|
|
3255
|
-
const current = exists ? await
|
|
3238
|
+
const current = exists ? await readFile7(path, "utf8") : null;
|
|
3256
3239
|
const next = render(current);
|
|
3257
3240
|
if (next === current)
|
|
3258
3241
|
return false;
|
|
3259
3242
|
if (exists)
|
|
3260
3243
|
await access(path, constants.W_OK);
|
|
3261
|
-
const temporary = await mkdtemp2(
|
|
3262
|
-
const prepared =
|
|
3244
|
+
const temporary = await mkdtemp2(join4(dirname2(path), ".rsp-write-"));
|
|
3245
|
+
const prepared = join4(temporary, "next");
|
|
3263
3246
|
let failure2;
|
|
3264
3247
|
try {
|
|
3265
3248
|
await writeFile3(prepared, next, { flag: "wx" });
|
|
3266
3249
|
if (exists)
|
|
3267
3250
|
await chmod2(prepared, (await stat2(path)).mode & 511);
|
|
3268
3251
|
const stillExists = requireManagedFile(path, "setup file", { allowMissing: true });
|
|
3269
|
-
if (stillExists !== exists || stillExists && await
|
|
3252
|
+
if (stillExists !== exists || stillExists && await readFile7(path, "utf8") !== current)
|
|
3270
3253
|
throw new Error(`Setup file changed during update: ${path}`);
|
|
3271
3254
|
if (exists)
|
|
3272
3255
|
await rename2(prepared, path);
|
|
@@ -3307,7 +3290,7 @@ async function initProject(args = {}) {
|
|
|
3307
3290
|
const pathIssue = await validateDecisionRecordsFilesystemPath(decisionRecordsPath);
|
|
3308
3291
|
if (pathIssue)
|
|
3309
3292
|
throw new Error(pathIssue);
|
|
3310
|
-
const dirs = [
|
|
3293
|
+
const dirs = [join5(RSP_DIR, "specs"), CHANGES_DIR, FOCUS_DIR, join5(RSP_DIR, "archives"), decisionRecordsPath];
|
|
3311
3294
|
for (const path of dirs)
|
|
3312
3295
|
resolveManagedDirectoryChain(process.cwd(), path.split("/"), "project directory");
|
|
3313
3296
|
const completed = [];
|
|
@@ -3327,12 +3310,12 @@ async function initProject(args = {}) {
|
|
|
3327
3310
|
}
|
|
3328
3311
|
await ensureFile(CONFIG_PATH, generateConfigTemplate());
|
|
3329
3312
|
clearConfigCache();
|
|
3330
|
-
await ensureFile(
|
|
3313
|
+
await ensureFile(join5(RSP_DIR, "specs/design.md"), generateDesignContent(projectName));
|
|
3331
3314
|
if ((await readdir3(decisionRecordsPath)).length === 0)
|
|
3332
|
-
await ensureFile(
|
|
3333
|
-
await ensureFile(
|
|
3334
|
-
await ensureFile(
|
|
3335
|
-
await ensureFile(
|
|
3315
|
+
await ensureFile(join5(decisionRecordsPath, ".gitkeep"), "");
|
|
3316
|
+
await ensureFile(join5(CHANGES_DIR, ".gitkeep"), "");
|
|
3317
|
+
await ensureFile(join5(FOCUS_DIR, ".gitkeep"), "");
|
|
3318
|
+
await ensureFile(join5(RSP_DIR, ".gitignore"), "# Transient files that should not be committed\n.lock\n");
|
|
3336
3319
|
if (args.withProjectSetup) {
|
|
3337
3320
|
const work = resolveWorkRef("project-setup", { executable: true });
|
|
3338
3321
|
await ensureFile(work.path, generateChangeContent("project-setup"));
|
|
@@ -3347,7 +3330,7 @@ async function initProject(args = {}) {
|
|
|
3347
3330
|
if (runtime.mode === "package" && await updateSetupFile(PROJECT_VERSION_PATH, () => `${version}
|
|
3348
3331
|
`))
|
|
3349
3332
|
completed.push(`pinned ${version}`);
|
|
3350
|
-
return { ok: true, isNew, created: completed.length > 0, decisionRecordsPath, withProjectSetup: Boolean(args.withProjectSetup), agentsUpdated, skills, managedAgents: mode === "print" ? await
|
|
3333
|
+
return { ok: true, isNew, created: completed.length > 0, decisionRecordsPath, withProjectSetup: Boolean(args.withProjectSetup), agentsUpdated, skills, managedAgents: mode === "print" ? await readFile8("AGENTS.md", "utf8") : void 0 };
|
|
3351
3334
|
} catch (error) {
|
|
3352
3335
|
throw new Error(`Init stopped; completed: ${completed.join(", ") || "none"}. No rollback was attempted. Fix the cause and rerun init with the same target CLI. ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
3353
3336
|
}
|
|
@@ -3356,17 +3339,17 @@ async function initProject(args = {}) {
|
|
|
3356
3339
|
|
|
3357
3340
|
// src/commands/update.ts
|
|
3358
3341
|
import { existsSync as existsSync3 } from "fs";
|
|
3359
|
-
import { readFile as
|
|
3360
|
-
import { join as
|
|
3342
|
+
import { readFile as readFile11, unlink as unlink3 } from "fs/promises";
|
|
3343
|
+
import { join as join7 } from "path";
|
|
3361
3344
|
|
|
3362
3345
|
// src/commands/archive-index-migration.ts
|
|
3363
|
-
import { readFile as
|
|
3364
|
-
import { join as
|
|
3346
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
3347
|
+
import { join as join6 } from "path";
|
|
3365
3348
|
async function isLegacyArchiveIndex() {
|
|
3366
|
-
const path =
|
|
3349
|
+
const path = join6(ARCHIVES_DIR, "INDEX.md");
|
|
3367
3350
|
if (!requireManagedFile(path, "legacy archive index", { allowMissing: true }))
|
|
3368
3351
|
return false;
|
|
3369
|
-
return isLegacyArchiveIndexContent(await
|
|
3352
|
+
return isLegacyArchiveIndexContent(await readFile9(path, "utf-8"));
|
|
3370
3353
|
}
|
|
3371
3354
|
function isLegacyArchiveIndexContent(content) {
|
|
3372
3355
|
try {
|
|
@@ -3378,7 +3361,7 @@ function isLegacyArchiveIndexContent(content) {
|
|
|
3378
3361
|
}
|
|
3379
3362
|
|
|
3380
3363
|
// src/commands/specs-index-migration.ts
|
|
3381
|
-
import { readFile as
|
|
3364
|
+
import { readFile as readFile10, unlink as unlink2 } from "fs/promises";
|
|
3382
3365
|
import { dirname as dirname3, resolve } from "path";
|
|
3383
3366
|
async function inspectGeneratedSpecsIndexMigration(cwd = process.cwd()) {
|
|
3384
3367
|
const inspection = await inspectSpecs({ cwd, allowMissingDecisionRecords: true });
|
|
@@ -3393,7 +3376,7 @@ async function removeGeneratedSpecsIndex(path) {
|
|
|
3393
3376
|
resolveManagedDirectoryChain(process.cwd(), dirname3(path).split("/"), "Specs parent");
|
|
3394
3377
|
if (!requireManagedFile(path, "generated Specs index", { allowMissing: true }))
|
|
3395
3378
|
return false;
|
|
3396
|
-
if (!isRecognizedGeneratedSpecsIndex(await
|
|
3379
|
+
if (!isRecognizedGeneratedSpecsIndex(await readFile10(resolve(path), "utf8"), path))
|
|
3397
3380
|
throw new Error(`Specs index is no longer generated; left untouched: ${path}`);
|
|
3398
3381
|
await unlink2(path);
|
|
3399
3382
|
return true;
|
|
@@ -3418,12 +3401,12 @@ async function updateProject(options = {}) {
|
|
|
3418
3401
|
return { ok: false, actions: [], updated: false, events: [{ type: "migration-diagnostics", diagnostics: rules.diagnostics }], migration };
|
|
3419
3402
|
requireManagedFile("AGENTS.md", "AGENTS.md", { allowMissing: true });
|
|
3420
3403
|
resolveManagedDirectoryChain(process.cwd(), [RSP_DIR, "archives"], "archive parent");
|
|
3421
|
-
requireManagedFile(
|
|
3404
|
+
requireManagedFile(join7(RSP_DIR, "archives/INDEX.md"), "archive index", { allowMissing: true });
|
|
3422
3405
|
const plan = await inspectGeneratedSpecsIndexMigration();
|
|
3423
3406
|
if (!plan.inspectionComplete || plan.ownerControlled.length)
|
|
3424
3407
|
throw new Error(`Generated Specs-index migration requires owner review before update: ${[...plan.ownerControlled, ...plan.diagnostics].join("; ")}`);
|
|
3425
3408
|
const projectName = await detectProjectName();
|
|
3426
|
-
const agents = requireManagedFile("AGENTS.md", "AGENTS.md", { allowMissing: true }) ? await
|
|
3409
|
+
const agents = requireManagedFile("AGENTS.md", "AGENTS.md", { allowMissing: true }) ? await readFile11("AGENTS.md", "utf8") : null;
|
|
3427
3410
|
const refreshAgents = (current) => upsertRspAgentsBlock(current ?? `# ${projectName}
|
|
3428
3411
|
|
|
3429
3412
|
`, runtime).content;
|
|
@@ -3464,7 +3447,7 @@ async function updateProject(options = {}) {
|
|
|
3464
3447
|
record(`generated Specs index removed: ${path}`);
|
|
3465
3448
|
}
|
|
3466
3449
|
if (await isLegacyArchiveIndex()) {
|
|
3467
|
-
await unlink3(
|
|
3450
|
+
await unlink3(join7(RSP_DIR, "archives/INDEX.md"));
|
|
3468
3451
|
record("legacy Archive Index removed");
|
|
3469
3452
|
}
|
|
3470
3453
|
if (await updateSetupFile("AGENTS.md", refreshAgents))
|
|
@@ -3578,7 +3561,7 @@ function presentUpdate(result, dryRun = false) {
|
|
|
3578
3561
|
}
|
|
3579
3562
|
|
|
3580
3563
|
// src/cli/commands/setup.ts
|
|
3581
|
-
var initCommand =
|
|
3564
|
+
var initCommand = defineCommand3({
|
|
3582
3565
|
meta: {
|
|
3583
3566
|
name: "init",
|
|
3584
3567
|
description: "Scaffold .rsp/ and ensure AGENTS.md contains the RSP entry block"
|
|
@@ -3606,7 +3589,7 @@ var initCommand = defineCommand2({
|
|
|
3606
3589
|
}, args);
|
|
3607
3590
|
}
|
|
3608
3591
|
});
|
|
3609
|
-
var addSpecCommand =
|
|
3592
|
+
var addSpecCommand = defineCommand3({
|
|
3610
3593
|
meta: {
|
|
3611
3594
|
name: "spec",
|
|
3612
3595
|
description: "Create .rsp/specs/<name>.md for direct Specs queries"
|
|
@@ -3626,7 +3609,7 @@ var addSpecCommand = defineCommand2({
|
|
|
3626
3609
|
}, args);
|
|
3627
3610
|
}
|
|
3628
3611
|
});
|
|
3629
|
-
var addCommand =
|
|
3612
|
+
var addCommand = defineCommand3({
|
|
3630
3613
|
meta: {
|
|
3631
3614
|
name: "add",
|
|
3632
3615
|
description: "Add an optional durable spec file"
|
|
@@ -3635,7 +3618,7 @@ var addCommand = defineCommand2({
|
|
|
3635
3618
|
spec: addSpecCommand
|
|
3636
3619
|
}
|
|
3637
3620
|
});
|
|
3638
|
-
var updateCommand =
|
|
3621
|
+
var updateCommand = defineCommand3({
|
|
3639
3622
|
meta: {
|
|
3640
3623
|
name: "update",
|
|
3641
3624
|
description: "Update the complete pinned environment and remove retired managed assets"
|
|
@@ -3660,7 +3643,7 @@ var setupCommands = {
|
|
|
3660
3643
|
};
|
|
3661
3644
|
|
|
3662
3645
|
// src/cli/commands/skills.ts
|
|
3663
|
-
import { defineCommand as
|
|
3646
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
3664
3647
|
|
|
3665
3648
|
// src/cli/presenters/skills.ts
|
|
3666
3649
|
function presentPackagedSkillInventory(inventory, json = false) {
|
|
@@ -3678,7 +3661,7 @@ function presentPackagedSkillInventory(inventory, json = false) {
|
|
|
3678
3661
|
}
|
|
3679
3662
|
|
|
3680
3663
|
// src/cli/commands/skills.ts
|
|
3681
|
-
var skillsListCommand =
|
|
3664
|
+
var skillsListCommand = defineCommand4({
|
|
3682
3665
|
meta: { name: "list", description: "Inspect the complete package-owned Skill inventory" },
|
|
3683
3666
|
args: { ...jsonArgs },
|
|
3684
3667
|
async run({ args }) {
|
|
@@ -3689,19 +3672,19 @@ var skillsListCommand = defineCommand3({
|
|
|
3689
3672
|
}
|
|
3690
3673
|
});
|
|
3691
3674
|
var skillsCommands = {
|
|
3692
|
-
skills:
|
|
3675
|
+
skills: defineCommand4({
|
|
3693
3676
|
meta: { name: "skills", description: "Inspect Skills; use init or update to manage the complete environment" },
|
|
3694
3677
|
subCommands: { list: skillsListCommand }
|
|
3695
3678
|
})
|
|
3696
3679
|
};
|
|
3697
3680
|
|
|
3698
3681
|
// src/cli/commands/work.ts
|
|
3699
|
-
import { defineCommand as
|
|
3682
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
3700
3683
|
|
|
3701
3684
|
// src/commands/archive.ts
|
|
3702
3685
|
import { existsSync as existsSync4, readdirSync } from "fs";
|
|
3703
|
-
import { mkdir as mkdir5, readFile as
|
|
3704
|
-
import { basename as basename2, join as
|
|
3686
|
+
import { mkdir as mkdir5, readFile as readFile12, rename as rename3, unlink as unlink4 } from "fs/promises";
|
|
3687
|
+
import { basename as basename2, join as join8 } from "path";
|
|
3705
3688
|
async function archiveChange(name) {
|
|
3706
3689
|
if (!name) {
|
|
3707
3690
|
return { ok: false, kind: "usage", message: "rsp archive <name>" };
|
|
@@ -3718,7 +3701,7 @@ async function archiveChange(name) {
|
|
|
3718
3701
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3719
3702
|
const base = basename2(workRef.name);
|
|
3720
3703
|
const archiveName = resolveArchiveName(archiveSubdir, date, base);
|
|
3721
|
-
const content = await
|
|
3704
|
+
const content = await readFile12(srcPath, "utf-8");
|
|
3722
3705
|
const configInspection = await inspectRspConfig();
|
|
3723
3706
|
if (configInspection.issues.length > 0) {
|
|
3724
3707
|
return {
|
|
@@ -3747,7 +3730,7 @@ async function archiveChange(name) {
|
|
|
3747
3730
|
};
|
|
3748
3731
|
}
|
|
3749
3732
|
await mkdir5(archiveSubdir, { recursive: true });
|
|
3750
|
-
await rename3(srcPath,
|
|
3733
|
+
await rename3(srcPath, join8(archiveSubdir, archiveName));
|
|
3751
3734
|
const postArchiveWarnings = [];
|
|
3752
3735
|
let focusCleared = false;
|
|
3753
3736
|
if (existsSync4(focusEntry)) {
|
|
@@ -3862,11 +3845,11 @@ async function createChange(name, summary = "", kind, options = {}) {
|
|
|
3862
3845
|
}
|
|
3863
3846
|
|
|
3864
3847
|
// src/commands/focus.ts
|
|
3865
|
-
import { Buffer
|
|
3848
|
+
import { Buffer } from "buffer";
|
|
3866
3849
|
import { randomUUID } from "crypto";
|
|
3867
3850
|
import { existsSync as existsSync6 } from "fs";
|
|
3868
3851
|
import { mkdir as mkdir7, open, rename as rename4, unlink as unlink6 } from "fs/promises";
|
|
3869
|
-
import { basename as basename3, dirname as dirname5, join as
|
|
3852
|
+
import { basename as basename3, dirname as dirname5, join as join9 } from "path";
|
|
3870
3853
|
async function focusChange(name, options = {}) {
|
|
3871
3854
|
if (!name) {
|
|
3872
3855
|
return { ok: false, kind: "usage", message: "rsp focus <name>" };
|
|
@@ -3901,7 +3884,7 @@ async function readCapsule(source) {
|
|
|
3901
3884
|
}
|
|
3902
3885
|
async function replaceFocusCapsule(path, content) {
|
|
3903
3886
|
requireManagedFile(path, "focus marker", { allowMissing: true });
|
|
3904
|
-
const temporaryPath =
|
|
3887
|
+
const temporaryPath = join9(dirname5(path), `.${basename3(path)}.${randomUUID()}.tmp`);
|
|
3905
3888
|
let handle;
|
|
3906
3889
|
try {
|
|
3907
3890
|
handle = await open(temporaryPath, "wx");
|
|
@@ -3934,13 +3917,13 @@ async function readBoundedStdin() {
|
|
|
3934
3917
|
const chunks = [];
|
|
3935
3918
|
let size = 0;
|
|
3936
3919
|
for await (const chunk of process.stdin) {
|
|
3937
|
-
const buffer =
|
|
3920
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
3938
3921
|
size += buffer.byteLength;
|
|
3939
3922
|
if (size > MAX_FOCUS_CAPSULE_BYTES)
|
|
3940
3923
|
throw new Error(`focus capsule exceeds ${MAX_FOCUS_CAPSULE_BYTES} UTF-8 bytes`);
|
|
3941
3924
|
chunks.push(buffer);
|
|
3942
3925
|
}
|
|
3943
|
-
return
|
|
3926
|
+
return Buffer.concat(chunks, size);
|
|
3944
3927
|
}
|
|
3945
3928
|
async function unfocusChange(name) {
|
|
3946
3929
|
if (!name) {
|
|
@@ -3975,8 +3958,8 @@ function workRefFailure(error) {
|
|
|
3975
3958
|
// src/commands/group.ts
|
|
3976
3959
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3977
3960
|
import { existsSync as existsSync7 } from "fs";
|
|
3978
|
-
import { link as link2, mkdir as mkdir8, readdir as readdir4, readFile as
|
|
3979
|
-
import { join as
|
|
3961
|
+
import { link as link2, mkdir as mkdir8, readdir as readdir4, readFile as readFile13, rename as rename5, unlink as unlink7, writeFile as writeFile5 } from "fs/promises";
|
|
3962
|
+
import { join as join10 } from "path";
|
|
3980
3963
|
var ChangeGroupError = class extends Error {
|
|
3981
3964
|
name = "ChangeGroupError";
|
|
3982
3965
|
};
|
|
@@ -4037,14 +4020,14 @@ async function closeChangeGroup(name) {
|
|
|
4037
4020
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4038
4021
|
const archiveName = resolveArchiveName(archiveDirectory, date, "brief");
|
|
4039
4022
|
await mkdir8(archiveDirectory, { recursive: true });
|
|
4040
|
-
await rename5(ref.path,
|
|
4023
|
+
await rename5(ref.path, join10(archiveDirectory, archiveName));
|
|
4041
4024
|
const warnings = [];
|
|
4042
4025
|
try {
|
|
4043
4026
|
await cleanupEmptyParentDirs(ref.path, CHANGES_DIR);
|
|
4044
4027
|
} catch (error) {
|
|
4045
4028
|
warnings.push(`changes directory cleanup failed: ${toErrorMessage(error)}`);
|
|
4046
4029
|
}
|
|
4047
|
-
return { ok: true, action: "close", groupName, archivePath:
|
|
4030
|
+
return { ok: true, action: "close", groupName, archivePath: join10(archiveDirectory, archiveName), warnings };
|
|
4048
4031
|
});
|
|
4049
4032
|
} catch (error) {
|
|
4050
4033
|
if (error instanceof WorkRefError || error instanceof ChangeGroupError) {
|
|
@@ -4068,8 +4051,8 @@ async function reopenChangeGroup(name, options) {
|
|
|
4068
4051
|
return await withRspLock("reopen-change-group", async () => {
|
|
4069
4052
|
const warnings = [];
|
|
4070
4053
|
const groupName = normalizeWorkRefSegment(name);
|
|
4071
|
-
await requireEmptyGroupRecoverySubtree(
|
|
4072
|
-
await requireEmptyGroupRecoverySubtree(
|
|
4054
|
+
await requireEmptyGroupRecoverySubtree(join10(CHANGES_DIR, groupName), `Change Group work subtree for ${groupName}`);
|
|
4055
|
+
await requireEmptyGroupRecoverySubtree(join10(FOCUS_DIR, groupName), `Change Group focus subtree for ${groupName}`);
|
|
4073
4056
|
const ref = resolveWorkRef(`${groupName}/brief`);
|
|
4074
4057
|
if (ref.kind !== "group-brief")
|
|
4075
4058
|
throw new ChangeGroupError(`invalid Change Group name: ${name}`);
|
|
@@ -4092,13 +4075,13 @@ async function reopenChangeGroup(name, options) {
|
|
|
4092
4075
|
}
|
|
4093
4076
|
if (existsSync7(ref.path))
|
|
4094
4077
|
throw new ArchiveHistoryError("open_group_exists", `open Change Group already exists: ${groupName}`);
|
|
4095
|
-
const archivedContent = await
|
|
4078
|
+
const archivedContent = await readFile13(record.sourcePath, "utf-8");
|
|
4096
4079
|
const document = parseRspDocument(archivedContent, GROUP_BRIEF_DOCUMENT_SCHEMA);
|
|
4097
4080
|
validateArchivedGroupBrief(document, groupName);
|
|
4098
4081
|
const evidenceKey = `${GROUP_REOPEN_COMPLETION_PREFIX} \`${record.path}\`: ${reason}`;
|
|
4099
4082
|
const retainedEvidence = /* @__PURE__ */ new Set();
|
|
4100
4083
|
for (const retained of inspection.groupBriefs.filter((candidate) => candidate.group === groupName)) {
|
|
4101
|
-
const retainedDocument = retained.sourcePath === record.sourcePath ? document : parseRspDocument(await
|
|
4084
|
+
const retainedDocument = retained.sourcePath === record.sourcePath ? document : parseRspDocument(await readFile13(retained.sourcePath, "utf-8"), GROUP_BRIEF_DOCUMENT_SCHEMA);
|
|
4102
4085
|
for (const item of getGroupReopenEvidence(retainedDocument))
|
|
4103
4086
|
retainedEvidence.add(item.key);
|
|
4104
4087
|
}
|
|
@@ -4115,7 +4098,7 @@ async function reopenChangeGroup(name, options) {
|
|
|
4115
4098
|
);
|
|
4116
4099
|
const groupDirectory = resolveManagedDirectoryChain(CHANGES_DIR, [groupName], "Change Group path");
|
|
4117
4100
|
await mkdir8(groupDirectory, { recursive: true });
|
|
4118
|
-
const temporaryPath =
|
|
4101
|
+
const temporaryPath = join10(RSP_DIR, `.group-reopen-${process.pid}-${randomUUID2()}.tmp`);
|
|
4119
4102
|
let mutationError;
|
|
4120
4103
|
try {
|
|
4121
4104
|
await writeFile5(temporaryPath, reopenedContent, { flag: "wx" });
|
|
@@ -4200,7 +4183,7 @@ function collectCloseReasons(group, errors) {
|
|
|
4200
4183
|
|
|
4201
4184
|
// src/commands/reopen.ts
|
|
4202
4185
|
import { existsSync as existsSync8 } from "fs";
|
|
4203
|
-
import { mkdir as mkdir9, readFile as
|
|
4186
|
+
import { mkdir as mkdir9, readFile as readFile14, unlink as unlink8, writeFile as writeFile6 } from "fs/promises";
|
|
4204
4187
|
import { dirname as dirname6 } from "path";
|
|
4205
4188
|
async function reopenChange(name, options) {
|
|
4206
4189
|
if (!name) {
|
|
@@ -4250,7 +4233,7 @@ async function reopenChange(name, options) {
|
|
|
4250
4233
|
const focusEntry = resolveFocusMarkerPath(workRef);
|
|
4251
4234
|
if (existsSync8(focusEntry))
|
|
4252
4235
|
throw new ArchiveHistoryError("focus_marker_exists", `focus marker already exists for non-open Change: ${name}; run rsp doctor`);
|
|
4253
|
-
const archivedContent = await
|
|
4236
|
+
const archivedContent = await readFile14(record.sourcePath, "utf-8");
|
|
4254
4237
|
const reopenedContent = appendChecklistItem(
|
|
4255
4238
|
appendChecklistItem(archivedContent, "tasks", `- [ ] Resolve reopened concern: ${reason}`),
|
|
4256
4239
|
"verify",
|
|
@@ -4420,7 +4403,7 @@ function presentReopen(result) {
|
|
|
4420
4403
|
|
|
4421
4404
|
// src/cli/commands/work.ts
|
|
4422
4405
|
function createCreateCommand(deprecatedLite) {
|
|
4423
|
-
return
|
|
4406
|
+
return defineCommand5({
|
|
4424
4407
|
meta: {
|
|
4425
4408
|
name: "create",
|
|
4426
4409
|
description: "Create .rsp/changes/<name>.md [summary]"
|
|
@@ -4445,7 +4428,7 @@ function createCreateCommand(deprecatedLite) {
|
|
|
4445
4428
|
}
|
|
4446
4429
|
},
|
|
4447
4430
|
async run({ args }) {
|
|
4448
|
-
const summary =
|
|
4431
|
+
const summary = args._.slice(1).join(" ");
|
|
4449
4432
|
await executeCliCommand({
|
|
4450
4433
|
execute: () => createChange(args.name, summary, args.kind, {
|
|
4451
4434
|
issue: args.issue,
|
|
@@ -4458,7 +4441,7 @@ function createCreateCommand(deprecatedLite) {
|
|
|
4458
4441
|
}
|
|
4459
4442
|
});
|
|
4460
4443
|
}
|
|
4461
|
-
var groupCreateCommand =
|
|
4444
|
+
var groupCreateCommand = defineCommand5({
|
|
4462
4445
|
meta: {
|
|
4463
4446
|
name: "create",
|
|
4464
4447
|
description: "Create an unfocused .rsp/changes/<group>/00-brief.md"
|
|
@@ -4471,18 +4454,18 @@ var groupCreateCommand = defineCommand4({
|
|
|
4471
4454
|
}
|
|
4472
4455
|
},
|
|
4473
4456
|
async run({ args }) {
|
|
4474
|
-
const goal =
|
|
4457
|
+
const goal = args._.slice(1).join(" ");
|
|
4475
4458
|
await executeCliCommand({ execute: () => createChangeGroup(args.name, goal), present: presentGroup, exitCode: (result) => result.ok ? void 0 : 1 }, args);
|
|
4476
4459
|
}
|
|
4477
4460
|
});
|
|
4478
|
-
var groupCommand =
|
|
4461
|
+
var groupCommand = defineCommand5({
|
|
4479
4462
|
meta: {
|
|
4480
4463
|
name: "group",
|
|
4481
4464
|
description: "Create, close, and explicitly reopen shallow Change Groups"
|
|
4482
4465
|
},
|
|
4483
4466
|
subCommands: {
|
|
4484
4467
|
create: groupCreateCommand,
|
|
4485
|
-
close:
|
|
4468
|
+
close: defineCommand5({
|
|
4486
4469
|
meta: {
|
|
4487
4470
|
name: "close",
|
|
4488
4471
|
description: "Archive a completed Group Brief after all child Changes are archived"
|
|
@@ -4498,7 +4481,7 @@ var groupCommand = defineCommand4({
|
|
|
4498
4481
|
await executeCliCommand({ execute: () => closeChangeGroup(args.name), present: presentGroup, exitCode: (result) => result.ok ? void 0 : 1 }, args);
|
|
4499
4482
|
}
|
|
4500
4483
|
}),
|
|
4501
|
-
reopen:
|
|
4484
|
+
reopen: defineCommand5({
|
|
4502
4485
|
meta: {
|
|
4503
4486
|
name: "reopen",
|
|
4504
4487
|
description: "Restore one archived Group Brief while retaining history"
|
|
@@ -4525,7 +4508,7 @@ var groupCommand = defineCommand4({
|
|
|
4525
4508
|
})
|
|
4526
4509
|
}
|
|
4527
4510
|
});
|
|
4528
|
-
var archiveCommand =
|
|
4511
|
+
var archiveCommand = defineCommand5({
|
|
4529
4512
|
meta: {
|
|
4530
4513
|
name: "archive",
|
|
4531
4514
|
description: "Archive an open change into .rsp/archives/"
|
|
@@ -4549,7 +4532,7 @@ var archiveCommand = defineCommand4({
|
|
|
4549
4532
|
await executeCliCommand({ execute: () => archiveChange(args.name), present: presentArchive, exitCode: (result) => result.ok ? void 0 : 1 }, args);
|
|
4550
4533
|
}
|
|
4551
4534
|
});
|
|
4552
|
-
var reopenCommand =
|
|
4535
|
+
var reopenCommand = defineCommand5({
|
|
4553
4536
|
meta: {
|
|
4554
4537
|
name: "reopen",
|
|
4555
4538
|
description: "Restore one archived Change as focused open work while retaining history"
|
|
@@ -4574,7 +4557,7 @@ var reopenCommand = defineCommand4({
|
|
|
4574
4557
|
await executeCliCommand({ execute: () => reopenChange(args.name, { from: args.from, reason: args.reason }), present: presentReopen, exitCode: (result) => result.ok ? void 0 : 1 }, args);
|
|
4575
4558
|
}
|
|
4576
4559
|
});
|
|
4577
|
-
var focusCommand =
|
|
4560
|
+
var focusCommand = defineCommand5({
|
|
4578
4561
|
meta: {
|
|
4579
4562
|
name: "focus",
|
|
4580
4563
|
description: "Mark an existing change as currently focused in .rsp/focus.d/"
|
|
@@ -4594,7 +4577,7 @@ var focusCommand = defineCommand4({
|
|
|
4594
4577
|
await executeCliCommand({ execute: () => focusChange(args.name, { capsuleFile: args["capsule-file"] }), present: presentFocus, exitCode: (result) => result.ok ? void 0 : 1 }, args);
|
|
4595
4578
|
}
|
|
4596
4579
|
});
|
|
4597
|
-
var unfocusCommand =
|
|
4580
|
+
var unfocusCommand = defineCommand5({
|
|
4598
4581
|
meta: {
|
|
4599
4582
|
name: "unfocus",
|
|
4600
4583
|
description: "Remove a change from the current focus set in .rsp/focus.d/"
|
|
@@ -4624,13 +4607,15 @@ function createWorkCommands(deprecatedLite) {
|
|
|
4624
4607
|
// src/cli/registry.ts
|
|
4625
4608
|
function createRootCommand(options) {
|
|
4626
4609
|
const workCommands = createWorkCommands(options.deprecatedLite);
|
|
4627
|
-
return
|
|
4610
|
+
return defineCommand6({
|
|
4628
4611
|
meta: {
|
|
4629
4612
|
name: "rsp",
|
|
4630
4613
|
version: options.version,
|
|
4631
4614
|
description: "RSP (Reliable Software Practice), a repository-native engineering workflow for humans and AI agents"
|
|
4632
4615
|
},
|
|
4633
4616
|
subCommands: {
|
|
4617
|
+
architecture: architectureCommand,
|
|
4618
|
+
view: viewCommand,
|
|
4634
4619
|
init: setupCommands.init,
|
|
4635
4620
|
add: setupCommands.add,
|
|
4636
4621
|
specs: inspectionCommands.specs,
|
|
@@ -4715,7 +4700,7 @@ async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
4715
4700
|
} else {
|
|
4716
4701
|
const { resolveUiLocale } = await import("./locale-HT44UIUP.mjs");
|
|
4717
4702
|
const locale = resolveUiLocale(lang, process.env.RSP_UI_LANG, process.env.LC_ALL ?? process.env.LC_MESSAGES ?? process.env.LANG ?? new Intl.DateTimeFormat().resolvedOptions().locale);
|
|
4718
|
-
const { runTui } = await import("./entry-
|
|
4703
|
+
const { runTui } = await import("./entry-NDASTAI5.mjs");
|
|
4719
4704
|
const exitCode = await runTui(locale);
|
|
4720
4705
|
if (exitCode !== 0)
|
|
4721
4706
|
process.exitCode = exitCode;
|