@adhdev/daemon-core 0.9.82-rc.21 → 0.9.82-rc.211

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 (269) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +1 -0
  2. package/dist/boot/process-hardening.d.ts +50 -0
  3. package/dist/chat/source-machine.d.ts +166 -0
  4. package/dist/chat/source-resolver.d.ts +104 -0
  5. package/dist/chat/subscription-updates.d.ts +1 -0
  6. package/dist/cli-adapter-types.d.ts +23 -2
  7. package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
  8. package/dist/cli-adapters/cli-state-engine.d.ts +178 -0
  9. package/dist/cli-adapters/provider-cli-adapter.d.ts +106 -63
  10. package/dist/cli-adapters/provider-cli-parse.d.ts +4 -0
  11. package/dist/cli-adapters/provider-cli-shared.d.ts +29 -0
  12. package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
  13. package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +3 -3
  14. package/dist/cli-adapters/terminal-backends/types.d.ts +1 -2
  15. package/dist/cli-adapters/terminal-screen.d.ts +2 -11
  16. package/dist/commands/cli-manager.d.ts +2 -1
  17. package/dist/commands/handler.d.ts +110 -0
  18. package/dist/commands/mesh-coordinator.d.ts +85 -1
  19. package/dist/commands/router.d.ts +32 -1
  20. package/dist/config/chat-history.d.ts +9 -0
  21. package/dist/config/config.d.ts +5 -0
  22. package/dist/config/mesh-config.d.ts +72 -1
  23. package/dist/git/git-commands.d.ts +9 -1
  24. package/dist/git/git-diff.d.ts +6 -0
  25. package/dist/git/git-types.d.ts +2 -0
  26. package/dist/index.d.ts +46 -11
  27. package/dist/index.js +35173 -15756
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +34880 -15556
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/installer.d.ts +1 -4
  32. package/dist/ipc/local-ipc-server.d.ts +91 -0
  33. package/dist/launch.d.ts +1 -1
  34. package/dist/logging/async-batch-writer.d.ts +10 -0
  35. package/dist/mesh/contracts.d.ts +164 -0
  36. package/dist/mesh/coordinator-prompt.d.ts +36 -0
  37. package/dist/mesh/coordinator-registry.d.ts +59 -0
  38. package/dist/mesh/mesh-active-work.d.ts +90 -0
  39. package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
  40. package/dist/mesh/mesh-events-coordinator.d.ts +151 -0
  41. package/dist/mesh/mesh-events-pending.d.ts +33 -0
  42. package/dist/mesh/mesh-events-stale.d.ts +40 -0
  43. package/dist/mesh/mesh-events-utils.d.ts +14 -0
  44. package/dist/mesh/mesh-events.d.ts +5 -40
  45. package/dist/mesh/mesh-fast-forward.d.ts +41 -0
  46. package/dist/mesh/mesh-host-ownership.d.ts +9 -0
  47. package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -3
  48. package/dist/mesh/mesh-ledger.d.ts +77 -1
  49. package/dist/mesh/mesh-missions.d.ts +58 -0
  50. package/dist/mesh/mesh-refine-status.d.ts +26 -0
  51. package/dist/mesh/mesh-review-inbox.d.ts +90 -0
  52. package/dist/mesh/mesh-runtime-store.d.ts +324 -0
  53. package/dist/mesh/mesh-task-stats.d.ts +49 -0
  54. package/dist/mesh/mesh-work-queue.d.ts +137 -6
  55. package/dist/mesh/preview-freshness.d.ts +18 -0
  56. package/dist/mesh/refine-config.d.ts +215 -0
  57. package/dist/mesh/worktree-bootstrap-config.d.ts +135 -0
  58. package/dist/providers/acp-provider-instance.d.ts +2 -0
  59. package/dist/providers/approval-utils.d.ts +13 -0
  60. package/dist/providers/cli-provider-instance.d.ts +39 -3
  61. package/dist/providers/contracts.d.ts +130 -6
  62. package/dist/providers/external-sources.d.ts +71 -0
  63. package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
  64. package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
  65. package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
  66. package/dist/providers/native-history/dispatcher.d.ts +27 -0
  67. package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
  68. package/dist/providers/native-history/index.d.ts +13 -0
  69. package/dist/providers/provider-instance-manager.d.ts +12 -0
  70. package/dist/providers/provider-instance.d.ts +13 -1
  71. package/dist/providers/provider-loader.d.ts +26 -4
  72. package/dist/providers/provider-trust.d.ts +31 -0
  73. package/dist/providers/read-chat-contract.d.ts +29 -0
  74. package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
  75. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +85 -0
  76. package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
  77. package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
  78. package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
  79. package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
  80. package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
  81. package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
  82. package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
  83. package/dist/providers/sdk/v1/index.d.ts +30 -0
  84. package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
  85. package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
  86. package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
  87. package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
  88. package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
  89. package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
  90. package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
  91. package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
  92. package/dist/providers/spec/adapter.d.ts +52 -0
  93. package/dist/providers/spec/cli-adapter.d.ts +101 -0
  94. package/dist/providers/spec/driver.d.ts +225 -0
  95. package/dist/providers/spec/evaluator.d.ts +54 -0
  96. package/dist/providers/spec/loader.d.ts +15 -0
  97. package/dist/providers/spec/native-history-executor.d.ts +41 -0
  98. package/dist/providers/spec/route.d.ts +4 -0
  99. package/dist/providers/spec/schema.gen.d.ts +2025 -0
  100. package/dist/providers/spec/types.d.ts +302 -0
  101. package/dist/providers/transcript-v2.d.ts +176 -0
  102. package/dist/providers/types/interactive-prompt.d.ts +48 -0
  103. package/dist/repo-mesh-types.d.ts +109 -1
  104. package/dist/sessions/registry.d.ts +3 -0
  105. package/dist/shared-types-extra.d.ts +2 -4
  106. package/dist/shared-types.d.ts +42 -1
  107. package/dist/status/normalize.d.ts +1 -1
  108. package/dist/status/normalize.js +1 -0
  109. package/dist/status/normalize.js.map +1 -1
  110. package/dist/status/normalize.mjs +1 -0
  111. package/dist/status/normalize.mjs.map +1 -1
  112. package/dist/status/reporter.d.ts +2 -0
  113. package/dist/status/snapshot.d.ts +1 -0
  114. package/dist/types.d.ts +5 -0
  115. package/package.json +6 -3
  116. package/src/agent-stream/poller.ts +2 -3
  117. package/src/boot/daemon-lifecycle.ts +22 -10
  118. package/src/boot/process-hardening.ts +89 -0
  119. package/src/chat/source-machine.ts +534 -0
  120. package/src/chat/source-resolver.ts +0 -0
  121. package/src/chat/subscription-updates.ts +20 -1
  122. package/src/cli-adapter-types.d.ts +1 -0
  123. package/src/cli-adapter-types.ts +22 -2
  124. package/src/cli-adapters/cli-script-runner.ts +421 -0
  125. package/src/cli-adapters/cli-state-engine.ts +1138 -0
  126. package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
  127. package/src/cli-adapters/provider-cli-adapter.ts +673 -1139
  128. package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
  129. package/src/cli-adapters/provider-cli-parse.ts +13 -0
  130. package/src/cli-adapters/provider-cli-runtime.ts +3 -1
  131. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  132. package/src/cli-adapters/provider-cli-shared.ts +59 -11
  133. package/src/cli-adapters/raw-terminal-io.ts +252 -0
  134. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +35 -29
  135. package/src/cli-adapters/terminal-backends/types.ts +1 -3
  136. package/src/cli-adapters/terminal-screen.ts +11 -81
  137. package/src/commands/chat-commands.ts +1787 -60
  138. package/src/commands/cli-manager.ts +283 -14
  139. package/src/commands/handler.ts +809 -2
  140. package/src/commands/mesh-coordinator.ts +332 -122
  141. package/src/commands/router.ts +4198 -557
  142. package/src/config/chat-history.ts +95 -24
  143. package/src/config/config.ts +12 -0
  144. package/src/config/mesh-config.ts +280 -2
  145. package/src/config/recent-activity.ts +8 -2
  146. package/src/daemon/dev-auto-implement.ts +3 -2
  147. package/src/daemon/dev-cli-debug.ts +10 -1
  148. package/src/detection/ide-detector.ts +26 -16
  149. package/src/git/git-commands.ts +42 -12
  150. package/src/git/git-diff.ts +53 -0
  151. package/src/git/git-status.ts +35 -6
  152. package/src/git/git-types.ts +2 -0
  153. package/src/git/git-worktree.ts +8 -1
  154. package/src/index.ts +126 -10
  155. package/src/installer.d.ts +1 -1
  156. package/src/installer.ts +8 -6
  157. package/src/ipc/local-ipc-server.ts +278 -0
  158. package/src/launch.d.ts +1 -1
  159. package/src/launch.ts +37 -28
  160. package/src/logging/async-batch-writer.ts +55 -0
  161. package/src/logging/logger.ts +2 -1
  162. package/src/mesh/contracts.ts +329 -0
  163. package/src/mesh/coordinator-prompt.ts +218 -31
  164. package/src/mesh/coordinator-registry.ts +121 -0
  165. package/src/mesh/mesh-active-work.ts +437 -0
  166. package/src/mesh/mesh-delivery-policy.ts +315 -0
  167. package/src/mesh/mesh-events-coordinator.ts +1404 -0
  168. package/src/mesh/mesh-events-pending.ts +371 -0
  169. package/src/mesh/mesh-events-stale.ts +283 -0
  170. package/src/mesh/mesh-events-utils.ts +161 -0
  171. package/src/mesh/mesh-events.ts +24 -908
  172. package/src/mesh/mesh-fast-forward.ts +438 -0
  173. package/src/mesh/mesh-host-ownership.ts +73 -0
  174. package/src/mesh/mesh-ledger-reconciliation.ts +12 -5
  175. package/src/mesh/mesh-ledger.ts +587 -102
  176. package/src/mesh/mesh-missions.ts +151 -0
  177. package/src/mesh/mesh-refine-status.ts +144 -0
  178. package/src/mesh/mesh-review-inbox.ts +307 -0
  179. package/src/mesh/mesh-runtime-store.ts +1305 -0
  180. package/src/mesh/mesh-task-stats.ts +154 -0
  181. package/src/mesh/mesh-work-queue.ts +490 -166
  182. package/src/mesh/preview-freshness.ts +118 -0
  183. package/src/mesh/refine-config.ts +403 -0
  184. package/src/mesh/worktree-bootstrap-config.ts +326 -0
  185. package/src/providers/acp-provider-instance.ts +15 -1
  186. package/src/providers/approval-utils.d.ts +4 -0
  187. package/src/providers/approval-utils.ts +47 -5
  188. package/src/providers/chat-message-normalization.ts +4 -11
  189. package/src/providers/cli-provider-instance.ts +833 -64
  190. package/src/providers/contracts.d.ts +55 -0
  191. package/src/providers/contracts.ts +141 -6
  192. package/src/providers/external-sources.ts +218 -0
  193. package/src/providers/ide-provider-instance.ts +19 -5
  194. package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
  195. package/src/providers/native-history/claude-cli-transcript.ts +396 -0
  196. package/src/providers/native-history/codex-cli-transcript.ts +419 -0
  197. package/src/providers/native-history/dispatcher.ts +340 -0
  198. package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
  199. package/src/providers/native-history/index.ts +30 -0
  200. package/src/providers/provider-instance-manager.ts +30 -0
  201. package/src/providers/provider-instance.ts +10 -1
  202. package/src/providers/provider-loader.ts +582 -50
  203. package/src/providers/provider-schema.ts +87 -14
  204. package/src/providers/provider-trust.ts +114 -0
  205. package/src/providers/read-chat-contract.ts +76 -16
  206. package/src/providers/sdk/README.md +49 -0
  207. package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
  208. package/src/providers/sdk/v1/builders/cli/detect-status.ts +262 -0
  209. package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
  210. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
  211. package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
  212. package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
  213. package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
  214. package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
  215. package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
  216. package/src/providers/sdk/v1/index.ts +152 -0
  217. package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
  218. package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
  219. package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
  220. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +504 -0
  221. package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
  222. package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
  223. package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
  224. package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
  225. package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
  226. package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
  227. package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
  228. package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
  229. package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
  230. package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
  231. package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
  232. package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
  233. package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
  234. package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
  235. package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
  236. package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
  237. package/src/providers/sdk/v1/types/cli/index.ts +365 -0
  238. package/src/providers/sdk/v1/types/common/index.ts +229 -0
  239. package/src/providers/sdk/v1/validators/index.ts +19 -0
  240. package/src/providers/sdk/v1/validators/manifest.ts +110 -0
  241. package/src/providers/sdk/v1/validators/taint.ts +309 -0
  242. package/src/providers/spec/adapter.ts +138 -0
  243. package/src/providers/spec/cli-adapter.ts +626 -0
  244. package/src/providers/spec/driver.ts +922 -0
  245. package/src/providers/spec/evaluator.ts +597 -0
  246. package/src/providers/spec/loader.ts +324 -0
  247. package/src/providers/spec/native-history-executor.ts +939 -0
  248. package/src/providers/spec/route.ts +51 -0
  249. package/src/providers/spec/schema.gen.ts +691 -0
  250. package/src/providers/spec/schema.json +341 -0
  251. package/src/providers/spec/types.ts +356 -0
  252. package/src/providers/transcript-v2.ts +567 -0
  253. package/src/providers/types/interactive-prompt.ts +425 -0
  254. package/src/providers/version-archive.ts +38 -20
  255. package/src/repo-mesh-types.ts +119 -1
  256. package/src/sessions/registry.ts +3 -0
  257. package/src/shared-types-extra.ts +2 -4
  258. package/src/shared-types.ts +45 -1
  259. package/src/status/builders.ts +24 -6
  260. package/src/status/normalize.ts +2 -0
  261. package/src/status/reporter.ts +15 -0
  262. package/src/status/snapshot.ts +84 -25
  263. package/src/system/host-memory.ts +29 -12
  264. package/src/types.ts +5 -0
  265. package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
  266. package/dist/mesh/mesh-sync.d.ts +0 -53
  267. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
  268. package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
  269. package/src/mesh/mesh-sync.ts +0 -111
@@ -0,0 +1,425 @@
1
+ export interface InteractivePrompt {
2
+ promptId: string;
3
+ origin: 'cli' | 'mcp' | 'agent';
4
+ providerType: string;
5
+ createdAt: number;
6
+ questions: InteractiveQuestion[];
7
+ }
8
+
9
+ export interface InteractiveQuestion {
10
+ questionId: string;
11
+ question: string;
12
+ header?: string;
13
+ multiSelect: boolean;
14
+ options: InteractiveOption[];
15
+ allowFreeform?: boolean;
16
+ }
17
+
18
+ export interface InteractiveOption {
19
+ label: string;
20
+ description?: string;
21
+ preview?: string;
22
+ }
23
+
24
+ export interface InteractivePromptResponse {
25
+ promptId: string;
26
+ answers: Record<string, InteractiveAnswer>;
27
+ }
28
+
29
+ export interface InteractiveAnswer {
30
+ selectedLabels: string[];
31
+ freeformText?: string;
32
+ }
33
+
34
+ function readString(value: unknown): string | undefined {
35
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
36
+ }
37
+
38
+ function readStringArray(value: unknown): string[] {
39
+ return Array.isArray(value)
40
+ ? value.map((item) => readString(item)).filter((item): item is string => !!item)
41
+ : [];
42
+ }
43
+
44
+ function normalizeOption(raw: unknown): InteractiveOption | null {
45
+ if (typeof raw === 'string') {
46
+ const label = raw.trim();
47
+ return label ? { label } : null;
48
+ }
49
+ if (!raw || typeof raw !== 'object') return null;
50
+ const record = raw as Record<string, unknown>;
51
+ const label = readString(record.label);
52
+ if (!label) return null;
53
+ const description = readString(record.description);
54
+ const preview = readString(record.preview);
55
+ return {
56
+ label,
57
+ ...(description ? { description } : {}),
58
+ ...(preview ? { preview } : {}),
59
+ };
60
+ }
61
+
62
+ function normalizeQuestion(raw: unknown, index: number): InteractiveQuestion | null {
63
+ if (!raw || typeof raw !== 'object') return null;
64
+ const record = raw as Record<string, unknown>;
65
+ const question = readString(record.question);
66
+ if (!question) return null;
67
+ const questionId = readString(record.questionId) || readString(record.id) || `q${index + 1}`;
68
+ const options = Array.isArray(record.options)
69
+ ? record.options.map(normalizeOption).filter((item): item is InteractiveOption => !!item)
70
+ : [];
71
+ const header = readString(record.header);
72
+ return {
73
+ questionId,
74
+ question,
75
+ ...(header ? { header } : {}),
76
+ multiSelect: record.multiSelect === true,
77
+ options,
78
+ ...(record.allowFreeform === true ? { allowFreeform: true } : {}),
79
+ };
80
+ }
81
+
82
+ export function normalizeInteractivePrompt(raw: unknown): InteractivePrompt | null {
83
+ if (!raw || typeof raw !== 'object') return null;
84
+ const record = raw as Record<string, unknown>;
85
+ const promptId = readString(record.promptId);
86
+ const providerType = readString(record.providerType);
87
+ const origin = record.origin === 'mcp' || record.origin === 'agent' ? record.origin : 'cli';
88
+ const questions = Array.isArray(record.questions)
89
+ ? record.questions.map(normalizeQuestion).filter((item): item is InteractiveQuestion => !!item)
90
+ : [];
91
+ if (!promptId || !providerType || questions.length === 0) return null;
92
+ const createdAt = typeof record.createdAt === 'number' && Number.isFinite(record.createdAt)
93
+ ? record.createdAt
94
+ : Date.now();
95
+ return { promptId, origin, providerType, createdAt, questions };
96
+ }
97
+
98
+ export function normalizeInteractivePromptResponse(raw: unknown): InteractivePromptResponse {
99
+ if (!raw || typeof raw !== 'object') throw new Error('Interactive prompt response must be an object');
100
+ const record = raw as Record<string, unknown>;
101
+ const promptId = readString(record.promptId);
102
+ if (!promptId) throw new Error('promptId must be a non-empty string');
103
+ if (!record.answers || typeof record.answers !== 'object' || Array.isArray(record.answers)) {
104
+ throw new Error('answers must be an object');
105
+ }
106
+ const answers: Record<string, InteractiveAnswer> = {};
107
+ for (const [questionId, answerRaw] of Object.entries(record.answers as Record<string, unknown>)) {
108
+ if (!answerRaw || typeof answerRaw !== 'object' || Array.isArray(answerRaw)) continue;
109
+ const answer = answerRaw as Record<string, unknown>;
110
+ const selectedLabels = readStringArray(answer.selectedLabels);
111
+ const freeformText = readString(answer.freeformText);
112
+ answers[questionId] = {
113
+ selectedLabels,
114
+ ...(freeformText ? { freeformText } : {}),
115
+ };
116
+ }
117
+ return { promptId, answers };
118
+ }
119
+
120
+ export function buildClaudeInteractiveToolResult(response: InteractivePromptResponse): string {
121
+ return JSON.stringify({
122
+ type: 'user',
123
+ message: {
124
+ role: 'user',
125
+ content: [{
126
+ type: 'tool_result',
127
+ tool_use_id: response.promptId,
128
+ content: JSON.stringify({ answers: response.answers }),
129
+ is_error: false,
130
+ }],
131
+ },
132
+ });
133
+ }
134
+
135
+ export interface ClaudeInteractiveTuiPage {
136
+ screenText: string;
137
+ header?: string;
138
+ }
139
+
140
+ const CLAUDE_TUI_OPTION_PATTERN = /^\s*(?:[❯›>]\s*)?(\d+)\.\s+(.+?)\s*$/;
141
+
142
+ function claudeTuiQuestionHeaders(screenText: string): string[] {
143
+ const navLine = screenText.split(/\r?\n/).find(line => line.includes('✔ Submit') && /[☐☒]/.test(line));
144
+ if (!navLine) return [];
145
+ const headers: string[] = [];
146
+ const pattern = /[☐☒]\s+(.+?)(?=\s+[☐☒]|\s+✔\s+Submit)/g;
147
+ for (const match of navLine.matchAll(pattern)) {
148
+ const header = readString(match[1]);
149
+ if (header) headers.push(header);
150
+ }
151
+ return headers;
152
+ }
153
+
154
+ function isClaudeTuiSelectFooter(text: string): boolean {
155
+ return /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
156
+ }
157
+
158
+ function readClaudeHeaderLine(lines: string[], beforeIndex: number): string | undefined {
159
+ for (let i = beforeIndex; i >= 0; i -= 1) {
160
+ const candidate = lines[i].trim();
161
+ if (!candidate) continue;
162
+ const match = candidate.match(/^[☐☒]\s+(.+?)\s*$/);
163
+ if (match?.[1]) return readString(match[1]);
164
+ if (/^─+$/.test(candidate)) break;
165
+ }
166
+ return undefined;
167
+ }
168
+
169
+ function readClaudeOptionDescription(lines: string[], optionLineIndex: number): string | undefined {
170
+ const nextLine = lines[optionLineIndex + 1];
171
+ const next = nextLine?.trim();
172
+ if (!next
173
+ || CLAUDE_TUI_OPTION_PATTERN.test(nextLine)
174
+ || /^─+$/.test(next)
175
+ || /^Enter to select\b/i.test(next)
176
+ || /^[☐☒]\s+/.test(next)) {
177
+ return undefined;
178
+ }
179
+ return next;
180
+ }
181
+
182
+ function parseClaudeHeaderlessInteractiveTuiQuestion(page: ClaudeInteractiveTuiPage, index: number): InteractiveQuestion | null {
183
+ if (!isClaudeTuiSelectFooter(page.screenText)) return null;
184
+ if (!/Type something\.?|Chat about this/i.test(page.screenText)) return null;
185
+
186
+ const lines = page.screenText.split(/\r?\n/);
187
+ let footerIndex = -1;
188
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
189
+ if (/Enter to select/i.test(lines[i])) {
190
+ footerIndex = i;
191
+ break;
192
+ }
193
+ }
194
+ if (footerIndex < 0) return null;
195
+
196
+ let optionBlockEnd = footerIndex - 1;
197
+ for (let i = footerIndex - 1; i >= 0; i -= 1) {
198
+ if (/^─+$/.test(lines[i].trim())) {
199
+ optionBlockEnd = i - 1;
200
+ break;
201
+ }
202
+ }
203
+
204
+ const optionLineIndexes: number[] = [];
205
+ for (let i = optionBlockEnd; i >= 0; i -= 1) {
206
+ const line = lines[i];
207
+ if (CLAUDE_TUI_OPTION_PATTERN.test(line)) {
208
+ optionLineIndexes.push(i);
209
+ continue;
210
+ }
211
+ if (optionLineIndexes.length > 0 && (!line.trim() || /^─+$/.test(line.trim()))) break;
212
+ }
213
+ optionLineIndexes.reverse();
214
+ if (optionLineIndexes.length === 0) return null;
215
+
216
+ const firstOptionIndex = optionLineIndexes[0];
217
+ let question = '';
218
+ for (let i = firstOptionIndex - 1; i >= 0; i -= 1) {
219
+ const candidate = lines[i].trim();
220
+ if (!candidate || /^─+$/.test(candidate)) continue;
221
+ // Standalone ☐/☒ markers (decorative section dividers in the headered
222
+ // variant) are not the question — keep skipping them.
223
+ if (/^[☐☒]\s*$/.test(candidate)) continue;
224
+ // The headerless variant introduced in claude-cli >=2.1 prefixes the
225
+ // actual question with `☐ ` (e.g. "☐ RPS R1 1라운드 — …"). Previously
226
+ // we skipped any ☐/☒ line and returned null, never opening the picker.
227
+ // Strip the marker so the dashboard label matches the on-screen text.
228
+ const markerMatch = candidate.match(/^[☐☒]\s+(.+)$/);
229
+ if (markerMatch) {
230
+ question = markerMatch[1].trim();
231
+ break;
232
+ }
233
+ question = candidate;
234
+ break;
235
+ }
236
+ if (!question) return null;
237
+
238
+ const options: InteractiveOption[] = [];
239
+ let allowFreeform = false;
240
+ for (const optionLineIndex of optionLineIndexes) {
241
+ const match = lines[optionLineIndex].match(CLAUDE_TUI_OPTION_PATTERN);
242
+ if (!match) continue;
243
+ const label = match[2].trim();
244
+ if (/^Chat about this$/i.test(label)) continue;
245
+ if (/^Type something\.?$/i.test(label)) allowFreeform = true;
246
+
247
+ const description = readClaudeOptionDescription(lines, optionLineIndex);
248
+ options.push({ label, ...(description ? { description } : {}) });
249
+ }
250
+ if (options.length === 0) return null;
251
+
252
+ const header = readString(page.header) || readClaudeHeaderLine(lines, firstOptionIndex - 1);
253
+ return {
254
+ questionId: `q${index + 1}`,
255
+ question,
256
+ ...(header ? { header } : {}),
257
+ multiSelect: /Space to select|toggle selections/i.test(page.screenText),
258
+ options,
259
+ ...(allowFreeform ? { allowFreeform: true } : {}),
260
+ };
261
+ }
262
+
263
+ function parseClaudeInteractiveTuiQuestion(page: ClaudeInteractiveTuiPage, index: number): InteractiveQuestion | null {
264
+ const lines = page.screenText.split(/\r?\n/);
265
+ let navIndex = -1;
266
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
267
+ if (lines[i].includes('✔ Submit') && /[☐☒]/.test(lines[i])) {
268
+ navIndex = i;
269
+ break;
270
+ }
271
+ }
272
+ if (navIndex < 0) return parseClaudeHeaderlessInteractiveTuiQuestion(page, index);
273
+ if (!page.screenText.includes('Enter to select')) return null;
274
+
275
+ let question = '';
276
+ let questionLineIndex = -1;
277
+ for (let i = navIndex + 1; i < lines.length; i += 1) {
278
+ const candidate = lines[i].trim();
279
+ if (!candidate || /^─+$/.test(candidate)) continue;
280
+ if (candidate === 'Review your answers' || candidate === 'Ready to submit your answers?') return null;
281
+ question = candidate;
282
+ questionLineIndex = i;
283
+ break;
284
+ }
285
+ if (!question) return null;
286
+
287
+ const options: InteractiveOption[] = [];
288
+ let allowFreeform = false;
289
+ for (let i = questionLineIndex + 1; i < lines.length; i += 1) {
290
+ const match = lines[i].match(CLAUDE_TUI_OPTION_PATTERN);
291
+ if (!match) continue;
292
+ const label = match[2].trim();
293
+ if (/^Type something\.?$/i.test(label)) {
294
+ allowFreeform = true;
295
+ continue;
296
+ }
297
+ if (/^Chat about this$/i.test(label)) continue;
298
+
299
+ let description: string | undefined;
300
+ const nextLine = lines[i + 1]?.trim();
301
+ if (nextLine
302
+ && !CLAUDE_TUI_OPTION_PATTERN.test(lines[i + 1])
303
+ && !/^─+$/.test(nextLine)
304
+ && !/^Enter to select\b/.test(nextLine)) {
305
+ description = nextLine;
306
+ }
307
+ options.push({ label, ...(description ? { description } : {}) });
308
+ }
309
+ if (options.length === 0) return null;
310
+
311
+ const header = readString(page.header);
312
+ return {
313
+ questionId: `q${index + 1}`,
314
+ question,
315
+ ...(header ? { header } : {}),
316
+ multiSelect: /Space to select|toggle selections/i.test(page.screenText),
317
+ options,
318
+ ...(allowFreeform ? { allowFreeform: true } : {}),
319
+ };
320
+ }
321
+
322
+ export function detectClaudeAskUserQuestionPromptFromTuiPages(
323
+ pages: ClaudeInteractiveTuiPage[],
324
+ options: { promptId: string; providerType?: string; createdAt?: number },
325
+ ): InteractivePrompt | null {
326
+ if (pages.length === 0) return null;
327
+ const headers = claudeTuiQuestionHeaders(pages[0].screenText);
328
+ const questions = pages.map((page, index) => parseClaudeInteractiveTuiQuestion({
329
+ ...page,
330
+ header: page.header || headers[index],
331
+ }, index)).filter((question): question is InteractiveQuestion => !!question);
332
+ if (questions.length !== pages.length) return null;
333
+ return {
334
+ promptId: options.promptId,
335
+ origin: 'cli',
336
+ providerType: options.providerType || 'claude-cli',
337
+ createdAt: options.createdAt || Date.now(),
338
+ questions,
339
+ };
340
+ }
341
+
342
+ export function buildClaudeInteractiveTuiAnswerSteps(
343
+ prompt: InteractivePrompt,
344
+ response: InteractivePromptResponse,
345
+ ): string[] {
346
+ if (response.promptId !== prompt.promptId) throw new Error('Interactive prompt response does not match active prompt');
347
+ const steps: string[] = [];
348
+ for (const question of prompt.questions) {
349
+ if (question.multiSelect) throw new Error('Claude TUI multi-select prompts are not supported yet');
350
+ const answer = response.answers[question.questionId];
351
+ if (!answer) throw new Error(`Missing answer for ${question.questionId}`);
352
+ const freeformText = answer.freeformText?.trim() ?? '';
353
+ if (freeformText) {
354
+ // Freeform: select the "Type something." option (always the last visible
355
+ // option before "Chat about this"), then type the text and confirm.
356
+ const typeOptionIndex = question.options.findIndex(o => /^Type something\.?$/i.test(o.label));
357
+ const optionNumber = typeOptionIndex >= 0 ? typeOptionIndex + 1 : question.options.length;
358
+ steps.push(String(optionNumber));
359
+ // Type the text character by character, then Enter to confirm.
360
+ for (const ch of freeformText) steps.push(ch);
361
+ steps.push('\r');
362
+ } else {
363
+ if (answer.selectedLabels.length !== 1) throw new Error(`Expected one selected label for ${question.questionId}`);
364
+ const selectedIndex = question.options.findIndex(option => option.label === answer.selectedLabels[0]);
365
+ if (selectedIndex < 0) throw new Error(`Unknown option for ${question.questionId}: ${answer.selectedLabels[0]}`);
366
+ // Use numeric key (1-based) to jump directly to the option. This avoids
367
+ // cursor-position drift between questions that arrow-key navigation suffers
368
+ // from — the TUI accepts a digit key to jump straight to that option index.
369
+ steps.push(String(selectedIndex + 1));
370
+ }
371
+ }
372
+ // After all questions are answered, Claude TUI shows a final confirm screen
373
+ // ("Submit answers" / "Cancel"). The first option is pre-selected, so a
374
+ // single Enter confirms and submits.
375
+ steps.push('\r');
376
+ return steps;
377
+ }
378
+
379
+ export function interactivePromptFromClaudeAskUserQuestion(input: unknown, options: {
380
+ promptId: string;
381
+ providerType: string;
382
+ createdAt?: number;
383
+ origin?: InteractivePrompt['origin'];
384
+ }): InteractivePrompt | null {
385
+ if (!input || typeof input !== 'object') return null;
386
+ const record = input as Record<string, unknown>;
387
+ const questions = Array.isArray(record.questions)
388
+ ? record.questions.map(normalizeQuestion).filter((item): item is InteractiveQuestion => !!item)
389
+ : [];
390
+ if (questions.length === 0) return null;
391
+ return {
392
+ promptId: options.promptId,
393
+ origin: options.origin || 'cli',
394
+ providerType: options.providerType,
395
+ createdAt: options.createdAt || Date.now(),
396
+ questions,
397
+ };
398
+ }
399
+
400
+ export function detectClaudeAskUserQuestionPromptFromJson(value: unknown, providerType = 'claude-cli'): InteractivePrompt | null {
401
+ if (!value || typeof value !== 'object') return null;
402
+ const record = value as Record<string, unknown>;
403
+ const blocks: unknown[] = [];
404
+ if (Array.isArray(record.content)) blocks.push(...record.content);
405
+ const message = record.message;
406
+ if (message && typeof message === 'object' && Array.isArray((message as Record<string, unknown>).content)) {
407
+ blocks.push(...((message as Record<string, unknown>).content as unknown[]));
408
+ }
409
+ if (record.type === 'tool_use') blocks.push(record);
410
+
411
+ for (const block of blocks) {
412
+ if (!block || typeof block !== 'object') continue;
413
+ const b = block as Record<string, unknown>;
414
+ const name = readString(b.name);
415
+ if (b.type !== 'tool_use' || name !== 'AskUserQuestion') continue;
416
+ const id = readString(b.id) || readString(record.id) || `ask-user-${Date.now()}`;
417
+ const prompt = interactivePromptFromClaudeAskUserQuestion(b.input, {
418
+ promptId: id,
419
+ providerType,
420
+ origin: 'cli',
421
+ });
422
+ if (prompt) return prompt;
423
+ }
424
+ return null;
425
+ }
@@ -12,7 +12,7 @@
12
12
  import * as fs from 'fs';
13
13
  import * as path from 'path';
14
14
  import * as os from 'os';
15
- import { execSync } from 'child_process';
15
+ // Removed execSync import
16
16
  import { platform } from 'os';
17
17
  import type { ProviderLoader } from './provider-loader.js';
18
18
  import type { ProviderModule } from './contracts.js';
@@ -117,22 +117,40 @@ export class VersionArchive {
117
117
 
118
118
  // ─── Version Detection ──────────────────────────────
119
119
 
120
- function runCommand(cmd: string, timeout = 10000): string | null {
121
- try {
122
- return execSync(cmd, {
120
+ import { exec } from 'child_process';
121
+
122
+ async function runCommand(cmd: string, timeout = 10000): Promise<string | null> {
123
+ return new Promise((resolve) => {
124
+ exec(cmd, {
123
125
  encoding: 'utf-8',
124
126
  timeout,
125
- stdio: ['pipe', 'pipe', 'pipe'],
126
- }).trim();
127
- } catch {
128
- return null;
129
- }
127
+ }, (error, stdout) => {
128
+ if (error) return resolve(null);
129
+ resolve(stdout.trim());
130
+ });
131
+ });
130
132
  }
131
133
 
132
134
  function findBinary(name: string): string | null {
133
- const cmd = platform() === 'win32' ? `where ${name}` : `which ${name}`;
134
- const result = runCommand(cmd, 5000);
135
- return result ? result.split('\n')[0] : null;
135
+ const isWin = platform() === 'win32';
136
+ const paths = (process.env.PATH || '').split(isWin ? ';' : ':');
137
+ const exes = isWin ? ['.exe', '.cmd', '.bat', ''] : [''];
138
+
139
+ for (const p of paths) {
140
+ if (!p) continue;
141
+ for (const ext of exes) {
142
+ const fullPath = path.join(p, name + ext);
143
+ try {
144
+ if (fs.existsSync(fullPath)) {
145
+ const stat = fs.statSync(fullPath);
146
+ if (stat.isFile() && (isWin || (stat.mode & 0o111))) {
147
+ return fullPath;
148
+ }
149
+ }
150
+ } catch { }
151
+ }
152
+ }
153
+ return null;
136
154
  }
137
155
 
138
156
  /** Extract version string from CLI output */
@@ -162,16 +180,16 @@ function getPlatformVersionCommand(
162
180
  return undefined;
163
181
  }
164
182
 
165
- function getVersion(binary: string, versionCommand?: string): string | null {
183
+ async function getVersion(binary: string, versionCommand?: string): Promise<string | null> {
166
184
  // Custom version command from provider.json
167
185
  if (versionCommand) {
168
- const raw = runCommand(versionCommand);
186
+ const raw = await runCommand(versionCommand);
169
187
  return raw ? parseVersion(raw) : null;
170
188
  }
171
189
 
172
190
  // Default: try --version, then -V, then -v
173
191
  for (const flag of ['--version', '-V', '-v']) {
174
- const raw = runCommand(`"${binary}" ${flag}`);
192
+ const raw = await runCommand(`"${binary}" ${flag}`);
175
193
  if (raw && raw.length < 500) return parseVersion(raw);
176
194
  }
177
195
  return null;
@@ -191,11 +209,11 @@ function checkPathExists(paths: string[]): string | null {
191
209
  }
192
210
 
193
211
  /** macOS: Get app version from Info.plist */
194
- function getMacAppVersion(appPath: string): string | null {
212
+ async function getMacAppVersion(appPath: string): Promise<string | null> {
195
213
  if (platform() !== 'darwin' || !appPath.endsWith('.app')) return null;
196
214
  const plistPath = path.join(appPath, 'Contents', 'Info.plist');
197
215
  if (!fs.existsSync(plistPath)) return null;
198
- const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
216
+ const raw = await runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
199
217
  return raw || null;
200
218
  }
201
219
 
@@ -242,10 +260,10 @@ export async function detectAllVersions(
242
260
 
243
261
  // Version: try CLI first, then plist
244
262
  if (resolvedBin) {
245
- info.version = getVersion(resolvedBin, versionCommand);
263
+ info.version = await getVersion(resolvedBin, versionCommand);
246
264
  }
247
265
  if (!info.version && appPath) {
248
- info.version = getMacAppVersion(appPath);
266
+ info.version = await getMacAppVersion(appPath);
249
267
  }
250
268
 
251
269
  } else if (provider.category === 'cli' || provider.category === 'acp') {
@@ -256,7 +274,7 @@ export async function detectAllVersions(
256
274
  info.binary = binPath || null;
257
275
 
258
276
  if (binPath) {
259
- info.version = getVersion(binPath, versionCommand);
277
+ info.version = await getVersion(binPath, versionCommand);
260
278
  }
261
279
 
262
280
  } else if (provider.category === 'extension') {