howtofixcli 0.7.8 → 0.7.9
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.
- package/dist/chunk-27TRKD65.js +2 -0
- package/dist/chunk-3FYCFLNK.js +1 -0
- package/dist/chunk-56SEAGAQ.js +1 -0
- package/dist/chunk-63ZCQDXH.js +6 -0
- package/dist/chunk-6FUWMLWO.js +1 -0
- package/dist/chunk-7CHDNTH6.js +1 -0
- package/dist/chunk-ACOMNDUL.js +185 -0
- package/dist/chunk-AEVPET7X.js +2 -0
- package/dist/chunk-AGGIYXN7.js +2 -0
- package/dist/chunk-BHVBO4SC.js +1 -0
- package/dist/chunk-FPXVDP32.js +8 -0
- package/dist/chunk-GIUYIKH3.js +109 -0
- package/dist/chunk-GQCAJ56I.js +5 -0
- package/dist/chunk-GXB7RZHH.js +1 -0
- package/dist/chunk-IGEY3AWF.js +1 -0
- package/dist/chunk-ILDDDLRC.js +1 -0
- package/dist/chunk-JFYVRWJI.js +2 -0
- package/dist/chunk-MVLCGU3G.js +1 -0
- package/dist/chunk-N345KWWK.js +1 -0
- package/dist/chunk-NBQFPCRT.js +1 -0
- package/dist/chunk-OIPSLIZE.js +5 -0
- package/dist/chunk-ORJ2QZBG.js +3 -0
- package/dist/chunk-PCALBZOO.js +2 -0
- package/dist/chunk-PDNBGUYP.js +2 -0
- package/dist/chunk-S656RE4A.js +1 -0
- package/dist/chunk-TSEKPGK4.js +1 -0
- package/dist/chunk-U6CI6KXN.js +34 -0
- package/dist/chunk-UTYFLLSM.js +1 -0
- package/dist/chunk-V3R4FYTJ.js +1 -0
- package/dist/chunk-V656XVXV.js +8 -0
- package/dist/chunk-WKUJ7CFO.js +1 -0
- package/dist/chunk-WOT6VMZA.js +1 -0
- package/dist/chunk-WUPU6LTD.js +4 -0
- package/dist/chunk-WX3V5P7U.js +5 -0
- package/dist/chunk-ZKZ6WCI3.js +2 -0
- package/dist/main.js +94 -536
- package/package.json +15 -62
- package/dist/account-credential.js +0 -136
- package/dist/account-credential.test.js +0 -38
- package/dist/activity.js +0 -124
- package/dist/agent-defs.js +0 -237
- package/dist/agent-limiter.js +0 -68
- package/dist/agents.js +0 -554
- package/dist/anim.js +0 -361
- package/dist/args.js +0 -228
- package/dist/at-file.js +0 -28
- package/dist/brand-extension.js +0 -87
- package/dist/brand.js +0 -76
- package/dist/browser.js +0 -58
- package/dist/cache-report.js +0 -139
- package/dist/checkpoint.js +0 -437
- package/dist/claims.js +0 -167
- package/dist/cli-auth.js +0 -142
- package/dist/cli-checkin.js +0 -120
- package/dist/cli-credentials.js +0 -254
- package/dist/clipboard.js +0 -149
- package/dist/collapse.js +0 -129
- package/dist/commands.js +0 -301
- package/dist/compaction-guard.js +0 -81
- package/dist/context-extension.js +0 -17
- package/dist/dcp/commands.js +0 -172
- package/dist/dcp/compress-state.js +0 -139
- package/dist/dcp/compress.js +0 -329
- package/dist/dcp/dcp-extension.js +0 -288
- package/dist/dcp/dedup.js +0 -93
- package/dist/dcp/ids.js +0 -189
- package/dist/dcp/index.js +0 -25
- package/dist/dcp/prompts.js +0 -68
- package/dist/dcp/protected-content.js +0 -221
- package/dist/dcp/protected.js +0 -161
- package/dist/dcp/prune.js +0 -418
- package/dist/dcp/state.js +0 -176
- package/dist/device-id.js +0 -90
- package/dist/device-id.test.js +0 -23
- package/dist/errors.js +0 -63
- package/dist/extensions.js +0 -48
- package/dist/fatal.js +0 -35
- package/dist/feed.js +0 -498
- package/dist/fs-atomic.js +0 -54
- package/dist/gateway-errors.js +0 -90
- package/dist/git.js +0 -230
- package/dist/goal-extension.js +0 -88
- package/dist/goal.js +0 -222
- package/dist/hooks-extension.js +0 -176
- package/dist/hooks.js +0 -443
- package/dist/howtofix-account.js +0 -715
- package/dist/hud-line.js +0 -153
- package/dist/hwid.js +0 -130
- package/dist/interrupt.js +0 -60
- package/dist/json-events.js +0 -50
- package/dist/level-style.js +0 -112
- package/dist/loop-guard.js +0 -95
- package/dist/lsp-tools.js +0 -32
- package/dist/lsp.js +0 -347
- package/dist/mcp-config.js +0 -85
- package/dist/mcp-lifecycle.js +0 -147
- package/dist/model-catalog.js +0 -217
- package/dist/model-resolve.js +0 -65
- package/dist/notes.js +0 -78
- package/dist/oauth-login.js +0 -568
- package/dist/paths.js +0 -61
- package/dist/permissions.js +0 -298
- package/dist/plan-mode.js +0 -363
- package/dist/preset-command.js +0 -227
- package/dist/preset-extension.js +0 -35
- package/dist/preset-mcp.js +0 -292
- package/dist/presets.js +0 -727
- package/dist/prompt-files.js +0 -103
- package/dist/prompt-text.js +0 -41
- package/dist/proxy.js +0 -137
- package/dist/quality/ast.js +0 -196
- package/dist/quality/cognitive.js +0 -83
- package/dist/quality/cyclomatic.js +0 -30
- package/dist/quality/halstead.js +0 -83
- package/dist/quality/index.js +0 -18
- package/dist/quality/length.js +0 -3
- package/dist/quality/maintainability.js +0 -15
- package/dist/quality/nesting.js +0 -57
- package/dist/quality/report.js +0 -803
- package/dist/quality/smells.js +0 -67
- package/dist/quality-tool.js +0 -63
- package/dist/read-only-commands.js +0 -597
- package/dist/repo-map.js +0 -292
- package/dist/rtk.js +0 -193
- package/dist/sandbox.js +0 -105
- package/dist/schema.js +0 -214
- package/dist/session-lifecycle.js +0 -19
- package/dist/session-retention.js +0 -111
- package/dist/session.js +0 -446
- package/dist/sessions.js +0 -100
- package/dist/settings.js +0 -148
- package/dist/skills.js +0 -35
- package/dist/slop-tool.js +0 -96
- package/dist/spend-cap.js +0 -43
- package/dist/subagent.js +0 -171
- package/dist/tar.js +0 -146
- package/dist/theme.js +0 -79
- package/dist/thinking.js +0 -45
- package/dist/todo.js +0 -114
- package/dist/tool-loadout.js +0 -97
- package/dist/tool-render.js +0 -769
- package/dist/tools.js +0 -176
- package/dist/ui.js +0 -4757
- package/dist/update.js +0 -533
- package/dist/usage-segment.js +0 -69
- package/dist/web-tools.js +0 -46
- package/dist/web.js +0 -265
- package/dist/welcome.js +0 -179
- package/dist/workflow-engine.js +0 -246
- package/dist/workflow-progress.js +0 -446
- package/dist/workflow-view.js +0 -195
- package/dist/workflow.js +0 -349
package/dist/theme.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { detectCapabilities, getTerminalColorMode, parseColor, styleText } from "@netrodev/hxf-tui";
|
|
2
|
-
/**
|
|
3
|
-
* Whether the terminal can show 24-bit colour, decided rather than assumed.
|
|
4
|
-
*
|
|
5
|
-
* This was `"truecolor"`, hardcoded, and that is wrong wherever the assumption
|
|
6
|
-
* does not hold: a `screen` session that never set `COLORTERM`, a `linux` console,
|
|
7
|
-
* an SSH client on an old terminal. The failure is not a crash — it is worse. A
|
|
8
|
-
* truecolor escape on a 256-colour terminal is not downsampled, it is *ignored*,
|
|
9
|
-
* so every colour in the interface collapses to the default foreground and the
|
|
10
|
-
* whole thing reads as one grey wall.
|
|
11
|
-
*
|
|
12
|
-
* `detectCapabilities` already knows the cases: it returns `trueColor: false` for
|
|
13
|
-
* `TERM=screen*` without a `COLORTERM` hint, for `dumb`, and for anything that
|
|
14
|
-
* does not advertise 24-bit. Reading it means the HUD degrades to the 256-colour
|
|
15
|
-
* cube instead of going monochrome.
|
|
16
|
-
*
|
|
17
|
-
* `NO_COLOR` (https://no-color.org) is honoured explicitly and beats everything
|
|
18
|
-
* else, because a user who sets it has asked for no colour at all — and it is
|
|
19
|
-
* also the escape hatch when the result on a particular terminal is unreadable.
|
|
20
|
-
*/
|
|
21
|
-
function resolveColorMode() {
|
|
22
|
-
const noColor = process.env.NO_COLOR;
|
|
23
|
-
if (noColor !== undefined && noColor !== "")
|
|
24
|
-
return "256color";
|
|
25
|
-
try {
|
|
26
|
-
return getTerminalColorMode(detectCapabilities());
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return "256color";
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
export const MODE = resolveColorMode();
|
|
33
|
-
/** True when the user asked for no colour: read at paint time, not once. */
|
|
34
|
-
function colorDisabled() {
|
|
35
|
-
const noColor = process.env.NO_COLOR;
|
|
36
|
-
return noColor !== undefined && noColor !== "";
|
|
37
|
-
}
|
|
38
|
-
export const ink = {
|
|
39
|
-
text: parseColor("#e6e6e6"),
|
|
40
|
-
mute: parseColor("#8b8b8b"),
|
|
41
|
-
dim: parseColor("#5c5c5c"),
|
|
42
|
-
user: parseColor("#d0d0d0"),
|
|
43
|
-
accent: parseColor("#c4b5fd"),
|
|
44
|
-
tool: parseColor("#6ee7b7"),
|
|
45
|
-
bad: parseColor("#f0a8a4"),
|
|
46
|
-
warn: parseColor("#e6b15a"),
|
|
47
|
-
line: parseColor("#3a3a3a"),
|
|
48
|
-
};
|
|
49
|
-
export function paint(text, style) {
|
|
50
|
-
if (colorDisabled()) {
|
|
51
|
-
// Keep the attributes that carry meaning without colour — bold and dim still
|
|
52
|
-
// separate things on a monochrome terminal, and dropping them too would make
|
|
53
|
-
// the output unreadable rather than plain.
|
|
54
|
-
const { bold, dim, italic, underline, strikethrough } = style;
|
|
55
|
-
if (!bold && !dim && !italic && !underline && !strikethrough)
|
|
56
|
-
return text;
|
|
57
|
-
return styleText(text, { bold, dim, italic, underline, strikethrough }, MODE);
|
|
58
|
-
}
|
|
59
|
-
return styleText(text, style, MODE);
|
|
60
|
-
}
|
|
61
|
-
export function fg(text, color, extra) {
|
|
62
|
-
return paint(text, { ...extra, fg: color });
|
|
63
|
-
}
|
|
64
|
-
export const mdTheme = {
|
|
65
|
-
heading: (text) => fg(text, ink.text, { bold: true }),
|
|
66
|
-
link: (text) => fg(text, ink.accent, { underline: true }),
|
|
67
|
-
linkUrl: (text) => fg(text, ink.mute),
|
|
68
|
-
code: (text) => fg(text, ink.text),
|
|
69
|
-
codeBlock: (text) => fg(text, ink.text),
|
|
70
|
-
codeBlockBorder: (text) => fg(text, ink.line),
|
|
71
|
-
quote: (text) => fg(text, ink.mute, { italic: true }),
|
|
72
|
-
quoteBorder: (text) => fg(text, ink.line),
|
|
73
|
-
hr: (text) => fg(text, ink.line),
|
|
74
|
-
listBullet: (text) => fg(text, ink.mute),
|
|
75
|
-
bold: (text) => fg(text, ink.text, { bold: true }),
|
|
76
|
-
italic: (text) => fg(text, ink.text, { italic: true }),
|
|
77
|
-
strikethrough: (text) => fg(text, ink.mute, { strikethrough: true }),
|
|
78
|
-
underline: (text) => fg(text, ink.text, { underline: true }),
|
|
79
|
-
};
|
package/dist/thinking.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/** Every level the CLI accepts, lowest first. One list, shared by every caller. */
|
|
2
|
-
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
3
|
-
/**
|
|
4
|
-
* The highest level this model actually reaches the provider with, or
|
|
5
|
-
* `undefined` when it reaches none — the ceiling decides whether the HUD may
|
|
6
|
-
* light the shimmer, so its three states have to stay distinguishable:
|
|
7
|
-
*
|
|
8
|
-
* - **No map** (`thinkingLevelMap` absent, or no model yet): nothing is known
|
|
9
|
-
* per level, so take the highest listed level. It is a guess, but the best
|
|
10
|
-
* available one, and it becomes exact the moment a provider fills the map in.
|
|
11
|
-
* - **With a map**: `null` *and* a missing key both mean unsupported — the
|
|
12
|
-
* type is `Partial`, so an absent entry is how a level that was never wired
|
|
13
|
-
* up appears. A value of `""` is still a value; `null` is the only marker
|
|
14
|
-
* of "does not reach the provider".
|
|
15
|
-
* - **Every value `null`**: the ceiling is `undefined` and the feature is off
|
|
16
|
-
* for this model. Falling back to `max` here would announce a top the model
|
|
17
|
-
* does not have.
|
|
18
|
-
*/
|
|
19
|
-
export function levelCeiling(model) {
|
|
20
|
-
const map = model?.thinkingLevelMap;
|
|
21
|
-
if (map === undefined)
|
|
22
|
-
return THINKING_LEVELS[THINKING_LEVELS.length - 1];
|
|
23
|
-
for (let index = THINKING_LEVELS.length - 1; index >= 0; index -= 1) {
|
|
24
|
-
const value = map[THINKING_LEVELS[index]];
|
|
25
|
-
if (typeof value === "string")
|
|
26
|
-
return THINKING_LEVELS[index];
|
|
27
|
-
}
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* One step up, wrapping from the top of the list back to `off`.
|
|
32
|
-
*
|
|
33
|
-
* The wrap runs over the whole list, not over the model's ceiling: the SDK
|
|
34
|
-
* clamps whatever the model rejects, and a wrap that stopped at the ceiling
|
|
35
|
-
* would leave the raise key dead exactly where the user keeps pressing it.
|
|
36
|
-
*/
|
|
37
|
-
export function nextLevel(level) {
|
|
38
|
-
const index = THINKING_LEVELS.indexOf(level);
|
|
39
|
-
return THINKING_LEVELS[(index + 1 + THINKING_LEVELS.length) % THINKING_LEVELS.length];
|
|
40
|
-
}
|
|
41
|
-
/** One step down, wrapping from `off` back to the top of the list. */
|
|
42
|
-
export function prevLevel(level) {
|
|
43
|
-
const index = THINKING_LEVELS.indexOf(level);
|
|
44
|
-
return THINKING_LEVELS[(index - 1 + THINKING_LEVELS.length) % THINKING_LEVELS.length];
|
|
45
|
-
}
|
package/dist/todo.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A session-scoped task list.
|
|
3
|
-
*
|
|
4
|
-
* Adapted from the SDK's own `examples/extensions/todo.ts`, which establishes the
|
|
5
|
-
* pattern that matters: the authoritative state lives in each tool result's
|
|
6
|
-
* `details`, and the in-memory copy is rebuilt by replaying the current session
|
|
7
|
-
* branch. Nothing is written to disk, so the list is automatically correct after
|
|
8
|
-
* a fork, a rewind or a resumed session — an external todo file would silently
|
|
9
|
-
* disagree with the transcript.
|
|
10
|
-
*
|
|
11
|
-
* The op model (`init`/`start`/`done`/`drop`/`view`) is the `todo`
|
|
12
|
-
* tool (`docs/tools/todo.md`), which is what makes a long run legible: the model
|
|
13
|
-
* records the whole plan up front, then moves one task at a time, and a stalled
|
|
14
|
-
* run leaves visible evidence of where it stopped.
|
|
15
|
-
*/
|
|
16
|
-
import { StringEnum } from "@netrodev/hxf-ai";
|
|
17
|
-
import { defineTool } from "@netrodev/hxf-coding-agent";
|
|
18
|
-
import { Type } from "typebox";
|
|
19
|
-
/** Marks the status glyph. All are non-emoji (see scripts/check-tool-render.mjs). */
|
|
20
|
-
const MARK = {
|
|
21
|
-
pending: "[ ]",
|
|
22
|
-
in_progress: "[>]",
|
|
23
|
-
completed: "[x]",
|
|
24
|
-
dropped: "[-]",
|
|
25
|
-
};
|
|
26
|
-
export const todoParams = Type.Object({
|
|
27
|
-
op: StringEnum(["init", "start", "done", "drop", "view"], {
|
|
28
|
-
description: "init replaces the list; start/done/drop move one task; view reads it",
|
|
29
|
-
}),
|
|
30
|
-
items: Type.Optional(Type.Array(Type.String(), {
|
|
31
|
-
description: "Task labels for init. 5-10 words each, specific enough to execute without re-planning",
|
|
32
|
-
})),
|
|
33
|
-
task: Type.Optional(Type.String({ description: "Exact label of the task to start, complete or drop" })),
|
|
34
|
-
});
|
|
35
|
-
/** Render the list the way the model should read it back. */
|
|
36
|
-
export function formatTodos(todos) {
|
|
37
|
-
if (todos.length === 0)
|
|
38
|
-
return "Todo list is empty.";
|
|
39
|
-
const done = todos.filter((item) => item.status === "completed").length;
|
|
40
|
-
const lines = todos.map((item) => `${MARK[item.status]} ${item.content}`);
|
|
41
|
-
const open = todos.filter((item) => item.status === "pending" || item.status === "in_progress").length;
|
|
42
|
-
return [`${done}/${todos.length} done, ${open} open`, ...lines].join("\n");
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Apply one op to a list. Pure, so the policy is testable without a session:
|
|
46
|
-
* `start` demotes any other in-progress task (only one task
|
|
47
|
-
* is ever in progress).
|
|
48
|
-
*/
|
|
49
|
-
export function applyOp(todos, op, items, task) {
|
|
50
|
-
const list = todos.map((item) => ({ ...item }));
|
|
51
|
-
switch (op) {
|
|
52
|
-
case "init": {
|
|
53
|
-
if (!items || items.length === 0)
|
|
54
|
-
return { todos: list, error: "init needs a non-empty items array" };
|
|
55
|
-
return { todos: items.map((content) => ({ content, status: "pending" })) };
|
|
56
|
-
}
|
|
57
|
-
case "view":
|
|
58
|
-
return { todos: list };
|
|
59
|
-
case "start":
|
|
60
|
-
case "done":
|
|
61
|
-
case "drop": {
|
|
62
|
-
if (!task)
|
|
63
|
-
return { todos: list, error: `${op} needs task` };
|
|
64
|
-
const target = list.find((item) => item.content === task);
|
|
65
|
-
if (!target)
|
|
66
|
-
return { todos: list, error: `no task matches ${JSON.stringify(task)}` };
|
|
67
|
-
if (op === "start") {
|
|
68
|
-
for (const item of list)
|
|
69
|
-
if (item.status === "in_progress")
|
|
70
|
-
item.status = "pending";
|
|
71
|
-
target.status = "in_progress";
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
target.status = op === "done" ? "completed" : "dropped";
|
|
75
|
-
}
|
|
76
|
-
return { todos: list };
|
|
77
|
-
}
|
|
78
|
-
default:
|
|
79
|
-
return { todos: list, error: `unknown op ${JSON.stringify(op)}` };
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/** Rebuild the list by replaying this branch's todo results in order. */
|
|
83
|
-
export function readTodos(ctx) {
|
|
84
|
-
let todos = [];
|
|
85
|
-
for (const entry of ctx.sessionManager.getBranch()) {
|
|
86
|
-
if (entry.type !== "message")
|
|
87
|
-
continue;
|
|
88
|
-
const message = entry.message;
|
|
89
|
-
if (message.role !== "toolResult" || message.toolName !== "todo")
|
|
90
|
-
continue;
|
|
91
|
-
const details = message.details;
|
|
92
|
-
if (details?.todos)
|
|
93
|
-
todos = details.todos;
|
|
94
|
-
}
|
|
95
|
-
return todos;
|
|
96
|
-
}
|
|
97
|
-
export const todoTool = defineTool({
|
|
98
|
-
name: "todo",
|
|
99
|
-
label: "todo",
|
|
100
|
-
description: "Track the plan for this task. Use `init` once with the whole plan (investigation through verification), then `start`/`done` as you go. Keep the list current so progress stays visible; do not re-init unless the plan materially changes.",
|
|
101
|
-
promptSnippet: "todo: record and advance the plan for the current task",
|
|
102
|
-
parameters: todoParams,
|
|
103
|
-
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
104
|
-
const before = readTodos(ctx);
|
|
105
|
-
const { todos, error } = applyOp(before, params.op, params.items, params.task);
|
|
106
|
-
const details = { op: params.op, todos, ...(error ? { error } : {}) };
|
|
107
|
-
const summary = error ? `Error: ${error}\n${formatTodos(todos)}` : formatTodos(todos);
|
|
108
|
-
return {
|
|
109
|
-
content: [{ type: "text", text: summary }],
|
|
110
|
-
details,
|
|
111
|
-
...(error ? { isError: true } : {}),
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
});
|
package/dist/tool-loadout.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Keeps this product's tool loadout in place without starving extension tools.
|
|
3
|
-
*
|
|
4
|
-
* The SDK's active set starts as `read, bash, edit, write` (`DEFAULT_TOOL_NAMES`).
|
|
5
|
-
* `grep`, `find` and `ls` are *registered* by the SDK but not active, so without
|
|
6
|
-
* naming them they are dead weight: `permissions.ts` carries approval tiers for
|
|
7
|
-
* them and `agents.ts` tells every subagent to use them, while the model cannot
|
|
8
|
-
* call them. Measured before this was wired correctly: the model reported
|
|
9
|
-
* `read, bash, edit, write, task, …` and answered "NO GREP TOOL".
|
|
10
|
-
*
|
|
11
|
-
* **Where this has to happen.** Not in an extension. An extension's
|
|
12
|
-
* `session_start` handler runs inside `AgentSession.bindExtensions()`, which the
|
|
13
|
-
* SDK immediately follows with `_refreshToolRegistry()` — so anything
|
|
14
|
-
* `setActiveTools` did there is silently overwritten before the first turn.
|
|
15
|
-
* Measured: the handler ran, `setActiveTools` was called, and the active set was
|
|
16
|
-
* still `bash, edit, read, write`. `activateExtraBuiltins` is called from
|
|
17
|
-
* `openSession` instead, on the session object, once the session exists.
|
|
18
|
-
*
|
|
19
|
-
* **Why not the SDK's `tools:` option.** It is an allowlist applied to *every*
|
|
20
|
-
* registered tool — extension tools included — in `_refreshToolRegistry` via
|
|
21
|
-
* `_isAllowedTool`. Two things break because of that:
|
|
22
|
-
*
|
|
23
|
-
* 1. `mcp__<server>__<tool>` names are not knowable in advance, so an allowlist
|
|
24
|
-
* discards them. Measured: an MCP server connected, `/mcp` reported "14
|
|
25
|
-
* tools", and the model could call none of them.
|
|
26
|
-
* 2. The MCP extension activates `codemode` at `session_start` for the default
|
|
27
|
-
* `exposure: "codemode"`, but the allowlist filter runs when the registry is
|
|
28
|
-
* rebuilt — after that activation — so a static list undid it, and the user
|
|
29
|
-
* was told "MCP tools are only reachable from the codemode or tool_search
|
|
30
|
-
* tool, but neither is active" on a fully connected server.
|
|
31
|
-
*
|
|
32
|
-
* Adding to the active set leaves everything the extensions registered exactly
|
|
33
|
-
* where it was.
|
|
34
|
-
*/
|
|
35
|
-
/**
|
|
36
|
-
* The built-ins this product offers that the SDK does not enable by default.
|
|
37
|
-
*
|
|
38
|
-
* `grep`/`find`/`ls` are registered by the SDK but absent from
|
|
39
|
-
* `DEFAULT_TOOL_NAMES` (`read, bash, edit, write`), so without naming them they
|
|
40
|
-
* are dead weight: they have approval tiers and every subagent is told to use
|
|
41
|
-
* them, while the main agent cannot call them.
|
|
42
|
-
*/
|
|
43
|
-
export const EXTRA_BUILTINS = ["grep", "find", "ls", "bash_output", "kill_shell"];
|
|
44
|
-
/**
|
|
45
|
-
* Wait, briefly, for extension-registered tools to appear.
|
|
46
|
-
*
|
|
47
|
-
* `session_start` handlers are async and the extension runner does not await them
|
|
48
|
-
* (`ExtensionRunner.emit` starts them and returns), so a handler that registers a
|
|
49
|
-
* tool — the MCP extension's `codemode` — lands some milliseconds after
|
|
50
|
-
* `bindExtensions()` resolves. Reading the loadout immediately would report a set
|
|
51
|
-
* that is about to grow, and the first turn would run without those tools.
|
|
52
|
-
*
|
|
53
|
-
* Bounded on purpose: it returns as soon as the set stops growing for a couple of
|
|
54
|
-
* ticks, and never waits longer than `timeoutMs` however slow a server is. A slow
|
|
55
|
-
* MCP server therefore cannot delay startup.
|
|
56
|
-
*/
|
|
57
|
-
export async function settleExtensionTools(session, { timeoutMs = 2_000, quietTicks = 2 } = {}) {
|
|
58
|
-
const deadline = Date.now() + timeoutMs;
|
|
59
|
-
let size = session.getActiveToolNames().length;
|
|
60
|
-
let quiet = 0;
|
|
61
|
-
while (Date.now() < deadline && quiet < quietTicks) {
|
|
62
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
63
|
-
const next = session.getActiveToolNames().length;
|
|
64
|
-
if (next > size) {
|
|
65
|
-
size = next;
|
|
66
|
-
quiet = 0;
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
quiet += 1;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Add the extra built-ins to a session's active set.
|
|
75
|
-
*
|
|
76
|
-
* Only a tool that actually exists is added: `powershell` is registered on every
|
|
77
|
-
* platform but throws off Windows, and a name that was never registered would
|
|
78
|
-
* simply be ignored. Adding, never replacing, is what keeps `codemode` and the
|
|
79
|
-
* MCP tools the MCP extension activated.
|
|
80
|
-
*
|
|
81
|
-
* Returns the names that were added, so a caller (or a test) can see what
|
|
82
|
-
* happened instead of guessing.
|
|
83
|
-
*/
|
|
84
|
-
export function activateExtraBuiltins(session) {
|
|
85
|
-
const active = new Set(session.getActiveToolNames());
|
|
86
|
-
const registered = new Set(session.getAllTools().map((tool) => tool.name));
|
|
87
|
-
const added = [];
|
|
88
|
-
for (const name of EXTRA_BUILTINS) {
|
|
89
|
-
if (registered.has(name) && !active.has(name)) {
|
|
90
|
-
active.add(name);
|
|
91
|
-
added.push(name);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (added.length > 0)
|
|
95
|
-
session.setActiveToolsByName([...active]);
|
|
96
|
-
return added;
|
|
97
|
-
}
|