@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.
Files changed (76) hide show
  1. package/README.md +24 -17
  2. package/README.zh-CN.md +25 -18
  3. package/dist/chunk-JO6NDJM4.mjs +360 -0
  4. package/dist/chunk-LGDCIQYL.mjs +725 -0
  5. package/dist/{chunk-7HXQNBIG.mjs → chunk-P3KO3GZW.mjs} +420 -268
  6. package/dist/chunk-U56XMIOO.mjs +66 -0
  7. package/dist/{chunk-TSUDEX5Y.mjs → chunk-VDRXFKBP.mjs} +142 -867
  8. package/dist/cli.mjs +1111 -2347
  9. package/dist/client-EHRWH5Q7.mjs +108 -0
  10. package/dist/{entry-GIDNR7CJ.mjs → entry-NDASTAI5.mjs} +128 -38
  11. package/dist/viewer/NOTICE.txt +299 -0
  12. package/dist/viewer/app.css +2 -0
  13. package/dist/viewer/app.js +670 -0
  14. package/dist/viewer-build.json +1 -0
  15. package/dist/viewer-service.mjs +551 -0
  16. package/package.json +51 -25
  17. package/skills/rsp/SKILL.md +44 -38
  18. package/skills/rsp/references/architecture.md +39 -0
  19. package/skills/rsp/references/closeout.md +38 -0
  20. package/skills/rsp/references/context-migration.md +22 -0
  21. package/skills/rsp/references/control-outcome.md +3 -29
  22. package/skills/rsp/references/coordination-recovery.md +25 -0
  23. package/skills/rsp/references/coordination-review.md +15 -0
  24. package/skills/rsp/references/coordination.md +59 -0
  25. package/skills/rsp/references/durable-review.md +60 -12
  26. package/skills/rsp/references/focus-continuation.md +12 -4
  27. package/skills/rsp/references/groups-dependencies.md +6 -4
  28. package/skills/rsp/references/response-language.md +2 -0
  29. package/skills/rsp/references/setup-repair.md +42 -7
  30. package/skills/rsp/references/work-model.md +37 -0
  31. package/skills/rsp-commit/SKILL.md +76 -31
  32. package/skills/rsp-commit/references/message.md +34 -0
  33. package/skills/rsp-doc/SKILL.md +68 -0
  34. package/skills/rsp-doc/agents/openai.yaml +3 -0
  35. package/skills/rsp-doc/references/release-communication.md +39 -0
  36. package/skills/rsp-doc/references/skill-authoring.md +36 -0
  37. package/skills/rsp-implement/SKILL.md +60 -25
  38. package/skills/rsp-implement/references/diagnosis.md +10 -0
  39. package/skills/rsp-implement/references/finding-resolution.md +21 -0
  40. package/skills/rsp-implement/references/test-first.md +7 -0
  41. package/skills/rsp-review/SKILL.md +46 -45
  42. package/skills/rsp-review/references/code-review.md +30 -9
  43. package/skills/rsp-review/references/document-review.md +24 -9
  44. package/skills/rsp-review/references/release-communication.md +21 -0
  45. package/skills/rsp-shape/SKILL.md +26 -45
  46. package/skills/rsp-shape/references/complex-shaping.md +3 -1
  47. package/skills/rsp-shape/references/deep-clarification.md +4 -4
  48. package/skills/rsp-shape/references/domain-modeling.md +13 -0
  49. package/skills/rsp-shape/references/module-seams.md +13 -0
  50. package/skills/rsp-shape/references/planning.md +42 -0
  51. package/skills/{rsp-design → rsp-shape}/references/reversible-exploration.md +2 -2
  52. package/skills/rsp-verify/SKILL.md +39 -12
  53. package/dist/chunk-JOMFFQRG.mjs +0 -99
  54. package/dist/chunk-QM2UVPQK.mjs +0 -375
  55. package/dist/entry-YAW3PYFA.mjs +0 -174
  56. package/rules/rsp-rules.md +0 -48
  57. package/skills/rsp/references/implementation-evidence.md +0 -12
  58. package/skills/rsp/references/managed-routing.md +0 -45
  59. package/skills/rsp-design/SKILL.md +0 -53
  60. package/skills/rsp-design/references/domain-modeling.md +0 -11
  61. package/skills/rsp-design/references/module-seams.md +0 -11
  62. package/skills/rsp-diagnose/SKILL.md +0 -48
  63. package/skills/rsp-manage/SKILL.md +0 -101
  64. package/skills/rsp-manage/references/closeout.md +0 -13
  65. package/skills/rsp-manage/references/delegation.md +0 -47
  66. package/skills/rsp-manage/references/interruption-recovery.md +0 -19
  67. package/skills/rsp-manage/references/review-convergence.md +0 -9
  68. package/skills/rsp-release-docs/SKILL.md +0 -77
  69. package/skills/rsp-release-docs/references/convention-discovery.md +0 -65
  70. package/skills/rsp-release-docs/references/evidence-and-surfaces.md +0 -21
  71. package/skills/rsp-release-docs/references/output-contracts.md +0 -97
  72. package/skills/rsp-release-docs/references/publication-lifecycle.md +0 -28
  73. package/skills/rsp-resolve-findings/SKILL.md +0 -61
  74. package/skills/rsp-structural-audit/SKILL.md +0 -71
  75. package/skills/rsp-structural-audit/references/structural-lenses.md +0 -41
  76. 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-TSUDEX5Y.mjs";
16
+ } from "./chunk-VDRXFKBP.mjs";
21
17
  import {
22
- displayWidth,
23
- openTerminalSession,
24
- truncateDisplay,
25
- wrapDisplay
26
- } from "./chunk-JOMFFQRG.mjs";
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
- } from "./chunk-7HXQNBIG.mjs";
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 = fromMarkdown(sanitizeMarkdown(stripFrontmatter(content)), { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] });
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.value, dim: true }] }];
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.value, ...inherited, dim: true }];
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, snapshot) {
755
- if (item.type !== "change")
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
- const external = projectExternalBlockers(item, snapshot);
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 = externalBlockers.length ? externalBlockers.join("; ") : item.type === "group" && item.group.blockers || snapshot.plan.blocked.some((blocked) => blocked.change === item.workRef && blocked.external) ? messages.yes : messages.none;
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 ?? { width: stdout.columns ?? 80, height: stdout.rows ?? 24 };
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", width: stdout.columns ?? 80, height: stdout.rows ?? 24 });
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";