@oevery/rsp 3.3.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/README.md +24 -17
- package/README.zh-CN.md +25 -18
- package/dist/chunk-JO6NDJM4.mjs +360 -0
- package/dist/chunk-LGDCIQYL.mjs +725 -0
- package/dist/{chunk-7HXQNBIG.mjs → chunk-P3KO3GZW.mjs} +420 -268
- package/dist/chunk-U56XMIOO.mjs +66 -0
- package/dist/{chunk-TSUDEX5Y.mjs → chunk-VDRXFKBP.mjs} +142 -867
- package/dist/cli.mjs +1111 -2347
- package/dist/client-EHRWH5Q7.mjs +108 -0
- package/dist/{entry-GIDNR7CJ.mjs → entry-NDASTAI5.mjs} +128 -38
- 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 +51 -25
- package/skills/rsp/SKILL.md +44 -38
- package/skills/rsp/references/architecture.md +39 -0
- 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 +12 -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 +42 -7
- package/skills/rsp/references/work-model.md +37 -0
- package/skills/rsp-commit/SKILL.md +76 -31
- package/skills/rsp-commit/references/message.md +34 -0
- package/skills/rsp-doc/SKILL.md +68 -0
- package/skills/rsp-doc/agents/openai.yaml +3 -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 +60 -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 +30 -9
- package/skills/rsp-review/references/document-review.md +24 -9
- package/skills/rsp-review/references/release-communication.md +21 -0
- package/skills/rsp-shape/SKILL.md +26 -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 +39 -12
- 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-structural-audit/references/structural-lenses.md +0 -41
- package/skills/rsp-tdd/SKILL.md +0 -56
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
discoverService,
|
|
4
|
+
serviceRequest,
|
|
5
|
+
viewerRuntime
|
|
6
|
+
} from "./chunk-U56XMIOO.mjs";
|
|
7
|
+
import "./chunk-P3KO3GZW.mjs";
|
|
8
|
+
|
|
9
|
+
// src/viewer/client.ts
|
|
10
|
+
import { spawn } from "child_process";
|
|
11
|
+
import { randomBytes } from "crypto";
|
|
12
|
+
import { chmod, mkdir, readFile, realpath, rm, stat, writeFile } from "fs/promises";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { setTimeout as delay } from "timers/promises";
|
|
15
|
+
async function startService() {
|
|
16
|
+
const runtime = await viewerRuntime();
|
|
17
|
+
await mkdir(runtime.directory, { recursive: true, mode: 448 });
|
|
18
|
+
await chmod(runtime.directory, 448);
|
|
19
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
20
|
+
const existing = await discoverService(runtime.recordPath, runtime.identity);
|
|
21
|
+
if (existing)
|
|
22
|
+
return existing;
|
|
23
|
+
try {
|
|
24
|
+
await mkdir(runtime.lockPath, { mode: 448 });
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error.code !== "EEXIST")
|
|
27
|
+
throw error;
|
|
28
|
+
try {
|
|
29
|
+
const pid = Number(await readFile(join(runtime.lockPath, "pid"), "utf8"));
|
|
30
|
+
if (Number.isInteger(pid) && pid > 0) {
|
|
31
|
+
try {
|
|
32
|
+
process.kill(pid, 0);
|
|
33
|
+
} catch (dead) {
|
|
34
|
+
if (dead.code === "ESRCH")
|
|
35
|
+
await rm(runtime.lockPath, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
try {
|
|
40
|
+
if (Date.now() - (await stat(runtime.lockPath)).mtimeMs > 1e4)
|
|
41
|
+
await rm(runtime.lockPath, { recursive: true, force: true });
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
await delay(150);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
await writeFile(join(runtime.lockPath, "pid"), String(process.pid), { mode: 384 });
|
|
50
|
+
const current = await discoverService(runtime.recordPath, runtime.identity);
|
|
51
|
+
if (current)
|
|
52
|
+
return current;
|
|
53
|
+
const child = spawn(process.execPath, [join(runtime.root, "dist/viewer-service.mjs")], { detached: true, stdio: "ignore", env: { ...process.env, RSP_VIEWER_BOOT: JSON.stringify({ ...runtime, adminToken: randomBytes(32).toString("hex"), readToken: randomBytes(32).toString("hex") }) } });
|
|
54
|
+
child.unref();
|
|
55
|
+
let spawnError;
|
|
56
|
+
child.on("error", (error) => {
|
|
57
|
+
spawnError = error;
|
|
58
|
+
});
|
|
59
|
+
for (let poll = 0; poll < 100; poll++) {
|
|
60
|
+
if (spawnError)
|
|
61
|
+
throw spawnError;
|
|
62
|
+
const record = await discoverService(runtime.recordPath, runtime.identity);
|
|
63
|
+
if (record)
|
|
64
|
+
return record;
|
|
65
|
+
await delay(100);
|
|
66
|
+
}
|
|
67
|
+
throw new Error("Viewer service did not become ready; rebuild this package and retry");
|
|
68
|
+
} finally {
|
|
69
|
+
await rm(runtime.lockPath, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
throw new Error("Another viewer startup has not completed; retry after it exits");
|
|
73
|
+
}
|
|
74
|
+
async function runView(action, open = true) {
|
|
75
|
+
if (action && !["status", "stop"].includes(action))
|
|
76
|
+
throw new Error("rsp view accepts only status, stop, or --no-open");
|
|
77
|
+
const runtime = await viewerRuntime();
|
|
78
|
+
if (action) {
|
|
79
|
+
const service2 = await discoverService(runtime.recordPath, runtime.identity);
|
|
80
|
+
if (!service2) {
|
|
81
|
+
console.log("Viewer service is not running for this runtime.");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (action === "stop") {
|
|
85
|
+
await serviceRequest(service2, "/control/stop", {});
|
|
86
|
+
for (let attempt = 0; attempt < 25 && await discoverService(runtime.recordPath, runtime.identity); attempt++)
|
|
87
|
+
await delay(40);
|
|
88
|
+
console.log("Viewer stopped; all workspaces in this runtime are disconnected.");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const status = await serviceRequest(service2, "/control/status");
|
|
92
|
+
console.log(JSON.stringify({ runtime: runtime.identity, pid: status.pid, port: status.port, workspaces: status.workspaces }, null, 2));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const service = await startService();
|
|
96
|
+
const registered = await serviceRequest(service, "/control/register", { root: await realpath(process.cwd()) }, 3e4);
|
|
97
|
+
const url = `http://127.0.0.1:${service.port}/?workspace=${registered.workspace}#token=${service.readToken}`;
|
|
98
|
+
console.log(url);
|
|
99
|
+
if (open) {
|
|
100
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
|
|
101
|
+
const browser = spawn(command, [url], { detached: true, stdio: "ignore" });
|
|
102
|
+
browser.on("error", () => console.error("Could not open browser; use the printed URL."));
|
|
103
|
+
browser.unref();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
runView
|
|
108
|
+
};
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ArchiveHistoryError,
|
|
4
|
-
SPECS_DEFAULT_RESULT_LIMIT,
|
|
5
|
-
SPECS_SEARCH_EXCERPT_CODE_POINTS,
|
|
6
4
|
WorkRefError,
|
|
7
5
|
historyInspectionComplete,
|
|
8
6
|
inspectArchiveHistory,
|
|
9
7
|
inspectProjectStatus,
|
|
10
|
-
inspectSpecs,
|
|
11
|
-
normalizeLogicalPath,
|
|
12
8
|
projectDependencyForest,
|
|
13
9
|
projectSpecsDetail,
|
|
14
10
|
projectSpecsSearch,
|
|
@@ -17,16 +13,19 @@ import {
|
|
|
17
13
|
readArchiveHistoryDetail,
|
|
18
14
|
readArchiveHistoryDocument,
|
|
19
15
|
resolveWorkRefPath
|
|
20
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VDRXFKBP.mjs";
|
|
21
17
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} from "./chunk-
|
|
18
|
+
SPECS_DEFAULT_RESULT_LIMIT,
|
|
19
|
+
SPECS_SEARCH_EXCERPT_CODE_POINTS,
|
|
20
|
+
inspectSpecs,
|
|
21
|
+
normalizeLogicalPath
|
|
22
|
+
} from "./chunk-LGDCIQYL.mjs";
|
|
27
23
|
import {
|
|
28
|
-
CHANGES_DIR
|
|
29
|
-
|
|
24
|
+
CHANGES_DIR,
|
|
25
|
+
isMarkdownComment,
|
|
26
|
+
markdownText,
|
|
27
|
+
parseMarkdown
|
|
28
|
+
} from "./chunk-P3KO3GZW.mjs";
|
|
30
29
|
|
|
31
30
|
// src/tui/entry.tsx
|
|
32
31
|
import process from "process";
|
|
@@ -37,6 +36,81 @@ import React from "react";
|
|
|
37
36
|
import { Box as Box3, Text as Text3, useApp, useInput, useStdout } from "ink";
|
|
38
37
|
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
39
38
|
|
|
39
|
+
// src/tui/display.ts
|
|
40
|
+
var segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
41
|
+
function isWide(codePoint) {
|
|
42
|
+
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);
|
|
43
|
+
}
|
|
44
|
+
function displayWidth(value) {
|
|
45
|
+
let width = 0;
|
|
46
|
+
for (const { segment } of segmenter.segment(value)) {
|
|
47
|
+
const codePoint = segment.codePointAt(0) ?? 0;
|
|
48
|
+
if (/^\p{Mark}$/u.test(segment) || codePoint === 8205)
|
|
49
|
+
continue;
|
|
50
|
+
width += isWide(codePoint) ? 2 : 1;
|
|
51
|
+
}
|
|
52
|
+
return width;
|
|
53
|
+
}
|
|
54
|
+
function truncateDisplay(value, maxWidth) {
|
|
55
|
+
if (displayWidth(value) <= maxWidth)
|
|
56
|
+
return value;
|
|
57
|
+
if (maxWidth <= 1)
|
|
58
|
+
return "\u2026".slice(0, maxWidth);
|
|
59
|
+
let result = "";
|
|
60
|
+
for (const { segment } of segmenter.segment(value)) {
|
|
61
|
+
if (displayWidth(result + segment) > maxWidth - 1)
|
|
62
|
+
break;
|
|
63
|
+
result += segment;
|
|
64
|
+
}
|
|
65
|
+
return `${result}\u2026`;
|
|
66
|
+
}
|
|
67
|
+
function wrapDisplay(value, maxWidth, firstPrefix = "", continuationPrefix = firstPrefix) {
|
|
68
|
+
const width = Math.max(1, maxWidth);
|
|
69
|
+
if (displayWidth(firstPrefix) >= width)
|
|
70
|
+
return [truncateDisplay(`${firstPrefix}${value}`, width)];
|
|
71
|
+
const lines = [];
|
|
72
|
+
let prefix = firstPrefix;
|
|
73
|
+
let line = prefix;
|
|
74
|
+
const pushLine = () => {
|
|
75
|
+
lines.push(line);
|
|
76
|
+
prefix = continuationPrefix;
|
|
77
|
+
line = prefix;
|
|
78
|
+
};
|
|
79
|
+
const appendUnbroken = (token) => {
|
|
80
|
+
for (const { segment } of segmenter.segment(token)) {
|
|
81
|
+
if (displayWidth(line + segment) > width && line !== prefix)
|
|
82
|
+
pushLine();
|
|
83
|
+
if (displayWidth(line + segment) > width) {
|
|
84
|
+
lines.push(truncateDisplay(line + segment, width));
|
|
85
|
+
prefix = continuationPrefix;
|
|
86
|
+
line = prefix;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
line += segment;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
let pendingWhitespace = "";
|
|
93
|
+
for (const token of value.split(/(\s+)/u).filter(Boolean)) {
|
|
94
|
+
if (token.trim().length === 0) {
|
|
95
|
+
if (line !== prefix)
|
|
96
|
+
pendingWhitespace += token;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const separator = line === prefix ? "" : pendingWhitespace;
|
|
100
|
+
if (displayWidth(line + separator + token) <= width) {
|
|
101
|
+
line += `${separator}${token}`;
|
|
102
|
+
} else {
|
|
103
|
+
if (line !== prefix && separator)
|
|
104
|
+
pushLine();
|
|
105
|
+
appendUnbroken(token);
|
|
106
|
+
}
|
|
107
|
+
pendingWhitespace = "";
|
|
108
|
+
}
|
|
109
|
+
if (line !== prefix || lines.length === 0)
|
|
110
|
+
lines.push(line);
|
|
111
|
+
return lines;
|
|
112
|
+
}
|
|
113
|
+
|
|
40
114
|
// src/tui/history-display.ts
|
|
41
115
|
function formatHistoryRow(record, width) {
|
|
42
116
|
const boundedWidth = Math.max(1, width);
|
|
@@ -138,18 +212,17 @@ function projectHistoryEvidence(evidence, options) {
|
|
|
138
212
|
}
|
|
139
213
|
|
|
140
214
|
// src/tui/markdown-presentation.ts
|
|
141
|
-
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
142
|
-
import { gfmFromMarkdown } from "mdast-util-gfm";
|
|
143
|
-
import { gfm } from "micromark-extension-gfm";
|
|
144
215
|
var EMPTY_LINE = { spans: [{ text: "" }] };
|
|
145
216
|
var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
146
217
|
function markdownLineText(line) {
|
|
147
218
|
return line.spans.map((span) => span.text).join("");
|
|
148
219
|
}
|
|
149
220
|
function projectMarkdownLines(content, width) {
|
|
150
|
-
const root =
|
|
221
|
+
const root = parseMarkdown(sanitizeMarkdown(content)).root;
|
|
151
222
|
const logical = [];
|
|
152
223
|
root.children.forEach((node, index) => {
|
|
224
|
+
if (node.type === "yaml" || isMarkdownComment(node))
|
|
225
|
+
return;
|
|
153
226
|
if (index > 0 && logical.length > 0 && markdownLineText(logical.at(-1)) !== "")
|
|
154
227
|
logical.push(EMPTY_LINE);
|
|
155
228
|
logical.push(...projectBlock(node, 0, Math.max(1, width)));
|
|
@@ -173,9 +246,6 @@ function projectMarkdownLineViewport(lines, options) {
|
|
|
173
246
|
hasNext: end < lines.length
|
|
174
247
|
};
|
|
175
248
|
}
|
|
176
|
-
function stripFrontmatter(content) {
|
|
177
|
-
return content.replace(/^---\r?\n[\s\S]*?\r?\n(?:---|\.\.\.)\r?\n/u, "");
|
|
178
|
-
}
|
|
179
249
|
function sanitizeMarkdown(value) {
|
|
180
250
|
let result = "";
|
|
181
251
|
for (let index = 0; index < value.length; index++) {
|
|
@@ -222,7 +292,7 @@ function projectBlock(node, depth, width) {
|
|
|
222
292
|
case "table":
|
|
223
293
|
return projectTable(node, width);
|
|
224
294
|
case "html":
|
|
225
|
-
return [metavariableLine(node.value) ?? { spans: [{ text: node.
|
|
295
|
+
return [metavariableLine(node.value) ?? { spans: [{ text: markdownText(node).trim(), dim: true }] }];
|
|
226
296
|
default:
|
|
227
297
|
return "children" in node ? inlineLines(node.children.filter((child) => isPhrasing(child))) : [];
|
|
228
298
|
}
|
|
@@ -263,11 +333,13 @@ function inlineLines(children, style = {}, prefix = "") {
|
|
|
263
333
|
return lines;
|
|
264
334
|
}
|
|
265
335
|
function projectInline(node, inherited) {
|
|
336
|
+
if (isMarkdownComment(node))
|
|
337
|
+
return [];
|
|
266
338
|
switch (node.type) {
|
|
267
339
|
case "text":
|
|
268
340
|
return [{ text: node.value, ...inherited }];
|
|
269
341
|
case "html":
|
|
270
|
-
return metavariableSpan(node.value, inherited) ?? [{ text: node
|
|
342
|
+
return metavariableSpan(node.value, inherited) ?? [{ text: markdownText(node), ...inherited, dim: true }];
|
|
271
343
|
case "inlineCode":
|
|
272
344
|
return [{ text: node.value, ...inherited, code: true, color: "cyan" }];
|
|
273
345
|
case "break":
|
|
@@ -751,16 +823,8 @@ function projectItemDependencyForest(item, snapshot) {
|
|
|
751
823
|
const forest = projectDependencyForest(snapshot.plan, roots);
|
|
752
824
|
return item.type === "change" ? forest[0]?.children ?? [] : forest;
|
|
753
825
|
}
|
|
754
|
-
function projectExternalBlockers(item
|
|
755
|
-
|
|
756
|
-
return [];
|
|
757
|
-
const edges = snapshot.plan.edges.filter((edge) => edge.change === item.workRef);
|
|
758
|
-
return item.record.blockerEntries.filter((entry) => {
|
|
759
|
-
const dependency = entry.match(/^requires\s+`([^`]+)`:\s*(\S.*)$/i);
|
|
760
|
-
if (!dependency)
|
|
761
|
-
return true;
|
|
762
|
-
return !edges.some((edge) => edge.requires === dependency[1] && edge.reason === dependency[2]);
|
|
763
|
-
});
|
|
826
|
+
function projectExternalBlockers(item) {
|
|
827
|
+
return item.type === "change" ? item.record.externalBlockers : [];
|
|
764
828
|
}
|
|
765
829
|
function projectNextAction(item, snapshot) {
|
|
766
830
|
if (item.type === "history")
|
|
@@ -808,9 +872,7 @@ function projectWorkDetailLines(item, snapshot, messages, width) {
|
|
|
808
872
|
add(messages.none);
|
|
809
873
|
if (item.type === "group")
|
|
810
874
|
add(`${messages.changes}: ${item.group.slices.length ? item.group.slices.map((slice) => `${slice.name} (${slice.state})`).join(", ") : messages.none}`);
|
|
811
|
-
|
|
812
|
-
const blockers = external.length ? external.join("; ") : item.type === "group" && item.group.blockers || snapshot.plan.blocked.some((blocked) => blocked.change === item.workRef && blocked.external) ? messages.yes : messages.none;
|
|
813
|
-
add(`${messages.blockers}: ${blockers}`);
|
|
875
|
+
add(`${messages.blockers}: ${blockersLabel(item, snapshot, messages)}`);
|
|
814
876
|
const next = projectNextAction(item, snapshot);
|
|
815
877
|
add(`${next.kind === "command" ? messages.nextCommand : next.kind === "blocked" ? messages.nextAction : messages.reviewGroupBrief}: ${next.kind === "blocked" ? messages.awaitingOwnerDecision : next.value}`);
|
|
816
878
|
return lines;
|
|
@@ -821,6 +883,13 @@ function workStateLabel(item, snapshot, messages) {
|
|
|
821
883
|
const state = projectItemState(item, snapshot);
|
|
822
884
|
return (state.focused ? [messages.focused, messages[state.execution]] : [messages[state.execution]]).join(" \xB7 ");
|
|
823
885
|
}
|
|
886
|
+
function blockersLabel(item, snapshot, messages) {
|
|
887
|
+
const external = projectExternalBlockers(item);
|
|
888
|
+
if (external.length)
|
|
889
|
+
return external.join("; ");
|
|
890
|
+
const inherited = item.type === "group" && item.group.blockers || snapshot.plan.blocked.some((blocked) => blocked.change === item.workRef && blocked.external);
|
|
891
|
+
return inherited ? messages.yes : messages.none;
|
|
892
|
+
}
|
|
824
893
|
function dependencySymbol(node, snapshot) {
|
|
825
894
|
if (snapshot.focused.includes(node.name))
|
|
826
895
|
return "\u25CE";
|
|
@@ -861,9 +930,8 @@ function WorkDetail({ expanded, item, messages, snapshot, width }) {
|
|
|
861
930
|
if (!item || item.type === "history")
|
|
862
931
|
return /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: messages.noWork });
|
|
863
932
|
const forest = projectItemDependencyForest(item, snapshot);
|
|
864
|
-
const externalBlockers = projectExternalBlockers(item, snapshot);
|
|
865
933
|
const nextAction = projectNextAction(item, snapshot);
|
|
866
|
-
const blockers =
|
|
934
|
+
const blockers = blockersLabel(item, snapshot, messages);
|
|
867
935
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingLeft: 1, width, children: [
|
|
868
936
|
/* @__PURE__ */ jsxs2(Text2, { bold: true, children: [
|
|
869
937
|
messages.detail,
|
|
@@ -982,10 +1050,16 @@ function scopeNavigation(scope, messages, width) {
|
|
|
982
1050
|
const navigation = labels.map(([candidate, label]) => candidate === scope ? `[${label}]` : label).join(" ");
|
|
983
1051
|
return truncateDisplay(`${messages.title} ${navigation}`, width);
|
|
984
1052
|
}
|
|
1053
|
+
function terminalDimensions(stdout) {
|
|
1054
|
+
return {
|
|
1055
|
+
width: "columns" in stdout && typeof stdout.columns === "number" ? stdout.columns : 80,
|
|
1056
|
+
height: "rows" in stdout && typeof stdout.rows === "number" ? stdout.rows : 24
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
985
1059
|
function DashboardApp({ initialDimensions, initialSnapshot, inspectHistory, inspectHistoryDetail, inspectHistoryDocument, inspectStatus, messages, specsSource, workSource }) {
|
|
986
1060
|
const { exit } = useApp();
|
|
987
1061
|
const { stdout } = useStdout();
|
|
988
|
-
const dimensions = initialDimensions ??
|
|
1062
|
+
const dimensions = initialDimensions ?? terminalDimensions(stdout);
|
|
989
1063
|
const [state, dispatch] = useReducer(reduceDashboard, initialDashboardState(initialSnapshot, dimensions));
|
|
990
1064
|
const filterRefs = useRef({ work: "", specs: "", history: "" });
|
|
991
1065
|
const refreshing = useRef(false);
|
|
@@ -1182,7 +1256,7 @@ function DashboardApp({ initialDimensions, initialSnapshot, inspectHistory, insp
|
|
|
1182
1256
|
}
|
|
1183
1257
|
}, [detailDocumentState, inspectHistoryDocument, selected, state.scope, workSource]);
|
|
1184
1258
|
useEffect(() => {
|
|
1185
|
-
const resize = () => dispatch({ type: "resize",
|
|
1259
|
+
const resize = () => dispatch({ type: "resize", ...terminalDimensions(stdout) });
|
|
1186
1260
|
stdout.on("resize", resize);
|
|
1187
1261
|
return () => {
|
|
1188
1262
|
stdout.off("resize", resize);
|
|
@@ -1655,6 +1729,22 @@ function createTuiSpecsSource(dependencies = defaultDependencies2) {
|
|
|
1655
1729
|
};
|
|
1656
1730
|
}
|
|
1657
1731
|
|
|
1732
|
+
// src/tui/terminal.ts
|
|
1733
|
+
function openTerminalSession(stdout, options) {
|
|
1734
|
+
let cleaned = false;
|
|
1735
|
+
if (!options.screenReader)
|
|
1736
|
+
stdout.write("\x1B[?1049h\x1B[?25l");
|
|
1737
|
+
return {
|
|
1738
|
+
cleanup() {
|
|
1739
|
+
if (cleaned)
|
|
1740
|
+
return;
|
|
1741
|
+
cleaned = true;
|
|
1742
|
+
if (!options.screenReader)
|
|
1743
|
+
stdout.write("\x1B[?25h\x1B[?1049l");
|
|
1744
|
+
}
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1658
1748
|
// src/tui/work-source.ts
|
|
1659
1749
|
import { Buffer } from "buffer";
|
|
1660
1750
|
import { constants } from "fs";
|