codebase-cli 2.0.0-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +266 -0
- package/bin/codebase +2 -0
- package/dist/agent/agent.js +198 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/config.js +117 -0
- package/dist/agent/config.js.map +1 -0
- package/dist/agent/events.js +153 -0
- package/dist/agent/events.js.map +1 -0
- package/dist/agent/router.js +35 -0
- package/dist/agent/router.js.map +1 -0
- package/dist/agent/system-prompt.js +21 -0
- package/dist/agent/system-prompt.js.map +1 -0
- package/dist/auth/cli.js +138 -0
- package/dist/auth/cli.js.map +1 -0
- package/dist/auth/credentials.js +105 -0
- package/dist/auth/credentials.js.map +1 -0
- package/dist/auth/flow.js +222 -0
- package/dist/auth/flow.js.map +1 -0
- package/dist/auth/pkce.js +46 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +1 -0
- package/dist/clipboard/copy.js +106 -0
- package/dist/clipboard/copy.js.map +1 -0
- package/dist/commands/builtins.js +203 -0
- package/dist/commands/builtins.js.map +1 -0
- package/dist/commands/registry.js +65 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/compaction/engine.js +209 -0
- package/dist/compaction/engine.js.map +1 -0
- package/dist/compaction/tokens.js +79 -0
- package/dist/compaction/tokens.js.map +1 -0
- package/dist/compaction/types.js +2 -0
- package/dist/compaction/types.js.map +1 -0
- package/dist/diagnostics/checkers.js +211 -0
- package/dist/diagnostics/checkers.js.map +1 -0
- package/dist/diagnostics/engine.js +71 -0
- package/dist/diagnostics/engine.js.map +1 -0
- package/dist/diagnostics/types.js +2 -0
- package/dist/diagnostics/types.js.map +1 -0
- package/dist/dotenv/loader.js +115 -0
- package/dist/dotenv/loader.js.map +1 -0
- package/dist/glue/client.js +47 -0
- package/dist/glue/client.js.map +1 -0
- package/dist/glue/intent.js +78 -0
- package/dist/glue/intent.js.map +1 -0
- package/dist/glue/narration.js +55 -0
- package/dist/glue/narration.js.map +1 -0
- package/dist/headless/run.js +89 -0
- package/dist/headless/run.js.map +1 -0
- package/dist/hooks/manager.js +158 -0
- package/dist/hooks/manager.js.map +1 -0
- package/dist/hooks/runner.js +70 -0
- package/dist/hooks/runner.js.map +1 -0
- package/dist/hooks/types.js +2 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/memory/inject.js +12 -0
- package/dist/memory/inject.js.map +1 -0
- package/dist/memory/store.js +178 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/types.js +10 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/permissions/store.js +172 -0
- package/dist/permissions/store.js.map +1 -0
- package/dist/plan/flow.js +214 -0
- package/dist/plan/flow.js.map +1 -0
- package/dist/plan/prompts.js +69 -0
- package/dist/plan/prompts.js.map +1 -0
- package/dist/plan/store.js +37 -0
- package/dist/plan/store.js.map +1 -0
- package/dist/plan/types.js +3 -0
- package/dist/plan/types.js.map +1 -0
- package/dist/sessions/store.js +105 -0
- package/dist/sessions/store.js.map +1 -0
- package/dist/skills/loader.js +41 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/platform-loader.js +63 -0
- package/dist/skills/platform-loader.js.map +1 -0
- package/dist/skills/types.js +21 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/tools/ask-user.js +61 -0
- package/dist/tools/ask-user.js.map +1 -0
- package/dist/tools/dispatch-agent.js +178 -0
- package/dist/tools/dispatch-agent.js.map +1 -0
- package/dist/tools/edit-file.js +80 -0
- package/dist/tools/edit-file.js.map +1 -0
- package/dist/tools/errors.js +89 -0
- package/dist/tools/errors.js.map +1 -0
- package/dist/tools/file-ops.js +136 -0
- package/dist/tools/file-ops.js.map +1 -0
- package/dist/tools/file-state-cache.js +92 -0
- package/dist/tools/file-state-cache.js.map +1 -0
- package/dist/tools/git/branch.js +84 -0
- package/dist/tools/git/branch.js.map +1 -0
- package/dist/tools/git/commit.js +83 -0
- package/dist/tools/git/commit.js.map +1 -0
- package/dist/tools/git/diff.js +72 -0
- package/dist/tools/git/diff.js.map +1 -0
- package/dist/tools/git/git-helpers.js +58 -0
- package/dist/tools/git/git-helpers.js.map +1 -0
- package/dist/tools/git/log.js +70 -0
- package/dist/tools/git/log.js.map +1 -0
- package/dist/tools/git/status.js +97 -0
- package/dist/tools/git/status.js.map +1 -0
- package/dist/tools/git/worktree.js +132 -0
- package/dist/tools/git/worktree.js.map +1 -0
- package/dist/tools/glob.js +128 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.js +199 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/list-files.js +120 -0
- package/dist/tools/list-files.js.map +1 -0
- package/dist/tools/memory-tools.js +127 -0
- package/dist/tools/memory-tools.js.map +1 -0
- package/dist/tools/multi-edit.js +87 -0
- package/dist/tools/multi-edit.js.map +1 -0
- package/dist/tools/notebook-edit.js +147 -0
- package/dist/tools/notebook-edit.js.map +1 -0
- package/dist/tools/permission.js +168 -0
- package/dist/tools/permission.js.map +1 -0
- package/dist/tools/plan-mode.js +76 -0
- package/dist/tools/plan-mode.js.map +1 -0
- package/dist/tools/read-file.js +135 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/registry.js +52 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/shell.js +216 -0
- package/dist/tools/shell.js.map +1 -0
- package/dist/tools/task-store.js +70 -0
- package/dist/tools/task-store.js.map +1 -0
- package/dist/tools/tasks.js +131 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/web-fetch.js +152 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.js +169 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/write-file.js +70 -0
- package/dist/tools/write-file.js.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/App.js +216 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/Input.js +90 -0
- package/dist/ui/Input.js.map +1 -0
- package/dist/ui/Message.js +89 -0
- package/dist/ui/Message.js.map +1 -0
- package/dist/ui/MessageList.js +35 -0
- package/dist/ui/MessageList.js.map +1 -0
- package/dist/ui/Permission.js +39 -0
- package/dist/ui/Permission.js.map +1 -0
- package/dist/ui/Status.js +34 -0
- package/dist/ui/Status.js.map +1 -0
- package/dist/ui/TaskPanel.js +43 -0
- package/dist/ui/TaskPanel.js.map +1 -0
- package/dist/ui/Throbber.js +20 -0
- package/dist/ui/Throbber.js.map +1 -0
- package/dist/ui/ToolPanel.js +83 -0
- package/dist/ui/ToolPanel.js.map +1 -0
- package/dist/ui/UserQuery.js +38 -0
- package/dist/ui/UserQuery.js.map +1 -0
- package/dist/ui/input-state.js +210 -0
- package/dist/ui/input-state.js.map +1 -0
- package/dist/ui/wrap.js +30 -0
- package/dist/ui/wrap.js.map +1 -0
- package/dist/user-queries/store.js +60 -0
- package/dist/user-queries/store.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join, relative, resolve } from "node:path";
|
|
3
|
+
import { Type } from "typebox";
|
|
4
|
+
import { resolveInsideCwd } from "./file-ops.js";
|
|
5
|
+
const Params = Type.Object({
|
|
6
|
+
path: Type.Optional(Type.String({
|
|
7
|
+
description: "Directory to list, relative to the project root. Defaults to the project root.",
|
|
8
|
+
})),
|
|
9
|
+
recursive: Type.Optional(Type.Boolean({
|
|
10
|
+
description: "Walk subdirectories. Default false.",
|
|
11
|
+
})),
|
|
12
|
+
max_results: Type.Optional(Type.Integer({
|
|
13
|
+
minimum: 1,
|
|
14
|
+
maximum: 5000,
|
|
15
|
+
description: "Cap on entries returned. Default 500.",
|
|
16
|
+
})),
|
|
17
|
+
});
|
|
18
|
+
const DEFAULT_LIMIT = 500;
|
|
19
|
+
const IGNORED_DIRS = new Set([
|
|
20
|
+
".git",
|
|
21
|
+
".hg",
|
|
22
|
+
".svn",
|
|
23
|
+
".cache",
|
|
24
|
+
".idea",
|
|
25
|
+
".next",
|
|
26
|
+
".nuxt",
|
|
27
|
+
".pytest_cache",
|
|
28
|
+
".turbo",
|
|
29
|
+
".venv",
|
|
30
|
+
".vscode",
|
|
31
|
+
"__pycache__",
|
|
32
|
+
"build",
|
|
33
|
+
"dist",
|
|
34
|
+
"node_modules",
|
|
35
|
+
"out",
|
|
36
|
+
"target",
|
|
37
|
+
"vendor",
|
|
38
|
+
"venv",
|
|
39
|
+
]);
|
|
40
|
+
const DESCRIPTION = `List entries in a directory. Defaults to the project root.
|
|
41
|
+
|
|
42
|
+
Behavior:
|
|
43
|
+
- Files and subdirectories returned as paths relative to the project root.
|
|
44
|
+
- Directory entries end with "/".
|
|
45
|
+
- Subdirectories with build/cache/VCS metadata (node_modules, .git, dist, target, etc.) are skipped automatically.
|
|
46
|
+
- Set recursive: true to walk subtrees. Output is capped (default 500); the truncation is reported in details.
|
|
47
|
+
|
|
48
|
+
Use this for orientation. For pattern-based discovery use the glob tool, and for content search use grep.`;
|
|
49
|
+
export function createListFiles(ctx) {
|
|
50
|
+
return {
|
|
51
|
+
name: "list_files",
|
|
52
|
+
label: "List",
|
|
53
|
+
description: DESCRIPTION,
|
|
54
|
+
parameters: Params,
|
|
55
|
+
executionMode: "parallel",
|
|
56
|
+
execute: async (_toolCallId, params) => {
|
|
57
|
+
const root = resolveInsideCwd(ctx.cwd, params.path ?? ".");
|
|
58
|
+
const limit = params.max_results ?? DEFAULT_LIMIT;
|
|
59
|
+
const recursive = params.recursive ?? false;
|
|
60
|
+
const entries = [];
|
|
61
|
+
let truncated = false;
|
|
62
|
+
const walk = (dir) => {
|
|
63
|
+
if (entries.length >= limit) {
|
|
64
|
+
truncated = true;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let names;
|
|
68
|
+
try {
|
|
69
|
+
names = readdirSync(dir).sort();
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
73
|
+
throw new Error(`Cannot list ${relative(ctx.cwd, dir) || "."}: ${reason}`);
|
|
74
|
+
}
|
|
75
|
+
for (const name of names) {
|
|
76
|
+
if (entries.length >= limit) {
|
|
77
|
+
truncated = true;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const abs = join(dir, name);
|
|
81
|
+
let stat;
|
|
82
|
+
try {
|
|
83
|
+
stat = statSync(abs);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const rel = relative(ctx.cwd, abs);
|
|
89
|
+
if (stat.isDirectory()) {
|
|
90
|
+
if (IGNORED_DIRS.has(name))
|
|
91
|
+
continue;
|
|
92
|
+
entries.push({ path: `${rel}/`, type: "dir", bytes: 0 });
|
|
93
|
+
if (recursive)
|
|
94
|
+
walk(abs);
|
|
95
|
+
}
|
|
96
|
+
else if (stat.isFile()) {
|
|
97
|
+
entries.push({ path: rel, type: "file", bytes: stat.size });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
walk(root);
|
|
102
|
+
const lines = entries.map((e) => e.path);
|
|
103
|
+
const tail = truncated ? `\n... (capped at ${limit} entries)` : "";
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: `${entries.length} entries under ${relative(ctx.cwd, root) || "."}:\n${lines.join("\n")}${tail}`,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
details: {
|
|
112
|
+
root: resolve(root),
|
|
113
|
+
entries,
|
|
114
|
+
truncated,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=list-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-files.js","sourceRoot":"","sources":["../../src/tools/list-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
const TypeSchema = Type.Union([
|
|
3
|
+
Type.Literal("user"),
|
|
4
|
+
Type.Literal("feedback"),
|
|
5
|
+
Type.Literal("project"),
|
|
6
|
+
Type.Literal("reference"),
|
|
7
|
+
]);
|
|
8
|
+
// ─── save_memory ─────────────────────────────────────────────
|
|
9
|
+
const SaveParams = Type.Object({
|
|
10
|
+
filename: Type.String({
|
|
11
|
+
minLength: 1,
|
|
12
|
+
maxLength: 80,
|
|
13
|
+
description: "Memory filename, lowercase letters/digits/-/_ only, ending in .md (e.g. 'user_role.md'). " +
|
|
14
|
+
"Use a name that describes the memory's subject.",
|
|
15
|
+
}),
|
|
16
|
+
name: Type.String({
|
|
17
|
+
minLength: 1,
|
|
18
|
+
maxLength: 100,
|
|
19
|
+
description: "Short human-readable title (a few words).",
|
|
20
|
+
}),
|
|
21
|
+
description: Type.String({
|
|
22
|
+
minLength: 1,
|
|
23
|
+
maxLength: 200,
|
|
24
|
+
description: "One-line description of when this memory applies. Used in MEMORY.md to decide relevance.",
|
|
25
|
+
}),
|
|
26
|
+
type: TypeSchema,
|
|
27
|
+
body: Type.String({
|
|
28
|
+
description: "Full memory content. Markdown allowed. Be specific about WHY this matters for future sessions.",
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
const SAVE_DESCRIPTION = `Persist a memory across sessions. Memory entries are written to ~/.codebase/projects/<projectHash>/memory/ and MEMORY.md is updated with a one-line index entry.
|
|
32
|
+
|
|
33
|
+
The 4-type taxonomy:
|
|
34
|
+
- user: stable facts about the user (role, preferences, expertise) that should shape how you collaborate.
|
|
35
|
+
- feedback: rules the user gave you ("don't do X", "always do Y"). Include the WHY so you can judge edge cases.
|
|
36
|
+
- project: context about the work itself — initiatives, blockers, decisions. These rot fast; convert relative dates to absolute.
|
|
37
|
+
- reference: pointers to external systems (Linear projects, dashboards, Slack channels).
|
|
38
|
+
|
|
39
|
+
Don't save derivable info (file paths, commit history, code conventions). Don't save ephemeral conversation state. Do save what was non-obvious or surprising.`;
|
|
40
|
+
export function createSaveMemory(ctx) {
|
|
41
|
+
return {
|
|
42
|
+
name: "save_memory",
|
|
43
|
+
label: "Save memory",
|
|
44
|
+
description: SAVE_DESCRIPTION,
|
|
45
|
+
parameters: SaveParams,
|
|
46
|
+
executionMode: "sequential",
|
|
47
|
+
execute: async (_id, params) => {
|
|
48
|
+
const record = ctx.memory.save({
|
|
49
|
+
filename: params.filename,
|
|
50
|
+
name: params.name,
|
|
51
|
+
description: params.description,
|
|
52
|
+
type: params.type,
|
|
53
|
+
body: params.body,
|
|
54
|
+
});
|
|
55
|
+
updateIndex(ctx);
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: `Saved memory ${record.filename} (${record.type}).` }],
|
|
58
|
+
details: { filename: record.filename, type: record.type, bytes: Buffer.byteLength(record.body, "utf8") },
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// ─── read_memory ─────────────────────────────────────────────
|
|
64
|
+
const ReadParams = Type.Object({
|
|
65
|
+
filename: Type.Optional(Type.String({ description: "Read a specific memory file (e.g. 'user_role.md')." })),
|
|
66
|
+
type: Type.Optional(Type.Union([TypeSchema], {
|
|
67
|
+
description: "List only memories of this type. Mutually exclusive with filename.",
|
|
68
|
+
})),
|
|
69
|
+
});
|
|
70
|
+
const READ_DESCRIPTION = `Read project memory.
|
|
71
|
+
- With filename: returns that single memory's frontmatter + body.
|
|
72
|
+
- With type: returns the list of memories of that type (frontmatter + body).
|
|
73
|
+
- With neither: returns the MEMORY.md index for orientation. Use this first if you want to know what's there.`;
|
|
74
|
+
export function createReadMemory(ctx) {
|
|
75
|
+
return {
|
|
76
|
+
name: "read_memory",
|
|
77
|
+
label: "Read memory",
|
|
78
|
+
description: READ_DESCRIPTION,
|
|
79
|
+
parameters: ReadParams,
|
|
80
|
+
executionMode: "parallel",
|
|
81
|
+
execute: async (_id, params) => {
|
|
82
|
+
if (params.filename) {
|
|
83
|
+
const record = ctx.memory.read(params.filename);
|
|
84
|
+
if (!record) {
|
|
85
|
+
throw new Error(`memory ${params.filename} not found`);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
content: [{ type: "text", text: formatRecord(record) }],
|
|
89
|
+
details: { mode: "single", record },
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (params.type) {
|
|
93
|
+
const records = ctx.memory.list(params.type);
|
|
94
|
+
const text = records.length === 0
|
|
95
|
+
? `(no memories of type ${params.type})`
|
|
96
|
+
: records.map(formatRecord).join("\n\n---\n\n");
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text }],
|
|
99
|
+
details: { mode: "list", records },
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const index = ctx.memory.index();
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: "text", text: index || "(MEMORY.md is empty — no memories saved yet)" }],
|
|
105
|
+
details: { mode: "index", index },
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function formatRecord(record) {
|
|
111
|
+
return [`# ${record.name} (${record.type})`, `> ${record.description}`, "", record.body.trim()].join("\n");
|
|
112
|
+
}
|
|
113
|
+
// ─── shared: update MEMORY.md after a save ───────────────────
|
|
114
|
+
function updateIndex(ctx) {
|
|
115
|
+
const records = ctx.memory.list();
|
|
116
|
+
if (records.length === 0) {
|
|
117
|
+
ctx.memory.writeIndex("");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const lines = records.map((r) => `- [${r.name}](${r.filename}) — ${r.description}`);
|
|
121
|
+
ctx.memory.writeIndex(`${lines.join("\n")}\n`);
|
|
122
|
+
}
|
|
123
|
+
// ─── factory bundle ──────────────────────────────────────────
|
|
124
|
+
export function createMemoryTools(ctx) {
|
|
125
|
+
return [createSaveMemory(ctx), createReadMemory(ctx)];
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=memory-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-tools.js","sourceRoot":"","sources":["../../src/tools/memory-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { applyEdit, atomicWrite, resolveInsideCwd, validateForOverwrite } from "./file-ops.js";
|
|
4
|
+
const Params = Type.Object({
|
|
5
|
+
path: Type.String({
|
|
6
|
+
description: "File path (absolute or relative to the project root). Must be read with read_file first.",
|
|
7
|
+
}),
|
|
8
|
+
edits: Type.Array(Type.Object({
|
|
9
|
+
old_string: Type.String({
|
|
10
|
+
description: "Exact text to replace. Must match including indentation and line endings.",
|
|
11
|
+
}),
|
|
12
|
+
new_string: Type.String({
|
|
13
|
+
description: "Replacement text.",
|
|
14
|
+
}),
|
|
15
|
+
replace_all: Type.Optional(Type.Boolean({
|
|
16
|
+
description: "Replace every occurrence within this edit. Default false.",
|
|
17
|
+
})),
|
|
18
|
+
}), { minItems: 1, maxItems: 100, description: "Edits to apply in order." }),
|
|
19
|
+
});
|
|
20
|
+
const DESCRIPTION = `Apply multiple edits to a single file atomically.
|
|
21
|
+
|
|
22
|
+
Hard rules:
|
|
23
|
+
- The file must have been read with read_file in the current turn (same as edit_file).
|
|
24
|
+
- If the file changed on disk between the last read and this call, the entire batch is rejected.
|
|
25
|
+
- Edits apply in order to a running in-memory copy. Edit N+1 sees the result of edit N — useful for cascading rename-style changes.
|
|
26
|
+
- If any edit fails (no match, ambiguous match, identical strings), the whole batch is aborted and nothing is written.
|
|
27
|
+
- BOM, line endings, and file mode are preserved exactly as edit_file.
|
|
28
|
+
|
|
29
|
+
Use this when you have several related changes in the same file (e.g. rename a symbol everywhere). For changes across many files, call edit_file separately per file — multi_edit is single-file by design.`;
|
|
30
|
+
export function createMultiEdit(ctx) {
|
|
31
|
+
return {
|
|
32
|
+
name: "multi_edit",
|
|
33
|
+
label: "Multi-edit",
|
|
34
|
+
description: DESCRIPTION,
|
|
35
|
+
parameters: Params,
|
|
36
|
+
executionMode: "sequential",
|
|
37
|
+
execute: async (_id, params) => {
|
|
38
|
+
const absPath = resolveInsideCwd(ctx.cwd, params.path);
|
|
39
|
+
const snap = validateForOverwrite(absPath, ctx.fileStateCache);
|
|
40
|
+
let content = snap.content;
|
|
41
|
+
let totalReplacements = 0;
|
|
42
|
+
for (let i = 0; i < params.edits.length; i++) {
|
|
43
|
+
const edit = params.edits[i];
|
|
44
|
+
try {
|
|
45
|
+
const next = applyEdit(content, {
|
|
46
|
+
oldString: edit.old_string,
|
|
47
|
+
newString: edit.new_string,
|
|
48
|
+
replaceAll: edit.replace_all ?? false,
|
|
49
|
+
path: params.path,
|
|
50
|
+
});
|
|
51
|
+
content = next.content;
|
|
52
|
+
totalReplacements += next.replacements;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
56
|
+
throw new Error(`edit #${i + 1} failed: ${reason}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const mode = statSync(absPath).mode & 0o777;
|
|
60
|
+
const { mtimeMs, size } = atomicWrite(absPath, content, {
|
|
61
|
+
hasBOM: snap.hasBOM,
|
|
62
|
+
eol: snap.eol,
|
|
63
|
+
mode,
|
|
64
|
+
});
|
|
65
|
+
ctx.fileStateCache.record({
|
|
66
|
+
path: absPath,
|
|
67
|
+
content,
|
|
68
|
+
mtimeMs,
|
|
69
|
+
size,
|
|
70
|
+
hasBOM: snap.hasBOM,
|
|
71
|
+
eol: snap.eol,
|
|
72
|
+
isPartialView: false,
|
|
73
|
+
storedAt: Date.now(),
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: `Applied ${params.edits.length} edit${params.edits.length === 1 ? "" : "s"} to ${params.path} (${totalReplacements} replacement${totalReplacements === 1 ? "" : "s"}).`,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
details: { path: absPath, edits: params.edits.length, replacements: totalReplacements, bytes: size },
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=multi-edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-edit.js","sourceRoot":"","sources":["../../src/tools/multi-edit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { atomicWrite, resolveInsideCwd, validateForOverwrite } from "./file-ops.js";
|
|
4
|
+
const Params = Type.Object({
|
|
5
|
+
path: Type.String({
|
|
6
|
+
description: "Path to a .ipynb file. Must be read with read_file first.",
|
|
7
|
+
}),
|
|
8
|
+
cell_index: Type.Integer({
|
|
9
|
+
minimum: 0,
|
|
10
|
+
description: "0-based cell index. For insert, this is the position where the new cell appears (subsequent cells shift down).",
|
|
11
|
+
}),
|
|
12
|
+
operation: Type.Union([Type.Literal("insert"), Type.Literal("update"), Type.Literal("delete")], {
|
|
13
|
+
description: "insert: add a new cell. update: replace source/type of existing cell. delete: remove the cell.",
|
|
14
|
+
}),
|
|
15
|
+
cell_type: Type.Optional(Type.Union([Type.Literal("code"), Type.Literal("markdown")], {
|
|
16
|
+
description: "Required for insert. Optional for update (omit to keep existing type).",
|
|
17
|
+
})),
|
|
18
|
+
source: Type.Optional(Type.String({
|
|
19
|
+
description: "Cell source. Required for insert and update. Ignored for delete.",
|
|
20
|
+
})),
|
|
21
|
+
});
|
|
22
|
+
const DESCRIPTION = `Insert, update, or delete a cell in a Jupyter .ipynb notebook.
|
|
23
|
+
|
|
24
|
+
Hard rules (same as edit_file):
|
|
25
|
+
- Notebook must be read with read_file first.
|
|
26
|
+
- If the file changed on disk between read and edit, the operation is rejected with "unexpectedly modified".
|
|
27
|
+
- BOM and line endings are preserved.
|
|
28
|
+
|
|
29
|
+
Operation semantics:
|
|
30
|
+
- insert: cell_type and source required. New cell goes at cell_index; existing cells shift down.
|
|
31
|
+
- update: source required; cell_type optional (omit to preserve type). cell_index must point at an existing cell.
|
|
32
|
+
- delete: cell_index must point at an existing cell. cell_type and source ignored.
|
|
33
|
+
|
|
34
|
+
Source is stored as a JSON string. Code cells get empty outputs and null execution_count; markdown cells get neither.`;
|
|
35
|
+
export function createNotebookEdit(ctx) {
|
|
36
|
+
return {
|
|
37
|
+
name: "notebook_edit",
|
|
38
|
+
label: "Notebook edit",
|
|
39
|
+
description: DESCRIPTION,
|
|
40
|
+
parameters: Params,
|
|
41
|
+
executionMode: "sequential",
|
|
42
|
+
execute: async (_id, params) => {
|
|
43
|
+
if (!params.path.endsWith(".ipynb")) {
|
|
44
|
+
throw new Error(`notebook_edit only operates on .ipynb files; got ${params.path}`);
|
|
45
|
+
}
|
|
46
|
+
const absPath = resolveInsideCwd(ctx.cwd, params.path);
|
|
47
|
+
const snap = validateForOverwrite(absPath, ctx.fileStateCache);
|
|
48
|
+
let notebook;
|
|
49
|
+
try {
|
|
50
|
+
notebook = JSON.parse(snap.content);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
54
|
+
throw new Error(`${params.path} is not valid JSON: ${reason}`);
|
|
55
|
+
}
|
|
56
|
+
if (!Array.isArray(notebook.cells)) {
|
|
57
|
+
throw new Error(`${params.path} does not look like a notebook (no cells array).`);
|
|
58
|
+
}
|
|
59
|
+
applyOperation(notebook, params);
|
|
60
|
+
const next = `${JSON.stringify(notebook, null, 1)}\n`;
|
|
61
|
+
const mode = statSync(absPath).mode & 0o777;
|
|
62
|
+
const { mtimeMs, size } = atomicWrite(absPath, next, {
|
|
63
|
+
hasBOM: snap.hasBOM,
|
|
64
|
+
eol: snap.eol,
|
|
65
|
+
mode,
|
|
66
|
+
});
|
|
67
|
+
ctx.fileStateCache.record({
|
|
68
|
+
path: absPath,
|
|
69
|
+
content: next,
|
|
70
|
+
mtimeMs,
|
|
71
|
+
size,
|
|
72
|
+
hasBOM: snap.hasBOM,
|
|
73
|
+
eol: snap.eol,
|
|
74
|
+
isPartialView: false,
|
|
75
|
+
storedAt: Date.now(),
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: `${params.operation} cell ${params.cell_index} in ${params.path} (${notebook.cells.length} cells total)`,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
details: {
|
|
85
|
+
path: absPath,
|
|
86
|
+
operation: params.operation,
|
|
87
|
+
cellIndex: params.cell_index,
|
|
88
|
+
cellCount: notebook.cells.length,
|
|
89
|
+
bytes: size,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function applyOperation(notebook, params) {
|
|
96
|
+
const cells = notebook.cells;
|
|
97
|
+
const idx = params.cell_index;
|
|
98
|
+
if (params.operation === "insert") {
|
|
99
|
+
if (!params.cell_type)
|
|
100
|
+
throw new Error("insert requires cell_type.");
|
|
101
|
+
if (params.source === undefined)
|
|
102
|
+
throw new Error("insert requires source.");
|
|
103
|
+
if (idx < 0 || idx > cells.length) {
|
|
104
|
+
throw new Error(`insert index ${idx} is out of range (0..${cells.length}).`);
|
|
105
|
+
}
|
|
106
|
+
cells.splice(idx, 0, makeCell(params.cell_type, params.source));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (params.operation === "delete") {
|
|
110
|
+
assertExists(cells, idx);
|
|
111
|
+
cells.splice(idx, 1);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// update
|
|
115
|
+
assertExists(cells, idx);
|
|
116
|
+
if (params.source === undefined)
|
|
117
|
+
throw new Error("update requires source.");
|
|
118
|
+
const existing = cells[idx];
|
|
119
|
+
const nextType = params.cell_type ?? (existing.cell_type === "code" ? "code" : "markdown");
|
|
120
|
+
const next = {
|
|
121
|
+
...existing,
|
|
122
|
+
cell_type: nextType,
|
|
123
|
+
source: params.source,
|
|
124
|
+
};
|
|
125
|
+
if (nextType === "code") {
|
|
126
|
+
next.outputs = existing.outputs ?? [];
|
|
127
|
+
next.execution_count = existing.execution_count ?? null;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Markdown cells don't carry outputs or execution_count.
|
|
131
|
+
delete next.outputs;
|
|
132
|
+
delete next.execution_count;
|
|
133
|
+
}
|
|
134
|
+
cells[idx] = next;
|
|
135
|
+
}
|
|
136
|
+
function assertExists(cells, idx) {
|
|
137
|
+
if (idx < 0 || idx >= cells.length) {
|
|
138
|
+
throw new Error(`cell index ${idx} is out of range (0..${cells.length - 1}).`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function makeCell(type, source) {
|
|
142
|
+
if (type === "code") {
|
|
143
|
+
return { cell_type: "code", source, metadata: {}, outputs: [], execution_count: null };
|
|
144
|
+
}
|
|
145
|
+
return { cell_type: "markdown", source, metadata: {} };
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=notebook-edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notebook-edit.js","sourceRoot":"","sources":["../../src/tools/notebook-edit.ts"],"names":[],"mappings":"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"}
|