@cjhyy/code-shell-capability-coding 0.9.44 → 0.9.47
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/capability-runtime.js +4 -1
- package/dist/cc-orchestrator/codex-session-history.d.ts +3 -1
- package/dist/cc-orchestrator/codex-session-history.js +11 -4
- package/dist/cc-orchestrator/external-agent-changes.js +7 -65
- package/dist/cc-orchestrator/external-agent-driver.js +61 -3
- package/dist/cc-orchestrator/session-discovery.d.ts +4 -2
- package/dist/cc-orchestrator/session-discovery.js +63 -6
- package/dist/cc-orchestrator/session-history.js +5 -0
- package/dist/external-runtimes/claude-code/event-translator.d.ts +7 -0
- package/dist/external-runtimes/claude-code/event-translator.js +58 -9
- package/dist/external-runtimes/claude-code/mcp-config.d.ts +8 -0
- package/dist/external-runtimes/claude-code/mcp-config.js +16 -3
- package/dist/external-runtimes/claude-code/permissions.d.ts +69 -0
- package/dist/external-runtimes/claude-code/permissions.js +120 -0
- package/dist/external-runtimes/claude-code/runtime.d.ts +43 -4
- package/dist/external-runtimes/claude-code/runtime.js +155 -39
- package/dist/external-runtimes/claude-code/text-inference.d.ts +22 -0
- package/dist/external-runtimes/claude-code/text-inference.js +240 -0
- package/dist/external-runtimes/codex/app-server-client.d.ts +15 -0
- package/dist/external-runtimes/codex/app-server-client.js +69 -6
- package/dist/external-runtimes/codex/event-translator.js +16 -2
- package/dist/external-runtimes/codex/runtime.d.ts +14 -0
- package/dist/external-runtimes/codex/runtime.js +152 -16
- package/dist/external-runtimes/index.d.ts +4 -0
- package/dist/external-runtimes/index.js +2 -0
- package/dist/external-runtimes/session-factory.d.ts +16 -0
- package/dist/external-runtimes/session-factory.js +43 -1
- package/dist/external-runtimes/shared/mcp-bridge.d.ts +36 -1
- package/dist/external-runtimes/shared/mcp-bridge.js +93 -13
- package/dist/git/parse-log.d.ts +6 -2
- package/dist/git/parse-log.js +7 -3
- package/dist/git/utils.d.ts +4 -0
- package/dist/git/utils.js +29 -10
- package/dist/git/worktree/crud.d.ts +2 -0
- package/dist/git/worktree/crud.js +29 -1
- package/dist/git/worktree/diff.js +57 -29
- package/dist/index.capability.js +14 -1
- package/dist/index.git.d.ts +1 -0
- package/dist/index.git.js +1 -0
- package/dist/lsp/client.d.ts +3 -0
- package/dist/lsp/client.js +96 -25
- package/dist/lsp/manager.d.ts +2 -0
- package/dist/lsp/manager.js +56 -15
- package/dist/prompt/coding.md +1 -0
- package/dist/quota/index.d.ts +2 -1
- package/dist/quota/index.js +27 -7
- package/dist/quota/types.d.ts +5 -2
- package/dist/review/review-prompt.d.ts +2 -0
- package/dist/review/review-prompt.js +70 -29
- package/dist/review/review-target.d.ts +29 -0
- package/dist/review/review-target.js +353 -0
- package/dist/tools/apply-patch/applier.js +64 -39
- package/dist/tools/apply-patch/backup-targets.d.ts +8 -0
- package/dist/tools/apply-patch/backup-targets.js +24 -0
- package/dist/tools/apply-patch/parser.js +4 -2
- package/dist/tools/check-quota.js +3 -1
- package/dist/tools/lsp.js +36 -5
- package/package.json +2 -2
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Algorithm uses code adapted from codex-rs/apply-patch/src/lib.rs (Apache-2.0)
|
|
11
11
|
// for the chunk-replacement phase (compute_replacements, apply_replacements).
|
|
12
|
-
import {
|
|
12
|
+
import { chmod, mkdir, open, unlink, writeFile } from "node:fs/promises";
|
|
13
13
|
import { existsSync, realpathSync } from "node:fs";
|
|
14
14
|
import { dirname, isAbsolute, resolve, sep } from "node:path";
|
|
15
15
|
import { seekSequence } from "./seek-sequence.js";
|
|
@@ -47,42 +47,38 @@ async function planHunks(hunks, cwd, workspaceRoots) {
|
|
|
47
47
|
const original = hunk.path;
|
|
48
48
|
const sourcePath = resolveAgainst(original, cwd, workspaceRoots);
|
|
49
49
|
if (hunk.kind === "add") {
|
|
50
|
-
const existed = await
|
|
50
|
+
const existed = await readSnapshotIfExists(sourcePath);
|
|
51
51
|
schedule(sourcePath, {
|
|
52
52
|
path: sourcePath,
|
|
53
53
|
newContent: hunk.contents,
|
|
54
|
-
|
|
54
|
+
original: existed,
|
|
55
55
|
});
|
|
56
56
|
set.added.push(original);
|
|
57
57
|
continue;
|
|
58
58
|
}
|
|
59
59
|
if (hunk.kind === "delete") {
|
|
60
|
-
const
|
|
61
|
-
if (!
|
|
60
|
+
const snapshot = await readSnapshotIfExists(sourcePath);
|
|
61
|
+
if (!snapshot)
|
|
62
62
|
throw new Error(`Failed to delete file: ${sourcePath} not found`);
|
|
63
|
-
if (stats.isDirectory()) {
|
|
64
|
-
throw new Error(`Failed to delete file: ${sourcePath} is a directory`);
|
|
65
|
-
}
|
|
66
|
-
const content = await readFile(sourcePath, "utf-8");
|
|
67
63
|
schedule(sourcePath, {
|
|
68
64
|
path: sourcePath,
|
|
69
65
|
newContent: null,
|
|
70
|
-
|
|
66
|
+
original: snapshot,
|
|
71
67
|
});
|
|
72
68
|
set.deleted.push(original);
|
|
73
69
|
continue;
|
|
74
70
|
}
|
|
75
71
|
// Update (with optional rename).
|
|
76
|
-
const
|
|
77
|
-
if (!
|
|
72
|
+
const snapshot = await readSnapshotIfExists(sourcePath);
|
|
73
|
+
if (!snapshot) {
|
|
78
74
|
throw new Error(`Failed to update file: ${sourcePath} does not exist`);
|
|
79
75
|
}
|
|
80
|
-
const originalRaw =
|
|
76
|
+
const originalRaw = snapshot.content.toString("utf-8");
|
|
81
77
|
// CRLF handling: patch context lines are LF (the model emits LF). Match in
|
|
82
78
|
// LF space, then write back in the file's original EOL so a CRLF file isn't
|
|
83
79
|
// silently converted to LF (which would diff the whole file). originalText
|
|
84
|
-
// (LF) is what we compare;
|
|
85
|
-
// restore writes back the exact bytes. See eol.ts.
|
|
80
|
+
// (LF) is what we compare; the undecoded snapshot is preserved for rollback
|
|
81
|
+
// so a restore writes back the exact bytes. See eol.ts.
|
|
86
82
|
const eol = detectEol(originalRaw);
|
|
87
83
|
const originalText = toLf(originalRaw);
|
|
88
84
|
const newText = applyEol(applyChunksToText(originalText, hunk.chunks, sourcePath), eol);
|
|
@@ -93,20 +89,21 @@ async function planHunks(hunks, cwd, workspaceRoots) {
|
|
|
93
89
|
schedule(sourcePath, {
|
|
94
90
|
path: sourcePath,
|
|
95
91
|
newContent: newText,
|
|
96
|
-
|
|
92
|
+
original: snapshot,
|
|
97
93
|
});
|
|
98
94
|
}
|
|
99
95
|
else {
|
|
100
|
-
const destOriginal = await
|
|
96
|
+
const destOriginal = await readSnapshotIfExists(destPath);
|
|
101
97
|
schedule(sourcePath, {
|
|
102
98
|
path: sourcePath,
|
|
103
99
|
newContent: null,
|
|
104
|
-
|
|
100
|
+
original: snapshot,
|
|
105
101
|
});
|
|
106
102
|
schedule(destPath, {
|
|
107
103
|
path: destPath,
|
|
108
104
|
newContent: newText,
|
|
109
|
-
|
|
105
|
+
original: destOriginal,
|
|
106
|
+
newMode: snapshot.mode,
|
|
110
107
|
});
|
|
111
108
|
}
|
|
112
109
|
set.modified.push(original);
|
|
@@ -115,7 +112,7 @@ async function planHunks(hunks, cwd, workspaceRoots) {
|
|
|
115
112
|
schedule(sourcePath, {
|
|
116
113
|
path: sourcePath,
|
|
117
114
|
newContent: newText,
|
|
118
|
-
|
|
115
|
+
original: snapshot,
|
|
119
116
|
});
|
|
120
117
|
set.modified.push(original);
|
|
121
118
|
}
|
|
@@ -191,27 +188,27 @@ async function commitPlanned(set, allowPartial) {
|
|
|
191
188
|
}
|
|
192
189
|
return { added: set.added, modified: set.modified, deleted: set.deleted };
|
|
193
190
|
}
|
|
194
|
-
//
|
|
195
|
-
// the
|
|
196
|
-
//
|
|
197
|
-
const
|
|
191
|
+
// Include the current attempt: writeFile may write partially before failing,
|
|
192
|
+
// and chmod can fail after the new content has already been written. Restore
|
|
193
|
+
// the bytes and permissions captured during planning, without re-reading.
|
|
194
|
+
const attempted = [];
|
|
198
195
|
try {
|
|
199
196
|
for (const change of ordered) {
|
|
197
|
+
attempted.push(change);
|
|
200
198
|
await writeChange(change);
|
|
201
|
-
committed.push(change);
|
|
202
199
|
}
|
|
203
200
|
}
|
|
204
201
|
catch (err) {
|
|
205
|
-
for (let i =
|
|
206
|
-
const c =
|
|
202
|
+
for (let i = attempted.length - 1; i >= 0; i--) {
|
|
203
|
+
const c = attempted[i];
|
|
207
204
|
try {
|
|
208
|
-
if (c.
|
|
205
|
+
if (c.original === null) {
|
|
209
206
|
// File did not exist before we wrote it — remove our write.
|
|
210
207
|
if (existsSync(c.path))
|
|
211
208
|
await unlink(c.path);
|
|
212
209
|
}
|
|
213
210
|
else {
|
|
214
|
-
await
|
|
211
|
+
await restoreSnapshot(c.path, c.original);
|
|
215
212
|
}
|
|
216
213
|
}
|
|
217
214
|
catch {
|
|
@@ -222,6 +219,25 @@ async function commitPlanned(set, allowPartial) {
|
|
|
222
219
|
}
|
|
223
220
|
return { added: set.added, modified: set.modified, deleted: set.deleted };
|
|
224
221
|
}
|
|
222
|
+
async function restoreSnapshot(path, snapshot) {
|
|
223
|
+
const writableMode = snapshot.mode | 0o200;
|
|
224
|
+
try {
|
|
225
|
+
// A successful move may have made an existing target read-only. writeFile's
|
|
226
|
+
// mode only applies to new files, so temporarily allow restoring its bytes.
|
|
227
|
+
try {
|
|
228
|
+
await chmod(path, writableMode);
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
if (error.code !== "ENOENT")
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
await writeFile(path, snapshot.content, { mode: writableMode });
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
// Also restore a read-only snapshot's mode if writing its bytes fails.
|
|
238
|
+
await chmod(path, snapshot.mode);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
225
241
|
async function writeChange(change) {
|
|
226
242
|
if (change.newContent === null) {
|
|
227
243
|
if (existsSync(change.path))
|
|
@@ -232,7 +248,11 @@ async function writeChange(change) {
|
|
|
232
248
|
if (!existsSync(parent)) {
|
|
233
249
|
await mkdir(parent, { recursive: true });
|
|
234
250
|
}
|
|
235
|
-
await writeFile(change.path, change.newContent, "utf-8");
|
|
251
|
+
await writeFile(change.path, change.newContent, { encoding: "utf-8", mode: change.newMode });
|
|
252
|
+
if (change.newMode !== undefined) {
|
|
253
|
+
// The creation mode is masked by umask and ignored for existing targets.
|
|
254
|
+
await chmod(change.path, change.newMode);
|
|
255
|
+
}
|
|
236
256
|
}
|
|
237
257
|
// ─── helpers ──────────────────────────────────────────────────────
|
|
238
258
|
/**
|
|
@@ -288,19 +308,24 @@ function isInside(child, parent) {
|
|
|
288
308
|
const p = parent.endsWith(sep) ? parent : parent + sep;
|
|
289
309
|
return child.startsWith(p);
|
|
290
310
|
}
|
|
291
|
-
async function
|
|
311
|
+
async function readSnapshotIfExists(path) {
|
|
312
|
+
let file;
|
|
292
313
|
try {
|
|
293
|
-
|
|
314
|
+
file = await open(path, "r");
|
|
294
315
|
}
|
|
295
|
-
catch {
|
|
296
|
-
|
|
316
|
+
catch (error) {
|
|
317
|
+
const code = error.code;
|
|
318
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
319
|
+
return null;
|
|
320
|
+
throw error;
|
|
297
321
|
}
|
|
298
|
-
}
|
|
299
|
-
async function readIfExists(path) {
|
|
300
322
|
try {
|
|
301
|
-
|
|
323
|
+
const stats = await file.stat();
|
|
324
|
+
if (stats.isDirectory())
|
|
325
|
+
throw new Error(`Cannot apply patch to directory: ${path}`);
|
|
326
|
+
return { content: await file.readFile(), mode: stats.mode & 0o7777 };
|
|
302
327
|
}
|
|
303
|
-
|
|
304
|
-
|
|
328
|
+
finally {
|
|
329
|
+
await file.close();
|
|
305
330
|
}
|
|
306
331
|
}
|
|
@@ -8,3 +8,11 @@
|
|
|
8
8
|
* up (undo of an add = delete, handled separately if/when /undo grows that).
|
|
9
9
|
*/
|
|
10
10
|
export declare function patchBackupTargets(patchText: string, cwd: string): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Every filesystem destination an ApplyPatch can change. Unlike undo backup
|
|
13
|
+
* targets, turn diffs need absent paths too: additions and move destinations
|
|
14
|
+
* must be captured before execution to distinguish creation from overwrite.
|
|
15
|
+
* Moves are represented by their source and destination; the tracker compares
|
|
16
|
+
* each path's real before/after state rather than inferring a rename from text.
|
|
17
|
+
*/
|
|
18
|
+
export declare function patchDiffTargets(patchText: string, cwd: string): string[];
|
|
@@ -28,3 +28,27 @@ export function patchBackupTargets(patchText, cwd) {
|
|
|
28
28
|
// but be defensive — saveSnapshot is idempotent on identical content anyway).
|
|
29
29
|
return [...new Set(out)];
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Every filesystem destination an ApplyPatch can change. Unlike undo backup
|
|
33
|
+
* targets, turn diffs need absent paths too: additions and move destinations
|
|
34
|
+
* must be captured before execution to distinguish creation from overwrite.
|
|
35
|
+
* Moves are represented by their source and destination; the tracker compares
|
|
36
|
+
* each path's real before/after state rather than inferring a rename from text.
|
|
37
|
+
*/
|
|
38
|
+
export function patchDiffTargets(patchText, cwd) {
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = parsePatch(patchText, "lenient");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const out = [];
|
|
47
|
+
for (const hunk of parsed.hunks) {
|
|
48
|
+
out.push(resolvePath(cwd, hunk.path));
|
|
49
|
+
if (hunk.kind === "update" && hunk.movePath) {
|
|
50
|
+
out.push(resolvePath(cwd, hunk.movePath));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [...new Set(out)];
|
|
54
|
+
}
|
|
@@ -108,8 +108,10 @@ function parseOneHunk(lines, offset, lineNumber) {
|
|
|
108
108
|
}
|
|
109
109
|
const chunks = [];
|
|
110
110
|
while (cursor < lines.length) {
|
|
111
|
-
// Skip blank lines between chunks.
|
|
112
|
-
|
|
111
|
+
// Skip blank lines between chunks. The first chunk may omit `@@`, so a
|
|
112
|
+
// space-marked line there is whitespace context, not a separator.
|
|
113
|
+
const leadingContext = chunks.length === 0 && lines[cursor].startsWith(" ");
|
|
114
|
+
if (lines[cursor].trim() === "" && !leadingContext) {
|
|
113
115
|
cursor++;
|
|
114
116
|
consumed++;
|
|
115
117
|
continue;
|
|
@@ -5,7 +5,9 @@ export const checkQuotaToolDef = {
|
|
|
5
5
|
description: "Check remaining usage/rate-limit quota for the external coding-agent CLIs (Claude Code and/or " +
|
|
6
6
|
"Codex) — the same subscription windows their status lines show. Use before or during " +
|
|
7
7
|
"orchestration (DriveAgent) to plan how much work to hand off, whether to wait for a reset, or " +
|
|
8
|
-
"which provider to use. Returns each window's used-% and reset time.
|
|
8
|
+
"which provider to use. Returns each window's used-% and reset time. " +
|
|
9
|
+
"Codex labels each window by its reported duration (e.g. 5h, 7d; 'primary'/'secondary' if " +
|
|
10
|
+
"the duration is missing); " +
|
|
9
11
|
"Claude reports whichever windows apply to the account (5h/7d normally, 'overage' when the " +
|
|
10
12
|
"account is running on overage), and marks the currently binding window with '*'. " +
|
|
11
13
|
"COST: 'codex' is free (reads a usage endpoint). 'claude' costs ~1 token (Anthropic exposes " +
|
package/dist/tools/lsp.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { getLSPManager } from "../lsp/manager.js";
|
|
5
5
|
import { detectLSPServer } from "../lsp/servers.js";
|
|
6
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
7
|
import { isAbsolute, resolve } from "node:path";
|
|
8
8
|
/**
|
|
9
9
|
* How long to wait for `textDocument/publishDiagnostics` after opening a file.
|
|
@@ -19,6 +19,12 @@ const DIAGNOSTIC_SEVERITY = {
|
|
|
19
19
|
3: "info",
|
|
20
20
|
4: "hint",
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Documents this tool has opened on each client, with their last synced
|
|
24
|
+
* version. LSP allows one didOpen per document; later reads must sync the
|
|
25
|
+
* current disk text with didChange instead of reopening it.
|
|
26
|
+
*/
|
|
27
|
+
const openDocuments = new WeakMap();
|
|
22
28
|
export const lspToolDef = {
|
|
23
29
|
name: "LSP",
|
|
24
30
|
description: "Use Language Server Protocol for code intelligence operations. " +
|
|
@@ -134,9 +140,23 @@ export async function lspTool(args, ctx) {
|
|
|
134
140
|
};
|
|
135
141
|
const timer = setTimeout(() => finish(undefined), DIAGNOSTICS_TIMEOUT_MS);
|
|
136
142
|
client.on?.("notification", onNotification);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
let versions = openDocuments.get(client);
|
|
144
|
+
if (!versions)
|
|
145
|
+
openDocuments.set(client, (versions = new Map()));
|
|
146
|
+
const openVersion = versions.get(uri);
|
|
147
|
+
if (openVersion === undefined) {
|
|
148
|
+
versions.set(uri, 1);
|
|
149
|
+
void client.notify("textDocument/didOpen", {
|
|
150
|
+
textDocument: { uri, languageId: serverConfig.language, version: 1, text },
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
versions.set(uri, openVersion + 1);
|
|
155
|
+
void client.notify("textDocument/didChange", {
|
|
156
|
+
textDocument: { uri, version: openVersion + 1 },
|
|
157
|
+
contentChanges: [{ text }],
|
|
158
|
+
});
|
|
159
|
+
}
|
|
140
160
|
});
|
|
141
161
|
if (diagnostics === undefined) {
|
|
142
162
|
return `No diagnostics published within ${DIAGNOSTICS_TIMEOUT_MS}ms. The server may still be indexing.`;
|
|
@@ -184,12 +204,23 @@ function formatLocationResult(result, label) {
|
|
|
184
204
|
const lines = locations.map((loc) => {
|
|
185
205
|
const uri = loc.uri ?? loc.targetUri ?? "";
|
|
186
206
|
const range = loc.range ?? loc.targetRange ?? {};
|
|
187
|
-
const path = uri
|
|
207
|
+
const path = locationPath(uri);
|
|
188
208
|
const line = (range.start?.line ?? 0) + 1;
|
|
189
209
|
return ` ${path}:${line}`;
|
|
190
210
|
});
|
|
191
211
|
return `${label} (${locations.length}):\n${lines.join("\n")}`;
|
|
192
212
|
}
|
|
213
|
+
/** Decode a `file:` URI to a filesystem path; keep other URIs as reported. */
|
|
214
|
+
function locationPath(uri) {
|
|
215
|
+
if (!uri.startsWith("file:"))
|
|
216
|
+
return uri;
|
|
217
|
+
try {
|
|
218
|
+
return fileURLToPath(uri);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return uri;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
193
224
|
const SYMBOL_KINDS = {
|
|
194
225
|
1: "File",
|
|
195
226
|
2: "Module",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cjhyy/code-shell-capability-coding",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.47",
|
|
4
4
|
"description": "Coding capability pack for the generic code-shell agent core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@cjhyy/code-shell-core": "0.9.
|
|
42
|
+
"@cjhyy/code-shell-core": "0.9.47"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=20.10"
|