howtofixcli 0.7.24 → 0.7.26

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 (167) hide show
  1. package/dist/account-credential.js +136 -0
  2. package/dist/acp.js +234 -0
  3. package/dist/activity.js +124 -0
  4. package/dist/agent-defs.js +237 -0
  5. package/dist/agent-limiter.js +68 -0
  6. package/dist/agents.js +554 -0
  7. package/dist/anim.js +361 -0
  8. package/dist/args.js +250 -0
  9. package/dist/at-file.js +28 -0
  10. package/dist/brand-extension.js +87 -0
  11. package/dist/brand.js +76 -0
  12. package/dist/browser.js +58 -0
  13. package/dist/cache-report.js +139 -0
  14. package/dist/checkpoint.js +437 -0
  15. package/dist/claims.js +167 -0
  16. package/dist/cli-auth.js +142 -0
  17. package/dist/cli-checkin.js +121 -0
  18. package/dist/cli-credentials.js +254 -0
  19. package/dist/clipboard.js +149 -0
  20. package/dist/collapse.js +129 -0
  21. package/dist/commands.js +381 -0
  22. package/dist/compaction-guard.js +81 -0
  23. package/dist/connect-prompt.js +22 -0
  24. package/dist/context-extension.js +17 -0
  25. package/dist/dcp/commands.js +172 -0
  26. package/dist/dcp/compress-state.js +139 -0
  27. package/dist/dcp/compress.js +329 -0
  28. package/dist/dcp/dcp-extension.js +295 -0
  29. package/dist/dcp/dedup.js +93 -0
  30. package/dist/dcp/ids.js +189 -0
  31. package/dist/dcp/index.js +25 -0
  32. package/dist/dcp/middle-prune.js +76 -0
  33. package/dist/dcp/prompts.js +68 -0
  34. package/dist/dcp/protected-content.js +221 -0
  35. package/dist/dcp/protected.js +161 -0
  36. package/dist/dcp/prune.js +418 -0
  37. package/dist/dcp/state.js +176 -0
  38. package/dist/errors.js +63 -0
  39. package/dist/extensions.js +48 -0
  40. package/dist/fatal.js +35 -0
  41. package/dist/feed.js +498 -0
  42. package/dist/fs-atomic.js +54 -0
  43. package/dist/gateway-errors.js +107 -0
  44. package/dist/git.js +230 -0
  45. package/dist/goal-extension.js +88 -0
  46. package/dist/goal.js +222 -0
  47. package/dist/hooks-extension.js +176 -0
  48. package/dist/hooks.js +443 -0
  49. package/dist/howtofix-account.js +834 -0
  50. package/dist/hud-line.js +158 -0
  51. package/dist/hwid.js +130 -0
  52. package/dist/interrupt.js +60 -0
  53. package/dist/json-events.js +50 -0
  54. package/dist/level-style.js +112 -0
  55. package/dist/loop-guard.js +95 -0
  56. package/dist/lsp-tools.js +32 -0
  57. package/dist/lsp.js +429 -0
  58. package/dist/main.js +573 -98
  59. package/dist/mcp-config.js +85 -0
  60. package/dist/mcp-lifecycle.js +147 -0
  61. package/dist/model-catalog.js +217 -0
  62. package/dist/model-facts.js +35 -0
  63. package/dist/model-resolve.js +65 -0
  64. package/dist/notes.js +78 -0
  65. package/dist/oauth-login.js +568 -0
  66. package/dist/paths.js +61 -0
  67. package/dist/permissions.js +298 -0
  68. package/dist/plan-mode.js +363 -0
  69. package/dist/preset-command.js +227 -0
  70. package/dist/preset-extension.js +35 -0
  71. package/dist/preset-mcp.js +292 -0
  72. package/dist/presets/mcp/rea/preset.json +28 -0
  73. package/dist/presets/mcp/rea/prompt.md +5 -0
  74. package/dist/presets/mcp/rea/skills/mcp-rea/SKILL.md +6 -0
  75. package/dist/presets/mcp/remotion/preset.json +27 -0
  76. package/dist/presets/mcp/remotion/prompt.md +7 -0
  77. package/dist/presets/mcp/remotion/skills/mcp-remotion/SKILL.md +6 -0
  78. package/dist/presets.js +727 -0
  79. package/dist/prompt-files.js +103 -0
  80. package/dist/prompt-text.js +41 -0
  81. package/dist/proxy.js +137 -0
  82. package/dist/quality/ast.js +196 -0
  83. package/dist/quality/cognitive.js +83 -0
  84. package/dist/quality/cyclomatic.js +30 -0
  85. package/dist/quality/halstead.js +83 -0
  86. package/dist/quality/index.js +18 -0
  87. package/dist/quality/length.js +3 -0
  88. package/dist/quality/maintainability.js +15 -0
  89. package/dist/quality/nesting.js +57 -0
  90. package/dist/quality/report.js +803 -0
  91. package/dist/quality/smells.js +67 -0
  92. package/dist/quality-tool.js +63 -0
  93. package/dist/read-only-commands.js +597 -0
  94. package/dist/repo-map.js +292 -0
  95. package/dist/roblox-connect.js +125 -0
  96. package/dist/rpc.js +91 -0
  97. package/dist/rtk.js +193 -0
  98. package/dist/sandbox.js +105 -0
  99. package/dist/schema.js +214 -0
  100. package/dist/serve.js +50 -0
  101. package/dist/session-lifecycle.js +19 -0
  102. package/dist/session-retention.js +111 -0
  103. package/dist/session.js +460 -0
  104. package/dist/sessions.js +100 -0
  105. package/dist/settings.js +148 -0
  106. package/dist/skills.js +35 -0
  107. package/dist/slop-tool.js +96 -0
  108. package/dist/spend-cap.js +43 -0
  109. package/dist/subagent.js +171 -0
  110. package/dist/tar.js +146 -0
  111. package/dist/theme.js +79 -0
  112. package/dist/thinking.js +45 -0
  113. package/dist/todo.js +114 -0
  114. package/dist/tool-loadout.js +102 -0
  115. package/dist/tool-render.js +769 -0
  116. package/dist/tools.js +176 -0
  117. package/dist/ui.js +4931 -0
  118. package/dist/update.js +551 -0
  119. package/dist/usage-segment.js +74 -0
  120. package/dist/web-tools.js +46 -0
  121. package/dist/web.js +265 -0
  122. package/dist/welcome.js +179 -0
  123. package/dist/workflow-engine.js +246 -0
  124. package/dist/workflow-progress.js +446 -0
  125. package/dist/workflow-view.js +195 -0
  126. package/dist/workflow.js +349 -0
  127. package/package.json +62 -15
  128. package/dist/chunk-27TRKD65.js +0 -2
  129. package/dist/chunk-3FYCFLNK.js +0 -1
  130. package/dist/chunk-3LZR7OEU.js +0 -1
  131. package/dist/chunk-56SEAGAQ.js +0 -1
  132. package/dist/chunk-5AWTL34S.js +0 -1
  133. package/dist/chunk-75P2SCOT.js +0 -1
  134. package/dist/chunk-7CHDNTH6.js +0 -1
  135. package/dist/chunk-7PHRNXLN.js +0 -34
  136. package/dist/chunk-AGGIYXN7.js +0 -2
  137. package/dist/chunk-AUDMRGWF.js +0 -189
  138. package/dist/chunk-C5E3SFOQ.js +0 -2
  139. package/dist/chunk-CMTKCUL5.js +0 -1
  140. package/dist/chunk-DAPXGSGD.js +0 -12
  141. package/dist/chunk-DOD7MUGX.js +0 -1
  142. package/dist/chunk-FPXVDP32.js +0 -8
  143. package/dist/chunk-GXB7RZHH.js +0 -1
  144. package/dist/chunk-I7KVLBMF.js +0 -2
  145. package/dist/chunk-ILDDDLRC.js +0 -1
  146. package/dist/chunk-JFYVRWJI.js +0 -2
  147. package/dist/chunk-LAHVHRCS.js +0 -2
  148. package/dist/chunk-LSJC73DG.js +0 -1
  149. package/dist/chunk-MNDKS6UH.js +0 -6
  150. package/dist/chunk-N345KWWK.js +0 -1
  151. package/dist/chunk-N3J2J7EQ.js +0 -5
  152. package/dist/chunk-ONZLNFQ2.js +0 -3
  153. package/dist/chunk-ORJ2QZBG.js +0 -3
  154. package/dist/chunk-PCALBZOO.js +0 -2
  155. package/dist/chunk-QPHSHL7H.js +0 -2
  156. package/dist/chunk-TKROS3BE.js +0 -5
  157. package/dist/chunk-TSEKPGK4.js +0 -1
  158. package/dist/chunk-UP7ENLEL.js +0 -117
  159. package/dist/chunk-UTYFLLSM.js +0 -1
  160. package/dist/chunk-V3R4FYTJ.js +0 -1
  161. package/dist/chunk-V656XVXV.js +0 -8
  162. package/dist/chunk-WKUJ7CFO.js +0 -1
  163. package/dist/chunk-WOT6VMZA.js +0 -1
  164. package/dist/chunk-WUPU6LTD.js +0 -4
  165. package/dist/chunk-WX3V5P7U.js +0 -5
  166. package/dist/chunk-XW6LMQGX.js +0 -1
  167. package/dist/chunk-YW7NORH3.js +0 -2
@@ -0,0 +1,349 @@
1
+ /**
2
+ * The `workflow` tool: one script becomes a fleet of agents.
3
+ *
4
+ * The model writes a JavaScript workflow and this runs it, which is Claude
5
+ * Code's `ultracode` mode reduced to what this harness's SDK can support. The
6
+ * script's bindings live in `workflow-engine.ts`; this file owns the two things
7
+ * that are about *this* harness:
8
+ *
9
+ * 1. **Which agent a call becomes.** The engine is backend-agnostic and asks for
10
+ * an agent; here that resolves to the subagent registry in `agents.ts`
11
+ * (`scout`/`task`/`reviewer`), opened directly rather than through the `task`
12
+ * tool — the tool's `drain()` runs *every* queued run, so concurrent
13
+ * `agent()` calls would each try to execute each other's runs. One run per
14
+ * call, one session per call, no shared queue.
15
+ *
16
+ * 2. **`meta` and the load-time rules.** The spec's file grammar: the first
17
+ * statement must be `export const meta = <pure literal>`, and the body must
18
+ * not import or export. `meta.phases[].title` values are matched against
19
+ * `phase()` calls for progress grouping.
20
+ */
21
+ import { readFileSync } from "node:fs";
22
+ import { basename } from "node:path";
23
+ import { StringEnum } from "@netrodev/hxf-ai";
24
+ import { defineTool } from "@netrodev/hxf-coding-agent";
25
+ import { Type } from "typebox";
26
+ import { agents, modelRef, openAgentSession } from "./agents.js";
27
+ import { createAgentWorktree } from "./git.js";
28
+ import { createBindings, WorkflowError } from "./workflow-engine.js";
29
+ import { formatElapsed, formatProgressLine } from "./workflow-progress.js";
30
+ /** The status key the live progress line is published under. */
31
+ export const WORKFLOW_STATUS_KEY = "workflow";
32
+ let workflowControls = null;
33
+ export function setWorkflowControls(next) {
34
+ workflowControls = next;
35
+ }
36
+ /** Cap on how long one run may take, so a runaway script cannot hang the tool. */
37
+ const RUN_TIMEOUT_MS = 10 * 60 * 1000;
38
+ /**
39
+ * Read `meta` and enforce the load-time grammar.
40
+ *
41
+ * The spec allows only literal forms in `meta` (no identifiers, calls, member
42
+ * access or spreads) and forbids `import`/`export` in the body. Rather than
43
+ * writing a full AST check, the export is extracted textually and the literal is
44
+ * evaluated by a `Function` with the binding names shadowed — so an identifier
45
+ * in `meta` fails loudly instead of silently evaluating.
46
+ */
47
+ export function parseMeta(code) {
48
+ // Comments and blank lines may precede `meta`: both reference parsers skip
49
+ // them, and the contract's own checklist *requires* a header comment ("use
50
+ // case, when to use, args, patterns demonstrated, cost profile, output
51
+ // shape") above the literal. Requiring the export to be the byte-zero
52
+ // statement rejected every real workflow.
53
+ const stripped = code.replace(/^(?:\s+|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)*/, "");
54
+ const head = /^export\s+const\s+meta\s*=\s*/.exec(stripped);
55
+ if (!head) {
56
+ throw new WorkflowError("a workflow script must start with `export const meta = { name, description }`");
57
+ }
58
+ // Keep the body relative to the original source so line numbers in later
59
+ // errors still point at the file the author wrote.
60
+ const bodyStart = code.length - stripped.length;
61
+ // Find the end of the literal by brace matching rather than by looking for a
62
+ // semicolon: the reference scripts omit it (`export const meta = { ... }`),
63
+ // and requiring one rejected every real workflow.
64
+ const literalStart = head[0].length;
65
+ let depth = 0;
66
+ let inString = null;
67
+ let end = -1;
68
+ for (let index = literalStart; index < stripped.length; index++) {
69
+ const char = stripped[index];
70
+ if (inString) {
71
+ if (char === "\\")
72
+ index++;
73
+ else if (char === inString)
74
+ inString = null;
75
+ continue;
76
+ }
77
+ if (char === '"' || char === "'" || char === "`")
78
+ inString = char;
79
+ else if (char === "{" || char === "[")
80
+ depth++;
81
+ else if (char === "}" || char === "]") {
82
+ depth--;
83
+ if (depth === 0) {
84
+ end = index + 1;
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ if (end === -1)
90
+ throw new WorkflowError("meta is not a closed literal object");
91
+ const literal = stripped.slice(literalStart, end);
92
+ // A trailing semicolon is optional.
93
+ const body = code.slice(bodyStart + end).replace(/^\s*;/, "");
94
+ if (/^\s*(import|export)\b/m.test(body)) {
95
+ throw new WorkflowError("a workflow body must not contain import or export declarations");
96
+ }
97
+ if (/\bimport\s*\(/.test(body)) {
98
+ throw new WorkflowError("a workflow body must not use dynamic import()");
99
+ }
100
+ let meta;
101
+ try {
102
+ meta = new Function(`"use strict"; return (${literal});`)();
103
+ }
104
+ catch (error) {
105
+ throw new WorkflowError(`meta must be a literal object: ${error instanceof Error ? error.message : String(error)}`);
106
+ }
107
+ if (typeof meta !== "object" || meta === null)
108
+ throw new WorkflowError("meta must be an object");
109
+ const record = meta;
110
+ if (typeof record.name !== "string" || !record.name.trim())
111
+ throw new WorkflowError("meta.name is required");
112
+ if (typeof record.description !== "string" || !record.description.trim()) {
113
+ throw new WorkflowError("meta.description is required");
114
+ }
115
+ const phases = Array.isArray(record.phases)
116
+ ? record.phases
117
+ .filter((entry) => typeof entry === "object" && entry !== null)
118
+ .map((entry) => ({
119
+ title: String(entry.title ?? ""),
120
+ ...(typeof entry.detail === "string" ? { detail: entry.detail } : {}),
121
+ }))
122
+ .filter((entry) => entry.title)
123
+ : undefined;
124
+ return {
125
+ meta: {
126
+ name: record.name,
127
+ description: record.description,
128
+ ...(typeof record.whenToUse === "string" ? { whenToUse: record.whenToUse } : {}),
129
+ ...(phases ? { phases } : {}),
130
+ },
131
+ body,
132
+ };
133
+ }
134
+ /**
135
+ * Compile a body into an async function.
136
+ *
137
+ * `Function` is the only option here: the sandbox `codemode` uses is not
138
+ * reachable with custom bindings (`CodemodeToolOptions` has no hook for them),
139
+ * and a workflow script is model-authored code running in the user's own
140
+ * session — the same trust level as the shell commands the agent already runs.
141
+ * The body is a module body, so top-level `await` and `return` are legal, which
142
+ * rules out a plain `Function`.
143
+ */
144
+ export function compileBody(body) {
145
+ const names = ["agent", "parallel", "pipeline", "phase", "log", "args", "budget"];
146
+ const source = `"use strict"; return (async () => {\n${body}\n})();`;
147
+ let factory;
148
+ try {
149
+ factory = new Function(...names, source);
150
+ }
151
+ catch (error) {
152
+ throw new WorkflowError(`workflow did not compile: ${error instanceof Error ? error.message : String(error)}`);
153
+ }
154
+ return factory;
155
+ }
156
+ /** Read the final text out of an agent session, whatever shape it returns. */
157
+ function finalText(session) {
158
+ return session.getLastAssistantText()?.trim() ?? "";
159
+ }
160
+ /**
161
+ * Run one agent call on a fresh subagent session.
162
+ *
163
+ * Output tokens, tool calls and the model come from the session's own stats,
164
+ * which is what makes the script's `budget` real rather than advisory and what
165
+ * fills the per-agent metric in the progress view.
166
+ *
167
+ * `isolation: "worktree"` gives the call its own checkout via `openAgentSession`,
168
+ * which is the only correct way to run parallel agents that write to the same
169
+ * files: siblings in one working tree race each other.
170
+ */
171
+ export function createInvoker(parentModel) {
172
+ return async ({ prompt, agentType, effort, model, isolation }) => {
173
+ const spec = agents.get(agentType) ?? agents.get("task");
174
+ if (!spec)
175
+ throw new WorkflowError(`no agent named ${agentType}`);
176
+ // `isolation: "worktree"` means a private checkout, not just a private cwd:
177
+ // parallel writers in one tree race each other's builds and `git add -A`.
178
+ const worktree = isolation === "worktree" ? await createAgentWorktree(process.cwd(), agentType) : null;
179
+ const session = await openAgentSession({
180
+ agent: spec.name,
181
+ effort: (effort ?? spec.effort),
182
+ // The script's `model` has to reach the session, not only the metric line:
183
+ // it used to be reported as the model that ran while the child actually
184
+ // ran the runtime default.
185
+ ...(model ? { model } : {}),
186
+ ...(parentModel ? { parentModel } : {}),
187
+ ...(worktree ? { worktree: worktree.path } : {}),
188
+ });
189
+ try {
190
+ await session.prompt(`${spec.prompt}\n\nTask:\n${prompt}`);
191
+ const stats = session.getSessionStats();
192
+ // The session first: it is the one that ran. The script's request is the
193
+ // fallback for a runtime that cannot name its own model.
194
+ const resolved = modelId(session) ?? model;
195
+ return {
196
+ text: finalText(session),
197
+ outputTokens: stats.tokens.output,
198
+ toolCalls: stats.toolCalls,
199
+ ...(resolved ? { model: resolved } : {}),
200
+ };
201
+ }
202
+ finally {
203
+ session.dispose();
204
+ await worktree?.remove();
205
+ }
206
+ };
207
+ }
208
+ /** The model a session actually ran on, when the SDK exposes it. */
209
+ function modelId(session) {
210
+ return session.model?.id ?? session.model?.name;
211
+ }
212
+ export const workflowParams = Type.Object({
213
+ code: Type.Optional(Type.String({ description: "The workflow script source" })),
214
+ path: Type.Optional(Type.String({ description: "Path to a workflow script, instead of code" })),
215
+ agent: Type.Optional(StringEnum(["scout", "task", "reviewer"], { description: "Default agent profile for calls" })),
216
+ budget: Type.Optional(Type.Number({ description: "Output-token ceiling for the whole run" })),
217
+ });
218
+ export function createWorkflowTool(options) {
219
+ // The workbench registers its panel hooks once, the same way `setAsker` and
220
+ // `setMirror` hand other modules a live surface. The tool is built during
221
+ // session construction, before the TUI exists, so it cannot be passed in.
222
+ const hooks = () => options ?? workflowControls;
223
+ return defineTool({
224
+ name: "workflow",
225
+ label: "workflow",
226
+ description: "Run a JavaScript workflow that orchestrates many agents deterministically. Provide `code` (or `path`). The script starts with `export const meta = { name, description, phases }` and then uses the injected bindings: `agent(prompt, {label, phase, schema, effort, model, agentType, isolation})`, `parallel(thunks)`, `pipeline(items, ...stages)`, `phase(title)`, `log(msg)`, `args`, `budget`. `agent()` resolves null on failure and throws only when the budget or the call cap is exhausted, so null-check every result. `parallel()` takes thunks and never rejects; `pipeline()` has no barrier between items. Use it for fan-out, for builder-verifier loops that converge on a verdict, and for anything needing many agents whose order and count must be deterministic.",
227
+ promptSnippet: "workflow: orchestrate many agents from one JavaScript script",
228
+ parameters: workflowParams,
229
+ executionMode: "sequential",
230
+ async execute(_toolCallId, params, signal, onUpdate, ctx) {
231
+ const code = params.path ? readFileSync(params.path, "utf8") : (params.code ?? "");
232
+ if (!code.trim())
233
+ throw new WorkflowError("provide code or path");
234
+ const { meta, body } = parseMeta(code);
235
+ // The contract requires `meta.name` to match the script's filename, so a
236
+ // run launched by path is identifiable in the progress list and in any
237
+ // artifact directory named after it. A mismatch is a real error, not a
238
+ // warning: it is what makes the name trustworthy.
239
+ if (params.path) {
240
+ const file = basename(params.path).replace(/\.(js|mjs|cjs|ts)$/, "");
241
+ if (file !== meta.name) {
242
+ throw new WorkflowError(`meta.name "${meta.name}" must match the script filename "${file}"`);
243
+ }
244
+ }
245
+ const factory = compileBody(body);
246
+ const startedAt = Date.now();
247
+ const steps = [];
248
+ let outputTokens = 0;
249
+ let callCount = 0;
250
+ const detailsOf = (ok, error) => ({
251
+ name: meta.name,
252
+ ...(meta.description ? { description: meta.description } : {}),
253
+ ok,
254
+ agents: callCount,
255
+ outputTokens,
256
+ startedAt,
257
+ phases: meta.phases ?? [],
258
+ steps,
259
+ ...(error ? { error } : {}),
260
+ });
261
+ // The live surface: the dock line shows the run in flight, the way the
262
+ // reference puts its progress line in the task panel under the input box.
263
+ const publish = (progress, ended) => {
264
+ ctx.ui.setStatus(WORKFLOW_STATUS_KEY, ended ? undefined : formatProgressLine(progress));
265
+ onUpdate?.({ content: [], details: detailsOf(false) });
266
+ };
267
+ const onProgress = (agents) => {
268
+ callCount = agents.length;
269
+ outputTokens = agents.reduce((sum, agent) => sum + agent.outputTokens, 0);
270
+ steps.splice(0, steps.length, ...agents.map((agent) => ({
271
+ label: agent.label,
272
+ phase: agent.phase,
273
+ agent: agent.agentType,
274
+ status: agent.status,
275
+ outputTokens: agent.outputTokens,
276
+ startedAt: agent.startedAt,
277
+ ...(agent.toolCalls !== undefined ? { toolCalls: agent.toolCalls } : {}),
278
+ ...(agent.model ? { model: agent.model } : {}),
279
+ ...(agent.endedAt ? { endedAt: agent.endedAt } : {}),
280
+ ...(agent.detail ? { detail: agent.detail } : {}),
281
+ ...(agent.tasks ? { tasks: agent.tasks } : {}),
282
+ })));
283
+ publish({ name: meta.name, ...(meta.description ? { description: meta.description } : {}), phases: meta.phases ?? [], steps, startedAt }, false);
284
+ };
285
+ const invoke = createInvoker(modelRef(ctx?.model));
286
+ const bindings = createBindings({
287
+ invoke: params.agent ? wrapAgent(invoke, params.agent) : invoke,
288
+ budgetTotal: params.budget ?? null,
289
+ onProgress,
290
+ ...(signal ? { signal } : {}),
291
+ ...(hooks()?.isPaused ? { isPaused: hooks().isPaused } : {}),
292
+ });
293
+ const controller = new AbortController();
294
+ const timer = setTimeout(() => controller.abort(), RUN_TIMEOUT_MS);
295
+ if (signal)
296
+ signal.addEventListener("abort", () => controller.abort(), { once: true });
297
+ // Hand the panel a way to stop this run. `x` in the panel calls it; the
298
+ // abort then resolves in-flight calls as `null`, which is the engine's
299
+ // normal failure mode, so a stopped run still returns a partial result
300
+ // instead of throwing.
301
+ hooks()?.onStop?.(() => controller.abort());
302
+ try {
303
+ const result = await Promise.race([
304
+ factory(bindings.agent, bindings.parallel, bindings.pipeline, bindings.phase, bindings.log, bindings.args, bindings.budget),
305
+ new Promise((_resolve, reject) => controller.signal.addEventListener("abort", () => reject(new WorkflowError("workflow timed out or was aborted")))),
306
+ ]);
307
+ const endedAt = Date.now();
308
+ ctx.ui.setStatus(WORKFLOW_STATUS_KEY, undefined);
309
+ return {
310
+ content: [
311
+ {
312
+ type: "text",
313
+ text: `${meta.name}: ${callCount} agent call(s), ${outputTokens} output tokens in ${formatElapsed(endedAt - startedAt)}\n\n${render(result)}`,
314
+ },
315
+ ],
316
+ details: { ...detailsOf(true), endedAt },
317
+ };
318
+ }
319
+ catch (error) {
320
+ const message = error instanceof Error ? error.message : String(error);
321
+ ctx.ui.setStatus(WORKFLOW_STATUS_KEY, undefined);
322
+ return {
323
+ content: [{ type: "text", text: `${meta.name} failed after ${callCount} agent call(s): ${message}` }],
324
+ details: { ...detailsOf(false, message), endedAt: Date.now() },
325
+ isError: true,
326
+ };
327
+ }
328
+ finally {
329
+ clearTimeout(timer);
330
+ hooks()?.onStop?.(null);
331
+ }
332
+ },
333
+ });
334
+ }
335
+ /** Force every call to one agent profile, whatever the script asked for. */
336
+ function wrapAgent(invoke, agentType) {
337
+ return (spec) => invoke({ ...spec, agentType });
338
+ }
339
+ /** The script's return value, as text for the model. */
340
+ function render(value) {
341
+ if (typeof value === "string")
342
+ return value;
343
+ try {
344
+ return JSON.stringify(value, null, 2);
345
+ }
346
+ catch {
347
+ return String(value);
348
+ }
349
+ }
package/package.json CHANGED
@@ -1,20 +1,8 @@
1
1
  {
2
2
  "name": "howtofixcli",
3
- "version": "0.7.24",
4
- "type": "module",
3
+ "version": "0.7.26",
5
4
  "description": "HowToFix coding agent. Workbench TUI, sessions, background agents.",
6
- "license": "UNLICENSED",
7
- "homepage": "https://howtofix.id",
8
- "author": "HowToFix",
9
- "keywords": [
10
- "coding-agent",
11
- "cli",
12
- "tui",
13
- "howtofix"
14
- ],
15
- "engines": {
16
- "node": ">=22.19"
17
- },
5
+ "type": "module",
18
6
  "bin": {
19
7
  "howtofix": "dist/main.js"
20
8
  },
@@ -23,6 +11,38 @@
23
11
  "README.md",
24
12
  "LICENSE"
25
13
  ],
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json && node --input-type=module -e \"import { rmSync, cpSync } from 'node:fs'; rmSync('dist/presets', { recursive: true, force: true }); cpSync('presets', 'dist/presets', { recursive: true });\" && node scripts/set-executable.mjs",
16
+ "start": "node dist/main.js",
17
+ "typecheck": "tsc -p tsconfig.json --noEmit",
18
+ "check": "node scripts/check-all.mjs",
19
+ "check:hub": "node scripts/check-hub-install.mjs",
20
+ "check:tar": "node scripts/check-tar.mjs --online",
21
+ "quality": "node scripts/quality.mjs",
22
+ "frame": "node scripts/render-frame.mjs",
23
+ "frame:workflow": "node scripts/render-workflow-frame.mjs",
24
+ "probe:commands": "python3 scripts/probe-slash-commands.py",
25
+ "build:release": "node scripts/build-release.mjs",
26
+ "version": "node scripts/sync-version.mjs",
27
+ "slop": "node scripts/slop.mjs",
28
+ "slop:commits": "node scripts/slop.mjs --commits",
29
+ "hooks:install": "git config core.hooksPath .githooks && echo 'hooks installed from .githooks'",
30
+ "check:sync": "node scripts/check-hub-sync.mjs --online",
31
+ "check:level-color": "python3 scripts/probe-level-color.py",
32
+ "check:skills-wire": "node scripts/check-skill-wire.mjs",
33
+ "check:agents-live": "node scripts/check-subagent-live.mjs",
34
+ "release:patch": "node scripts/release.mjs patch",
35
+ "release:minor": "node scripts/release.mjs minor",
36
+ "release:major": "node scripts/release.mjs major",
37
+ "release:dry": "node scripts/release.mjs patch --dry",
38
+ "test": "node --test test/*.test.mjs",
39
+ "format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.mjs\" \"test/**/*.mjs\"",
40
+ "format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.mjs\" \"test/**/*.mjs\""
41
+ },
42
+ "license": "UNLICENSED",
43
+ "engines": {
44
+ "node": ">=22.19"
45
+ },
26
46
  "dependencies": {
27
47
  "@netrodev/hxf-agent-core": "^1.0.22",
28
48
  "@netrodev/hxf-ai": "^1.0.22",
@@ -31,5 +51,32 @@
31
51
  "typebox": "^1.4.3",
32
52
  "typescript": "7.0.2",
33
53
  "undici": "^8.12.3"
34
- }
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^22.20.5",
57
+ "esbuild": "^0.28.2",
58
+ "prettier": "^3.9.10"
59
+ },
60
+ "homepage": "https://howtofix.id",
61
+ "author": "HowToFix",
62
+ "keywords": [
63
+ "coding-agent",
64
+ "cli",
65
+ "tui",
66
+ "howtofix"
67
+ ],
68
+ "contributors": [
69
+ {
70
+ "name": "hshinosa",
71
+ "url": "https://github.com/hshinosa"
72
+ },
73
+ {
74
+ "name": "yhagus",
75
+ "url": "https://github.com/yhagus"
76
+ },
77
+ {
78
+ "name": "mmatttttt",
79
+ "url": "https://github.com/mmatttttt"
80
+ }
81
+ ]
35
82
  }
@@ -1,2 +0,0 @@
1
- import{a as u}from"./chunk-UTYFLLSM.js";import{existsSync as l,mkdirSync as f,readFileSync as c}from"node:fs";import{dirname as p,join as m}from"node:path";var a={skipPermissions:!1,autoUpdate:!0,rightClickPaste:"auto",welcomeSeen:!1,compressToolOutput:!1};function g(t){return m(t,"settings.json")}function k(t){try{let e=JSON.parse(c(g(t),"utf8")),n=r=>{let i=e[r];return typeof i=="boolean"?i:a[r]},o=e.rightClickPaste;return{skipPermissions:n("skipPermissions"),autoUpdate:n("autoUpdate"),rightClickPaste:o==="on"||o==="off"||o==="auto"?o:a.rightClickPaste,welcomeSeen:n("welcomeSeen"),compressToolOutput:n("compressToolOutput"),...typeof e.agents=="number"&&Number.isInteger(e.agents)&&e.agents>0?{agents:e.agents}:{}}}catch{return{...a}}}function h(t,e,n){return s(t,e,n)}function w(t){return s(t,"welcomeSeen",!0)}function P(t,e){return s(t,"rightClickPaste",e==="auto"?void 0:e)}function y(t){return t==="on"?!0:t==="off"?!1:process.platform!=="win32"}function x(t,e){let n=typeof e=="number"&&Number.isInteger(e)&&e>0?e:void 0;return s(t,"agents",n)}function C(t,e,n){let o=n.trim();return s(t,e,o===""?void 0:o)}function s(t,e,n){let o=g(t),r={};try{let i=JSON.parse(c(o,"utf8"));i&&typeof i=="object"&&!Array.isArray(i)&&(r=i)}catch{if(l(o))return!1}n===void 0?delete r[e]:r[e]=n;try{f(p(o),{recursive:!0}),u(o,`${JSON.stringify(r,null,2)}
2
- `)}catch{return!1}return!0}export{a,k as b,h as c,w as d,P as e,y as f,x as g,C as h};
@@ -1 +0,0 @@
1
- import{readFileSync as u}from"node:fs";import{join as c}from"node:path";import{EnvHttpProxyAgent as p,ProxyAgent as l,getGlobalDispatcher as a,setGlobalDispatcher as s}from"undici";var f="httpsProxy";function d(n){let e;try{e=JSON.parse(u(c(n,"settings.json"),"utf8"))}catch{return}let r=e[f];if(typeof r!="string")return;let t=r.trim();return t===""?void 0:t}function m(){let e=(process.env.HTTPS_PROXY??process.env.HTTP_PROXY??process.env.ALL_PROXY)?.trim();return e||void 0}function g(n){let e=n.trim();if(e==="")return{ok:!1,problem:"empty"};let r;try{r=new URL(e)}catch{return{ok:!1,problem:`"${e}" is not a URL`}}let t=r.protocol.replace(/:$/,"").toLowerCase();return t!=="http"&&t!=="https"?{ok:!1,problem:`unsupported scheme "${t}"; use http:// or https://`}:r.hostname?{ok:!0,url:e}:{ok:!1,problem:"no host in the proxy URL"}}var i;function P(n){let e=()=>{i===void 0&&(i=a()),s(i)},r=d(n);if(r){let o=g(r);if(!o.ok)return{source:"settings"};try{s(new l(o.url))}catch{return e(),{source:"settings"}}return{url:o.url,source:"settings"}}let t=m();if(t){try{s(new p)}catch{return e(),{source:"env"}}return{url:t,source:"env"}}return e(),{source:"none"}}export{f as a,d as b,m as c,g as d,P as e};
@@ -1 +0,0 @@
1
- var o=/no available channel for model\s+([^\s"',)\]]+)/i;function s(t){let e=t??"";if(e.trim())return o.exec(e)?.[1]}function a(t){let e=t??"";if(!e.trim())return;let n=s(e);if(n)return`the gateway has no live channel for ${n} right now \u2014 /model lists the models that answer; pick another.`;if(/\bmissing api key\b|\bauthenticationerror\b|\bauth\s*error\b/i.test(e))return"no key for this model yet \u2014 /connect signs you in and pulls the model list.";if(/\bunknown model\b/i.test(e))return"/connect pulls the current model list from the gateway.";if(/\bcode 11128\b|\billegal api invocation\b/i.test(e))return"the gateway refused the request shape (code 11128) \u2014 /connect re-syncs each model's compatibility flags.";if(/\binsufficient (?:balance|quota|credit)\b|\bexceeded your current quota\b|http\s*402/i.test(e))return"the account is out of credit \u2014 /usage shows what is left.";if(/\bserver error\b|http\s*5\d\d/i.test(e))return"the gateway's channel is failing, not the CLI \u2014 /model lists what your account can reach; pick another.";if(/\brate.?limit|\b429\b/i.test(e))return"the gateway is rate-limiting this token \u2014 wait a moment, then resend the prompt.";if(/\bprompt (?:is )?too long\b|\bprompt too long\b|\bexceeds? (?:the )?(?:model'?s )?maximum context length\b|\bmaximum context length is \d+ tokens\b|\bexceeds the context window\b|\bcontext[_ ]length[_ ]exceeded\b|\breduce the length of the messages\b/i.test(e))return"the conversation is past this model's context window \u2014 /compact summarises it and continues, or /model picks a model with a bigger window.";if(/\bupstream api error\b/i.test(e))return"the gateway refused the request \u2014 run /connect again to re-sync your models."}function u(t){let e=0,n;return{limit:t,close(r){e+=r},total(r){return e+r},check(r){if(t===void 0||n!==void 0)return n;let i=e+r;return i-t>1e-9&&(n={spent:i,limit:t}),n}}}function c(t,e){return`stopped \u2014 spent $${t.toFixed(4)} of a $${e.toFixed(2)} budget.`}function l(t,e){return`--max-budget-usd needs a model with prices, and ${t} declares none (its rates are zero, so every turn costs $0 and the budget would never fire). Add a cost to that model in ${e}/models.json, or drop the flag.`}export{s as a,a as b,u as c,c as d,l as e};
@@ -1 +0,0 @@
1
- import{b as a,c as b,d as c,e as d,f as e,g as f,h as g,i as h,j as i,k as j,l as k,m as l,n as m,o as n,p as o,q as p,r as q}from"./chunk-JFYVRWJI.js";import"./chunk-WOT6VMZA.js";export{c as APPROVAL_LABELS,b as APPROVAL_MODES,f as MODE_MAX_TIER,e as TIER_RANK,q as createPermissionExtension,h as criticalCommand,n as decideToolCall,o as describeToolCall,p as formatApprovalPrompt,j as getApprovalMode,d as isApprovalMode,l as isDangerousBypass,a as isReadOnlyCommand,g as resolveTier,i as setApprovalMode,m as setApprovalPrompt,k as setDangerousBypass};
@@ -1 +0,0 @@
1
- import{a,b,c,d,e,f,g,h,i,j}from"./chunk-C5E3SFOQ.js";import"./chunk-N3J2J7EQ.js";import"./chunk-WOT6VMZA.js";export{h as CLI_CLIENT_HEADER,a as CLI_SIGNING_KEY_ID,i as cliClientHeader,c as cliSignaturePayload,f as cliSigningKeyFingerprint,e as cliSigningKeyMatchesPanel,b as cliSigningPublicKey,d as createNonce,g as describeClient,j as signLoginRequest};
@@ -1 +0,0 @@
1
- import{A,B,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}from"./chunk-MNDKS6UH.js";import"./chunk-UTYFLLSM.js";import"./chunk-C5E3SFOQ.js";import"./chunk-N3J2J7EQ.js";import"./chunk-WOT6VMZA.js";export{g as CATALOG_CACHE_TTL_MS,m as DEFAULT_CONTEXT_WINDOW,a as HOWTOFIX_BASE_URL,b as HOWTOFIX_CONSOLE_URL,l as HOWTOFIX_MODEL_COMPAT,c as HOWTOFIX_PROVIDERS,j as HTTP_IDLE_TIMEOUT_MS,r as UNLIMITED_SENTINEL,v as applyCatalog,w as applyCompat,A as checkAccount,s as checkUsage,d as configuredProviderIds,x as countNewModels,k as ensureGatewaySettings,B as fetchGatewayPrices,z as findLiveModels,q as isHowtofixProvider,h as loadCatalog,o as looksLikeToken,n as maskToken,u as mergeModels,e as newProviderEntry,p as parseModelIds,y as probeModel,f as repairGatewayModels,i as repairModelFacts,t as syncGatewayModelsOnStart};
@@ -1 +0,0 @@
1
- import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u}from"./chunk-WUPU6LTD.js";import"./chunk-WOT6VMZA.js";export{f as BROKEN_LOCK_GRACE_MS,c as CHECK_ATTEMPTS,k as CHECK_FAILED_MESSAGE,b as CHECK_TIMEOUT_MS,d as INSTALL_TIMEOUT_MS,e as LOCK_TIMEOUT_MS,j as NPM_MISSING_MESSAGE,a as PACKAGE_NAME,n as acquireUpdateLock,u as autoUpdateEnabled,l as checkFailedNote,q as checkForUpdate,g as compareVersions,s as installUpdate,t as isInstalledCopy,h as isNewer,p as latestVersion,r as npmFailureDetail,m as pidAlive,o as releaseUpdateLock,i as resolveNpm};
@@ -1,34 +0,0 @@
1
- import{$ as Ne,A as Te,B as $e,D as A,E as G,Ea as Xe,G as M,H as Ee,P as Ae,Pa as Ye,Q as ve,Qa as Ve,R as je,S as Oe,T as Pe,V as Re,W as Ce,Y as De,_ as W,a as C,b as D,ba as Fe,c as ie,ca as Me,d as ae,da as Ie,e as ce,ea as Le,f as le,fa as Ge,g as $,i as de,ia as Ue,j as ue,k as fe,ka as Be,l as me,la as U,m as ge,ma as He,na as We,o as he,p as we,pa as z,q as ye,r as be,s as ke,t as E,ta as ze,u as xe,ua as qe,v as Se,w as L,wa as Ze,x as _e,xa as Ke}from"./chunk-AUDMRGWF.js";import{r as re}from"./chunk-JFYVRWJI.js";import{b as pe}from"./chunk-27TRKD65.js";import{D as k,G as te,r as Q,s as ee}from"./chunk-V656XVXV.js";import{c as ne}from"./chunk-MNDKS6UH.js";import{h as oe,l as se}from"./chunk-QPHSHL7H.js";import"./chunk-UTYFLLSM.js";import"./chunk-C5E3SFOQ.js";import{b as I,c as V,d as J}from"./chunk-N3J2J7EQ.js";import"./chunk-WOT6VMZA.js";import{join as _t}from"node:path";import{hostname as Fn}from"node:os";import{createAgentSession as Mn,createCodemodeExtension as In,createMcpExtension as Ln,DefaultResourceLoader as Gn,ProjectTrustStore as Un,SessionManager as B,SettingsManager as Bn}from"@netrodev/hxf-coding-agent";import{existsSync as $t,readFileSync as Et}from"node:fs";import{join as At}from"node:path";var Je=At(k,"APPEND_SYSTEM.md"),vt=["<non-interactive>","This run has no user attached: it was started with --print, and there is nobody to answer a question. Anything that stops and waits for a reply \u2014 `ask`, a confirmation, a skill that resolves a mode by asking which mode to use \u2014 will simply end the run with the task unfinished.","So do not ask. Where an instruction tells you to ask the user and wait, take the default it describes and continue, and say which default you took and why in your final answer. The user reads that answer; they cannot answer mid-run.","Where a skill offers a choice of modes, prefer the one that applies the rules while you work over the one that audits afterwards: this is the mode for building something now, and the audit mode can still be run later by the same person who reads your answer.","</non-interactive>"].join(`
2
- `),jt=16*1024;function Ot(e){try{if(!$t(e))return;let o=Et(e,"utf8").trim();return!o||Buffer.byteLength(o,"utf8")>jt?void 0:o}catch{return}}function Qe(e,o){let s=[o?e:`${e}
3
-
4
- ${vt}`],t=Ot(Je);return t&&s.push(`# Standing instructions from ${Je}
5
- ${t}`),s}function Pt(e){return e.replace(/operating inside hxf, a coding agent harness/gi,"operating as HowToFix, a coding agent harness").replace(/^HowToFix documentation \(read only when the user asks about[^\n]*\):?$/gim,"Harness documentation (internal reference; the user is never told the harness name):").replace(/\bhxf itself\b/gi,"the harness itself").replace(/^- When reading hxf docs or examples/m,"- When reading harness docs or examples").replace(/^- When working on hxf topics/m,"- When working on harness topics").replace(/^- Always read hxf \.md files/m,"- Always read the harness .md files").replace(/\bhxf packages\b/gi,"harness packages").replace(/\bhxf \.md files\b/gi,"harness .md files").replace(/\bhxf docs\b/gi,"harness docs").replace(/^-\s*You can inspect HXF_\* environment variables/m,"- You can inspect the harness's environment variables")}var Rt=["preamble","rules","docs","addendum"];function Ct(e){if(!e)return;let o=!1,s={...e};for(let t of Rt){let n=s[t];if(typeof n!="string")continue;let r=Pt(n);r!==n&&(s[t]=r,o=!0)}return o?s:void 0}function et(){return e=>{e.on("context_with_system",async o=>{let s=o.messages[0];if(!s||s.role!=="system")return;let t=Ct(s.sections);return t?{messages:o.messages.map(r=>r===s?{...s,sections:t}:r)}:void 0})}}var x={hooks:{}},N;function tt(){return e=>{let o=ce(ie);x=o.config,N=void 0,le(),e.on("session_start",async(s,t)=>{for(let a of o.diagnostics)t.ui.notify(`hooks.json: ${a.message}`,"warning");let{outcome:n,ran:r}=await $(x,"SessionStart",{hook_event_name:"SessionStart",cwd:t.cwd},{cwd:t.cwd});n.context&&(N=n.context),n.error&&t.ui.notify(`SessionStart hook: ${n.error}`,"warning"),r&&N&&t.ui.notify(`SessionStart hooks added ${N.length} characters of context`,"info")}),e.on("input",async(s,t)=>{if(!x.hooks.UserPromptSubmit)return;let n={hook_event_name:"UserPromptSubmit",cwd:t.cwd,prompt:s.text},{outcome:r}=await $(x,"UserPromptSubmit",n,{cwd:t.cwd});if(r.error&&t.ui.notify(`UserPromptSubmit hook: ${r.error}`,"warning"),r.block)return t.ui.notify(r.reason??"blocked by a hook","warning"),{action:"handled"};if(r.context)return{action:"transform",text:`${s.text}
6
-
7
- ${r.context}`}}),e.on("tool_call",async(s,t)=>{if(!x.hooks.PreToolUse)return;let n={hook_event_name:"PreToolUse",cwd:t.cwd,tool_name:s.toolName,tool_input:s.input},{outcome:r}=await $(x,"PreToolUse",n,{cwd:t.cwd});if(r.error&&t.ui.notify(`PreToolUse hook: ${r.error}`,"warning"),r.block)return{block:!0,reason:r.reason??"blocked by a hook"};if(r.updatedInput){let i=e.getAllTools().find(f=>f.name===s.toolName)?.parameters;if(!i)t.ui.notify(`PreToolUse hook rewrote \`${s.toolName}\`, but no schema is known for that tool \u2014 the rewrite was ignored`,"warning");else{let f=ae(i,r.updatedInput);f?t.ui.notify(`PreToolUse hook rewrite ignored \u2014 ${f}`,"warning"):Object.assign(s.input,r.updatedInput)}}}),e.on("tool_result",async(s,t)=>{if(!x.hooks.PostToolUse)return;let n=s.content.map(i=>i.type==="text"?i.text:"").join(`
8
- `),r={hook_event_name:"PostToolUse",cwd:t.cwd,tool_name:s.toolName,tool_input:s.input,tool_result:n,tool_is_error:s.isError},{outcome:a}=await $(x,"PostToolUse",r,{cwd:t.cwd});if(a.error&&t.ui.notify(`PostToolUse hook: ${a.error}`,"warning"),!!a.context)return{content:[...s.content,{type:"text",text:`
9
- [hook] ${a.context}`}],structuredContent:s.structuredContent}}),e.on("context_with_system",async s=>{if(!N)return;let t=s.messages[0];if(!t||t.role!=="system")return;let n=`${C(t.content)}
10
-
11
- ${N}`;return{messages:s.messages.map(a=>a===t?{...t,content:D(t.content,n)}:a)}}),e.on("session_before_compact",async(s,t)=>{if(!x.hooks.PreCompact)return;let{outcome:n}=await $(x,"PreCompact",{hook_event_name:"PreCompact",cwd:t.cwd,compact_reason:s.reason},{cwd:t.cwd});n.error&&t.ui.notify(`PreCompact hook: ${n.error}`,"warning")}),e.on("session_shutdown",async(s,t)=>{if(!x.hooks.SessionEnd)return;let{outcome:n}=await $(x,"SessionEnd",{hook_event_name:"SessionEnd",cwd:t.cwd,reason:s.reason},{cwd:t.cwd});n.error&&t.ui.notify(`SessionEnd hook: ${n.error}`,"warning")}),e.on("agent_settled",async(s,t)=>{if(!x.hooks.Stop)return;let{outcome:n}=await $(x,"Stop",{hook_event_name:"Stop",cwd:t.cwd},{cwd:t.cwd});n.error&&t.ui.notify(`Stop hook: ${n.error}`,"warning")})}}var Dt=new Set(["bash","grep","find","ls","bash_output","powershell"]),Nt=new Set(["bash","bash_output","powershell"]),Ft=40,Mt=12,It=8,ot=6;function Lt(e,o){let s=e.trim();return!!(s===""||/^[-=*_~+#]{3,}$/.test(s)||o!==void 0&&e===o)}function nt(e){return e.replace(/\d+/g,"#").trimEnd()}function Gt(e){let o=[],s=0,t=0;for(;t<e.length;){let n=nt(e[t]);if(n===""||!/\d/.test(e[t])){o.push(e[t]),t+=1;continue}let r=t+1;for(;r<e.length&&nt(e[r])===n&&/\d/.test(e[r]);)r+=1;let a=r-t;if(a>=ot)o.push(e[t]),o.push(` \u2026 ${a-2} more line${a-2===1?"":"s"} of the same shape \u2026`),o.push(e[r-1]),s+=1;else for(let i=t;i<r;i+=1)o.push(e[i]);t=r}return{out:o,collapsed:s}}function Ut(e,o,s={}){if(o?.truncated)return;let t=e.split(`
12
- `);if(t.length<Ft)return;let n=[],r=0;for(let g=0;g<t.length;g+=1){if(Lt(t[g],n[n.length-1])){if(t[g].trim()===""&&n[n.length-1]?.trim()!==""){n.push("");continue}r+=1;continue}n.push(t[g])}let a=s.toolName===void 0||Nt.has(s.toolName),{out:i,collapsed:f}=a?Gt(n):{out:n,collapsed:0},p=s.head??Mt,u=s.tail??It,c=i,d=0;if(i.length>p+u+ot&&(d=i.length-p-u,c=[...i.slice(0,p),` \u2026 ${d} line${d===1?"":"s"} omitted (${p} kept from the top, ${u} from the end) \u2026`,...i.slice(i.length-u)]),!(r+f===0&&d===0))return{text:c.join(`
13
- `),stats:{before:t.length,after:c.length,collapsed:f}}}function st(e){return o=>{o.on("tool_result",s=>{if(!e.enabled()||s.isError||!Dt.has(s.toolName))return;let t=s.content.find(i=>i.type==="text");if(!t||t.type!=="text")return;let n=s.details,r=Ut(t.text,n,{toolName:s.toolName});return r?{content:s.content.map(i=>i===t?{type:"text",text:r.text}:i)}:void 0})}}import{readdirSync as Bt,readFileSync as Ht,statSync as Wt}from"node:fs";import{extname as zt,join as rt,relative as qt,resolve as Zt,sep as it}from"node:path";import{Type as P}from"@netrodev/hxf-ai";import{defineTool as Kt}from"@netrodev/hxf-coding-agent";var Xt=new Set(["node_modules",".git","dist","build","out","coverage","vendor","target","__pycache__",".venv","venv",".next",".turbo",".cache"]),v={".ts":{kind:"ts",patterns:[/^\s*export\s+(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/gm,/^\s*export\s+(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/gm,/^\s*export\s+(?:interface|type)\s+([A-Za-z_$][\w$]*)/gm,/^\s*export\s+const\s+([A-Za-z_$][\w$]*)/gm,/^\s*(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/gm,/^\s*(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/gm]},".tsx":{kind:"tsx",patterns:[]},".js":{kind:"js",patterns:[]},".mjs":{kind:"js",patterns:[]},".go":{kind:"go",patterns:[/^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/gm,/^type\s+([A-Za-z_]\w*)\s+(?:struct|interface)/gm]},".py":{kind:"py",patterns:[/^(?:async\s+)?def\s+([A-Za-z_]\w*)/gm,/^class\s+([A-Za-z_]\w*)/gm]},".rs":{kind:"rs",patterns:[/^\s*(?:pub\s+)?(?:async\s+)?fn\s+([A-Za-z_]\w*)/gm,/^\s*(?:pub\s+)?(?:struct|enum|trait)\s+([A-Za-z_]\w*)/gm]}};v[".tsx"].patterns=v[".ts"].patterns;v[".js"].patterns=v[".ts"].patterns;v[".mjs"].patterns=v[".ts"].patterns;var Yt=512*1024,Vt=12;function Jt(e,o){let s=v[o];if(!s)return[];let t=new Set,n=[];for(let r of s.patterns){r.lastIndex=0;for(let a of e.matchAll(r)){let i=a[1];i&&!t.has(i)&&(t.add(i),n.push(i))}}return n}function Qt(e,o){let s=new Set,t=n=>{n&&(n.startsWith("./")||n.startsWith("../"))&&s.add(n)};if(o===".ts"||o===".tsx"||o===".js"||o===".mjs"){for(let n of e.matchAll(/(?:from|require\()\s*["']([^"']+)["']/g))t(n[1]);for(let n of e.matchAll(/^\s*import\s+["']([^"']+)["']/gm))t(n[1])}else if(o===".go")for(let n of e.matchAll(/^\s*(?:[\w.]+\s+)?"([^"]+)"/gm))t(n[1]);else if(o===".py")for(let n of e.matchAll(/^\s*from\s+(\.+[\w.]*)\s+import/gm)){let r=n[1].replace(/\./g,"/");t(r.startsWith("/")?`.${r}`:`./${r}`)}else if(o===".rs")for(let n of e.matchAll(/^\s*use\s+(?:crate|super|self)::([\w:]+)/gm))t(`./${n[1].split("::").join("/")}`);return[...s]}function en(e,o,s){let t=e.split("/").slice(0,-1).join("/"),n=rt(t,o).split(it).join("/").replace(/^\.\//,"");return[n,`${n}.ts`,`${n}.tsx`,`${n}.js`,`${n}.mjs`,`${n}.go`,`${n}.py`,`${n}.rs`,`${n}/index.ts`,`${n}/index.tsx`,`${n}/index.js`,`${n}/mod.rs`,`${n}.py`].find(a=>s.has(a))}function tn(e,o="."){let s=[],t=(n,r)=>{if(r>Vt)return;let a;try{a=Bt(n,{withFileTypes:!0})}catch{return}for(let i of a){let f=rt(n,i.name);if(i.isDirectory()){if(Xt.has(i.name)||i.name.startsWith("."))continue;t(f,r+1);continue}let p=zt(i.name);if(v[p])try{if(Wt(f).size>Yt)continue;let u=Ht(f,"utf8"),c=qt(e,f).split(it).join("/");s.push({path:c,symbols:Jt(u,p),imports:Qt(u,p)})}catch{}}};return t(Zt(e,o),0),s}function nn(e,o){if(e.length===0)return[];let s=new Set(e.map(c=>c.path)),t=new Map(e.map((c,d)=>[c.path,d])),n=e.map(()=>[]),r=e.map(()=>[]);e.forEach((c,d)=>{for(let l of c.imports){let g=en(c.path,l,s);if(!g||g===c.path)continue;let b=t.get(g);b!==void 0&&(n[d].push(b),r[b].push(d))}});let a=e.length,i=o?.trim().toLowerCase(),f=e.map(c=>i&&`${c.path} ${c.symbols.join(" ")}`.toLowerCase().includes(i)?4/a:1/a),p=f.reduce((c,d)=>c+d,0),u=e.map((c,d)=>f[d]/p);for(let c=0;c<20;c++){let d=e.map((l,g)=>.15*(f[g]/p));for(let l=0;l<a;l++){if(n[l].length===0){for(let g=0;g<a;g++)d[g]+=.85*u[l]/a;continue}for(let g of n[l])d[g]+=.85*u[l]/n[l].length}u=d}return e.map((c,d)=>({file:c,score:u[d],match:i?`${c.path} ${c.symbols.join(" ")}`.toLowerCase().includes(i):!1})).sort((c,d)=>Number(d.match)-Number(c.match)||d.score-c.score||c.file.path.localeCompare(d.file.path)).map(c=>c.file)}function on(e,o={}){let t=Math.max(200,Math.min(8e3,o.budget??1200))*4,n=nn(e,o.query),r=[],a=0;for(let f of n){let p=f.symbols.slice(0,8).join(", "),u=p?`${f.path}: ${p}`:`${f.path}`;if(a+u.length+1>t)break;r.push(u),a+=u.length+1}if(r.length===0)return"(no source files found)";let i=n.length-r.length;return i>0&&r.push(`\u2026 ${i} more file${i===1?"":"s"} not shown`),r.join(`
14
- `)}var at;function ct(e){at=e}var lt=Kt({name:"repo_map",label:"repo_map",description:"A ranked outline of the repository: files by importance with their top declarations. Use it to find where something lives before reading files, instead of listing directories.",promptSnippet:"repo_map: ranked file outline of the project",parameters:P.Object({path:P.Optional(P.String({description:"Subdirectory to map, relative to the project root"})),query:P.Optional(P.String({description:"Keyword to bias the ranking, e.g. 'billing'"})),budget:P.Optional(P.Number({description:"Token budget, default 1200"}))}),async execute(e,o){let s=at?.()??process.cwd(),t=tn(s,o.path??".");return{content:[{type:"text",text:on(t,{budget:o.budget,query:o.query})}],details:{files:t.length}}}});function q(){return{consecutiveFailures:0,tripped:!1}}function sn(e,o){return o==="manual"?!1:e.consecutiveFailures>=3}function rn(e,o){if(o)return e;let s=e.consecutiveFailures+1;return{consecutiveFailures:s,tripped:e.tripped||s>=3}}function an(){return q()}function cn(e){return[`Auto-compaction failed ${e.consecutiveFailures} times in a row, so it is switched off for this session.`,"The context is over the limit and compaction cannot recover it. Start a new session, or switch to a model with a larger context window.","Manual /compact is still allowed."].join(" ")}var j=q();function dt(){return e=>{e.on("session_start",async()=>{j=q()}),e.on("session_compact",async()=>{j=an()}),e.on("session_compact_failed",async(o,s)=>{let t=j;j=rn(j,o.aborted),!t.tripped&&j.tripped&&s?.ui&&s.ui.notify(cn(j),"warning")}),e.on("session_before_compact",async o=>{if(sn(j,o.reason))return{cancel:!0}})}}function ln(e){A(M(e))}function pt(e){return`budget exhausted (${e.budget??"default"} continuations)`}function ut(){return e=>{e.on("session_start",async(o,s)=>{ln(s)}),e.on("context_with_system",async(o,s)=>{let t=M(s);if(A(t),!t||t.status!=="active")return;let n=o.messages[0];if(!n||n.role!=="system")return;let r=`${C(n.content)}
15
-
16
- ${Te(t)}`;return{messages:o.messages.map(i=>i===n?{...n,content:D(n.content,r)}:i)}}),e.on("agent_before_settle",async(o,s)=>{try{let t=M(s);if(A(t),G(t).action==="stop"||!t||o.continue||!o.context.canContinue)return;let n={...t,used:t.used+1};if(e.appendEntry(L,n),A(n),G(n).action==="stop"){let r={...n,status:"done",note:pt(n)};e.appendEntry(L,r),A(r);return}return{entries:[{type:"custom_message",customType:_e,content:$e(n),display:!0}],continue:!0}}catch{return}}),e.on("agent_settled",async(o,s)=>{try{let t=M(s),n=G(t);if(t&&n.action==="stop"&&n.close){let r={...t,status:"done",note:pt(t)};e.appendEntry(L,r),A(r)}else A(t)}catch{return}})}}function dn(e){let o=ee(e);return o.length===0?"":["The user installed the following presets. Their rules apply to this session.","",...o.map(t=>`<preset name="${t.name}">
17
- ${t.text}
18
- </preset>`)].join(`
19
- `)}function ft(e){return o=>{o.on("resources_discover",async()=>{let s=Q(e);return s.length>0?{skillPaths:s}:void 0}),o.on("context_with_system",async s=>{let t=dn(e);if(!t)return;let n=s.messages[0];if(!n||n.role!=="system")return;let r=`${C(n.content)}
20
-
21
- ${t}`;return{messages:s.messages.map(i=>i===n?{...n,content:D(n.content,r)}:i)}})}}import{Type as w}from"@netrodev/hxf-ai";import{defineTool as pn}from"@netrodev/hxf-coding-agent";function un(){return[...U.values()].map(e=>`${e.name} (${e.description})`).join(", ")}function mt(){return pn({name:"task",label:"task",description:`Spawn subagents. Available: ${un()}. One call can carry tasks[]; each item may set agent, model, effort and isolation. Agents run concurrently, park when done, and can be resumed or messaged with /agents. Pass background: true to return as soon as they start instead of waiting for them. Use architect to design an approach before a large change. Set isolation: "worktree" on every task that writes files.`,promptSnippet:"task: spawn background agents",parameters:w.Object({agent:w.Optional(w.String()),effort:w.Optional(w.String()),model:w.Optional(w.String()),task:w.Optional(w.String()),isolation:w.Optional(w.String()),background:w.Optional(w.Boolean()),tasks:w.Optional(w.Array(w.Object({agent:w.Optional(w.String()),effort:w.Optional(w.String()),model:w.Optional(w.String()),task:w.String(),isolation:w.Optional(w.String())})))}),executionMode:"parallel",async execute(e,o,s,t,n){let r=o.tasks&&o.tasks.length>0?o.tasks.map(l=>({agent:l.agent??o.agent??"task",task:l.task,effort:l.effort??o.effort,model:l.model??o.model,isolation:l.isolation??o.isolation})):[{agent:o.agent??"task",task:o.task??"",effort:o.effort,model:o.model,isolation:o.isolation}],a=r.slice(0,z),i=r.length-a.length,f=!!o.background,p=[],u=[],c=[],d=[];try{for(let m of a){if(!m.task.trim())continue;let _;if(m.isolation==="worktree"){let h=await Xe(n?.cwd??process.cwd(),m.agent);if(h){u.push(h),_=h.path;for(let S of h.skipped)d.push(`${m.agent}:${S}`)}else c.push(m.agent)}p.push(await qe({...m,cwd:n?.cwd,background:f,parentModel:ze(n?.model),worktree:_})),t?.({content:[],details:{agents:p.map(h=>({id:h.id,agent:h.agent,status:h.status}))}})}if(p.length===0)throw new Error("task or tasks is required");let l=i>0?`(only the first ${z} of ${r.length} tasks ran)`:"",g=p.filter(m=>m.worktree).length,b=[l,c.length>0?`isolation: "worktree" was requested but not available for: ${c.join(", ")}. Those agents ran in the shared working tree \u2014 check their edits for collisions before trusting them.`:"",d.length>0?`too large to copy into the isolated worktree, so it was left out: ${d.join(", ")}. An agent that needs it cannot install or build \u2014 treat any "done" from those runs as unverified.`:""].filter(Boolean),y={agents:p.map(m=>({id:m.id,status:m.status,isolated:!!m.worktree}))};if(f){Ze();let m=p.map(_=>`${_.agent}#${_.id}`).join(", ");return{content:[{type:"text",text:`started ${p.length} agent${p.length===1?"":"s"} in the background: ${m}.`+(g>0?` ${g} of them in their own worktree.`:"")+`
22
- Their results are not part of this turn \u2014 keep working. Read them with /agents, or message one with /agents <id> <text>.`+(b.length>0?`
23
- ${b.join(`
24
- `)}`:"")}],details:y}}await Ke(s);let T=p.map(m=>`${We(m)}${m.worktree?" [isolated]":""}
25
- ${m.result??""}`);return b.length>0&&T.push(b.join(`
26
- `)),{content:[{type:"text",text:T.join(`
27
-
28
- `)}],details:y}}finally{for(let l of u)try{await l.remove()}catch{}}}})}import{readFileSync as fn,readdirSync as mn,statSync as gn}from"node:fs";import{dirname as hn,join as Z}from"node:path";import{getAgentDir as wn,parseFrontmatter as yn}from"@netrodev/hxf-coding-agent";var gt=["off","minimal","low","medium","high","xhigh","max"],bn=["name","description","tools","model","effort"],kn=["read","bash","edit","write","grep","find","ls","bash_output","kill_shell","todo","context","goal","task","workflow","web_search","web_fetch","lsp","quality","slop","claim","repo_map"],xn=["read","bash","edit","write","grep","find","ls","context","todo","web_search","web_fetch","lsp","claim"];function Sn(e){let s=(typeof e=="string"?e.split(","):Array.isArray(e)?e:[]).map(t=>String(t).trim().replace(/^["']|["']$/g,"")).filter(Boolean);return s.length>0?s:void 0}function _n(e){try{return gn(e).isDirectory()}catch{return!1}}function Tn(e){let o=e;for(;;){let s=Z(o,".howtofix","agents");if(_n(s))return s;let t=hn(o);if(t===o)return;o=t}}function $n(e){let o=[{scope:"user",dir:Z(wn(),"agents")}],s=Tn(e);return s&&o.push({scope:"project",dir:s}),o}function En(e,o){let s=[],t=[],n=e.replace(/^\uFEFF/,"");if(!/^---\r?\n/.test(n))return{notes:s,problems:[`${o}: no frontmatter \u2014 the file must start with --- and end the block with ---`]};let r={},a="";try{let m=yn(n);r=m.frontmatter??{},a=(m.body??"").trim()}catch(m){return{notes:s,problems:[`${o}: the frontmatter is not readable YAML \u2014 ${m instanceof Error?m.message:String(m)}`]}}for(let m of Object.keys(r))bn.includes(m)||s.push(`${o}: ignoring the unknown frontmatter key '${m}'`);let i=(o.split(/[\\/]/).pop()??o).replace(/\.md$/i,""),f=r.name,p=(typeof f=="string"&&f.trim()?f:i).trim();/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(p)||t.push(`${o}: '${p}' is not a usable agent name \u2014 letters, digits, dot, dash and underscore only`);let u=r.description,c=typeof u=="string"?u.trim():"";c||t.push(`${o}: a description is required \u2014 it is how the model decides to use this agent`),a||t.push(`${o}: no prompt body \u2014 the text below the frontmatter is the agent's standing instruction`);let d=r.effort,l=typeof d=="string"?d.trim().toLowerCase():"";l&&!gt.includes(l)&&t.push(`${o}: effort '${l}' is not one of ${gt.join(", ")}`);let g=Sn(r.tools),b=g??[...xn];if(g){let m=g.filter(_=>!kn.includes(_)&&!_.startsWith("mcp__"));m.length>0&&s.push(`${o}: unknown tool${m.length===1?"":"s"} ${m.join(", ")} \u2014 kept, but nothing registers them yet`)}let y=r.model,T=typeof y=="string"&&y.trim()?y.trim():void 0;return t.length>0?{notes:s,problems:t}:{def:{name:p,description:c,tools:b,prompt:a,effort:l||"medium",model:T,source:o},notes:s,problems:t}}function ht(e,o){let s=[],t=[],n=[],r=[];for(let{scope:a,dir:i}of $n(e)){let f;try{f=mn(i)}catch{continue}for(let p of f.filter(u=>u.toLowerCase().endsWith(".md")).sort()){let u=Z(i,p),c;try{c=fn(u,"utf8")}catch(g){n.push(`${u}: ${g instanceof Error?g.message:String(g)}`);continue}let d=En(c,u);t.push(...d.notes),n.push(...d.problems);let l=d.def;l&&(o.has(l.name)&&t.push(`${u}: replaces ${He.has(l.name)?"the built-in":"an earlier"} '${l.name}'`),o.set(l.name,l),s.push(l),r.includes(a)||r.push(a))}}if(s.length>0){let a=s.filter(i=>o.get(i.name)===i);t.unshift(`loaded ${a.length} subagent definition${a.length===1?"":"s"} (${r.join(" and ")}): ${a.map(i=>i.name).join(", ")}`)}return{loaded:s,notes:t,problems:n}}var An=["grep","find","ls","bash_output","kill_shell"];async function wt(e,{timeoutMs:o=2e3,quietTicks:s=2}={}){let t=Date.now()+o,n=e.getActiveToolNames().length,r=0;for(;Date.now()<t&&r<s;){await new Promise(i=>setTimeout(i,50));let a=e.getActiveToolNames().length;a>n?(n=a,r=0):r+=1}}function yt(e){let o=new Set(e.getActiveToolNames()),s=new Set(e.getAllTools().map(n=>n.name)),t=[];for(let n of An)s.has(n)&&!o.has(n)&&(o.add(n),t.push(n));return t.length>0&&e.setActiveToolsByName([...o]),t}import{spawnSync as vn}from"node:child_process";import{join as jn}from"node:path";import{defineTool as On}from"@netrodev/hxf-coding-agent";var Pn=6e3,bt=On({name:"slop",label:"slop",description:"Check code against the project's slop gate: naming, comments, placeholder values, swallowed errors, and the LLM tells that appear in model-written code. Read-only. Use it before reporting a task finished; it returns each finding as a rule id, a line, and what is wrong, so a claim of clean code becomes something the user can verify. Defaults to the changed files; pass a path to narrow it.",promptSnippet:"slop: naming, comment and placeholder findings for changed code",parameters:E.Object({path:E.Optional(E.String({description:"One file or directory, relative to the project root. Omit to scan the changed set."})),changes:E.Optional(E.Boolean({description:"Scan only files changed against the base ref. Default true when path is omitted."})),base:E.Optional(E.String({description:"Git ref for `changes`. Defaults to HEAD."}))}),async execute(e,o,s,t,n){let r=n.cwd??process.cwd(),i=[jn(r,"scripts","slop.mjs"),"--json"];o.path!==void 0?i.push(o.path):(o.changes??!0)&&i.push("--changes"),o.base!==void 0&&i.push("--base",o.base);let f=vn("node",i,{cwd:r,encoding:"utf8",timeout:12e4}),p;try{p=JSON.parse(f.stdout)}catch{p=void 0}if(!p){let b=(f.stderr||f.stdout||"").trim()||f.error?.message||(f.signal?`killed by ${f.signal} (timed out after 120 s?)`:"no output");return{content:[{type:"text",text:`slop gate produced no report: ${b.slice(0,400)}`}],details:{error:b.slice(0,400)}}}if(p.off)return{content:[{type:"text",text:"slop gate is off"}],details:{off:!0}};let u=p.files??{},c=[],d=0;for(let[b,y]of Object.entries(u)){c.push(`${b}`);for(let T of y)d+=1,c.push(` ${T.rule} L${T.line} ${T.message}`)}if(d===0)return{content:[{type:"text",text:"slop gate: no findings in the scanned files"}],details:{files:Object.keys(u).length,findings:0}};let l=c.join(`
29
- `).slice(0,Pn),g=l.length<c.join(`
30
- `).length?`
31
- \u2026 truncated`:"";return{content:[{type:"text",text:`${d} finding${d===1?"":"s"}:
32
-
33
- ${l}${g}`}],details:{files:Object.keys(u).length,findings:d}}}});function K(e){return e.extensionRunner.emit({type:"session_shutdown",reason:"quit"}).catch(()=>{}).finally(()=>e.dispose())}import{readdir as kt,rmdir as Rn,stat as Cn,unlink as Dn}from"node:fs/promises";import{dirname as Nn,join as X,resolve as xt}from"node:path";async function St(e={}){let o={removed:[],failed:[],removedDirs:[]},s=X(e.agentDir??k,"sessions"),t=(e.now??Date.now())-Ve*24*60*60*1e3,n=e.keep?xt(e.keep):void 0,r;try{r=await kt(s,{withFileTypes:!0})}catch{return o}for(let a of r){if(!a.isDirectory())continue;let i=X(s,a.name),f;try{f=await kt(i)}catch{continue}let p=f.filter(u=>!u.endsWith(".jsonl"));for(let u of f){if(!u.endsWith(".jsonl"))continue;let c=X(i,u),d;try{n&&xt(c)===n?d=!1:d=(await Cn(c)).mtimeMs<t}catch{d=!1}if(!d){p.push(u);continue}try{await Dn(c),o.removed.push(c)}catch{o.failed.push(c),p.push(u)}}if(p.length===0){if(n&&Nn(n)===i)continue;try{await Rn(i),o.removedDirs.push(i)}catch{}}}return o}var Y="howtofix.build",Ts=["read","repo_map","bash","edit","write","grep","find","ls","bash_output","kill_shell","task","think","ask","claim","sessions","todo","context","goal","enter_plan_mode","exit_plan_mode","workflow","web_search","web_fetch","lsp","quality","compress","slop"],Hn=()=>({exclude:process.platform==="win32"?[]:["powershell"]}),Tt=!1;async function $s(e){let o=new Un(k),s=Bn.create(e.cwd,k,{projectTrusted:o.get(e.cwd)===!0}),t=pe(k).compressToolOutput,n=e.sessionPath?B.open(e.sessionPath):e.noSession?B.inMemory(e.cwd):e.continueSession?B.continueRecent(e.cwd):B.create(e.cwd),r=(()=>{try{let S=n.getEntries().find(F=>F.type==="custom"&&F.customType===Y)?.data?.version;return!S||S===I?void 0:`this session was written by HowToFix ${S}; you are running ${I}`}catch{return}})(),a=new Gn({cwd:e.cwd,agentDir:k,settingsManager:s,appendSystemPrompt:Qe(V,e.interactive??!0),additionalSkillPaths:te(e.cwd),extensionFactories:[Ae(),re(),et(),tt(),Ne(),Pe(),ft(e.cwd),ut(),ve(),dt(),In(),st({enabled:()=>t}),Ln({loadConfig:h=>de({cwd:h.cwd,projectTrusted:h.isProjectTrusted?.()??!1})})]});await a.reload();let i=ht(e.cwd,U),f=mt(),p=n.getSessionId();ct(()=>e.cwd);let u=_t(k,fe);ye({root:()=>e.cwd,holder:()=>`session:${p}`,presenceDir:()=>u,selfId:()=>p});let c=()=>{try{me({dir:u},{id:p,cwd:e.cwd,host:Fn(),pid:process.pid,at:Date.now()})}catch{}};c();let d=setInterval(c,Math.max(5e3,Math.floor(ue/3)));d.unref?.();let l=await Mn({cwd:e.cwd,agentDir:k,settingsManager:s,sessionManager:n,resourceLoader:a,excludeTools:Hn().exclude,customTools:[f,lt,he,we,ke,be,xe,Se,Ee,je,Oe,Ye(),Fe,Me,Le,Ge,bt,Re({state:()=>Ce(W()??"dcp-sessionless"),messages:()=>De(W()??"dcp-sessionless")})],thinkingLevel:e.thinking});await l.session.bindExtensions({mode:"print",onError:()=>{}});let g=oe(k),b=[];if(g){let h=await se(k,g);h.renewed?await l.session.modelRuntime.setRuntimeApiKey("howtofix",h.credential.accessToken):(h.terminal&&h.message||h.message)&&b.push(h.message)}await wt(l.session),yt(l.session);let y=l.session.model;if(e.model){let h=await l.session.modelRuntime.getAvailable(),S=Ue(h,e.model);if(!S)throw await K(l.session),new Error(`unknown model '${e.model}'`);(y?.id!==S.id||y.provider!==S.provider)&&(await l.session.setModel(S),y=l.session.model)}else{let h=await l.session.modelRuntime.getAvailable(),S=new Map,F=[];for(let O of h){let R=S.get(O.provider);R===void 0&&(R=!!(await l.session.modelRuntime.getAuth(O.provider))?.auth?.apiKey,S.set(O.provider,R)),R&&F.push(O)}let H=ne;if(!H.includes(y?.provider??"")){let O=Be(F.filter(R=>H.includes(R.provider)),H);O&&(await l.session.setModel(O),y=l.session.model)}}if(!e.noSession&&n.getEntries().every(h=>h.type!=="custom"||h.customType!==Y))try{n.appendCustomEntry(Y,{version:I})}catch{}Tt||(Tt=!0,St({keep:l.session.sessionFile}).catch(()=>{}));let T=y?`${y.provider}/${y.id}`:"no model",m=!!(y&&y.cost&&(y.cost.input>0||y.cost.output>0)),_=[J(l.modelFallbackMessage),...b,...i.notes,...i.problems].filter(h=>!!h);return y||_.push(`no model configured. run \`howtofix\` and use /login, or edit ${_t(k,"models.json")}`),{session:l.session,dispose:async()=>{Ie(),await K(l.session),clearInterval(d),ge({dir:u},p)},modelLabel:T,priced:m,setCompressToolOutput:h=>{t=h},note:[r,..._].filter(Boolean).join(`
34
- `)||void 0}}export{Y as BUILD_ENTRY,Ts as MAIN_TOOLS,$s as openSession};
@@ -1,2 +0,0 @@
1
- function d(n,t){let e=`${n??""} ${t}`;if(/EACCES|EPERM/.test(e))return"the file or directory is not writable; check permissions and try again";if(/ENOENT/.test(e))return"a file or directory this command needs is missing";if(/ENOSPC/.test(e))return"the disk is full";if(/ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|UND_ERR|fetch failed/i.test(e))return"the network request failed; check the connection and the gateway address";if(/EADDRINUSE/.test(e))return"another process is already using that port";if(/MODULE_NOT_FOUND|ERR_MODULE_NOT_FOUND/.test(e))return"a required file or package is missing; reinstall HowToFix";if(/JSON/i.test(e)&&/parse|unexpected token|end of input/i.test(e))return"a configuration file is not valid JSON";if(/not a git repository/i.test(e))return"run the command inside a project, or pass --cwd"}function c(n){let e=(n instanceof Error?n.message:String(n)).trim()||"unknown error",i=n?.code;return{message:e,hint:d(i,e)}}function u(n,t,e,i=!1){let{message:r,hint:s}=c(e),o=[t?`${n}: ${t}: ${r}`:`${n}: ${r}`];return s&&o.push(` ${s}`),i&&e instanceof Error&&e.stack&&o.push(e.stack),o.join(`
2
- `)}function f(n,t,e,i){let r=i?.debug??process.env.HOWTOFIX_DEBUG==="1";(i?.write??(a=>console.error(a)))(u(n,t,e,r))}export{c as a,f as b};