howtofixcli 0.7.13 → 0.7.15

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 (161) hide show
  1. package/dist/chunk-27TRKD65.js +2 -0
  2. package/dist/chunk-3FYCFLNK.js +1 -0
  3. package/dist/chunk-56SEAGAQ.js +1 -0
  4. package/dist/chunk-6FUWMLWO.js +1 -0
  5. package/dist/chunk-7CHDNTH6.js +1 -0
  6. package/dist/chunk-7DTQKDO4.js +2 -0
  7. package/dist/chunk-AGGIYXN7.js +2 -0
  8. package/dist/chunk-AUDMRGWF.js +189 -0
  9. package/dist/chunk-CPJKKHGH.js +1 -0
  10. package/dist/chunk-DAPXGSGD.js +12 -0
  11. package/dist/chunk-DOD7MUGX.js +1 -0
  12. package/dist/chunk-DV2BBBBJ.js +2 -0
  13. package/dist/chunk-EIBOS5KO.js +5 -0
  14. package/dist/chunk-EYWOVCSS.js +6 -0
  15. package/dist/chunk-FPXVDP32.js +8 -0
  16. package/dist/chunk-GXB7RZHH.js +1 -0
  17. package/dist/chunk-HT3MFLAV.js +34 -0
  18. package/dist/chunk-ILDDDLRC.js +1 -0
  19. package/dist/chunk-JFYVRWJI.js +2 -0
  20. package/dist/chunk-JMKK26AT.js +2 -0
  21. package/dist/chunk-LAHVHRCS.js +2 -0
  22. package/dist/chunk-LOOFOBTS.js +1 -0
  23. package/dist/chunk-LSJC73DG.js +1 -0
  24. package/dist/chunk-MOKUBKE7.js +1 -0
  25. package/dist/chunk-N345KWWK.js +1 -0
  26. package/dist/chunk-ONZLNFQ2.js +3 -0
  27. package/dist/chunk-ORJ2QZBG.js +3 -0
  28. package/dist/chunk-PCALBZOO.js +2 -0
  29. package/dist/chunk-SX5VE6XA.js +110 -0
  30. package/dist/chunk-TSEKPGK4.js +1 -0
  31. package/dist/chunk-UNK5HRQR.js +1 -0
  32. package/dist/chunk-UTYFLLSM.js +1 -0
  33. package/dist/chunk-V2RPZMLK.js +1 -0
  34. package/dist/chunk-V3R4FYTJ.js +1 -0
  35. package/dist/chunk-V656XVXV.js +8 -0
  36. package/dist/chunk-VLMQSD25.js +1 -0
  37. package/dist/chunk-WKUJ7CFO.js +1 -0
  38. package/dist/chunk-WOT6VMZA.js +1 -0
  39. package/dist/chunk-WUPU6LTD.js +4 -0
  40. package/dist/chunk-WX3V5P7U.js +5 -0
  41. package/dist/chunk-Y7YM5R75.js +5 -0
  42. package/dist/chunk-YW7NORH3.js +2 -0
  43. package/dist/main.js +99 -540
  44. package/package.json +17 -64
  45. package/dist/account-credential.js +0 -136
  46. package/dist/account-credential.test.js +0 -38
  47. package/dist/activity.js +0 -124
  48. package/dist/agent-defs.js +0 -237
  49. package/dist/agent-limiter.js +0 -68
  50. package/dist/agents.js +0 -554
  51. package/dist/anim.js +0 -361
  52. package/dist/args.js +0 -228
  53. package/dist/at-file.js +0 -28
  54. package/dist/brand-extension.js +0 -87
  55. package/dist/brand.js +0 -76
  56. package/dist/browser.js +0 -58
  57. package/dist/cache-report.js +0 -139
  58. package/dist/checkpoint.js +0 -437
  59. package/dist/claims.js +0 -167
  60. package/dist/cli-auth.js +0 -142
  61. package/dist/cli-checkin.js +0 -120
  62. package/dist/cli-credentials.js +0 -254
  63. package/dist/clipboard.js +0 -149
  64. package/dist/collapse.js +0 -129
  65. package/dist/commands.js +0 -294
  66. package/dist/compaction-guard.js +0 -81
  67. package/dist/context-extension.js +0 -17
  68. package/dist/dcp/commands.js +0 -172
  69. package/dist/dcp/compress-state.js +0 -139
  70. package/dist/dcp/compress.js +0 -329
  71. package/dist/dcp/dcp-extension.js +0 -295
  72. package/dist/dcp/dedup.js +0 -93
  73. package/dist/dcp/ids.js +0 -189
  74. package/dist/dcp/index.js +0 -25
  75. package/dist/dcp/middle-prune.js +0 -76
  76. package/dist/dcp/prompts.js +0 -68
  77. package/dist/dcp/protected-content.js +0 -221
  78. package/dist/dcp/protected.js +0 -161
  79. package/dist/dcp/prune.js +0 -418
  80. package/dist/dcp/state.js +0 -176
  81. package/dist/device-id.js +0 -90
  82. package/dist/device-id.test.js +0 -23
  83. package/dist/errors.js +0 -63
  84. package/dist/extensions.js +0 -48
  85. package/dist/fatal.js +0 -35
  86. package/dist/feed.js +0 -498
  87. package/dist/fs-atomic.js +0 -54
  88. package/dist/gateway-errors.js +0 -90
  89. package/dist/git.js +0 -230
  90. package/dist/goal-extension.js +0 -88
  91. package/dist/goal.js +0 -222
  92. package/dist/hooks-extension.js +0 -176
  93. package/dist/hooks.js +0 -443
  94. package/dist/howtofix-account.js +0 -735
  95. package/dist/hud-line.js +0 -153
  96. package/dist/hwid.js +0 -130
  97. package/dist/interrupt.js +0 -60
  98. package/dist/json-events.js +0 -50
  99. package/dist/level-style.js +0 -112
  100. package/dist/loop-guard.js +0 -95
  101. package/dist/lsp-tools.js +0 -32
  102. package/dist/lsp.js +0 -429
  103. package/dist/mcp-config.js +0 -85
  104. package/dist/mcp-lifecycle.js +0 -147
  105. package/dist/model-catalog.js +0 -217
  106. package/dist/model-facts.js +0 -35
  107. package/dist/model-resolve.js +0 -65
  108. package/dist/notes.js +0 -78
  109. package/dist/oauth-login.js +0 -568
  110. package/dist/paths.js +0 -61
  111. package/dist/permissions.js +0 -298
  112. package/dist/plan-mode.js +0 -363
  113. package/dist/preset-command.js +0 -227
  114. package/dist/preset-extension.js +0 -35
  115. package/dist/preset-mcp.js +0 -292
  116. package/dist/presets.js +0 -727
  117. package/dist/prompt-files.js +0 -103
  118. package/dist/prompt-text.js +0 -41
  119. package/dist/proxy.js +0 -137
  120. package/dist/quality/ast.js +0 -196
  121. package/dist/quality/cognitive.js +0 -83
  122. package/dist/quality/cyclomatic.js +0 -30
  123. package/dist/quality/halstead.js +0 -83
  124. package/dist/quality/index.js +0 -18
  125. package/dist/quality/length.js +0 -3
  126. package/dist/quality/maintainability.js +0 -15
  127. package/dist/quality/nesting.js +0 -57
  128. package/dist/quality/report.js +0 -803
  129. package/dist/quality/smells.js +0 -67
  130. package/dist/quality-tool.js +0 -63
  131. package/dist/read-only-commands.js +0 -597
  132. package/dist/repo-map.js +0 -292
  133. package/dist/rtk.js +0 -193
  134. package/dist/sandbox.js +0 -105
  135. package/dist/schema.js +0 -214
  136. package/dist/session-lifecycle.js +0 -19
  137. package/dist/session-retention.js +0 -111
  138. package/dist/session.js +0 -447
  139. package/dist/sessions.js +0 -100
  140. package/dist/settings.js +0 -148
  141. package/dist/skills.js +0 -35
  142. package/dist/slop-tool.js +0 -96
  143. package/dist/spend-cap.js +0 -43
  144. package/dist/subagent.js +0 -171
  145. package/dist/tar.js +0 -146
  146. package/dist/theme.js +0 -79
  147. package/dist/thinking.js +0 -45
  148. package/dist/todo.js +0 -114
  149. package/dist/tool-loadout.js +0 -97
  150. package/dist/tool-render.js +0 -769
  151. package/dist/tools.js +0 -176
  152. package/dist/ui.js +0 -4778
  153. package/dist/update.js +0 -551
  154. package/dist/usage-segment.js +0 -69
  155. package/dist/web-tools.js +0 -46
  156. package/dist/web.js +0 -265
  157. package/dist/welcome.js +0 -179
  158. package/dist/workflow-engine.js +0 -246
  159. package/dist/workflow-progress.js +0 -446
  160. package/dist/workflow-view.js +0 -195
  161. package/dist/workflow.js +0 -349
package/dist/main.js CHANGED
@@ -1,541 +1,100 @@
1
1
  #!/usr/bin/env node
2
- import { helpText, parseArgs } from "./args.js";
3
- import { APP_TITLE, APP_VERSION } from "./brand.js";
4
- import { installCrashHandlers } from "./fatal.js";
5
- import { reportError } from "./errors.js";
6
- import { AGENT_DIR, pinAgentDir } from "./paths.js";
7
- import { jsonLine } from "./json-events.js";
8
- import { spendCapMessage, unpricedBudgetMessage } from "./spend-cap.js";
9
- /**
10
- * The working directory to run in, or a reason the process cannot start.
11
- *
12
- * `process.cwd()` throws `ENOENT` when the directory it was started in has been
13
- * deleted — a real case (the scratch dir is cleaned up mid-session) and an
14
- * uncaught crash with a stack trace, since the throw happens while building the
15
- * session options. Report it as a sentence instead.
16
- */
17
- function workingDirectory() {
18
- try {
19
- return { cwd: process.cwd() };
20
- }
21
- catch {
22
- return {
23
- error: "the current directory no longer exists (it was deleted while the shell stayed in it). " +
24
- "cd to a directory that exists, or pass one on the command line.",
25
- };
26
- }
27
- }
28
- /**
29
- * A closed stdout (`howtofix --version | head -1`) makes every later write throw
30
- * `EPIPE`, which the top-level handler used to report as a crash with a stack
31
- * trace. A pipe that went away is not an error the user needs to see.
32
- */
33
- function exitQuietlyOnEpipe() {
34
- process.stdout.on("error", (error) => {
35
- if (error.code === "EPIPE")
36
- process.exit(0);
37
- throw error;
38
- });
39
- }
40
- async function main() {
41
- exitQuietlyOnEpipe();
42
- pinAgentDir();
43
- const startedAt = performance.now();
44
- const here = workingDirectory();
45
- if ("error" in here) {
46
- console.error(`${APP_TITLE}: ${here.error}`);
47
- process.exitCode = 1;
48
- return;
49
- }
50
- const cwd = here.cwd;
51
- // A Node too old to be supported fails deep inside a dependency instead of here.
52
- // `undici` 8 reaches `zlib.createZstdDecompress` at module load, and that function
53
- // only exists from Node 22.19 — so on Node 20 the user gets
54
- // `TypeError: Cannot read properties of undefined (reading 'bind')` with a stack
55
- // trace pointing at `undici/lib/interceptor/decompress.js`, which explains nothing
56
- // and names no fix. `engines` in `package.json` is not enough on its own: npm's
57
- // `engine-strict` is false by default, so the install succeeds with a warning the
58
- // user never sees.
59
- //
60
- // `--help` and `--version` are deliberately still served. They exit before `undici`
61
- // is ever loaded, so they cannot hit the bug, and the user who most needs to read the
62
- // help is the one on the runtime the CLI cannot run — refusing to answer leaves them
63
- // with a command that looks broken and no way to learn why. Everything else is
64
- // refused, because a session that cannot start the agent is not a usable CLI and
65
- // failing here is what turns a stack trace inside a dependency into a sentence.
66
- //
67
- // Gated behind an env var so the check itself is what breaks under test, not the
68
- // runtime: `HXF_FAKE_NODE` lets a check drive this branch on a modern Node. The
69
- // same value feeds the message, so a test sees the version it asked about rather
70
- // than the one it happens to be running on.
71
- //
72
- // The exit code says whether the ENVIRONMENT is supported, not whether output was
73
- // produced: an old Node exits non-zero even for `--help`, so a script reading the
74
- // code is not told the runtime is fine.
75
- const reportedVersion = process.env.HXF_FAKE_NODE ?? process.versions.node;
76
- const [major, minor] = reportedVersion.split(".").map((part) => Number.parseInt(part, 10));
77
- if (!Number.isFinite(major) || major < 22 || (major === 22 && Number.isFinite(minor) && minor < 19)) {
78
- // Same sentence on both streams, and stderr carries it in the informational case
79
- // too: `--version` is often parsed by a script, and a warning on stdout would
80
- // corrupt the value it is reading.
81
- console.error(`${APP_TITLE}: Node ${reportedVersion} is too old. Node 22.19 or newer is required ` +
82
- `(run "node --version" to check yours), because undici needs zstd, which Node added in 22.19. ` +
83
- `Upgrade Node and run this again.`);
84
- process.exitCode = 1;
85
- const argv = process.argv.slice(2);
86
- // The two flags that work on any runtime, served from here so the account repair
87
- // below — which makes an HTTP call, and therefore loads undici — is never reached
88
- // on a runtime that cannot load it.
89
- if (argv.some((token) => token === "-h" || token === "--help")) {
90
- console.log(helpText());
91
- return;
92
- }
93
- if (argv.some((token) => token === "-v" || token === "--version")) {
94
- console.log(`${APP_TITLE} ${APP_VERSION}`);
95
- return;
96
- }
97
- return;
98
- }
99
- // Questions about the installation, answered without a session, a model, or
100
- // any of the repairs below. They run first on purpose: `howtofix models` used
101
- // to rewrite settings.json and print two notices about it before listing a
102
- // single model, which reads like the command did something it did not.
103
- const subArgv = process.argv.slice(2);
104
- if (subArgv[0] === "doctor" || subArgv[0] === "models" || subArgv[0] === "sessions") {
105
- const context = subArgv[0];
106
- try {
107
- const commands = await import("./commands.js");
108
- if (context === "doctor")
109
- process.exitCode = await commands.runDoctor();
110
- else if (context === "models")
111
- process.exitCode = commands.runModels();
112
- else
113
- process.exitCode = commands.runSessions(subArgv.slice(1));
114
- }
115
- catch (error) {
116
- reportError(APP_TITLE, context, error);
117
- process.exitCode = 1;
118
- }
119
- return;
120
- }
121
- // Arguments are read and checked before the start-up repairs. A typo must not
122
- // rewrite settings.json, print a hint about a file the user never mentioned,
123
- // or reach the network: the answer to "you forgot the prompt" is one line.
124
- let earlyArgs;
125
- try {
126
- earlyArgs = parseArgs(process.argv.slice(2));
127
- }
128
- catch (error) {
129
- const message = error instanceof Error ? error.message : String(error);
130
- console.error(`${APP_TITLE}: ${message}\n${helpText()}`);
131
- process.exitCode = 2;
132
- return;
133
- }
134
- if (earlyArgs.print && !earlyArgs.prompt) {
135
- console.error(`${APP_TITLE}: --print needs a prompt`);
136
- process.exitCode = 2;
137
- return;
138
- }
139
- // Repair a howtofix gateway entry that predates `compat.supportsDeveloperRole`.
140
- // The gateway refuses the `developer` role that the SDK selects for every
141
- // reasoning model, so such an entry fails every prompt silently — the spinner
142
- // turns and nothing is ever reported. `/connect` writes the flag, but only for
143
- // models it adds, so an account connected earlier stays broken until something
144
- // repairs the file. The repair is silent: it runs on every start and only says
145
- // anything when it actually changed something. Imported lazily to keep
146
- // `--version` and `--help` free of the model stack.
147
- const { repairGatewayModels, ensureGatewaySettings, repairModelFacts, syncGatewayModelsOnStart, checkAccount } = await import("./howtofix-account.js");
148
- const { readCliCredential, renewCliCredentialIfNeeded } = await import("./cli-credentials.js");
149
- const { AGENT_DIR: stateDir } = await import("./paths.js");
150
- const repaired = repairGatewayModels(stateDir);
151
- if (repaired > 0) {
152
- console.error(`${APP_TITLE}: repaired ${repaired} howtofix model(s) that the gateway would have rejected`);
153
- }
154
- // Repair the numbers a synced model never got: a real context window and the
155
- // reasoning flag that enables thinking. Both used to be guesses — 128k for
156
- // every model, and no capability at all — so a model with a 1M window read as
157
- // almost full and a reasoning model showed no thinking levels. The catalogue is
158
- // cached next to the agent state, so this is a file read on a normal start and
159
- // a fetch only when the cache is missing or stale.
160
- const facts = await repairModelFacts(stateDir);
161
- if (facts.changed > 0) {
162
- console.error(`${APP_TITLE}: filled in the context window and reasoning flag for ${facts.changed} model(s) from the model catalogue`);
163
- }
164
- else if (facts.message) {
165
- // A catalogue that could not be read is not a failure — the models keep the
166
- // conservative defaults — but it is worth one line, because otherwise the
167
- // wrong numbers look like the model's own.
168
- console.error(`${APP_TITLE}: model catalogue unavailable (${facts.message}); context windows stay at their conservative defaults`);
169
- }
170
- // The SDK's HTTP idle timeout defaults to 5 minutes, so a provider that goes
171
- // quiet mid-request leaves a spinner and nothing else for that long. 90s lets
172
- // the SDK's own retry recover while the user is still watching. Written only
173
- // when the key is missing, so a value the user set is kept.
174
- if (ensureGatewaySettings(stateDir)) {
175
- console.error(`${APP_TITLE}: set httpIdleTimeoutMs in settings.json so a stalled provider recovers instead of hanging`);
176
- }
177
- // The account's model list follows its tier, and `models.json` is a snapshot:
178
- // after a tier change the file still offered what the account could reach on
179
- // the day it was written, and the only refresh was a manual `/sync`. Pull the
180
- // list once per start when a sign-in exists and add whatever is new — the
181
- // gateway filters by the account's own group, so a free account can never
182
- // collect a VIP model this way. Silent unless something was added, and never
183
- // fatal: an unreachable gateway must not stop the CLI from starting.
184
- try {
185
- const stored = readCliCredential(stateDir);
186
- if (stored) {
187
- const renewed = await renewCliCredentialIfNeeded(stateDir, stored);
188
- const account = await checkAccount(renewed.credential.accessToken);
189
- if (account.ok) {
190
- const synced = await syncGatewayModelsOnStart(stateDir, account.models);
191
- if (synced.added > 0) {
192
- console.error(`${APP_TITLE}: added ${synced.added} model(s) this account can now reach`);
193
- }
194
- }
195
- }
196
- }
197
- catch {
198
- // A start-time re-sync that fails costs the previous list, which still
199
- // works; `/sync` remains the manual way to try again.
200
- }
201
- const args = earlyArgs;
202
- // These two exit before a session exists, so they must not load the SDK.
203
- if (args.help) {
204
- console.log(helpText());
205
- return;
206
- }
207
- if (args.version) {
208
- console.log(`${APP_TITLE} ${APP_VERSION}`);
209
- // A build whose signing key does not match the panel's would fail every
210
- // sign-in with a 401 that reads like a server fault. Say it here, where it
211
- // costs nothing, rather than leaving it to be discovered in the login flow.
212
- const { cliSigningKeyMatchesPanel, cliSigningKeyFingerprint } = await import("./cli-auth.js");
213
- if (!cliSigningKeyMatchesPanel()) {
214
- console.error(`${APP_TITLE}: this build's CLI signing key (${cliSigningKeyFingerprint()}) does not match ` +
215
- `the key the panel verifies against, so sign-in will be refused.`);
216
- process.exitCode = 1;
217
- }
218
- return;
219
- }
220
- // The sandbox re-executes this process once, before any session exists. The
221
- // guard in the environment is what stops the child from wrapping itself again.
222
- if (args.sandbox && !process.env.HOWTOFIX_SANDBOXED) {
223
- const { detectSandbox, enterSandbox, describeSandbox } = await import("./sandbox.js");
224
- const kind = detectSandbox();
225
- if (!kind) {
226
- console.error(`${APP_TITLE}: --sandbox needs a working bubblewrap (Linux) or sandbox-exec (macOS). ` +
227
- `Neither could be used here — on Linux this usually means unprivileged user namespaces are disabled.`);
228
- process.exitCode = 1;
229
- return;
230
- }
231
- console.error(describeSandbox(kind));
232
- process.exitCode = enterSandbox(kind, { cwd, agentDir: AGENT_DIR, argv: [process.execPath, ...process.argv.slice(1)] });
233
- return;
234
- }
235
- // Route through a proxy before the first request goes out. Here rather than at
236
- // the top of `main` because this is the first line that cannot exit early:
237
- // `--version` and `--help` have already returned above, and loading the proxy
238
- // agent for a command that makes no request is a cost with no purpose. The
239
- // update check and the sign-in exchange both go through it, and they run
240
- // before any session exists — a proxy installed after them would be a setting
241
- // that works only sometimes.
242
- const { installProxy } = await import("./proxy.js");
243
- installProxy(AGENT_DIR);
244
- // `--update` installs the newest release; `--check-update` only looks. Both
245
- // exit without a session, so neither loads the model stack.
246
- if (args.update || args.checkUpdate) {
247
- const { PACKAGE_NAME, checkForUpdate, installUpdate, isInstalledCopy } = await import("./update.js");
248
- const check = await checkForUpdate(APP_VERSION);
249
- if (!check.latest) {
250
- // Not reaching the registry is a failure only when an install was asked
251
- // for; a plain check that could not run has nothing to report.
252
- console.error(`${APP_TITLE}: ${check.message}`);
253
- process.exitCode = args.update ? 1 : 0;
254
- return;
255
- }
256
- console.log(check.message);
257
- if (args.checkUpdate || !check.available)
258
- return;
259
- if (!isInstalledCopy()) {
260
- console.error(`${APP_TITLE}: this copy was not installed by npm, so npm cannot replace it. ` +
261
- `install it with: npm install -g ${PACKAGE_NAME}`);
262
- process.exitCode = 1;
263
- return;
264
- }
265
- const result = await installUpdate({ version: check.latest });
266
- console.log(result.message);
267
- if (!result.ok)
268
- process.exitCode = 1;
269
- return;
270
- }
271
- // `howtofix preset <new|check|pack>` authors a preset and exits. It runs before
272
- // anything loads the SDK, because authoring has nothing to do with a model and
273
- // a preset author on a machine without credentials should still be able to
274
- // validate what they wrote.
275
- const presetArgv = process.argv.slice(2);
276
- if (presetArgv[0] === "preset") {
277
- const { runPresetCommand } = await import("./preset-command.js");
278
- process.exitCode = runPresetCommand(presetArgv.slice(1));
279
- return;
280
- }
281
- const { expandFileReferences } = await import("./at-file.js");
282
- const { isApprovalMode, setApprovalMode, setDangerousBypass, isDangerousBypass } = await import("./permissions.js");
283
- const { openSession } = await import("./session.js");
284
- // Print mode has no dialogs, so anything that would ask must not block. The
285
- // interactive default is `full-auto` too: an approval prompt that fires on
286
- // every write is friction the user has to configure away, and a critical
287
- // command is denied in every mode regardless.
288
- setApprovalMode(args.approval && isApprovalMode(args.approval) ? args.approval : "full-auto");
289
- // `--dangerously-skip-permissions` lifts the last two refusals print mode can
290
- // still hit: a critical command and the loop guard. A stored preference does
291
- // the same for someone who never passes flags — the flag wins when it is
292
- // given, so a one-off run cannot be overridden by a file.
293
- const { readWorkbenchSettings } = await import("./settings.js");
294
- if (args.dangerouslySkipPermissions || readWorkbenchSettings(stateDir).skipPermissions)
295
- setDangerousBypass(true);
296
- // A bypass nobody can see is the dangerous kind: it removes the refusals a
297
- // user relies on and leaves no trace of why. Say it is on, every run.
298
- if (isDangerousBypass()) {
299
- console.error(`${APP_TITLE}: permission checks are OFF — critical commands (filesystem wipes, ` +
300
- `fork bombs, host shutdown) and the repeated-call guard are NOT enforced for this run. ` +
301
- `Turn it off with /settings, or by removing skipPermissions from settings.json.`);
302
- }
303
- const open = (options) => openSession({
304
- cwd,
305
- model: args.model,
306
- thinking: args.thinking,
307
- continueSession: options.continueSession ?? false,
308
- noSession: options.noSession ?? false,
309
- sessionPath: options.sessionPath,
310
- // The caller chooses the session, not the prompt. `--print` is the only
311
- // route that runs without a terminal, and the prompt must say so or a
312
- // skill that asks a question becomes a dead end that still exits 0.
313
- interactive: !args.print,
314
- });
315
- const opened = await open({
316
- continueSession: args.continueSession,
317
- sessionPath: args.sessionPath,
318
- noSession: args.noSession || args.print,
319
- });
320
- // The note reports how the session was opened, so a refusal below must not
321
- // swallow it: print writes it here, ahead of any guard; the TUI carries it
322
- // into the feed as `options.note`.
323
- if (args.print && opened.note)
324
- console.error(opened.note);
325
- // A budget on an unpriced model can never fire, so refuse instead of
326
- // pretending to guard — in *both* modes, now that the workbench enforces the
327
- // cap too: a TUI drawing a cap over a model that reports $0 a turn would be
328
- // the exact lie this refusal exists to prevent. See `priced` on
329
- // `OpenedSession`.
330
- if (args.maxBudgetUsd !== undefined && !opened.priced) {
331
- console.error(`${APP_TITLE}: ${unpricedBudgetMessage(opened.modelLabel, stateDir)}`);
332
- await opened.dispose();
333
- process.exitCode = 2;
334
- return;
335
- }
336
- if (args.print) {
337
- // `--print` has no slash router, but the TUI routes every "/" prompt to one
338
- // (submit → runSlash) and never lets those reach the model. Without this
339
- // guard the same prompt is forwarded verbatim — `/connect sk-...` pasted
340
- // into a script sends the credential to the provider and records it in the
341
- // transcript, and the command silently does not run. Skills are exempt:
342
- // the TUI forwards those as prompts too (runSlash → skillInvocation).
343
- const trimmedPrompt = args.prompt.trimStart();
344
- if (trimmedPrompt.startsWith("/")) {
345
- const name = trimmedPrompt.slice(1).split(/\s+/)[0] ?? "";
346
- const { skillInvocation } = await import("./skills.js");
347
- const skillNames = opened.session.resourceLoader.getSkills().skills.map((skill) => skill.name);
348
- const isSkill = skillInvocation(trimmedPrompt, skillNames) !== undefined;
349
- if (!isSkill) {
350
- const { slashCommands } = await import("./ui.js");
351
- if (slashCommands(opened.session).some((command) => command.name === name)) {
352
- console.error(`${APP_TITLE}: /${name} runs in the TUI only — --print sends prompts straight to the model. ` +
353
- `Run \`howtofix\` without --print for interactive commands.`);
354
- await opened.dispose();
355
- process.exitCode = 2;
356
- return;
357
- }
358
- }
359
- }
360
- const stream = args.outputFormat === "stream-json";
361
- const json = args.outputFormat === "json";
362
- // Both machine formats end with a result record; only `stream-json` writes
363
- // the events along the way.
364
- const machine = stream || json;
365
- // A `system`-shaped header first, so a consumer learns the model and the
366
- // session id before any event that refers to them — the same courtesy the
367
- // SDK's own JSON mode pays. Only the streaming format has events to head.
368
- if (stream) {
369
- const header = {
370
- type: "system",
371
- subtype: "init",
372
- cwd,
373
- session_id: opened.session.sessionId,
374
- model: opened.modelLabel,
375
- output_format: args.outputFormat,
376
- max_budget_usd: args.maxBudgetUsd ?? null,
377
- dangerously_skip_permissions: args.dangerouslySkipPermissions,
378
- cli_version: APP_VERSION,
379
- };
380
- process.stdout.write(`${JSON.stringify(header)}\n`);
381
- }
382
- let unsubscribe;
383
- let budgetHit;
384
- const checkBudget = () => {
385
- if (args.maxBudgetUsd === undefined || budgetHit !== undefined)
386
- return;
387
- const spent = opened.session.getSessionStats().cost;
388
- if (spent > args.maxBudgetUsd) {
389
- budgetHit = { spent };
390
- void opened.session.abort();
391
- }
392
- };
393
- try {
394
- // Both machine formats and text need the same budget signal, and the only
395
- // reliable one is the event stream: usage is updated as events arrive, so
396
- // a timer that samples the total can miss the update entirely and let the
397
- // run overspend. Subscribe in every mode; only `stream-json` writes the
398
- // lines out.
399
- unsubscribe = opened.session.subscribe((event) => {
400
- if (stream) {
401
- const line = jsonLine(event);
402
- if (line !== undefined)
403
- process.stdout.write(line);
404
- }
405
- checkBudget();
406
- });
407
- await opened.session.prompt(expandFileReferences(args.prompt, cwd));
408
- const text = opened.session.getLastAssistantText() ?? "";
409
- if (budgetHit) {
410
- console.error(`${APP_TITLE}: ${spendCapMessage(budgetHit.spent, args.maxBudgetUsd)}`);
411
- // A consumer reading the stream needs a terminal record on this path
412
- // too: without it the run just stops mid-stream and looks truncated.
413
- if (machine) {
414
- const stats = opened.session.getSessionStats();
415
- process.stdout.write(`${JSON.stringify({
416
- type: "result",
417
- subtype: "error_max_budget_usd",
418
- is_error: true,
419
- result: text,
420
- session_id: opened.session.sessionId,
421
- duration_ms: Math.round(performance.now() - startedAt),
422
- num_turns: stats.assistantMessages,
423
- total_cost_usd: stats.cost,
424
- usage: {
425
- input_tokens: stats.tokens.input,
426
- output_tokens: stats.tokens.output,
427
- cache_read_input_tokens: stats.tokens.cacheRead,
428
- cache_creation_input_tokens: stats.tokens.cacheWrite,
429
- },
430
- })}\n`);
431
- }
432
- process.exitCode = 1;
433
- return;
434
- }
435
- if (machine) {
436
- const stats = opened.session.getSessionStats();
437
- const last = opened.session.state.messages.at(-1);
438
- process.stdout.write(`${JSON.stringify({
439
- type: "result",
440
- subtype: "success",
441
- is_error: false,
442
- result: text,
443
- session_id: opened.session.sessionId,
444
- duration_ms: Math.round(performance.now() - startedAt),
445
- num_turns: stats.assistantMessages,
446
- total_cost_usd: stats.cost,
447
- usage: {
448
- input_tokens: stats.tokens.input,
449
- output_tokens: stats.tokens.output,
450
- cache_read_input_tokens: stats.tokens.cacheRead,
451
- cache_creation_input_tokens: stats.tokens.cacheWrite,
452
- },
453
- stop_reason: last?.role === "assistant" ? last.stopReason : undefined,
454
- })}\n`);
455
- }
456
- else {
457
- process.stdout.write(text.endsWith("\n") ? text : `${text}\n`);
458
- }
459
- // The SDK contract: a final assistant response that ended in `error` or
460
- // `aborted` produces a nonzero exit status, so scripts can branch on it.
461
- const last = opened.session.state.messages.at(-1);
462
- if (last?.role === "assistant") {
463
- if (last.stopReason === "error" || last.stopReason === "aborted")
464
- process.exitCode = 1;
465
- else if (last.errorMessage)
466
- process.exitCode = 1;
467
- // A failed turn used to exit 1 with no output at all, which is
468
- // indistinguishable from a crash. Say what the provider said.
469
- if (process.exitCode === 1 && last.errorMessage) {
470
- console.error(`${APP_TITLE}: ${last.errorMessage}`);
471
- // Same gap as the TUI: the gateway's own sentence, and the fix for it.
472
- // Imported here rather than at the top so a run that never fails does
473
- // not pay to parse it.
474
- const { diagnoseGatewayFailure } = await import("./gateway-errors.js");
475
- const fix = diagnoseGatewayFailure(last.errorMessage);
476
- if (fix !== undefined)
477
- console.error(`${APP_TITLE}: ${fix}`);
478
- }
479
- // Same reasoning as the budget path: a JSON consumer needs the terminal
480
- // record even when the run failed.
481
- if (machine && process.exitCode === 1) {
482
- const stats = opened.session.getSessionStats();
483
- process.stdout.write(`${JSON.stringify({
484
- type: "result",
485
- subtype: last.stopReason === "aborted" ? "error_aborted" : "error_during_execution",
486
- is_error: true,
487
- result: text,
488
- session_id: opened.session.sessionId,
489
- duration_ms: Math.round(performance.now() - startedAt),
490
- num_turns: stats.assistantMessages,
491
- total_cost_usd: stats.cost,
492
- usage: {
493
- input_tokens: stats.tokens.input,
494
- output_tokens: stats.tokens.output,
495
- cache_read_input_tokens: stats.tokens.cacheRead,
496
- cache_creation_input_tokens: stats.tokens.cacheWrite,
497
- },
498
- stop_reason: last.stopReason,
499
- })}\n`);
500
- }
501
- }
502
- }
503
- finally {
504
- unsubscribe?.();
505
- await opened.dispose();
506
- }
507
- return;
508
- }
509
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
510
- console.error(`${APP_TITLE}: interactive mode needs a terminal. use --print for pipes.`);
511
- await opened.dispose();
512
- process.exitCode = 2;
513
- return;
514
- }
515
- const { runWorkbench } = await import("./ui.js");
516
- // Only a copy npm owns can be replaced by npm, and only when the user has not
517
- // turned it off. The check itself is handed to the workbench so it runs after
518
- // the UI is up rather than before it.
519
- const { autoUpdateEnabled, isInstalledCopy } = await import("./update.js");
520
- const canSelfUpdate = isInstalledCopy() && autoUpdateEnabled(`${stateDir}/settings.json`);
521
- await runWorkbench({
522
- cwd,
523
- modelLabel: opened.modelLabel,
524
- session: opened.session,
525
- // The `/rtk` command toggles the flag the tool-result extension reads; the setter
526
- // lives beside that flag so the two cannot drift apart.
527
- setCompressToolOutput: opened.setCompressToolOutput,
528
- initialPrompt: args.prompt || undefined,
529
- note: opened.note,
530
- // The cap the print path enforces over one run, handed to a UI that has to
531
- // enforce it over many: same flag, same dollars, different lifetime.
532
- maxBudgetUsd: args.maxBudgetUsd,
533
- update: canSelfUpdate ? { current: APP_VERSION, install: true } : undefined,
534
- reopen: open,
535
- });
536
- }
537
- installCrashHandlers(APP_TITLE);
538
- main().catch((error) => {
539
- reportError(APP_TITLE, undefined, error);
540
- process.exitCode = 1;
541
- });
2
+ import{b as U,c as I}from"./chunk-6FUWMLWO.js";import{b as N}from"./chunk-YW7NORH3.js";import{a as R,b as O}from"./chunk-AGGIYXN7.js";import{D as j,K as F}from"./chunk-V656XVXV.js";import"./chunk-UTYFLLSM.js";import{a as r,b as x}from"./chunk-Y7YM5R75.js";import"./chunk-WOT6VMZA.js";var W={rpc:!0,acp:!0},B={off:!0,minimal:!0,low:!0,medium:!0,high:!0,xhigh:!0,max:!0},L={suggest:!0,"auto-edit":!0,"full-auto":!0},D={text:!0,json:!0,"stream-json":!0};function he(c){let e=Number(c);if(!Number.isFinite(e)||e<=0)throw new Error(`--max-budget-usd needs a positive number of dollars, got '${c}'`);return e}function v(c,e,l){let d=c[e];if(d?.includes("="))return d.slice(d.indexOf("=")+1);let p=c[e+1];if(!p||p.startsWith("-"))throw new Error(`${l} needs a value`);return c.splice(e+1,1),p}function G(c){let e={print:!1,continueSession:!1,noSession:!1,help:!1,version:!1,update:!1,checkUpdate:!1,outputFormat:"text",dangerouslySkipPermissions:!1,sandbox:!1,prompt:""},l=[],d=[...c];for(let p=0;p<d.length;p++){let s=d[p]??"";if(s==="--"){l.push(...d.slice(p+1));break}if(s==="-h"||s==="--help")e.help=!0;else if(s==="-v"||s==="--version")e.version=!0;else if(s==="-p"||s==="--print")e.print=!0;else if(s==="-c"||s==="--continue")e.continueSession=!0;else if(s==="--no-session")e.noSession=!0;else if(s==="--sandbox")e.sandbox=!0;else if(s==="--update")e.update=!0;else if(s==="--check-update")e.checkUpdate=!0;else if(s==="-r"||s==="--resume")e.continueSession=!0;else if(s==="--session"||s.startsWith("--session="))e.sessionPath=v(d,p,"--session");else if(s==="-m"||s==="--model"||s.startsWith("--model="))e.model=v(d,p,"--model");else if(s==="--thinking"||s.startsWith("--thinking="))e.thinking=v(d,p,"--thinking");else if(s==="--approval"||s.startsWith("--approval="))e.approval=v(d,p,"--approval");else if(s==="--output-format"||s.startsWith("--output-format="))e.outputFormat=v(d,p,"--output-format");else if(s==="--max-budget-usd"||s.startsWith("--max-budget-usd="))e.maxBudgetUsd=he(v(d,p,"--max-budget-usd"));else if(s==="--mode"||s.startsWith("--mode=")){let h=v(d,p,"--mode");if(!W[h])throw new Error(`--mode must be one of: ${Object.keys(W).join(", ")}`);e.mode=h}else if(s==="--acp")e.mode="acp";else if(s==="--dangerously-skip-permissions")e.dangerouslySkipPermissions=!0;else{if(s.startsWith("-"))throw new Error(`unknown option ${s}`);l.push(s)}}if(e.thinking&&!B[e.thinking])throw new Error(`thinking must be one of: ${Object.keys(B).join(", ")}`);if(e.approval&&!L[e.approval])throw new Error(`approval must be one of: ${Object.keys(L).join(", ")}`);if(!D[e.outputFormat])throw new Error(`output-format must be one of: ${Object.keys(D).join(", ")}`);if(e.outputFormat!=="text"&&!e.print)throw new Error(`--output-format ${e.outputFormat} is a machine contract; it needs --print`);if(e.dangerouslySkipPermissions&&!e.print)throw new Error("--dangerously-skip-permissions is for unattended runs; it needs --print. interactively, pick an approval mode instead (/approval, or --approval)");if(e.prompt=l.join(" ").trim(),e.update&&e.checkUpdate)throw new Error("--update installs; --check-update only looks. pick one");if((e.update||e.checkUpdate)&&(e.print||e.continueSession||e.sessionPath||e.prompt))throw new Error("--update and --check-update act on the installation, not a session; they take no prompt");if(e.continueSession&&e.noSession)throw new Error("--continue and --no-session cannot be combined");if(e.sessionPath&&e.noSession)throw new Error("--session and --no-session cannot be combined");if(e.sessionPath&&e.continueSession)throw new Error("--session and --continue cannot be combined");if(e.print&&(e.continueSession||e.sessionPath))throw new Error("--print runs one prompt in a throwaway session; it cannot be combined with --continue or --session");if(e.mode&&(e.print||e.prompt))throw new Error(`--mode ${e.mode} is a protocol server: prompts arrive over the protocol, not the command line`);if(e.mode&&(e.continueSession||e.sessionPath))throw new Error(`--mode ${e.mode} opens its own sessions; it cannot be combined with --continue or --session`);if(e.sessionPath&&!e.sessionPath.endsWith(".jsonl"))throw new Error(`--session expects a session file, got '${e.sessionPath}'`);return e}function P(){return`${r} ${x}
3
+ coding agent. layout is a workbench, not a chat log.
4
+
5
+ usage:
6
+ howtofix [options] [prompt]
7
+ howtofix -p "explain this repo"
8
+ howtofix acp serve the Agent Client Protocol on stdio
9
+ howtofix --mode rpc serve JSON-line commands on stdio
10
+ howtofix preset new|check|pack author a preset
11
+ howtofix doctor|models|sessions inspect the installation
12
+ howtofix sessions --search <text> find a session by what was said in it
13
+ howtofix sessions --export <n> print session n from the list as markdown
14
+
15
+ options:
16
+ -p, --print run one prompt and print the answer
17
+ -c, --continue resume the latest session in this folder
18
+ -r, --resume alias for --continue
19
+ --session <file> run inside a specific saved session file
20
+ --no-session do not write a session file
21
+ -m, --model <id> provider/model, or a fuzzy model id
22
+ --thinking <lv> off | minimal | low | medium | high | xhigh | max
23
+ --approval <m> suggest | auto-edit | full-auto
24
+ --output-format <f> text | json | stream-json
25
+ text for a person; json writes the final result only;
26
+ stream-json writes every event as it happens
27
+ --max-budget-usd <n> stop the run once it has spent n dollars
28
+ --mode <m> rpc | acp: serve a protocol on stdin/stdout, no terminal
29
+ --acp same as --mode acp
30
+ --sandbox run the whole session inside a filesystem sandbox:
31
+ the project is writable, the rest is read-only.
32
+ needs bubblewrap (Linux) or sandbox-exec (macOS)
33
+ --dangerously-skip-permissions
34
+ run everything, including commands otherwise refused.
35
+ unattended runs only; pairs with --print
36
+ --update install the newest release and exit
37
+ --check-update say whether a newer release exists
38
+ -h, --help
39
+ -v, --version
40
+
41
+ keys:
42
+ enter send
43
+ shift+enter new line
44
+ ctrl+c stop the run, or quit when idle
45
+ ctrl+d quit
46
+ ctrl+l jump to the latest line
47
+ ctrl+o clear the screen (history is kept)
48
+ ctrl+t raise the thinking level (wraps to off)
49
+ ctrl+shift+t lower the thinking level
50
+ esc clear the draft
51
+ ctrl+v paste from the clipboard
52
+ pgup / pgdn scroll the transcript a page
53
+ home / end jump to the top / back to the latest line
54
+ wheel scroll the transcript
55
+
56
+ commands:
57
+ /init /model /providers /thinking /approval /login /connect /logout /new
58
+ /resume /compact /name /stats /rtk /diff /commit /undo /rewind /review /tools /export
59
+ /copy /agents /goal /plan /workflow /session /help /quit /exit
60
+ /dcp /context /decompress /sweep
61
+ /update /usage /sync
62
+ /skills /preset /settings /proxy /roblox
63
+ /roblox login|logout|status|tunnel manages the Roblox connection: sign in with Roblox
64
+ (no API key), see what is connected, and link this machine to the hosted workspace.
65
+
66
+ /stats shows session tokens, cost, and the prompt-cache hit rate; a high read
67
+ total alone hides re-billed context, so the rate is what tells you the truth.
68
+ /rtk compresses long tool output (logs, diffs, greps) before the model reads it.
69
+ Off by default: /rtk on to save tokens when skimming, /rtk off to keep every line.
70
+ Nothing is invented by it \u2014 it drops redundant lines and says how many it dropped,
71
+ and never touches a failed command or one already truncated.
72
+
73
+ /preset lists skills bundled with this build and skills from the registry, by
74
+ category; picking one downloads it into the project and loads it as a skill.
75
+ An integration preset installs disabled: its skills and MCP server stay dormant
76
+ until /preset enable <name>, and /preset disable <name> takes the server back
77
+ out of the live session. /roblox reports that state for RoToolbox in one line.
78
+ /skills lists what is loaded now.
79
+
80
+ /proxy <url> routes requests through a proxy, /proxy off stops, /proxy env uses
81
+ HTTPS_PROXY / HTTP_PROXY / ALL_PROXY; /proxy alone shows what is set.
82
+
83
+ /agents <id> <message> messages a parked agent; /agents <id> shows one.
84
+
85
+ a line starting with ! runs in the project shell.
86
+
87
+ an npm install checks for a newer release at startup and can install it itself;
88
+ /update does it on demand. config and sessions live in ~/.howtofix/agent.
89
+ /connect signs in to howtofix.id in your browser, and falls back to a pasted
90
+ token; /login takes another provider's key.
91
+ `}function fe(c,e=!1){let{message:l}=R(c);if(!e)return l;let d=c instanceof Error?c.stack:void 0;return d&&d.includes(`
92
+ `)?`${l}
93
+ ${d}`:l}function H(c,e){let l=e?.debug??process.env.HOWTOFIX_DEBUG==="1",d=e?.exit??(s=>process.exit(s)),p=s=>{console.error(`${c}: ${fe(s,l)}`),d(1)};process.on("uncaughtException",p),process.on("unhandledRejection",p)}function ge(){try{return{cwd:process.cwd()}}catch{return{error:"the current directory no longer exists (it was deleted while the shell stayed in it). cd to a directory that exists, or pass one on the command line."}}}function we(){process.stdout.on("error",c=>{throw c.code==="EPIPE"&&process.exit(0),c})}async function ke(){we(),F();let c=performance.now(),e=ge();if("error"in e){console.error(`${r}: ${e.error}`),process.exitCode=1;return}let l=e.cwd,d=process.env.HXF_FAKE_NODE??process.versions.node,[p,s]=d.split(".").map(o=>Number.parseInt(o,10));if(!Number.isFinite(p)||p<22||p===22&&Number.isFinite(s)&&s<19){console.error(`${r}: Node ${d} is too old. Node 22.19 or newer is required (run "node --version" to check yours), because undici needs zstd, which Node added in 22.19. Upgrade Node and run this again.`),process.exitCode=1;let o=process.argv.slice(2);if(o.some(a=>a==="-h"||a==="--help")){console.log(P());return}if(o.some(a=>a==="-v"||a==="--version")){console.log(`${r} ${x}`);return}return}let h=process.argv.slice(2);if(h[0]==="doctor"||h[0]==="models"||h[0]==="sessions"){let o=h[0];try{let a=await import("./chunk-EIBOS5KO.js");o==="doctor"?process.exitCode=await a.runDoctor():o==="models"?process.exitCode=a.runModels():process.exitCode=a.runSessions(h.slice(1))}catch(a){O(r,o,a),process.exitCode=1}return}let X=h[0]==="acp"?["--acp",...h.slice(1)]:h,S;try{S=G(X)}catch(o){let a=o instanceof Error?o.message:String(o);console.error(`${r}: ${a}
94
+ ${P()}`),process.exitCode=2;return}if(S.print&&!S.prompt){console.error(`${r}: --print needs a prompt`),process.exitCode=2;return}let{repairGatewayModels:J,ensureGatewaySettings:V,repairModelFacts:q,syncGatewayModelsOnStart:K,checkAccount:Y,fetchGatewayPrices:z}=await import("./chunk-UNK5HRQR.js"),{readCliCredential:Q,renewCliCredentialIfNeeded:Z}=await import("./chunk-CPJKKHGH.js"),{AGENT_DIR:w}=await import("./chunk-GXB7RZHH.js"),A=J(w);A>0&&console.error(`${r}: repaired ${A} howtofix model(s) that the gateway would have rejected`);let _,C;try{let o=Q(w);if(o){let b=(await Z(w,o)).credential.accessToken,[m,f]=await Promise.all([z(b),Y(b)]);m.size>0&&(_=m),f.ok&&(C=f.models)}}catch{}let $=await q(w,_?{prices:_}:void 0);if($.changed>0?console.error(`${r}: filled in the context window, reasoning flag and price for ${$.changed} model(s)`):$.message&&console.error(`${r}: model catalogue unavailable (${$.message}); context windows stay at their conservative defaults`),V(w)&&console.error(`${r}: set httpIdleTimeoutMs in settings.json so a stalled provider recovers instead of hanging`),C)try{let o=await K(w,C,_);o.added>0&&console.error(`${r}: added ${o.added} model(s) this account can now reach`)}catch{}let t=S;if(t.help){console.log(P());return}if(t.version){console.log(`${r} ${x}`);let{cliSigningKeyMatchesPanel:o,cliSigningKeyFingerprint:a}=await import("./chunk-LOOFOBTS.js");o()||(console.error(`${r}: this build's CLI signing key (${a()}) does not match the key the panel verifies against, so sign-in will be refused.`),process.exitCode=1);return}if(t.sandbox&&!process.env.HOWTOFIX_SANDBOXED){let{detectSandbox:o,enterSandbox:a,describeSandbox:b}=await import("./chunk-N345KWWK.js"),m=o();if(!m){console.error(`${r}: --sandbox needs a working bubblewrap (Linux) or sandbox-exec (macOS). Neither could be used here \u2014 on Linux this usually means unprivileged user namespaces are disabled.`),process.exitCode=1;return}console.error(b(m)),process.exitCode=a(m,{cwd:l,agentDir:j,argv:[process.execPath,...process.argv.slice(1)]});return}let{installProxy:ee}=await import("./chunk-WKUJ7CFO.js");if(ee(j),t.update||t.checkUpdate){let{PACKAGE_NAME:o,checkForUpdate:a,installUpdate:b,isInstalledCopy:m}=await import("./chunk-7CHDNTH6.js"),f=await a(x);if(!f.latest){console.error(`${r}: ${f.message}`),process.exitCode=t.update?1:0;return}if(console.log(f.message),t.checkUpdate||!f.available)return;if(!m()){console.error(`${r}: this copy was not installed by npm, so npm cannot replace it. install it with: npm install -g ${o}`),process.exitCode=1;return}let y=await b({version:f.latest});console.log(y.message),y.ok||(process.exitCode=1);return}let T=process.argv.slice(2);if(T[0]==="preset"){let{runPresetCommand:o}=await import("./chunk-FPXVDP32.js");process.exitCode=o(T.slice(1));return}let{expandFileReferences:se}=await import("./chunk-TSEKPGK4.js"),{isApprovalMode:te,setApprovalMode:oe,setDangerousBypass:ne,isDangerousBypass:re}=await import("./chunk-56SEAGAQ.js"),{openSession:ie}=await import("./chunk-HT3MFLAV.js");oe(t.approval&&te(t.approval)?t.approval:"full-auto");let{readWorkbenchSettings:ae}=await import("./chunk-V3R4FYTJ.js");if((t.dangerouslySkipPermissions||ae(w).skipPermissions)&&ne(!0),re()&&console.error(`${r}: permission checks are OFF \u2014 critical commands (filesystem wipes, fork bombs, host shutdown) and the repeated-call guard are NOT enforced for this run. Turn it off with /settings, or by removing skipPermissions from settings.json.`),t.mode){let{serveProtocol:o}=await import("./chunk-V2RPZMLK.js");await o(t.mode,{cwd:l,model:t.model,thinking:t.thinking});return}let M=o=>ie({cwd:l,model:t.model,thinking:t.thinking,continueSession:o.continueSession??!1,noSession:o.noSession??!1,sessionPath:o.sessionPath,interactive:!t.print}),n=await M({continueSession:t.continueSession,sessionPath:t.sessionPath,noSession:t.noSession||t.print});if(t.print&&n.note&&console.error(n.note),t.maxBudgetUsd!==void 0&&!n.priced){console.error(`${r}: ${I(n.modelLabel,w)}`),await n.dispose(),process.exitCode=2;return}if(t.print){let o=t.prompt.trimStart();if(o.startsWith("/")){let u=o.slice(1).split(/\s+/)[0]??"",{skillInvocation:g}=await import("./chunk-ILDDDLRC.js"),i=n.session.resourceLoader.getSkills().skills.map(E=>E.name);if(!(g(o,i)!==void 0)){let{slashCommands:E}=await import("./chunk-SX5VE6XA.js");if(E(n.session).some(me=>me.name===u)){console.error(`${r}: /${u} runs in the TUI only \u2014 --print sends prompts straight to the model. Run \`howtofix\` without --print for interactive commands.`),await n.dispose(),process.exitCode=2;return}}}let a=t.outputFormat==="stream-json",b=t.outputFormat==="json",m=a||b;if(a){let u={type:"system",subtype:"init",cwd:l,session_id:n.session.sessionId,model:n.modelLabel,output_format:t.outputFormat,max_budget_usd:t.maxBudgetUsd??null,dangerously_skip_permissions:t.dangerouslySkipPermissions,cli_version:x};process.stdout.write(`${JSON.stringify(u)}
95
+ `)}let f,y,ue=()=>{if(t.maxBudgetUsd===void 0||y!==void 0)return;let u=n.session.getSessionStats().cost;u>t.maxBudgetUsd&&(y={spent:u},n.session.abort())};try{f=n.session.subscribe(i=>{if(a){let k=N(i);k!==void 0&&process.stdout.write(k)}ue()}),await n.session.prompt(se(t.prompt,l));let u=n.session.getLastAssistantText()??"";if(y){if(console.error(`${r}: ${U(y.spent,t.maxBudgetUsd)}`),m){let i=n.session.getSessionStats();process.stdout.write(`${JSON.stringify({type:"result",subtype:"error_max_budget_usd",is_error:!0,result:u,session_id:n.session.sessionId,duration_ms:Math.round(performance.now()-c),num_turns:i.assistantMessages,total_cost_usd:i.cost,usage:{input_tokens:i.tokens.input,output_tokens:i.tokens.output,cache_read_input_tokens:i.tokens.cacheRead,cache_creation_input_tokens:i.tokens.cacheWrite}})}
96
+ `)}process.exitCode=1;return}if(m){let i=n.session.getSessionStats(),k=n.session.state.messages.at(-1);process.stdout.write(`${JSON.stringify({type:"result",subtype:"success",is_error:!1,result:u,session_id:n.session.sessionId,duration_ms:Math.round(performance.now()-c),num_turns:i.assistantMessages,total_cost_usd:i.cost,usage:{input_tokens:i.tokens.input,output_tokens:i.tokens.output,cache_read_input_tokens:i.tokens.cacheRead,cache_creation_input_tokens:i.tokens.cacheWrite},stop_reason:k?.role==="assistant"?k.stopReason:void 0})}
97
+ `)}else process.stdout.write(u.endsWith(`
98
+ `)?u:`${u}
99
+ `);let g=n.session.state.messages.at(-1);if(g?.role==="assistant"){if((g.stopReason==="error"||g.stopReason==="aborted"||g.errorMessage)&&(process.exitCode=1),process.exitCode===1&&g.errorMessage){console.error(`${r}: ${g.errorMessage}`);let{diagnoseGatewayFailure:i}=await import("./chunk-VLMQSD25.js"),k=i(g.errorMessage);k!==void 0&&console.error(`${r}: ${k}`)}if(m&&process.exitCode===1){let i=n.session.getSessionStats();process.stdout.write(`${JSON.stringify({type:"result",subtype:g.stopReason==="aborted"?"error_aborted":"error_during_execution",is_error:!0,result:u,session_id:n.session.sessionId,duration_ms:Math.round(performance.now()-c),num_turns:i.assistantMessages,total_cost_usd:i.cost,usage:{input_tokens:i.tokens.input,output_tokens:i.tokens.output,cache_read_input_tokens:i.tokens.cacheRead,cache_creation_input_tokens:i.tokens.cacheWrite},stop_reason:g.stopReason})}
100
+ `)}}}finally{f?.(),await n.dispose()}return}if(!process.stdin.isTTY||!process.stdout.isTTY){console.error(`${r}: interactive mode needs a terminal. use --print for pipes.`),await n.dispose(),process.exitCode=2;return}let{runWorkbench:ce}=await import("./chunk-SX5VE6XA.js"),{autoUpdateEnabled:de,isInstalledCopy:pe}=await import("./chunk-7CHDNTH6.js"),le=pe()&&de(`${w}/settings.json`);await ce({cwd:l,modelLabel:n.modelLabel,session:n.session,setCompressToolOutput:n.setCompressToolOutput,initialPrompt:t.prompt||void 0,note:n.note,maxBudgetUsd:t.maxBudgetUsd,update:le?{current:x,install:!0}:void 0,reopen:M})}H(r);ke().catch(c=>{O(r,void 0,c),process.exitCode=1});