@autopilot-agent/cli 0.0.0-stage → 0.2.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 (144) hide show
  1. package/README.md +52 -2
  2. package/dist/activity-animation.d.ts +59 -0
  3. package/dist/activity-animation.js +148 -0
  4. package/dist/animation-clock.d.ts +22 -0
  5. package/dist/animation-clock.js +76 -0
  6. package/dist/composition-root.d.ts +105 -0
  7. package/dist/composition-root.js +600 -0
  8. package/dist/context-report.d.ts +20 -0
  9. package/dist/context-report.js +173 -0
  10. package/dist/controller.d.ts +79 -0
  11. package/dist/controller.js +741 -0
  12. package/dist/design-tokens.d.ts +38 -0
  13. package/dist/design-tokens.js +38 -0
  14. package/dist/error-diagnostics.d.ts +8 -0
  15. package/dist/error-diagnostics.js +62 -0
  16. package/dist/event-projector.d.ts +26 -0
  17. package/dist/event-projector.js +335 -0
  18. package/dist/file-mention-context.d.ts +34 -0
  19. package/dist/file-mention-context.js +173 -0
  20. package/dist/index.d.ts +19 -0
  21. package/dist/index.js +14 -0
  22. package/dist/main.d.ts +40 -0
  23. package/dist/main.js +543 -0
  24. package/dist/parser.d.ts +17 -0
  25. package/dist/parser.js +400 -0
  26. package/dist/renderer.d.ts +9 -0
  27. package/dist/renderer.js +62 -0
  28. package/dist/scroll-viewport.d.ts +22 -0
  29. package/dist/scroll-viewport.js +62 -0
  30. package/dist/termination-format.d.ts +4 -0
  31. package/dist/termination-format.js +18 -0
  32. package/dist/timeline-lines.d.ts +38 -0
  33. package/dist/timeline-lines.js +104 -0
  34. package/dist/tui/app.d.ts +30 -0
  35. package/dist/tui/app.js +413 -0
  36. package/dist/tui/approval.d.ts +19 -0
  37. package/dist/tui/approval.js +41 -0
  38. package/dist/tui/brand/brand-header.d.ts +20 -0
  39. package/dist/tui/brand/brand-header.js +22 -0
  40. package/dist/tui/brand/intro.d.ts +12 -0
  41. package/dist/tui/brand/intro.js +88 -0
  42. package/dist/tui/brand/pixel.d.ts +8 -0
  43. package/dist/tui/brand/pixel.js +50 -0
  44. package/dist/tui/constants.d.ts +56 -0
  45. package/dist/tui/constants.js +57 -0
  46. package/dist/tui/context.d.ts +11 -0
  47. package/dist/tui/context.js +12 -0
  48. package/dist/tui/diff-stats.d.ts +8 -0
  49. package/dist/tui/diff-stats.js +7 -0
  50. package/dist/tui/format.d.ts +75 -0
  51. package/dist/tui/format.js +178 -0
  52. package/dist/tui/hooks/use-file-mention-index.d.ts +15 -0
  53. package/dist/tui/hooks/use-file-mention-index.js +59 -0
  54. package/dist/tui/hooks/use-pickers.d.ts +47 -0
  55. package/dist/tui/hooks/use-pickers.js +288 -0
  56. package/dist/tui/hooks/use-prompt-input.d.ts +54 -0
  57. package/dist/tui/hooks/use-prompt-input.js +258 -0
  58. package/dist/tui/hooks/use-status-view.d.ts +8 -0
  59. package/dist/tui/hooks/use-status-view.js +13 -0
  60. package/dist/tui/hooks/use-viewport.d.ts +22 -0
  61. package/dist/tui/hooks/use-viewport.js +117 -0
  62. package/dist/tui/input/file-mention.d.ts +33 -0
  63. package/dist/tui/input/file-mention.js +112 -0
  64. package/dist/tui/input/graphemes.d.ts +5 -0
  65. package/dist/tui/input/graphemes.js +10 -0
  66. package/dist/tui/input/keys.d.ts +16 -0
  67. package/dist/tui/input/keys.js +58 -0
  68. package/dist/tui/input/prompt-layout.d.ts +27 -0
  69. package/dist/tui/input/prompt-layout.js +84 -0
  70. package/dist/tui/input-area.d.ts +53 -0
  71. package/dist/tui/input-area.js +89 -0
  72. package/dist/tui/jobs.d.ts +10 -0
  73. package/dist/tui/jobs.js +24 -0
  74. package/dist/tui/lifecycle.d.ts +23 -0
  75. package/dist/tui/lifecycle.js +114 -0
  76. package/dist/tui/markdown/diff.d.ts +8 -0
  77. package/dist/tui/markdown/diff.js +19 -0
  78. package/dist/tui/markdown/inline.d.ts +4 -0
  79. package/dist/tui/markdown/inline.js +32 -0
  80. package/dist/tui/markdown/markdown.d.ts +17 -0
  81. package/dist/tui/markdown/markdown.js +117 -0
  82. package/dist/tui/markdown/table.d.ts +19 -0
  83. package/dist/tui/markdown/table.js +74 -0
  84. package/dist/tui/performance-diagnostics.d.ts +7 -0
  85. package/dist/tui/performance-diagnostics.js +57 -0
  86. package/dist/tui/pickers/effort.d.ts +10 -0
  87. package/dist/tui/pickers/effort.js +12 -0
  88. package/dist/tui/pickers/file-mention.d.ts +11 -0
  89. package/dist/tui/pickers/file-mention.js +22 -0
  90. package/dist/tui/pickers/model.d.ts +10 -0
  91. package/dist/tui/pickers/model.js +12 -0
  92. package/dist/tui/pickers/permission.d.ts +10 -0
  93. package/dist/tui/pickers/permission.js +13 -0
  94. package/dist/tui/pickers/skill.d.ts +13 -0
  95. package/dist/tui/pickers/skill.js +25 -0
  96. package/dist/tui/pickers/slash.d.ts +10 -0
  97. package/dist/tui/pickers/slash.js +11 -0
  98. package/dist/tui/prompt-input.d.ts +11 -0
  99. package/dist/tui/prompt-input.js +73 -0
  100. package/dist/tui/render-mode.d.ts +20 -0
  101. package/dist/tui/render-mode.js +31 -0
  102. package/dist/tui/run.d.ts +6 -0
  103. package/dist/tui/run.js +237 -0
  104. package/dist/tui/status-line.d.ts +16 -0
  105. package/dist/tui/status-line.js +37 -0
  106. package/dist/tui/store.d.ts +4 -0
  107. package/dist/tui/store.js +380 -0
  108. package/dist/tui/timeline/live.d.ts +16 -0
  109. package/dist/tui/timeline/live.js +153 -0
  110. package/dist/tui/timeline/presentation-timeline.d.ts +15 -0
  111. package/dist/tui/timeline/presentation-timeline.js +128 -0
  112. package/dist/tui/timeline/progress.d.ts +12 -0
  113. package/dist/tui/timeline/progress.js +43 -0
  114. package/dist/tui/timeline/static-timeline.d.ts +13 -0
  115. package/dist/tui/timeline/static-timeline.js +131 -0
  116. package/dist/tui/timeline/tool-tree.d.ts +7 -0
  117. package/dist/tui/timeline/tool-tree.js +25 -0
  118. package/dist/tui/timeline/turn-items.d.ts +12 -0
  119. package/dist/tui/timeline/turn-items.js +28 -0
  120. package/dist/tui/timeline/turn.d.ts +19 -0
  121. package/dist/tui/timeline/turn.js +21 -0
  122. package/dist/tui/todo-list.d.ts +50 -0
  123. package/dist/tui/todo-list.js +120 -0
  124. package/dist/tui/transcript.d.ts +24 -0
  125. package/dist/tui/transcript.js +34 -0
  126. package/dist/tui/trust-dialog.d.ts +51 -0
  127. package/dist/tui/trust-dialog.js +127 -0
  128. package/dist/tui/types.d.ts +189 -0
  129. package/dist/tui/types.js +5 -0
  130. package/dist/tui/viewport/selection.d.ts +27 -0
  131. package/dist/tui/viewport/selection.js +79 -0
  132. package/dist/tui/viewport/sources.d.ts +29 -0
  133. package/dist/tui/viewport/sources.js +592 -0
  134. package/dist/tui/viewport/viewport.d.ts +12 -0
  135. package/dist/tui/viewport/viewport.js +91 -0
  136. package/dist/tui-input-history.d.ts +23 -0
  137. package/dist/tui-input-history.js +42 -0
  138. package/dist/tui-input-router.d.ts +24 -0
  139. package/dist/tui-input-router.js +115 -0
  140. package/dist/tui.d.ts +15 -0
  141. package/dist/tui.js +13 -0
  142. package/dist/types.d.ts +467 -0
  143. package/dist/types.js +7 -0
  144. package/package.json +58 -4
@@ -0,0 +1,173 @@
1
+ /**
2
+ * CLI 提交侧 @文件 mention 解析:从 prompt 原文提取 @ 引用,校验工作区边界后读取文件或
3
+ * 列目录一层,把有界内容以 `<system-reminder>` 附加块按出现顺序拼回 content;
4
+ * 失败路径(未找到/越界/过大/读取失败)降级为 notice,不阻断提交。
5
+ */
6
+ import { readFile as nodeReadFile, readdir as nodeReaddir, stat as nodeStat } from "node:fs/promises";
7
+ import path from "node:path";
8
+ import { FILE_MENTION_TOKEN_CHARACTER } from "./tui/input/file-mention.js";
9
+ /** 单文件扫描上限(对齐 core read_file 的 8MiB 扫描上限),超出直接拒绝。 */
10
+ const FILE_MENTION_SIZE_LIMIT = 8 * 1024 * 1024;
11
+ /** 附加内容行数上限,超出截断。 */
12
+ const FILE_MENTION_LINE_LIMIT = 2000;
13
+ /** 附加内容字节上限(UTF-8),超出截断。 */
14
+ const FILE_MENTION_BYTES_LIMIT = 256 * 1024;
15
+ /** 目录一层条目上限,超出截断并标注。 */
16
+ const FILE_MENTION_DIRECTORY_LIMIT = 100;
17
+ /** 默认接缝实现:直接使用 node:fs/promises。 */
18
+ const nodeFileMentionFs = {
19
+ async stat(absolutePath) {
20
+ const stats = await nodeStat(absolutePath);
21
+ return { isFile: stats.isFile(), isDirectory: stats.isDirectory(), size: stats.size };
22
+ },
23
+ readFile(absolutePath) {
24
+ return nodeReadFile(absolutePath, "utf8");
25
+ },
26
+ async listDirectory(absolutePath) {
27
+ const entries = await nodeReaddir(absolutePath, { withFileTypes: true });
28
+ return entries.map(function toEntry(entry) {
29
+ return { name: entry.name, isDirectory: entry.isDirectory() };
30
+ });
31
+ },
32
+ };
33
+ /** 从 prompt 原文提取 @ 引用的相对路径;`@` 必须位于行首或空白后,去重保序,`\` 归一为 `/`。 */
34
+ export function extractFileMentions(content) {
35
+ const mentions = [];
36
+ for (const match of content.matchAll(/(^|\s)@/gu)) {
37
+ // 从 @ 后取连续合法 token 字符:空白结束,中文标点等非法字符同样结束(plan 冻结的 token 字符集契约)。
38
+ const atEnd = match.index + match[0].length;
39
+ let end = atEnd;
40
+ while (end < content.length && FILE_MENTION_TOKEN_CHARACTER.test(content[end]))
41
+ end += 1;
42
+ if (end === atEnd)
43
+ continue;
44
+ const mentionPath = content.slice(atEnd, end).replace(/\\/g, "/");
45
+ if (!mentions.includes(mentionPath))
46
+ mentions.push(mentionPath);
47
+ }
48
+ return mentions;
49
+ }
50
+ /** 校验 mention 为工作区内相对路径:拒绝空路径、绝对路径、UNC/盘符与 `..` 逃逸。 */
51
+ function isInsideWorkspace(workspaceRoot, mentionPath) {
52
+ if (mentionPath === "" || mentionPath.includes("\0"))
53
+ return false;
54
+ if (mentionPath.startsWith("/") || path.win32.isAbsolute(mentionPath))
55
+ return false;
56
+ const relative = path.relative(workspaceRoot, path.resolve(workspaceRoot, mentionPath));
57
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
58
+ }
59
+ /** 按 UTF-8 字节边界截断单行文本,回退扫描避免切断多字节字符。 */
60
+ function truncateUtf8ByBytes(text, maxBytes) {
61
+ const buffer = Buffer.from(text, "utf8");
62
+ if (buffer.length <= maxBytes)
63
+ return text;
64
+ let end = maxBytes;
65
+ while (end > 0 && (buffer[end] & 0xc0) === 0x80)
66
+ end -= 1;
67
+ return buffer.subarray(0, end).toString("utf8");
68
+ }
69
+ /** 逐行累积保留前 2000 行且不超过 256KiB;单行超限时对该行做字节级截断兜底。 */
70
+ function boundedFileText(text) {
71
+ const allLines = text.split("\n");
72
+ if (allLines.length > 0 && allLines[allLines.length - 1] === "")
73
+ allLines.pop();
74
+ const selected = [];
75
+ let bytes = 0;
76
+ for (const line of allLines) {
77
+ const lineBytes = Buffer.byteLength(line, "utf8") + (selected.length > 0 ? 1 : 0);
78
+ if (selected.length >= FILE_MENTION_LINE_LIMIT || bytes + lineBytes > FILE_MENTION_BYTES_LIMIT)
79
+ break;
80
+ selected.push(line);
81
+ bytes += lineBytes;
82
+ }
83
+ if (selected.length === 0 && allLines.length > 0) {
84
+ return {
85
+ content: truncateUtf8ByBytes(allLines[0], FILE_MENTION_BYTES_LIMIT),
86
+ label: `前 256KiB,共 ${allLines.length} 行`,
87
+ };
88
+ }
89
+ if (selected.length < allLines.length) {
90
+ return { content: selected.join("\n"), label: `前 ${selected.length} 行,共 ${allLines.length} 行` };
91
+ }
92
+ return { content: selected.join("\n"), label: `${allLines.length} 行` };
93
+ }
94
+ /** 解析单个 @ 引用;成功返回附加块小节,失败写入 notice 并返回 undefined。 */
95
+ async function resolveSingleMention(mentionPath, workspaceRoot, fs, notices) {
96
+ if (!isInsideWorkspace(workspaceRoot, mentionPath)) {
97
+ notices.push(`@${mentionPath} 超出工作区边界,已拒绝读取`);
98
+ return undefined;
99
+ }
100
+ const absolutePath = path.resolve(workspaceRoot, mentionPath);
101
+ let stats;
102
+ try {
103
+ stats = await fs.stat(absolutePath);
104
+ }
105
+ catch {
106
+ notices.push(`@${mentionPath} 未找到`);
107
+ return undefined;
108
+ }
109
+ if (stats.isFile) {
110
+ if (stats.size > FILE_MENTION_SIZE_LIMIT) {
111
+ notices.push(`@${mentionPath} 超过 8MiB,已拒绝读取`);
112
+ return undefined;
113
+ }
114
+ let text;
115
+ try {
116
+ text = await fs.readFile(absolutePath);
117
+ }
118
+ catch {
119
+ notices.push(`@${mentionPath} 读取失败`);
120
+ return undefined;
121
+ }
122
+ const bounded = boundedFileText(text);
123
+ return `--- @${mentionPath} (${bounded.label}) ---\n${bounded.content}`;
124
+ }
125
+ if (stats.isDirectory) {
126
+ let entries;
127
+ try {
128
+ entries = await fs.listDirectory(absolutePath);
129
+ }
130
+ catch {
131
+ notices.push(`@${mentionPath} 读取失败`);
132
+ return undefined;
133
+ }
134
+ const sorted = entries.slice().sort(function compareEntries(left, right) {
135
+ return left.name.localeCompare(right.name);
136
+ });
137
+ const selected = sorted.slice(0, FILE_MENTION_DIRECTORY_LIMIT);
138
+ const label = sorted.length > FILE_MENTION_DIRECTORY_LIMIT
139
+ ? `前 ${FILE_MENTION_DIRECTORY_LIMIT} 项,共 ${sorted.length} 项`
140
+ : `${sorted.length} 项`;
141
+ const content = selected
142
+ .map(function renderEntry(entry) {
143
+ return entry.isDirectory ? `${entry.name}/` : entry.name;
144
+ })
145
+ .join("\n");
146
+ return `--- @${mentionPath} (${label}) ---\n${content}`;
147
+ }
148
+ notices.push(`@${mentionPath} 未找到`);
149
+ return undefined;
150
+ }
151
+ /** 解析 prompt 中的全部 @ 引用并构造增强 content;至少一个引用成功时在原文后追加附加块。 */
152
+ export async function resolveFileMentionContext(content, workspaceRoot, options = {}) {
153
+ const mentions = extractFileMentions(content);
154
+ if (mentions.length === 0)
155
+ return { content, notices: [] };
156
+ const fs = options.fs ?? nodeFileMentionFs;
157
+ const sections = [];
158
+ const notices = [];
159
+ for (const mentionPath of mentions) {
160
+ const section = await resolveSingleMention(mentionPath, workspaceRoot, fs, notices);
161
+ if (section !== undefined)
162
+ sections.push(section);
163
+ }
164
+ if (sections.length === 0)
165
+ return { content, notices };
166
+ const block = [
167
+ "<system-reminder>",
168
+ "用户通过 @ 引用了以下工作区文件,内容已直接附加:",
169
+ ...sections,
170
+ "</system-reminder>",
171
+ ].join("\n");
172
+ return { content: `${content}\n\n${block}`, notices };
173
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @autopilot-agent/cli — Step 5.2 CLI 宿主壳公开出口。
3
+ *
4
+ * 这里只暴露 parser、Composition Root、Controller、事件投影和 Plain Renderer;
5
+ * 不暴露 Core private 类型,不加载 React/Ink,也不在模块导入时启动 Runtime。
6
+ */
7
+ export { createCliCompositionRoot } from "./composition-root.js";
8
+ export type { CliCapabilityLoader, CliCompositionRoot, CreateCliCompositionRootOptions } from "./composition-root.js";
9
+ export { exitCodeForSignal, main } from "./main.js";
10
+ export type { CliMainStreams } from "./main.js";
11
+ export { createCliController, commandToAction } from "./controller.js";
12
+ export type { CliApprovalSubmit, CliController, CliControllerResult, CreateCliControllerOptions, } from "./controller.js";
13
+ export { createCliEventProjector, createInitialCliProjection, reduceCliEvent } from "./event-projector.js";
14
+ export { createPlainRenderer } from "./renderer.js";
15
+ export { parseArgv, parseSlashAction, renderHelp } from "./parser.js";
16
+ export type { CliAction, CliActionParseResult, CliCommand, CliEventEnvelope, CliEventListener, CliQueuedInputStarted, CliQueuedInputStartedListener, CliExitCode, CliHostOptions, CliInvocation, CliOutputFormat, CliPermissionMode, CliPermissionOption, CliParseResult, CliProjection, CliPublicEvent, CliRenderer, CliStatusView, CliRuntime, CliTurnStatus, } from "./types.js";
17
+ export { createCliTuiStore, runCliTui } from "./tui.js";
18
+ export type { CliTuiInitialOperation, CliTuiRecap, CliTuiSnapshot, CliTuiStore, CliTuiTranscriptItem, RunCliTuiOptions, RunCliTuiResult, } from "./tui.js";
19
+ export { formatCliTuiRecap } from "./tui.js";
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @autopilot-agent/cli — Step 5.2 CLI 宿主壳公开出口。
3
+ *
4
+ * 这里只暴露 parser、Composition Root、Controller、事件投影和 Plain Renderer;
5
+ * 不暴露 Core private 类型,不加载 React/Ink,也不在模块导入时启动 Runtime。
6
+ */
7
+ export { createCliCompositionRoot } from "./composition-root.js";
8
+ export { exitCodeForSignal, main } from "./main.js";
9
+ export { createCliController, commandToAction } from "./controller.js";
10
+ export { createCliEventProjector, createInitialCliProjection, reduceCliEvent } from "./event-projector.js";
11
+ export { createPlainRenderer } from "./renderer.js";
12
+ export { parseArgv, parseSlashAction, renderHelp } from "./parser.js";
13
+ export { createCliTuiStore, runCliTui } from "./tui.js";
14
+ export { formatCliTuiRecap } from "./tui.js";
package/dist/main.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * agent CLI 的 Step 5.3 运行入口。
4
+ *
5
+ * 入口只负责 argv、Composition Root、Plain Controller 和进程生命周期的编排;
6
+ * Session、Turn、审批、恢复和持久化规则全部委托给 Core Runtime。
7
+ */
8
+ import { type CliCompositionRoot, type CreateCliCompositionRootOptions } from "./composition-root.js";
9
+ import type { CliExitCode } from "./types.js";
10
+ /** CLI 主入口使用的最小输出/输入流接口。 */
11
+ export interface CliMainStreams {
12
+ stdout: {
13
+ write(chunk: string): void;
14
+ isTTY?: boolean;
15
+ columns?: number;
16
+ };
17
+ stderr: {
18
+ write(chunk: string): void;
19
+ isTTY?: boolean;
20
+ };
21
+ stdin?: (NodeJS.ReadableStream & {
22
+ isTTY?: boolean;
23
+ });
24
+ }
25
+ /** CLI 可监听的进程信号来源;抽象后可测试且不会残留真实监听器。 */
26
+ export interface CliSignalSource {
27
+ on(signal: "SIGINT" | "SIGTERM", listener: () => void): void;
28
+ removeListener(signal: "SIGINT" | "SIGTERM", listener: () => void): void;
29
+ }
30
+ /** CLI 运行依赖;生产入口使用真实 Composition Root 和 process,测试可注入 double。 */
31
+ export interface CliMainDependencies {
32
+ createCompositionRoot?: (options: CreateCliCompositionRootOptions) => CliCompositionRoot;
33
+ signalSource?: CliSignalSource;
34
+ forceExit?: (code: 130) => void;
35
+ cwd?: string;
36
+ }
37
+ /** 将宿主信号映射为固定 CLI 退出码。 */
38
+ export declare function exitCodeForSignal(signal: "SIGINT" | "SIGTERM"): 130 | 143;
39
+ /** 执行 Step 5.3 CLI 入口并返回固定退出码。 */
40
+ export declare function main(argv?: readonly string[], streams?: CliMainStreams, dependencies?: CliMainDependencies): Promise<CliExitCode>;