harness-wingman 0.4.3 → 0.5.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/apps/cli/dist/main.js +2475 -432
- package/apps/panel/dist/assets/index-B8Ew9Kx1.js +216 -0
- package/apps/panel/dist/assets/{index-AwMHEwTe.css → index-DWuwFRYW.css} +1 -1
- package/apps/panel/dist/index.html +2 -2
- package/package.json +2 -2
- package/apps/panel/dist/assets/index-CccFfpXM.js +0 -212
package/apps/cli/dist/main.js
CHANGED
|
@@ -55,15 +55,134 @@ var init_feedback_url = __esm({
|
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
+
// packages/core/dist/plan-protocol.js
|
|
59
|
+
import { createHash } from "node:crypto";
|
|
60
|
+
function planProtocolBlockStart(version = PLAN_PROTOCOL_VERSION) {
|
|
61
|
+
return `<!-- >>> wingman plan protocol v${version} >>> -->`;
|
|
62
|
+
}
|
|
63
|
+
function buildPlanProtocolBlock(version = PLAN_PROTOCOL_VERSION, text) {
|
|
64
|
+
let body = normalizeMarkdownLineEndings(text ?? KNOWN_PROTOCOL_TEXT.get(version) ?? "");
|
|
65
|
+
if (body === "")
|
|
66
|
+
throw new Error(`\u672A\u77E5 Plan Protocol \u7248\u672C v${version}\uFF0C\u4E14\u672A\u63D0\u4F9B\u6B63\u6587`);
|
|
67
|
+
if (!body.endsWith("\n"))
|
|
68
|
+
body += "\n";
|
|
69
|
+
return `${planProtocolBlockStart(version)}
|
|
70
|
+
${body}${PLAN_PROTOCOL_BLOCK_END}
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
function decodeUtf8Strict(bytes, sourcePath = "<memory>") {
|
|
74
|
+
try {
|
|
75
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error(`\u76EE\u6807\u6587\u4EF6\u4E0D\u662F\u4E25\u683C UTF-8(${sourcePath})`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function sha256Hex(contents) {
|
|
81
|
+
return createHash("sha256").update(contents).digest("hex");
|
|
82
|
+
}
|
|
83
|
+
function normalizeMarkdownLineEndings(text) {
|
|
84
|
+
return text.replaceAll("\r\n", "\n");
|
|
85
|
+
}
|
|
86
|
+
function preferredMarkdownLineEnding(text) {
|
|
87
|
+
const hasCrlf = text.includes("\r\n");
|
|
88
|
+
const hasBareLf = text.replaceAll("\r\n", "").includes("\n");
|
|
89
|
+
return hasCrlf && !hasBareLf ? "\r\n" : "\n";
|
|
90
|
+
}
|
|
91
|
+
function splitPlanProtocolBlock(text, sourcePath = "<memory>") {
|
|
92
|
+
const starts = [...text.matchAll(BLOCK_START)];
|
|
93
|
+
const ends = allIndexesOf(text, PLAN_PROTOCOL_BLOCK_END);
|
|
94
|
+
if (starts.length === 0 && ends.length === 0) {
|
|
95
|
+
return { before: text, after: "", remainder: text };
|
|
96
|
+
}
|
|
97
|
+
if (starts.length > 1 || ends.length > 1) {
|
|
98
|
+
throw new Error(`wingman plan protocol \u6807\u8BB0\u5757\u91CD\u590D(${sourcePath})`);
|
|
99
|
+
}
|
|
100
|
+
const start = starts[0];
|
|
101
|
+
const endMark = ends[0];
|
|
102
|
+
if (start === void 0 || endMark === void 0 || endMark < start.index) {
|
|
103
|
+
throw new Error(`wingman plan protocol \u6807\u8BB0\u5757\u6B8B\u7F3A(${sourcePath})`);
|
|
104
|
+
}
|
|
105
|
+
let end = endMark + PLAN_PROTOCOL_BLOCK_END.length;
|
|
106
|
+
if (text.startsWith("\r\n", end))
|
|
107
|
+
end += 2;
|
|
108
|
+
else if (text[end] === "\n")
|
|
109
|
+
end += 1;
|
|
110
|
+
const before = text.slice(0, start.index);
|
|
111
|
+
const after = text.slice(end);
|
|
112
|
+
return {
|
|
113
|
+
before,
|
|
114
|
+
after,
|
|
115
|
+
remainder: before + after,
|
|
116
|
+
block: text.slice(start.index, end),
|
|
117
|
+
version: Number(start[1])
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function inspectPlanProtocolBlock(block) {
|
|
121
|
+
const normalized = normalizeMarkdownLineEndings(block);
|
|
122
|
+
const split = splitPlanProtocolBlock(normalized);
|
|
123
|
+
const version = split.version;
|
|
124
|
+
const knownText = version === void 0 ? void 0 : KNOWN_PROTOCOL_TEXT.get(version);
|
|
125
|
+
const knownBlock = version === void 0 || knownText === void 0 ? void 0 : buildPlanProtocolBlock(version, knownText);
|
|
126
|
+
const known = split.before === "" && split.after === "" && normalized === knownBlock;
|
|
127
|
+
return {
|
|
128
|
+
kind: known && version === PLAN_PROTOCOL_VERSION ? "current" : known ? "legacy" : "modified",
|
|
129
|
+
...version !== void 0 ? { version } : {},
|
|
130
|
+
sha256: sha256Hex(normalized)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function inspectPlanProtocolFile(bytes, sourcePath = "<memory>") {
|
|
134
|
+
decodeUtf8Strict(bytes, sourcePath);
|
|
135
|
+
const sha256 = sha256Hex(bytes);
|
|
136
|
+
for (const [version, text] of KNOWN_PROTOCOL_TEXT) {
|
|
137
|
+
if (sha256 === sha256Hex(text)) {
|
|
138
|
+
return {
|
|
139
|
+
kind: version === PLAN_PROTOCOL_VERSION ? "current" : "legacy",
|
|
140
|
+
version,
|
|
141
|
+
sha256
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { kind: "modified", sha256 };
|
|
146
|
+
}
|
|
147
|
+
function allIndexesOf(text, needle) {
|
|
148
|
+
const indexes = [];
|
|
149
|
+
for (let at = text.indexOf(needle); at !== -1; at = text.indexOf(needle, at + needle.length)) {
|
|
150
|
+
indexes.push(at);
|
|
151
|
+
}
|
|
152
|
+
return indexes;
|
|
153
|
+
}
|
|
154
|
+
var PLAN_PROTOCOL_VERSION, PLAN_PROTOCOL_TEXT, PLAN_PROTOCOL_BLOCK_END, LEGACY_PLAN_PROTOCOL_TEXT, KNOWN_PROTOCOL_TEXT, BLOCK_START;
|
|
155
|
+
var init_plan_protocol = __esm({
|
|
156
|
+
"packages/core/dist/plan-protocol.js"() {
|
|
157
|
+
"use strict";
|
|
158
|
+
PLAN_PROTOCOL_VERSION = 1;
|
|
159
|
+
PLAN_PROTOCOL_TEXT = `Wingman plan protocol v1 \u2014 a behavioral convention, not a gate. Wingman only reads; it never replies to you.
|
|
160
|
+
- The project root is the directory that contains this instruction file (for Claude Code: the parent of \`.claude/\`).
|
|
161
|
+
- Before starting work, read \`<project root>/.wingman/plan.md\`. If it does not exist, create it: a \`# Title\` line, then a nested checkbox tree of steps, each step with an indented \`Verify:\` line describing how it will be checked.
|
|
162
|
+
- Mark the step you are working on \`[/]\` and add sub-steps under it as you learn more. Mark finished steps \`[x]\`, abandoned steps \`[-]\`, and steps that need the user's decision \`[?]\`.
|
|
163
|
+
- Re-read the file before every write; change only your own lines; never reorder or rewrite other entries. Use your file-editing tool, not shell redirection.
|
|
164
|
+
- Keep your in-session todo list as sub-steps of the \`[/]\` step; do not copy the whole file into it.
|
|
165
|
+
- Never put secrets, credentials, or personal data in the plan.
|
|
166
|
+
`;
|
|
167
|
+
PLAN_PROTOCOL_BLOCK_END = "<!-- <<< wingman plan protocol <<< -->";
|
|
168
|
+
LEGACY_PLAN_PROTOCOL_TEXT = "Wingman plan protocol v0 \u2014 legacy fixture text.\n";
|
|
169
|
+
KNOWN_PROTOCOL_TEXT = /* @__PURE__ */ new Map([
|
|
170
|
+
[0, LEGACY_PLAN_PROTOCOL_TEXT],
|
|
171
|
+
[PLAN_PROTOCOL_VERSION, PLAN_PROTOCOL_TEXT]
|
|
172
|
+
]);
|
|
173
|
+
BLOCK_START = /<!-- >>> wingman plan protocol v(\d+) >>> -->/gu;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
58
177
|
// packages/core/dist/refscan.js
|
|
59
178
|
function scanReferences(sources, name) {
|
|
60
179
|
if (name.length === 0)
|
|
61
180
|
return [];
|
|
62
181
|
const hits = [];
|
|
63
182
|
for (const source of sources) {
|
|
64
|
-
const
|
|
65
|
-
for (let i = 0; i <
|
|
66
|
-
const line =
|
|
183
|
+
const lines2 = source.text.split("\n");
|
|
184
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
185
|
+
const line = lines2[i] ?? "";
|
|
67
186
|
if (!lineMatches(line, name))
|
|
68
187
|
continue;
|
|
69
188
|
hits.push({
|
|
@@ -236,6 +355,7 @@ var init_dist = __esm({
|
|
|
236
355
|
"packages/core/dist/index.js"() {
|
|
237
356
|
"use strict";
|
|
238
357
|
init_feedback_url();
|
|
358
|
+
init_plan_protocol();
|
|
239
359
|
init_refscan();
|
|
240
360
|
SLOT_IDS = ["base", "rules", "skills", "tools", "gates", "history"];
|
|
241
361
|
HARNESS_IDS = ["cc", "codex", "dsh"];
|
|
@@ -263,7 +383,7 @@ var init_dist = __esm({
|
|
|
263
383
|
|
|
264
384
|
// packages/adapter-cc/dist/walk.js
|
|
265
385
|
import { readdirSync } from "node:fs";
|
|
266
|
-
import { join as
|
|
386
|
+
import { join as join4, parse, resolve } from "node:path";
|
|
267
387
|
function newWalkStats(entryBudget = MAX_WALK_ENTRIES) {
|
|
268
388
|
return { unknownErrors: 0, truncated: false, entriesLeft: entryBudget };
|
|
269
389
|
}
|
|
@@ -290,7 +410,7 @@ function walkFiles(dir, fileName, stats = newWalkStats()) {
|
|
|
290
410
|
return;
|
|
291
411
|
}
|
|
292
412
|
stats.entriesLeft -= 1;
|
|
293
|
-
const p =
|
|
413
|
+
const p = join4(d, entry.name);
|
|
294
414
|
if (entry.isDirectory()) {
|
|
295
415
|
if (entry.name.startsWith(".") || SKIP_DIR_NAMES.has(entry.name))
|
|
296
416
|
continue;
|
|
@@ -307,7 +427,7 @@ function walkFiles(dir, fileName, stats = newWalkStats()) {
|
|
|
307
427
|
return found;
|
|
308
428
|
}
|
|
309
429
|
function listMdFiles(dir, stats) {
|
|
310
|
-
return safeReaddir(dir, stats).map((e) => e.name).filter((n) => n.endsWith(".md")).sort().map((n) =>
|
|
430
|
+
return safeReaddir(dir, stats).map((e) => e.name).filter((n) => n.endsWith(".md")).sort().map((n) => join4(dir, n));
|
|
311
431
|
}
|
|
312
432
|
function isBareProjectDir(projectDir, homeDir) {
|
|
313
433
|
const r = resolve(projectDir);
|
|
@@ -327,19 +447,19 @@ var init_walk = __esm({
|
|
|
327
447
|
// packages/adapter-cc/dist/detect.js
|
|
328
448
|
import { existsSync as existsSync2, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
329
449
|
import { homedir as homedir3 } from "node:os";
|
|
330
|
-
import { join as
|
|
450
|
+
import { join as join5 } from "node:path";
|
|
331
451
|
function probeVersion(homeDir, stats = newWalkStats()) {
|
|
332
|
-
const root =
|
|
452
|
+
const root = join5(homeDir, ".claude", "projects");
|
|
333
453
|
let latest;
|
|
334
454
|
for (const entry of safeReaddir(root, stats)) {
|
|
335
455
|
if (!entry.isDirectory())
|
|
336
456
|
continue;
|
|
337
|
-
const dir =
|
|
457
|
+
const dir = join5(root, entry.name);
|
|
338
458
|
for (const sub of safeReaddir(dir, stats)) {
|
|
339
459
|
const name = sub.name;
|
|
340
460
|
if (!name.endsWith(".jsonl"))
|
|
341
461
|
continue;
|
|
342
|
-
const file =
|
|
462
|
+
const file = join5(dir, name);
|
|
343
463
|
let mtimeMs;
|
|
344
464
|
try {
|
|
345
465
|
mtimeMs = statSync(file).mtimeMs;
|
|
@@ -353,15 +473,15 @@ function probeVersion(homeDir, stats = newWalkStats()) {
|
|
|
353
473
|
}
|
|
354
474
|
if (!latest)
|
|
355
475
|
return void 0;
|
|
356
|
-
let
|
|
476
|
+
let lines2;
|
|
357
477
|
try {
|
|
358
|
-
|
|
478
|
+
lines2 = readFileSync3(latest.file, "utf8").split("\n");
|
|
359
479
|
} catch (error) {
|
|
360
480
|
recordWalkError(error, stats);
|
|
361
481
|
return void 0;
|
|
362
482
|
}
|
|
363
|
-
for (let i =
|
|
364
|
-
const line =
|
|
483
|
+
for (let i = lines2.length - 1; i >= 0; i--) {
|
|
484
|
+
const line = lines2[i];
|
|
365
485
|
if (!line)
|
|
366
486
|
continue;
|
|
367
487
|
try {
|
|
@@ -375,7 +495,7 @@ function probeVersion(homeDir, stats = newWalkStats()) {
|
|
|
375
495
|
}
|
|
376
496
|
async function detectCc(options) {
|
|
377
497
|
const homeDir = options?.homeDir ?? homedir3();
|
|
378
|
-
const installed = existsSync2(
|
|
498
|
+
const installed = existsSync2(join5(homeDir, ".claude")) || existsSync2(join5(homeDir, ".claude.json"));
|
|
379
499
|
if (!installed)
|
|
380
500
|
return { installed: false };
|
|
381
501
|
const version = probeVersion(homeDir);
|
|
@@ -392,7 +512,7 @@ var init_detect = __esm({
|
|
|
392
512
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
393
513
|
import { existsSync as existsSync3, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
|
|
394
514
|
import { homedir as homedir4 } from "node:os";
|
|
395
|
-
import { basename, dirname as dirname3, join as
|
|
515
|
+
import { basename, dirname as dirname3, join as join6, resolve as resolve2, sep } from "node:path";
|
|
396
516
|
function readJsonFile(file) {
|
|
397
517
|
const text = readFileSync4(file, "utf8");
|
|
398
518
|
try {
|
|
@@ -410,9 +530,9 @@ function recordField(v, key) {
|
|
|
410
530
|
}
|
|
411
531
|
function readSettingsChain(homeDir, projectDir) {
|
|
412
532
|
const chain = [
|
|
413
|
-
{ file:
|
|
414
|
-
{ file:
|
|
415
|
-
{ file:
|
|
533
|
+
{ file: join6(homeDir, ".claude", "settings.json"), source: "user" },
|
|
534
|
+
{ file: join6(projectDir, ".claude", "settings.json"), source: "project" },
|
|
535
|
+
{ file: join6(projectDir, ".claude", "settings.local.json"), source: "local" }
|
|
416
536
|
];
|
|
417
537
|
const out = [];
|
|
418
538
|
for (const { file, source } of chain) {
|
|
@@ -479,20 +599,20 @@ function buildRules(homeDir, projectDir, stats) {
|
|
|
479
599
|
if (item !== void 0)
|
|
480
600
|
items.push(item);
|
|
481
601
|
};
|
|
482
|
-
const userClaudeMd =
|
|
602
|
+
const userClaudeMd = join6(homeDir, ".claude", "CLAUDE.md");
|
|
483
603
|
if (existsSync3(userClaudeMd))
|
|
484
604
|
append(userClaudeMd, "user");
|
|
485
|
-
for (const f of listMdFiles(
|
|
605
|
+
for (const f of listMdFiles(join6(homeDir, ".claude", "rules"), stats))
|
|
486
606
|
append(f, "user");
|
|
487
607
|
if (isBareProjectDir(projectDir, homeDir)) {
|
|
488
|
-
const direct =
|
|
608
|
+
const direct = join6(projectDir, "CLAUDE.md");
|
|
489
609
|
if (existsSync3(direct))
|
|
490
610
|
append(direct, "project");
|
|
491
611
|
} else {
|
|
492
612
|
for (const f of walkFiles(projectDir, "CLAUDE.md", stats))
|
|
493
613
|
append(f, "project");
|
|
494
614
|
}
|
|
495
|
-
for (const f of listMdFiles(
|
|
615
|
+
for (const f of listMdFiles(join6(projectDir, ".claude", "rules"), stats)) {
|
|
496
616
|
append(f, "project");
|
|
497
617
|
}
|
|
498
618
|
return items;
|
|
@@ -515,14 +635,14 @@ function parseSkillFrontmatter(content) {
|
|
|
515
635
|
return out;
|
|
516
636
|
}
|
|
517
637
|
function buildSkills(homeDir, projectDir, overrides, stats) {
|
|
518
|
-
const userRoot =
|
|
519
|
-
const syncedRoot =
|
|
638
|
+
const userRoot = join6(homeDir, ".claude", "skills");
|
|
639
|
+
const syncedRoot = join6(userRoot, "synced");
|
|
520
640
|
const roots = [
|
|
521
641
|
{ root: userRoot, source: "user", exclude: syncedRoot },
|
|
522
642
|
// projectDir 为家目录时,项目 skills 根与 user 根是同一目录,再走一遍会把每个
|
|
523
643
|
// skill 重复列为 project 层;bare root 无项目上下文 → 不设项目 skills 根
|
|
524
|
-
...isBareProjectDir(projectDir, homeDir) ? [] : [{ root:
|
|
525
|
-
{ root:
|
|
644
|
+
...isBareProjectDir(projectDir, homeDir) ? [] : [{ root: join6(projectDir, ".claude", "skills"), source: "project" }],
|
|
645
|
+
{ root: join6(homeDir, ".claude", "plugins"), source: "plugin" },
|
|
526
646
|
{ root: syncedRoot, source: "synced" }
|
|
527
647
|
];
|
|
528
648
|
const items = [];
|
|
@@ -556,7 +676,7 @@ function buildSkills(homeDir, projectDir, overrides, stats) {
|
|
|
556
676
|
}
|
|
557
677
|
function buildTools(homeDir, projectDir) {
|
|
558
678
|
const items = [];
|
|
559
|
-
const claudeJsonPath =
|
|
679
|
+
const claudeJsonPath = join6(homeDir, ".claude.json");
|
|
560
680
|
let claudeJson;
|
|
561
681
|
if (existsSync3(claudeJsonPath))
|
|
562
682
|
claudeJson = asRecord(readJsonFile(claudeJsonPath));
|
|
@@ -582,7 +702,7 @@ function buildTools(homeDir, projectDir) {
|
|
|
582
702
|
for (const name of Object.keys(localServers).sort()) {
|
|
583
703
|
items.push(mcpItem(name, "local", claudeJsonPath));
|
|
584
704
|
}
|
|
585
|
-
const mcpJsonPath =
|
|
705
|
+
const mcpJsonPath = join6(projectDir, ".mcp.json");
|
|
586
706
|
if (existsSync3(mcpJsonPath)) {
|
|
587
707
|
const projectServers = recordField(readJsonFile(mcpJsonPath), "mcpServers") ?? {};
|
|
588
708
|
for (const name of Object.keys(projectServers).sort()) {
|
|
@@ -657,10 +777,10 @@ function lastContextTotal(file, stats) {
|
|
|
657
777
|
return total;
|
|
658
778
|
}
|
|
659
779
|
function buildHistory(homeDir, projectDir, stats) {
|
|
660
|
-
const dir =
|
|
780
|
+
const dir = join6(homeDir, ".claude", "projects", projectSlug(projectDir));
|
|
661
781
|
const items = [];
|
|
662
782
|
for (const name of safeReaddir(dir, stats).map((e) => e.name).filter((n) => n.endsWith(".jsonl")).sort()) {
|
|
663
|
-
const file =
|
|
783
|
+
const file = join6(dir, name);
|
|
664
784
|
const total = lastContextTotal(file, stats);
|
|
665
785
|
if (total === void 0)
|
|
666
786
|
continue;
|
|
@@ -773,7 +893,7 @@ var init_scan = __esm({
|
|
|
773
893
|
// packages/adapter-cc/dist/actions.js
|
|
774
894
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
775
895
|
import { homedir as homedir5 } from "node:os";
|
|
776
|
-
import { basename as basename2, dirname as dirname4, join as
|
|
896
|
+
import { basename as basename2, dirname as dirname4, join as join7, resolve as resolve3 } from "node:path";
|
|
777
897
|
function asRecord2(v) {
|
|
778
898
|
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
|
|
779
899
|
}
|
|
@@ -797,13 +917,13 @@ function deepEqual(a, b) {
|
|
|
797
917
|
}
|
|
798
918
|
function backupDirPath(homeDir) {
|
|
799
919
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
800
|
-
const root =
|
|
801
|
-
let dir =
|
|
920
|
+
const root = join7(homeDir, ".wingman", "backups");
|
|
921
|
+
let dir = join7(root, ts);
|
|
802
922
|
for (let n = 2; existsSync4(dir); n++)
|
|
803
|
-
dir =
|
|
923
|
+
dir = join7(root, `${ts}-${n}`);
|
|
804
924
|
return dir;
|
|
805
925
|
}
|
|
806
|
-
function
|
|
926
|
+
function decodeUtf8Strict2(buffer, file) {
|
|
807
927
|
const text = buffer.toString("utf8");
|
|
808
928
|
if (!buffer.equals(Buffer.from(text, "utf8"))) {
|
|
809
929
|
throw new Error(`\u62D2\u5199:${file} \u4E0D\u662F UTF-8 \u6587\u672C,\u672A\u77E5\u683C\u5F0F\u4E0D\u731C`);
|
|
@@ -812,15 +932,15 @@ function decodeUtf8Strict(buffer, file) {
|
|
|
812
932
|
}
|
|
813
933
|
function readSettingsSnapshots(homeDir, projectDir) {
|
|
814
934
|
const files = [
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
935
|
+
join7(homeDir, ".claude", "settings.json"),
|
|
936
|
+
join7(projectDir, ".claude", "settings.json"),
|
|
937
|
+
join7(projectDir, ".claude", "settings.local.json")
|
|
818
938
|
];
|
|
819
939
|
const out = [];
|
|
820
940
|
for (const file of files) {
|
|
821
941
|
if (!existsSync4(file))
|
|
822
942
|
continue;
|
|
823
|
-
const raw =
|
|
943
|
+
const raw = decodeUtf8Strict2(readFileSync5(file), file);
|
|
824
944
|
let parsed;
|
|
825
945
|
try {
|
|
826
946
|
parsed = JSON.parse(raw);
|
|
@@ -843,7 +963,7 @@ function overridesRecord(s) {
|
|
|
843
963
|
return rec;
|
|
844
964
|
}
|
|
845
965
|
function skillTargetFile(item, homeDir, projectDir) {
|
|
846
|
-
return item.source === "project" ?
|
|
966
|
+
return item.source === "project" ? join7(projectDir, ".claude", "settings.json") : join7(homeDir, ".claude", "settings.json");
|
|
847
967
|
}
|
|
848
968
|
function planSkillToggle(item, enable, homeDir, projectDir) {
|
|
849
969
|
const name = item.nativeName;
|
|
@@ -896,7 +1016,7 @@ function planSkillToggle(item, enable, homeDir, projectDir) {
|
|
|
896
1016
|
});
|
|
897
1017
|
}
|
|
898
1018
|
function planMcpToggle(name, enable, homeDir, projectDir) {
|
|
899
|
-
const file =
|
|
1019
|
+
const file = join7(homeDir, ".claude.json");
|
|
900
1020
|
if (!existsSync4(file)) {
|
|
901
1021
|
throw new Error(`\u62D2\u7EDD:\u627E\u4E0D\u5230 ${file}(MCP \u8FDE\u63A5\u5F00\u5173\u6309\u9879\u76EE\u5B58\u653E\u4E8E\u8BE5\u6587\u4EF6)`);
|
|
902
1022
|
}
|
|
@@ -978,20 +1098,20 @@ function collectRefSources(homeDir, projectDir, exclude) {
|
|
|
978
1098
|
push(file, readFileSync5(file, "utf8"));
|
|
979
1099
|
};
|
|
980
1100
|
const bareRoot = isBareProjectDir(projectDir, homeDir);
|
|
981
|
-
pushFile(
|
|
982
|
-
for (const f of listMdFiles(
|
|
1101
|
+
pushFile(join7(homeDir, ".claude", "CLAUDE.md"));
|
|
1102
|
+
for (const f of listMdFiles(join7(homeDir, ".claude", "rules")))
|
|
983
1103
|
pushFile(f);
|
|
984
1104
|
if (bareRoot)
|
|
985
|
-
pushFile(
|
|
1105
|
+
pushFile(join7(projectDir, "CLAUDE.md"));
|
|
986
1106
|
else
|
|
987
1107
|
for (const f of walkFiles(projectDir, "CLAUDE.md"))
|
|
988
1108
|
pushFile(f);
|
|
989
|
-
for (const f of listMdFiles(
|
|
1109
|
+
for (const f of listMdFiles(join7(projectDir, ".claude", "rules")))
|
|
990
1110
|
pushFile(f);
|
|
991
1111
|
const skillRoots = [
|
|
992
|
-
|
|
993
|
-
...bareRoot ? [] : [
|
|
994
|
-
|
|
1112
|
+
join7(homeDir, ".claude", "skills"),
|
|
1113
|
+
...bareRoot ? [] : [join7(projectDir, ".claude", "skills")],
|
|
1114
|
+
join7(homeDir, ".claude", "plugins")
|
|
995
1115
|
];
|
|
996
1116
|
for (const root of skillRoots) {
|
|
997
1117
|
for (const f of walkFiles(root, "SKILL.md")) {
|
|
@@ -1000,17 +1120,17 @@ function collectRefSources(homeDir, projectDir, exclude) {
|
|
|
1000
1120
|
}
|
|
1001
1121
|
}
|
|
1002
1122
|
for (const dir of [
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1123
|
+
join7(homeDir, ".claude", "agents"),
|
|
1124
|
+
join7(projectDir, ".claude", "agents"),
|
|
1125
|
+
join7(homeDir, ".claude", "commands"),
|
|
1126
|
+
join7(projectDir, ".claude", "commands")
|
|
1007
1127
|
]) {
|
|
1008
1128
|
for (const f of listMdFiles(dir))
|
|
1009
1129
|
pushFile(f);
|
|
1010
1130
|
}
|
|
1011
1131
|
for (const s of readSettingsSnapshots(homeDir, projectDir))
|
|
1012
1132
|
push(s.file, s.raw);
|
|
1013
|
-
for (const file of [
|
|
1133
|
+
for (const file of [join7(projectDir, ".mcp.json"), join7(homeDir, ".claude.json")]) {
|
|
1014
1134
|
if (seen.has(file) || !existsSync4(file))
|
|
1015
1135
|
continue;
|
|
1016
1136
|
const raw = readFileSync5(file, "utf8");
|
|
@@ -1141,9 +1261,9 @@ async function applyActionCc(plan, _options) {
|
|
|
1141
1261
|
target: w.file
|
|
1142
1262
|
}));
|
|
1143
1263
|
toBackup.forEach((w, i) => {
|
|
1144
|
-
writeFileSync3(
|
|
1264
|
+
writeFileSync3(join7(plan.backupDir, `${i}-${basename2(w.file)}`), currentByFile.get(w.file));
|
|
1145
1265
|
});
|
|
1146
|
-
writeFileSync3(
|
|
1266
|
+
writeFileSync3(join7(plan.backupDir, "manifest.json"), serialize({
|
|
1147
1267
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1148
1268
|
harness: "cc",
|
|
1149
1269
|
action: plan.action,
|
|
@@ -1209,7 +1329,7 @@ var init_actions = __esm({
|
|
|
1209
1329
|
// packages/adapter-cc/dist/hooks.js
|
|
1210
1330
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1211
1331
|
import { homedir as homedir6 } from "node:os";
|
|
1212
|
-
import { dirname as dirname5, join as
|
|
1332
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
1213
1333
|
function wingmanUrl(port) {
|
|
1214
1334
|
return `http://127.0.0.1:${port}${SIGNATURE}`;
|
|
1215
1335
|
}
|
|
@@ -1221,10 +1341,10 @@ function isWingmanHandler(h) {
|
|
|
1221
1341
|
return rec?.type === "http" && typeof rec.url === "string" && rec.url.includes(SIGNATURE);
|
|
1222
1342
|
}
|
|
1223
1343
|
function wingmanRoot(homeDir) {
|
|
1224
|
-
return
|
|
1344
|
+
return join8(homeDir, ".wingman");
|
|
1225
1345
|
}
|
|
1226
1346
|
function statePath(homeDir) {
|
|
1227
|
-
return
|
|
1347
|
+
return join8(wingmanRoot(homeDir), "state", "cc-hooks.json");
|
|
1228
1348
|
}
|
|
1229
1349
|
function readState(homeDir) {
|
|
1230
1350
|
const file = statePath(homeDir);
|
|
@@ -1259,10 +1379,10 @@ function writeState(homeDir, state) {
|
|
|
1259
1379
|
}
|
|
1260
1380
|
function makeBackupDir(homeDir) {
|
|
1261
1381
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
1262
|
-
const root =
|
|
1263
|
-
let dir =
|
|
1382
|
+
const root = join8(wingmanRoot(homeDir), "backups");
|
|
1383
|
+
let dir = join8(root, ts);
|
|
1264
1384
|
for (let n = 2; existsSync5(dir); n++)
|
|
1265
|
-
dir =
|
|
1385
|
+
dir = join8(root, `${ts}-${n}`);
|
|
1266
1386
|
mkdirSync3(dir, { recursive: true });
|
|
1267
1387
|
return dir;
|
|
1268
1388
|
}
|
|
@@ -1372,7 +1492,7 @@ function deepEqual2(a, b) {
|
|
|
1372
1492
|
return false;
|
|
1373
1493
|
}
|
|
1374
1494
|
function targetPath(homeDir) {
|
|
1375
|
-
return
|
|
1495
|
+
return join8(homeDir, ".claude", "settings.json");
|
|
1376
1496
|
}
|
|
1377
1497
|
async function installHooksCc(port, options) {
|
|
1378
1498
|
const homeDir = options?.homeDir ?? homedir6();
|
|
@@ -1394,7 +1514,7 @@ async function installHooksCc(port, options) {
|
|
|
1394
1514
|
}
|
|
1395
1515
|
const backupDir = makeBackupDir(homeDir);
|
|
1396
1516
|
if (bytes !== null)
|
|
1397
|
-
writeFileSync4(
|
|
1517
|
+
writeFileSync4(join8(backupDir, "settings.json"), bytes);
|
|
1398
1518
|
const prev = readState(homeDir);
|
|
1399
1519
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1400
1520
|
writeState(homeDir, prev ? { ...prev, port, installedAt: now } : { target, original: bytes, backupDir, port, installedAt: now });
|
|
@@ -1426,7 +1546,7 @@ async function uninstallHooksCc(options) {
|
|
|
1426
1546
|
}
|
|
1427
1547
|
const backupDir = makeBackupDir(homeDir);
|
|
1428
1548
|
if (bytes !== null)
|
|
1429
|
-
writeFileSync4(
|
|
1549
|
+
writeFileSync4(join8(backupDir, "settings.json"), bytes);
|
|
1430
1550
|
if (state && state.original !== null) {
|
|
1431
1551
|
const originalParsed = asRecord3(JSON.parse(state.original));
|
|
1432
1552
|
if (originalParsed && deepEqual2(normalizeForCompare(settings), normalizeForCompare(originalParsed))) {
|
|
@@ -1463,12 +1583,217 @@ var init_hooks = __esm({
|
|
|
1463
1583
|
}
|
|
1464
1584
|
});
|
|
1465
1585
|
|
|
1586
|
+
// packages/adapter-cc/dist/protocol.js
|
|
1587
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync7, rmdirSync, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1588
|
+
import { homedir as homedir7 } from "node:os";
|
|
1589
|
+
import { basename as basename3, dirname as dirname6, join as join9, resolve as resolve4 } from "node:path";
|
|
1590
|
+
function protocolStatusCc(options) {
|
|
1591
|
+
const paths = resolveOptions(options);
|
|
1592
|
+
if (!existsSync6(paths.targetPath)) {
|
|
1593
|
+
return { harness: "cc", state: "not_installed", targetPath: paths.targetPath };
|
|
1594
|
+
}
|
|
1595
|
+
try {
|
|
1596
|
+
return statusFromIdentity(paths.targetPath, inspectPlanProtocolFile(readFileSync7(paths.targetPath), paths.targetPath));
|
|
1597
|
+
} catch (error) {
|
|
1598
|
+
return {
|
|
1599
|
+
harness: "cc",
|
|
1600
|
+
state: "invalid",
|
|
1601
|
+
targetPath: paths.targetPath,
|
|
1602
|
+
reason: error.message
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
function installProtocolCc(options) {
|
|
1607
|
+
const paths = resolveOptions(options);
|
|
1608
|
+
const existing = readExisting(paths.targetPath);
|
|
1609
|
+
const previousState = stateForIdentity(existing?.identity);
|
|
1610
|
+
if (previousState === "installed") {
|
|
1611
|
+
return result(paths.targetPath, "install", false, previousState, existing?.text ?? "", true);
|
|
1612
|
+
}
|
|
1613
|
+
if (previousState === "modified")
|
|
1614
|
+
throw modifiedError(paths.targetPath, "\u5B89\u88C5");
|
|
1615
|
+
const before = existing?.text ?? "";
|
|
1616
|
+
const backupDir = nextBackupDir(paths.homeDir);
|
|
1617
|
+
const installedSha256 = sha256Hex(PLAN_PROTOCOL_TEXT);
|
|
1618
|
+
if (!paths.dryRun) {
|
|
1619
|
+
const claudeDir = join9(paths.projectDir, ".claude");
|
|
1620
|
+
const rulesDir = dirname6(paths.targetPath);
|
|
1621
|
+
const prior = readState2(paths.statePath);
|
|
1622
|
+
backupTarget(backupDir, paths.targetPath, existing?.bytes);
|
|
1623
|
+
writeState2(paths.statePath, {
|
|
1624
|
+
version: 1,
|
|
1625
|
+
harness: "cc",
|
|
1626
|
+
projectDir: paths.projectDir,
|
|
1627
|
+
targetPath: paths.targetPath,
|
|
1628
|
+
protocolVersion: PLAN_PROTOCOL_VERSION,
|
|
1629
|
+
installedSha256,
|
|
1630
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1631
|
+
backupDir,
|
|
1632
|
+
original: prior?.original ?? (existing === void 0 ? { existed: false } : { existed: true, base64: existing.bytes.toString("base64") }),
|
|
1633
|
+
createdClaudeDir: prior?.createdClaudeDir ?? !existsSync6(claudeDir),
|
|
1634
|
+
createdRulesDir: prior?.createdRulesDir ?? !existsSync6(rulesDir)
|
|
1635
|
+
});
|
|
1636
|
+
mkdirSync4(rulesDir, { recursive: true });
|
|
1637
|
+
writeFileSync5(paths.targetPath, PLAN_PROTOCOL_TEXT);
|
|
1638
|
+
}
|
|
1639
|
+
return {
|
|
1640
|
+
...result(paths.targetPath, "install", true, previousState, before, true),
|
|
1641
|
+
state: "installed",
|
|
1642
|
+
version: PLAN_PROTOCOL_VERSION,
|
|
1643
|
+
sha256: installedSha256,
|
|
1644
|
+
after: PLAN_PROTOCOL_TEXT,
|
|
1645
|
+
backupDir,
|
|
1646
|
+
totalBytes: Buffer.byteLength(PLAN_PROTOCOL_TEXT)
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
function uninstallProtocolCc(options) {
|
|
1650
|
+
const paths = resolveOptions(options);
|
|
1651
|
+
const existing = readExisting(paths.targetPath);
|
|
1652
|
+
if (existing === void 0) {
|
|
1653
|
+
return result(paths.targetPath, "uninstall", false, "not_installed", "", false);
|
|
1654
|
+
}
|
|
1655
|
+
const previousState = stateForIdentity(existing.identity);
|
|
1656
|
+
if (previousState === "modified")
|
|
1657
|
+
throw modifiedError(paths.targetPath, "\u5378\u8F7D");
|
|
1658
|
+
const state = readState2(paths.statePath);
|
|
1659
|
+
const restored = restoredOriginal(state);
|
|
1660
|
+
const after = restored?.toString("utf8") ?? "";
|
|
1661
|
+
const afterExists = restored !== void 0;
|
|
1662
|
+
const backupDir = nextBackupDir(paths.homeDir);
|
|
1663
|
+
if (!paths.dryRun) {
|
|
1664
|
+
backupTarget(backupDir, paths.targetPath, existing.bytes);
|
|
1665
|
+
if (restored === void 0)
|
|
1666
|
+
rmSync3(paths.targetPath, { force: true });
|
|
1667
|
+
else
|
|
1668
|
+
writeFileSync5(paths.targetPath, restored);
|
|
1669
|
+
if (restored === void 0)
|
|
1670
|
+
removeCreatedEmptyDirs(paths, state);
|
|
1671
|
+
rmSync3(paths.statePath, { force: true });
|
|
1672
|
+
}
|
|
1673
|
+
return {
|
|
1674
|
+
...result(paths.targetPath, "uninstall", true, previousState, existing.text, afterExists),
|
|
1675
|
+
state: "not_installed",
|
|
1676
|
+
after,
|
|
1677
|
+
backupDir,
|
|
1678
|
+
totalBytes: restored?.byteLength ?? 0
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
function resolveOptions(options) {
|
|
1682
|
+
const homeDir = resolve4(options?.homeDir ?? homedir7());
|
|
1683
|
+
const projectDir = resolve4(options?.projectDir ?? process.cwd());
|
|
1684
|
+
const targetPath2 = join9(projectDir, ".claude", "rules", "wingman-plan-protocol.md");
|
|
1685
|
+
const projectHash = sha256Hex(projectDir);
|
|
1686
|
+
return {
|
|
1687
|
+
homeDir,
|
|
1688
|
+
projectDir,
|
|
1689
|
+
dryRun: options?.dryRun ?? false,
|
|
1690
|
+
targetPath: targetPath2,
|
|
1691
|
+
statePath: join9(homeDir, ".wingman", "state", `plan-protocol-cc-${projectHash}.json`)
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
function readExisting(targetPath2) {
|
|
1695
|
+
if (!existsSync6(targetPath2))
|
|
1696
|
+
return void 0;
|
|
1697
|
+
const bytes = readFileSync7(targetPath2);
|
|
1698
|
+
const identity = inspectPlanProtocolFile(bytes, targetPath2);
|
|
1699
|
+
return { bytes, text: bytes.toString("utf8"), identity };
|
|
1700
|
+
}
|
|
1701
|
+
function stateForIdentity(identity) {
|
|
1702
|
+
if (identity === void 0)
|
|
1703
|
+
return "not_installed";
|
|
1704
|
+
if (identity.kind === "current")
|
|
1705
|
+
return "installed";
|
|
1706
|
+
if (identity.kind === "legacy")
|
|
1707
|
+
return "outdated";
|
|
1708
|
+
return "modified";
|
|
1709
|
+
}
|
|
1710
|
+
function statusFromIdentity(targetPath2, identity) {
|
|
1711
|
+
return {
|
|
1712
|
+
harness: "cc",
|
|
1713
|
+
state: stateForIdentity(identity),
|
|
1714
|
+
targetPath: targetPath2,
|
|
1715
|
+
...identity.version !== void 0 ? { version: identity.version } : {},
|
|
1716
|
+
sha256: identity.sha256,
|
|
1717
|
+
...identity.kind === "modified" ? { reason: "\u53D7\u7BA1\u534F\u8BAE\u6587\u4EF6\u5DF2\u88AB\u4FEE\u6539" } : {}
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
function result(targetPath2, operation, changed, previousState, before, afterExists) {
|
|
1721
|
+
return {
|
|
1722
|
+
harness: "cc",
|
|
1723
|
+
operation,
|
|
1724
|
+
changed,
|
|
1725
|
+
previousState,
|
|
1726
|
+
state: previousState,
|
|
1727
|
+
targetPath: targetPath2,
|
|
1728
|
+
before,
|
|
1729
|
+
after: before,
|
|
1730
|
+
afterExists,
|
|
1731
|
+
effectiveAt: "next_session",
|
|
1732
|
+
totalBytes: Buffer.byteLength(before)
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
function modifiedError(targetPath2, operation) {
|
|
1736
|
+
return new Error(`\u53D7\u7BA1\u534F\u8BAE\u6587\u4EF6\u5DF2\u88AB\u4FEE\u6539(${targetPath2})\uFF0C\u62D2\u7EDD${operation}\u5E76\u4FDD\u7559\u7528\u6237\u5185\u5BB9`);
|
|
1737
|
+
}
|
|
1738
|
+
function nextBackupDir(homeDir) {
|
|
1739
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
1740
|
+
const root = join9(homeDir, ".wingman", "backups");
|
|
1741
|
+
let dir = join9(root, stamp);
|
|
1742
|
+
for (let n = 2; existsSync6(dir); n += 1)
|
|
1743
|
+
dir = join9(root, `${stamp}-${n}`);
|
|
1744
|
+
return dir;
|
|
1745
|
+
}
|
|
1746
|
+
function backupTarget(backupDir, targetPath2, bytes) {
|
|
1747
|
+
mkdirSync4(backupDir, { recursive: true });
|
|
1748
|
+
if (bytes !== void 0)
|
|
1749
|
+
writeFileSync5(join9(backupDir, basename3(targetPath2)), bytes);
|
|
1750
|
+
}
|
|
1751
|
+
function readState2(statePath2) {
|
|
1752
|
+
if (!existsSync6(statePath2))
|
|
1753
|
+
return void 0;
|
|
1754
|
+
try {
|
|
1755
|
+
const state = JSON.parse(readFileSync7(statePath2, "utf8"));
|
|
1756
|
+
return state.version === 1 && state.harness === "cc" && typeof state.targetPath === "string" ? state : void 0;
|
|
1757
|
+
} catch {
|
|
1758
|
+
return void 0;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
function writeState2(statePath2, state) {
|
|
1762
|
+
mkdirSync4(dirname6(statePath2), { recursive: true });
|
|
1763
|
+
writeFileSync5(statePath2, `${JSON.stringify(state, null, 2)}
|
|
1764
|
+
`);
|
|
1765
|
+
}
|
|
1766
|
+
function restoredOriginal(state) {
|
|
1767
|
+
if (state?.original.existed !== true)
|
|
1768
|
+
return void 0;
|
|
1769
|
+
return Buffer.from(state.original.base64 ?? "", "base64");
|
|
1770
|
+
}
|
|
1771
|
+
function removeCreatedEmptyDirs(paths, state) {
|
|
1772
|
+
const rulesDir = dirname6(paths.targetPath);
|
|
1773
|
+
const claudeDir = dirname6(rulesDir);
|
|
1774
|
+
if (state?.createdRulesDir && existsSync6(rulesDir) && readdirSync2(rulesDir).length === 0) {
|
|
1775
|
+
rmdirSync(rulesDir);
|
|
1776
|
+
}
|
|
1777
|
+
if (state?.createdClaudeDir && existsSync6(claudeDir) && readdirSync2(claudeDir).length === 0) {
|
|
1778
|
+
rmdirSync(claudeDir);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
var init_protocol = __esm({
|
|
1782
|
+
"packages/adapter-cc/dist/protocol.js"() {
|
|
1783
|
+
"use strict";
|
|
1784
|
+
init_dist();
|
|
1785
|
+
}
|
|
1786
|
+
});
|
|
1787
|
+
|
|
1466
1788
|
// packages/adapter-cc/dist/index.js
|
|
1467
1789
|
var dist_exports = {};
|
|
1468
1790
|
__export(dist_exports, {
|
|
1469
1791
|
WIRED_EVENTS: () => WIRED_EVENTS,
|
|
1470
1792
|
createAdapter: () => createAdapter,
|
|
1471
|
-
feedbackSignals: () => feedbackSignalsCc
|
|
1793
|
+
feedbackSignals: () => feedbackSignalsCc,
|
|
1794
|
+
installProtocolCc: () => installProtocolCc,
|
|
1795
|
+
protocolStatusCc: () => protocolStatusCc,
|
|
1796
|
+
uninstallProtocolCc: () => uninstallProtocolCc
|
|
1472
1797
|
});
|
|
1473
1798
|
function createAdapter(_options) {
|
|
1474
1799
|
return {
|
|
@@ -1489,6 +1814,7 @@ var init_dist2 = __esm({
|
|
|
1489
1814
|
init_hooks();
|
|
1490
1815
|
init_scan();
|
|
1491
1816
|
init_hooks();
|
|
1817
|
+
init_protocol();
|
|
1492
1818
|
init_scan();
|
|
1493
1819
|
}
|
|
1494
1820
|
});
|
|
@@ -1592,15 +1918,15 @@ var init_date = __esm({
|
|
|
1592
1918
|
|
|
1593
1919
|
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/error.js
|
|
1594
1920
|
function getLineColFromPtr(string, ptr) {
|
|
1595
|
-
let
|
|
1596
|
-
return [
|
|
1921
|
+
let lines2 = string.slice(0, ptr).split(/\r\n|\n|\r/g);
|
|
1922
|
+
return [lines2.length, lines2.pop().length + 1];
|
|
1597
1923
|
}
|
|
1598
1924
|
function makeCodeBlock(string, line, column) {
|
|
1599
|
-
let
|
|
1925
|
+
let lines2 = string.split(/\r\n|\n|\r/g);
|
|
1600
1926
|
let codeblock = "";
|
|
1601
1927
|
let numberLen = (Math.log10(line + 1) | 0) + 1;
|
|
1602
1928
|
for (let i = line - 1; i <= line + 1; i++) {
|
|
1603
|
-
let l =
|
|
1929
|
+
let l = lines2[i - 1];
|
|
1604
1930
|
if (!l)
|
|
1605
1931
|
continue;
|
|
1606
1932
|
codeblock += i.toString().padEnd(numberLen, " ");
|
|
@@ -2202,10 +2528,10 @@ var init_dist3 = __esm({
|
|
|
2202
2528
|
});
|
|
2203
2529
|
|
|
2204
2530
|
// packages/adapter-codex/dist/shared.js
|
|
2205
|
-
import { homedir as
|
|
2531
|
+
import { homedir as homedir8 } from "node:os";
|
|
2206
2532
|
import { join as join10 } from "node:path";
|
|
2207
2533
|
function resolvePaths(options) {
|
|
2208
|
-
const homeDir = options?.homeDir ??
|
|
2534
|
+
const homeDir = options?.homeDir ?? homedir8();
|
|
2209
2535
|
const projectDir = options?.projectDir ?? process.cwd();
|
|
2210
2536
|
return { homeDir, projectDir, codexHome: join10(homeDir, ".codex") };
|
|
2211
2537
|
}
|
|
@@ -2519,8 +2845,8 @@ var init_toml_edit = __esm({
|
|
|
2519
2845
|
});
|
|
2520
2846
|
|
|
2521
2847
|
// packages/adapter-codex/dist/actions.js
|
|
2522
|
-
import { existsSync as
|
|
2523
|
-
import { basename as
|
|
2848
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2849
|
+
import { basename as basename4, dirname as dirname7, join as join11 } from "node:path";
|
|
2524
2850
|
async function planAction(action, options) {
|
|
2525
2851
|
const paths = resolvePaths(options);
|
|
2526
2852
|
const resolved = resolveToggle(action, paths);
|
|
@@ -2530,7 +2856,7 @@ async function planAction(action, options) {
|
|
|
2530
2856
|
writes: [
|
|
2531
2857
|
{ file: resolved.file, kind: "modify", before: resolved.before, after: resolved.after }
|
|
2532
2858
|
],
|
|
2533
|
-
backupDir:
|
|
2859
|
+
backupDir: nextBackupDir2(paths),
|
|
2534
2860
|
references: scanReferences(collectCorpus(paths), resolved.bareName),
|
|
2535
2861
|
// §4b 地面真值:config.toml 启动时读,外改对运行中会话不生效 → 如实报 next_session
|
|
2536
2862
|
effectiveAt: "next_session"
|
|
@@ -2550,10 +2876,10 @@ async function applyAction(plan, options) {
|
|
|
2550
2876
|
};
|
|
2551
2877
|
}
|
|
2552
2878
|
const backupsRoot = join11(wingmanStateRoot(paths), "backups");
|
|
2553
|
-
if (
|
|
2879
|
+
if (dirname7(plan.backupDir) !== backupsRoot) {
|
|
2554
2880
|
return { ok: false, error: `backupDir \u4E0D\u5728 wingman \u5907\u4EFD\u6839(${backupsRoot})\u4E0B,\u62D2\u7EDD\u6267\u884C` };
|
|
2555
2881
|
}
|
|
2556
|
-
const currentRaw =
|
|
2882
|
+
const currentRaw = readFileSync8(write.file);
|
|
2557
2883
|
if (!currentRaw.equals(Buffer.from(write.before, "utf8"))) {
|
|
2558
2884
|
return { ok: false, error: `\u6587\u4EF6\u5728 plan \u4E4B\u540E\u88AB\u4FEE\u6539(${write.file}),\u62D2\u7EDD\u6267\u884C;\u8BF7\u91CD\u65B0 plan` };
|
|
2559
2885
|
}
|
|
@@ -2564,19 +2890,19 @@ async function applyAction(plan, options) {
|
|
|
2564
2890
|
if (resolved.after !== write.after) {
|
|
2565
2891
|
return { ok: false, error: "plan \u4E0E\u672C adapter \u7684\u63A8\u6F14\u4E0D\u4E00\u81F4,\u62D2\u7EDD\u6267\u884C;\u8BF7\u91CD\u65B0 plan" };
|
|
2566
2892
|
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2893
|
+
mkdirSync5(plan.backupDir, { recursive: true });
|
|
2894
|
+
writeFileSync6(join11(plan.backupDir, basename4(write.file)), currentRaw);
|
|
2895
|
+
writeFileSync6(write.file, write.after);
|
|
2570
2896
|
return { ok: true, backupDir: plan.backupDir };
|
|
2571
2897
|
} catch (error) {
|
|
2572
2898
|
return { ok: false, error: error.message };
|
|
2573
2899
|
}
|
|
2574
2900
|
}
|
|
2575
|
-
function
|
|
2901
|
+
function nextBackupDir2(paths) {
|
|
2576
2902
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
2577
2903
|
const base = join11(wingmanStateRoot(paths), "backups");
|
|
2578
2904
|
let dir = join11(base, stamp);
|
|
2579
|
-
for (let n = 1;
|
|
2905
|
+
for (let n = 1; existsSync7(dir); n += 1)
|
|
2580
2906
|
dir = join11(base, `${stamp}-${n}`);
|
|
2581
2907
|
return dir;
|
|
2582
2908
|
}
|
|
@@ -2592,9 +2918,9 @@ function resolveToggle(action, paths) {
|
|
|
2592
2918
|
const source = m[2];
|
|
2593
2919
|
const nativeName = m[3];
|
|
2594
2920
|
const file = source === "user" ? join11(paths.codexHome, "config.toml") : join11(paths.projectDir, ".codex", "config.toml");
|
|
2595
|
-
if (!
|
|
2921
|
+
if (!existsSync7(file))
|
|
2596
2922
|
throw new Error(`itemId \u627E\u4E0D\u5230:\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728(${file})`);
|
|
2597
|
-
const before =
|
|
2923
|
+
const before = readFileSync8(file, "utf8");
|
|
2598
2924
|
let table;
|
|
2599
2925
|
try {
|
|
2600
2926
|
table = parse2(before);
|
|
@@ -2833,14 +3159,14 @@ function collectCorpus(paths) {
|
|
|
2833
3159
|
} catch {
|
|
2834
3160
|
continue;
|
|
2835
3161
|
}
|
|
2836
|
-
sources.push({ file, text:
|
|
3162
|
+
sources.push({ file, text: readFileSync8(file, "utf8") });
|
|
2837
3163
|
}
|
|
2838
3164
|
return sources;
|
|
2839
3165
|
}
|
|
2840
3166
|
function listFilesRecursive(dir) {
|
|
2841
3167
|
let entries;
|
|
2842
3168
|
try {
|
|
2843
|
-
entries =
|
|
3169
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
2844
3170
|
} catch {
|
|
2845
3171
|
return [];
|
|
2846
3172
|
}
|
|
@@ -2912,8 +3238,9 @@ var init_actions2 = __esm({
|
|
|
2912
3238
|
});
|
|
2913
3239
|
|
|
2914
3240
|
// packages/adapter-codex/dist/hooks.js
|
|
2915
|
-
import {
|
|
2916
|
-
import {
|
|
3241
|
+
import { spawnSync } from "node:child_process";
|
|
3242
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync9, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
3243
|
+
import { basename as basename5, dirname as dirname8, join as join12 } from "node:path";
|
|
2917
3244
|
async function installHooks(port, options) {
|
|
2918
3245
|
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
2919
3246
|
throw new Error(`\u975E\u6CD5\u7AEF\u53E3:${port}`);
|
|
@@ -2922,22 +3249,22 @@ async function installHooks(port, options) {
|
|
|
2922
3249
|
const root = wingmanStateRoot(paths);
|
|
2923
3250
|
const scriptPath = join12(root, FORWARD_SCRIPT_NAME);
|
|
2924
3251
|
const configPath = join12(paths.codexHome, "config.toml");
|
|
2925
|
-
const raw =
|
|
3252
|
+
const raw = existsSync8(configPath) ? readFileSync9(configPath) : void 0;
|
|
2926
3253
|
const text = raw?.toString("utf8") ?? "";
|
|
2927
3254
|
const { before, after, remainder, block } = splitManagedBlock(text, configPath);
|
|
2928
3255
|
const lineEnding = preferredLineEnding(text);
|
|
2929
|
-
const newBlock = buildManagedBlock(scriptPath).replaceAll("\n", lineEnding);
|
|
3256
|
+
const newBlock = buildManagedBlock(scriptPath, resolveNodeExecutable()).replaceAll("\n", lineEnding);
|
|
2930
3257
|
const knownBlock = block === void 0 ? void 0 : inspectManagedBlock(block, scriptPath);
|
|
2931
3258
|
if (block !== void 0 && knownBlock === void 0) {
|
|
2932
3259
|
throw new Error(`Wingman Codex hook \u53D7\u7BA1\u5757\u5DF2\u88AB\u4FEE\u6539\u6216\u6B8B\u7F3A(${configPath}),\u62D2\u7EDD\u8C0E\u62A5\u63A5\u7EBF\u6210\u529F;\u8BF7\u5148\u4ECE <wingman\u6839>/backups/ \u6062\u590D\u5B8C\u6574\u5757,\u6216\u4EBA\u5DE5\u6838\u5BF9\u540E\u79FB\u9664\u8BE5\u53D7\u7BA1\u5757\u518D\u91CD\u65B0\u5B89\u88C5\u5E76\u4FE1\u4EFB`);
|
|
2933
3260
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
3261
|
+
mkdirSync6(root, { recursive: true });
|
|
3262
|
+
writeFileSync7(scriptPath, FORWARD_SCRIPT);
|
|
2936
3263
|
const statePath2 = hooksStatePath(root);
|
|
2937
|
-
const prior =
|
|
3264
|
+
const prior = readState3(statePath2);
|
|
2938
3265
|
if (knownBlock !== void 0 && knownBlock.state === void 0) {
|
|
2939
3266
|
if (prior?.port !== port || prior.configPath !== configPath || prior.forwardScriptPath !== scriptPath) {
|
|
2940
|
-
|
|
3267
|
+
writeState3(statePath2, {
|
|
2941
3268
|
...prior,
|
|
2942
3269
|
version: 1,
|
|
2943
3270
|
port,
|
|
@@ -2951,7 +3278,7 @@ async function installHooks(port, options) {
|
|
|
2951
3278
|
const hasStateOutsideBlock = assertWritable(remainder, configPath);
|
|
2952
3279
|
const backupDir = raw === void 0 ? void 0 : backupFile(root, configPath, raw);
|
|
2953
3280
|
if (knownBlock !== void 0) {
|
|
2954
|
-
|
|
3281
|
+
writeState3(statePath2, {
|
|
2955
3282
|
...prior,
|
|
2956
3283
|
version: 1,
|
|
2957
3284
|
port,
|
|
@@ -2961,11 +3288,11 @@ async function installHooks(port, options) {
|
|
|
2961
3288
|
...backupDir !== void 0 ? { backupDir } : {}
|
|
2962
3289
|
});
|
|
2963
3290
|
const state = hasStateOutsideBlock ? "" : knownBlock.state;
|
|
2964
|
-
|
|
3291
|
+
writeFileSync7(configPath, before + knownBlock.managedBlock + state + after);
|
|
2965
3292
|
return;
|
|
2966
3293
|
}
|
|
2967
3294
|
const original = prior?.original ?? (raw === void 0 ? { existed: false } : { existed: true, base64: raw.toString("base64") });
|
|
2968
|
-
|
|
3295
|
+
writeState3(statePath2, {
|
|
2969
3296
|
version: 1,
|
|
2970
3297
|
port,
|
|
2971
3298
|
configPath,
|
|
@@ -2977,8 +3304,8 @@ async function installHooks(port, options) {
|
|
|
2977
3304
|
let base = remainder;
|
|
2978
3305
|
if (base !== "" && !base.endsWith("\n"))
|
|
2979
3306
|
base += lineEnding;
|
|
2980
|
-
|
|
2981
|
-
|
|
3307
|
+
mkdirSync6(paths.codexHome, { recursive: true });
|
|
3308
|
+
writeFileSync7(configPath, base + newBlock);
|
|
2982
3309
|
}
|
|
2983
3310
|
async function uninstallHooks(options) {
|
|
2984
3311
|
const paths = resolvePaths(options);
|
|
@@ -2986,8 +3313,8 @@ async function uninstallHooks(options) {
|
|
|
2986
3313
|
const scriptPath = join12(root, FORWARD_SCRIPT_NAME);
|
|
2987
3314
|
const statePath2 = hooksStatePath(root);
|
|
2988
3315
|
const configPath = join12(paths.codexHome, "config.toml");
|
|
2989
|
-
const state =
|
|
2990
|
-
const raw =
|
|
3316
|
+
const state = readState3(statePath2);
|
|
3317
|
+
const raw = existsSync8(configPath) ? readFileSync9(configPath) : void 0;
|
|
2991
3318
|
if (raw !== void 0) {
|
|
2992
3319
|
const text = raw.toString("utf8");
|
|
2993
3320
|
const { before, after, remainder, block } = splitManagedBlock(text, configPath);
|
|
@@ -3000,20 +3327,20 @@ async function uninstallHooks(options) {
|
|
|
3000
3327
|
const preservedState = hasHooksState(remainder) ? "" : knownBlock.state ?? "";
|
|
3001
3328
|
const restored = restoreTarget(before + preservedState + after, state);
|
|
3002
3329
|
if (restored === null)
|
|
3003
|
-
|
|
3330
|
+
rmSync4(configPath);
|
|
3004
3331
|
else
|
|
3005
|
-
|
|
3332
|
+
writeFileSync7(configPath, restored);
|
|
3006
3333
|
}
|
|
3007
3334
|
}
|
|
3008
|
-
|
|
3009
|
-
|
|
3335
|
+
rmSync4(scriptPath, { force: true });
|
|
3336
|
+
rmSync4(statePath2, { force: true });
|
|
3010
3337
|
}
|
|
3011
3338
|
function hasManagedHooks(paths) {
|
|
3012
3339
|
const configPath = join12(paths.codexHome, "config.toml");
|
|
3013
|
-
if (!
|
|
3340
|
+
if (!existsSync8(configPath))
|
|
3014
3341
|
return false;
|
|
3015
3342
|
try {
|
|
3016
|
-
const { block } = splitManagedBlock(
|
|
3343
|
+
const { block } = splitManagedBlock(readFileSync9(configPath, "utf8"), configPath);
|
|
3017
3344
|
return block !== void 0 && inspectManagedBlock(block, join12(wingmanStateRoot(paths), FORWARD_SCRIPT_NAME)) !== void 0;
|
|
3018
3345
|
} catch {
|
|
3019
3346
|
return false;
|
|
@@ -3023,9 +3350,9 @@ function feedbackSignalsCodex(options) {
|
|
|
3023
3350
|
const paths = resolvePaths(options);
|
|
3024
3351
|
const configPath = join12(paths.codexHome, "config.toml");
|
|
3025
3352
|
const wiredEvents = [];
|
|
3026
|
-
if (
|
|
3353
|
+
if (existsSync8(configPath)) {
|
|
3027
3354
|
try {
|
|
3028
|
-
const { block } = splitManagedBlock(
|
|
3355
|
+
const { block } = splitManagedBlock(readFileSync9(configPath, "utf8"), configPath);
|
|
3029
3356
|
if (block !== void 0) {
|
|
3030
3357
|
const knownBlock = inspectManagedBlock(block, join12(wingmanStateRoot(paths), FORWARD_SCRIPT_NAME));
|
|
3031
3358
|
const hooks = knownBlock === void 0 ? void 0 : parse2(normalizeLineEndings(knownBlock.managedBlock)).hooks;
|
|
@@ -3051,39 +3378,68 @@ function restoreTarget(remainder, state) {
|
|
|
3051
3378
|
if (!original.existed) {
|
|
3052
3379
|
return remainder.trim() === "" ? null : remainder;
|
|
3053
3380
|
}
|
|
3054
|
-
const
|
|
3055
|
-
const originalText =
|
|
3381
|
+
const originalBytes3 = Buffer.from(original.base64 ?? "", "base64");
|
|
3382
|
+
const originalText = originalBytes3.toString("utf8");
|
|
3056
3383
|
try {
|
|
3057
3384
|
if (deepEqual3(parse2(remainder), parse2(originalText))) {
|
|
3058
3385
|
const separatorOnly = remainder === `${originalText}
|
|
3059
3386
|
` || remainder === `${originalText}\r
|
|
3060
3387
|
`;
|
|
3061
3388
|
if (separatorOnly || lineEndingStyle(remainder) === lineEndingStyle(originalText)) {
|
|
3062
|
-
return
|
|
3389
|
+
return originalBytes3;
|
|
3063
3390
|
}
|
|
3064
3391
|
}
|
|
3065
3392
|
} catch {
|
|
3066
3393
|
}
|
|
3067
3394
|
return remainder;
|
|
3068
3395
|
}
|
|
3069
|
-
function
|
|
3070
|
-
|
|
3396
|
+
function resolveNodeExecutable(probe = {}) {
|
|
3397
|
+
const execPath = probe.execPath ?? process.execPath;
|
|
3398
|
+
if (isNodeExecutable(execPath))
|
|
3399
|
+
return execPath;
|
|
3400
|
+
const loginShellWhich = probe.loginShellWhich ?? defaultLoginShellWhich;
|
|
3401
|
+
try {
|
|
3402
|
+
const shellNode = loginShellWhich(probe.shell ?? process.env.SHELL ?? "/bin/sh")?.trim();
|
|
3403
|
+
if (shellNode !== void 0 && shellNode !== "node" && isNodeExecutable(shellNode)) {
|
|
3404
|
+
return shellNode;
|
|
3405
|
+
}
|
|
3406
|
+
} catch {
|
|
3407
|
+
}
|
|
3408
|
+
const exists = probe.exists ?? existsSync8;
|
|
3409
|
+
for (const candidate of ["/opt/homebrew/bin/node", "/usr/local/bin/node"]) {
|
|
3410
|
+
try {
|
|
3411
|
+
if (exists(candidate) && isNodeExecutable(candidate))
|
|
3412
|
+
return candidate;
|
|
3413
|
+
} catch {
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
return "node";
|
|
3417
|
+
}
|
|
3418
|
+
function defaultLoginShellWhich(shell) {
|
|
3419
|
+
const result4 = spawnSync(shell, ["-lc", "command -v node"], {
|
|
3420
|
+
encoding: "utf8",
|
|
3421
|
+
timeout: 3e3
|
|
3422
|
+
});
|
|
3423
|
+
if (result4.error !== void 0)
|
|
3424
|
+
return void 0;
|
|
3425
|
+
return result4.stdout.trim() || void 0;
|
|
3426
|
+
}
|
|
3427
|
+
function buildManagedBlock(scriptPath, nodeExecutable = "node") {
|
|
3428
|
+
return buildManagedBlockForCommand(`${nodeExecutable} "${scriptPath}"`);
|
|
3071
3429
|
}
|
|
3072
3430
|
function buildManagedBlockForCommand(rawCommand) {
|
|
3073
3431
|
const command = tomlBasicString(rawCommand);
|
|
3074
3432
|
const handler = `[{ hooks = [{ type = "command", command = ${command} }] }]`;
|
|
3075
|
-
const
|
|
3433
|
+
const lines2 = [MANAGED_BLOCK_START, "[hooks]"];
|
|
3076
3434
|
for (const event of HOOK_EVENTS)
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
return `${
|
|
3435
|
+
lines2.push(`${event} = ${handler}`);
|
|
3436
|
+
lines2.push(MANAGED_BLOCK_END);
|
|
3437
|
+
return `${lines2.join("\n")}
|
|
3080
3438
|
`;
|
|
3081
3439
|
}
|
|
3082
3440
|
function inspectManagedBlock(block, scriptPath) {
|
|
3083
3441
|
const inspected = separateCodexHooksState(block);
|
|
3084
3442
|
const normalized = normalizeLineEndings(inspected.managedBlock);
|
|
3085
|
-
if (normalized === buildManagedBlock(scriptPath))
|
|
3086
|
-
return inspected;
|
|
3087
3443
|
let table;
|
|
3088
3444
|
try {
|
|
3089
3445
|
table = parse2(normalized);
|
|
@@ -3102,17 +3458,25 @@ function inspectManagedBlock(block, scriptPath) {
|
|
|
3102
3458
|
const command = handlers[0].command;
|
|
3103
3459
|
if (typeof command !== "string")
|
|
3104
3460
|
return void 0;
|
|
3105
|
-
const
|
|
3106
|
-
if (!
|
|
3107
|
-
return void 0;
|
|
3108
|
-
const legacyPortText = command.slice(prefix.length);
|
|
3109
|
-
if (!/^[1-9]\d{0,4}$/.test(legacyPortText))
|
|
3110
|
-
return void 0;
|
|
3111
|
-
const legacyPort = Number(legacyPortText);
|
|
3112
|
-
if (legacyPort > 65535 || String(legacyPort) !== legacyPortText)
|
|
3461
|
+
const match = /^(\S+) "(.+)"(?: (\d+))?$/.exec(command);
|
|
3462
|
+
if (match === null || match[2] !== scriptPath || !isNodeExecutable(match[1] ?? "")) {
|
|
3113
3463
|
return void 0;
|
|
3464
|
+
}
|
|
3465
|
+
const legacyPortText = match[3];
|
|
3466
|
+
if (legacyPortText !== void 0) {
|
|
3467
|
+
if (!/^[1-9]\d{0,4}$/.test(legacyPortText))
|
|
3468
|
+
return void 0;
|
|
3469
|
+
const legacyPort = Number(legacyPortText);
|
|
3470
|
+
if (legacyPort > 65535 || String(legacyPort) !== legacyPortText)
|
|
3471
|
+
return void 0;
|
|
3472
|
+
}
|
|
3114
3473
|
return normalized === buildManagedBlockForCommand(command) ? inspected : void 0;
|
|
3115
3474
|
}
|
|
3475
|
+
function isNodeExecutable(executable) {
|
|
3476
|
+
if (executable === "node")
|
|
3477
|
+
return true;
|
|
3478
|
+
return /^(?:\/|[A-Za-z]:[\\/])[^\s"]*[\\/]node(?:\.exe)?$/.test(executable);
|
|
3479
|
+
}
|
|
3116
3480
|
function separateCodexHooksState(block) {
|
|
3117
3481
|
const stateHeader = /^[ \t]*\[[ \t]*hooks[ \t]*\.[ \t]*state(?:[ \t]*|[ \t]*\..+)\][ \t]*(?:#.*)?\r?$/m;
|
|
3118
3482
|
const match = stateHeader.exec(block);
|
|
@@ -3211,27 +3575,27 @@ function hasHooksState(text) {
|
|
|
3211
3575
|
function backupFile(root, filePath, contents) {
|
|
3212
3576
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
3213
3577
|
let dir = join12(root, "backups", stamp);
|
|
3214
|
-
for (let n = 1;
|
|
3578
|
+
for (let n = 1; existsSync8(dir); n += 1)
|
|
3215
3579
|
dir = join12(root, "backups", `${stamp}-${n}`);
|
|
3216
|
-
|
|
3217
|
-
|
|
3580
|
+
mkdirSync6(dir, { recursive: true });
|
|
3581
|
+
writeFileSync7(join12(dir, basename5(filePath)), contents);
|
|
3218
3582
|
return dir;
|
|
3219
3583
|
}
|
|
3220
3584
|
function hooksStatePath(root) {
|
|
3221
3585
|
return join12(root, "state", "codex-hooks.json");
|
|
3222
3586
|
}
|
|
3223
|
-
function
|
|
3224
|
-
if (!
|
|
3587
|
+
function readState3(statePath2) {
|
|
3588
|
+
if (!existsSync8(statePath2))
|
|
3225
3589
|
return void 0;
|
|
3226
3590
|
try {
|
|
3227
|
-
return JSON.parse(
|
|
3591
|
+
return JSON.parse(readFileSync9(statePath2, "utf8"));
|
|
3228
3592
|
} catch {
|
|
3229
3593
|
return void 0;
|
|
3230
3594
|
}
|
|
3231
3595
|
}
|
|
3232
|
-
function
|
|
3233
|
-
|
|
3234
|
-
|
|
3596
|
+
function writeState3(statePath2, state) {
|
|
3597
|
+
mkdirSync6(dirname8(statePath2), { recursive: true });
|
|
3598
|
+
writeFileSync7(statePath2, `${JSON.stringify(state, null, 2)}
|
|
3235
3599
|
`);
|
|
3236
3600
|
}
|
|
3237
3601
|
function deepEqual3(a, b) {
|
|
@@ -3387,11 +3751,11 @@ process.stdin.on('end', async () => {
|
|
|
3387
3751
|
});
|
|
3388
3752
|
|
|
3389
3753
|
// packages/adapter-codex/dist/scan.js
|
|
3390
|
-
import { existsSync as
|
|
3391
|
-
import { dirname as
|
|
3754
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync4 } from "node:fs";
|
|
3755
|
+
import { dirname as dirname9, isAbsolute, join as join13, resolve as resolve5 } from "node:path";
|
|
3392
3756
|
async function detect(options) {
|
|
3393
3757
|
const { codexHome } = resolvePaths(options);
|
|
3394
|
-
return { installed:
|
|
3758
|
+
return { installed: existsSync9(codexHome) };
|
|
3395
3759
|
}
|
|
3396
3760
|
async function scan(options) {
|
|
3397
3761
|
const paths = resolvePaths(options);
|
|
@@ -3435,7 +3799,7 @@ function loadConfigLayers(paths) {
|
|
|
3435
3799
|
for (const [source, path] of candidates) {
|
|
3436
3800
|
if (fileSize(path) === void 0)
|
|
3437
3801
|
continue;
|
|
3438
|
-
const text =
|
|
3802
|
+
const text = readFileSync10(path, "utf8");
|
|
3439
3803
|
try {
|
|
3440
3804
|
layers.push({ source, path, table: parse2(text) });
|
|
3441
3805
|
} catch (error) {
|
|
@@ -3482,7 +3846,7 @@ function collectRules(out, paths, layers) {
|
|
|
3482
3846
|
}
|
|
3483
3847
|
const instructionsFile = layer.table.model_instructions_file;
|
|
3484
3848
|
if (typeof instructionsFile === "string") {
|
|
3485
|
-
const filePath = isAbsolute(instructionsFile) ? instructionsFile :
|
|
3849
|
+
const filePath = isAbsolute(instructionsFile) ? instructionsFile : resolve5(dirname9(layer.path), instructionsFile);
|
|
3486
3850
|
const item = {
|
|
3487
3851
|
id: `codex:rules:${layer.source}:model_instructions_file`,
|
|
3488
3852
|
slot: "rules",
|
|
@@ -3614,7 +3978,7 @@ function collectHistory(out, paths, layers) {
|
|
|
3614
3978
|
function expandPath(value, paths) {
|
|
3615
3979
|
if (value === "~" || value.startsWith("~/"))
|
|
3616
3980
|
return join13(paths.homeDir, value.slice(2));
|
|
3617
|
-
return isAbsolute(value) ? value :
|
|
3981
|
+
return isAbsolute(value) ? value : resolve5(paths.codexHome, value);
|
|
3618
3982
|
}
|
|
3619
3983
|
function assertUniqueIds(slots) {
|
|
3620
3984
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -3644,7 +4008,7 @@ function isDirectory(path) {
|
|
|
3644
4008
|
}
|
|
3645
4009
|
function directoryBytes(dir) {
|
|
3646
4010
|
let total = 0;
|
|
3647
|
-
for (const entry of
|
|
4011
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
3648
4012
|
const entryPath = join13(dir, entry.name);
|
|
3649
4013
|
if (entry.isDirectory())
|
|
3650
4014
|
total += directoryBytes(entryPath);
|
|
@@ -3666,12 +4030,219 @@ var init_scan2 = __esm({
|
|
|
3666
4030
|
}
|
|
3667
4031
|
});
|
|
3668
4032
|
|
|
4033
|
+
// packages/adapter-codex/dist/protocol.js
|
|
4034
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync11, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
4035
|
+
import { basename as basename6, dirname as dirname10, join as join14, resolve as resolve6 } from "node:path";
|
|
4036
|
+
function protocolStatusCodex(options) {
|
|
4037
|
+
const paths = resolveOptions2(options);
|
|
4038
|
+
if (!existsSync10(paths.targetPath))
|
|
4039
|
+
return status(paths.targetPath, "not_installed");
|
|
4040
|
+
try {
|
|
4041
|
+
const file = readAgents(paths.targetPath);
|
|
4042
|
+
if (file.identity === void 0)
|
|
4043
|
+
return status(paths.targetPath, "not_installed");
|
|
4044
|
+
const state = stateForIdentity2(file.identity);
|
|
4045
|
+
return status(paths.targetPath, state, file.identity);
|
|
4046
|
+
} catch (error) {
|
|
4047
|
+
return status(paths.targetPath, "invalid", void 0, error.message);
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
function installProtocolCodex(options) {
|
|
4051
|
+
const paths = resolveOptions2(options);
|
|
4052
|
+
const existing = existsSync10(paths.targetPath) ? readAgents(paths.targetPath) : void 0;
|
|
4053
|
+
const previousState = stateForIdentity2(existing?.identity);
|
|
4054
|
+
if (previousState === "installed") {
|
|
4055
|
+
return result2(paths.targetPath, "install", false, previousState, existing?.text ?? "", true);
|
|
4056
|
+
}
|
|
4057
|
+
if (previousState === "modified")
|
|
4058
|
+
throw modifiedError2(paths.targetPath, "\u5B89\u88C5");
|
|
4059
|
+
const before = existing?.text ?? "";
|
|
4060
|
+
const lineEnding = preferredMarkdownLineEnding(before);
|
|
4061
|
+
const block = buildPlanProtocolBlock().replaceAll("\n", lineEnding);
|
|
4062
|
+
const after = existing?.split.block ? existing.split.before + block + existing.split.after : appendBlock(before, block, lineEnding);
|
|
4063
|
+
const installedRemainder = splitPlanProtocolBlock(after, paths.targetPath).remainder;
|
|
4064
|
+
const backupDir = nextBackupDir3(paths.homeDir);
|
|
4065
|
+
if (!paths.dryRun) {
|
|
4066
|
+
const prior = readState4(paths.statePath, paths.targetPath);
|
|
4067
|
+
backupTarget2(backupDir, paths.targetPath, existing?.bytes);
|
|
4068
|
+
writeState4(paths.statePath, {
|
|
4069
|
+
version: 1,
|
|
4070
|
+
projectDir: paths.projectDir,
|
|
4071
|
+
targetPath: paths.targetPath,
|
|
4072
|
+
protocolVersion: PLAN_PROTOCOL_VERSION,
|
|
4073
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4074
|
+
backupDir,
|
|
4075
|
+
installedRemainderSha256: sha256Hex(installedRemainder),
|
|
4076
|
+
original: prior?.original ?? (existing === void 0 ? { existed: false } : { existed: true, base64: existing.bytes.toString("base64") })
|
|
4077
|
+
});
|
|
4078
|
+
writeFileSync8(paths.targetPath, after);
|
|
4079
|
+
}
|
|
4080
|
+
return {
|
|
4081
|
+
...result2(paths.targetPath, "install", true, previousState, before, true),
|
|
4082
|
+
state: "installed",
|
|
4083
|
+
version: PLAN_PROTOCOL_VERSION,
|
|
4084
|
+
after,
|
|
4085
|
+
backupDir,
|
|
4086
|
+
totalBytes: Buffer.byteLength(after)
|
|
4087
|
+
};
|
|
4088
|
+
}
|
|
4089
|
+
function uninstallProtocolCodex(options) {
|
|
4090
|
+
const paths = resolveOptions2(options);
|
|
4091
|
+
if (!existsSync10(paths.targetPath)) {
|
|
4092
|
+
return result2(paths.targetPath, "uninstall", false, "not_installed", "", false);
|
|
4093
|
+
}
|
|
4094
|
+
const existing = readAgents(paths.targetPath);
|
|
4095
|
+
if (existing.identity === void 0) {
|
|
4096
|
+
return result2(paths.targetPath, "uninstall", false, "not_installed", existing.text, true);
|
|
4097
|
+
}
|
|
4098
|
+
const previousState = stateForIdentity2(existing.identity);
|
|
4099
|
+
if (previousState === "modified")
|
|
4100
|
+
throw modifiedError2(paths.targetPath, "\u5378\u8F7D");
|
|
4101
|
+
const state = readState4(paths.statePath, paths.targetPath);
|
|
4102
|
+
const canRestore = state !== void 0 && sha256Hex(existing.split.remainder) === state.installedRemainderSha256;
|
|
4103
|
+
const restored = canRestore ? originalBytes(state) : void 0;
|
|
4104
|
+
const deleteTarget = canRestore ? state?.original.existed === false : existing.split.remainder.trim() === "";
|
|
4105
|
+
const after = deleteTarget ? "" : restored?.toString("utf8") ?? existing.split.remainder;
|
|
4106
|
+
const backupDir = nextBackupDir3(paths.homeDir);
|
|
4107
|
+
if (!paths.dryRun) {
|
|
4108
|
+
backupTarget2(backupDir, paths.targetPath, existing.bytes);
|
|
4109
|
+
if (deleteTarget)
|
|
4110
|
+
rmSync5(paths.targetPath, { force: true });
|
|
4111
|
+
else
|
|
4112
|
+
writeFileSync8(paths.targetPath, restored ?? existing.split.remainder);
|
|
4113
|
+
rmSync5(paths.statePath, { force: true });
|
|
4114
|
+
}
|
|
4115
|
+
return {
|
|
4116
|
+
...result2(paths.targetPath, "uninstall", true, previousState, existing.text, !deleteTarget),
|
|
4117
|
+
state: "not_installed",
|
|
4118
|
+
message: canRestore ? SHARED_MESSAGE : `${SHARED_MESSAGE}\uFF1B\u5DF2\u79FB\u9664\u5757\u5E76\u4FDD\u7559\u7528\u6237\u6539\u52A8`,
|
|
4119
|
+
after,
|
|
4120
|
+
backupDir,
|
|
4121
|
+
totalBytes: Buffer.byteLength(after),
|
|
4122
|
+
restoredOriginal: canRestore
|
|
4123
|
+
};
|
|
4124
|
+
}
|
|
4125
|
+
function resolveOptions2(options) {
|
|
4126
|
+
const base = resolvePaths(options);
|
|
4127
|
+
const homeDir = resolve6(base.homeDir);
|
|
4128
|
+
const projectDir = resolve6(base.projectDir);
|
|
4129
|
+
const targetPath2 = join14(projectDir, "AGENTS.md");
|
|
4130
|
+
const statePath2 = join14(wingmanStateRoot({ ...base, homeDir, projectDir }), "state", `plan-protocol-agents-${sha256Hex(projectDir)}.json`);
|
|
4131
|
+
return { homeDir, projectDir, targetPath: targetPath2, statePath: statePath2, dryRun: options?.dryRun ?? false };
|
|
4132
|
+
}
|
|
4133
|
+
function readAgents(targetPath2) {
|
|
4134
|
+
const bytes = readFileSync11(targetPath2);
|
|
4135
|
+
const text = decodeUtf8Strict(bytes, targetPath2);
|
|
4136
|
+
let split;
|
|
4137
|
+
try {
|
|
4138
|
+
split = splitPlanProtocolBlock(text, targetPath2);
|
|
4139
|
+
} catch (error) {
|
|
4140
|
+
throw new Error(`${targetPath2}: ${error.message}`);
|
|
4141
|
+
}
|
|
4142
|
+
return {
|
|
4143
|
+
bytes,
|
|
4144
|
+
text,
|
|
4145
|
+
split,
|
|
4146
|
+
...split.block !== void 0 ? { identity: inspectPlanProtocolBlock(split.block) } : {}
|
|
4147
|
+
};
|
|
4148
|
+
}
|
|
4149
|
+
function appendBlock(text, block, lineEnding) {
|
|
4150
|
+
if (text === "")
|
|
4151
|
+
return block;
|
|
4152
|
+
let base = text;
|
|
4153
|
+
if (!base.endsWith("\n"))
|
|
4154
|
+
base += lineEnding;
|
|
4155
|
+
if (!base.endsWith(`${lineEnding}${lineEnding}`))
|
|
4156
|
+
base += lineEnding;
|
|
4157
|
+
return base + block;
|
|
4158
|
+
}
|
|
4159
|
+
function stateForIdentity2(identity) {
|
|
4160
|
+
if (identity === void 0)
|
|
4161
|
+
return "not_installed";
|
|
4162
|
+
if (identity.kind === "current")
|
|
4163
|
+
return "installed";
|
|
4164
|
+
if (identity.kind === "legacy")
|
|
4165
|
+
return "outdated";
|
|
4166
|
+
return "modified";
|
|
4167
|
+
}
|
|
4168
|
+
function status(targetPath2, state, identity, reason) {
|
|
4169
|
+
return {
|
|
4170
|
+
harness: "codex",
|
|
4171
|
+
state,
|
|
4172
|
+
targetPath: targetPath2,
|
|
4173
|
+
shared: true,
|
|
4174
|
+
message: SHARED_MESSAGE,
|
|
4175
|
+
...identity?.version !== void 0 ? { version: identity.version } : {},
|
|
4176
|
+
...reason !== void 0 ? { reason } : {}
|
|
4177
|
+
};
|
|
4178
|
+
}
|
|
4179
|
+
function result2(targetPath2, operation, changed, previousState, before, afterExists) {
|
|
4180
|
+
return {
|
|
4181
|
+
...status(targetPath2, previousState),
|
|
4182
|
+
operation,
|
|
4183
|
+
changed,
|
|
4184
|
+
previousState,
|
|
4185
|
+
before,
|
|
4186
|
+
after: before,
|
|
4187
|
+
afterExists,
|
|
4188
|
+
effectiveAt: "next_session",
|
|
4189
|
+
totalBytes: Buffer.byteLength(before)
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
function modifiedError2(targetPath2, operation) {
|
|
4193
|
+
return new Error(`${targetPath2}: \u53D7\u7BA1\u5757\u5DF2\u88AB\u4FEE\u6539\uFF0C\u62D2\u7EDD${operation}\u5E76\u4FDD\u7559\u7528\u6237\u5185\u5BB9`);
|
|
4194
|
+
}
|
|
4195
|
+
function nextBackupDir3(homeDir) {
|
|
4196
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
4197
|
+
const root = join14(homeDir, ".wingman", "backups");
|
|
4198
|
+
let dir = join14(root, stamp);
|
|
4199
|
+
for (let n = 2; existsSync10(dir); n += 1)
|
|
4200
|
+
dir = join14(root, `${stamp}-${n}`);
|
|
4201
|
+
return dir;
|
|
4202
|
+
}
|
|
4203
|
+
function backupTarget2(backupDir, targetPath2, bytes) {
|
|
4204
|
+
mkdirSync7(backupDir, { recursive: true });
|
|
4205
|
+
if (bytes !== void 0)
|
|
4206
|
+
writeFileSync8(join14(backupDir, basename6(targetPath2)), bytes);
|
|
4207
|
+
}
|
|
4208
|
+
function readState4(statePath2, targetPath2) {
|
|
4209
|
+
if (!existsSync10(statePath2))
|
|
4210
|
+
return void 0;
|
|
4211
|
+
try {
|
|
4212
|
+
const value = JSON.parse(readFileSync11(statePath2, "utf8"));
|
|
4213
|
+
return value.version === 1 && value.targetPath === targetPath2 ? value : void 0;
|
|
4214
|
+
} catch {
|
|
4215
|
+
return void 0;
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
function writeState4(statePath2, state) {
|
|
4219
|
+
mkdirSync7(dirname10(statePath2), { recursive: true });
|
|
4220
|
+
writeFileSync8(statePath2, `${JSON.stringify(state, null, 2)}
|
|
4221
|
+
`);
|
|
4222
|
+
}
|
|
4223
|
+
function originalBytes(state) {
|
|
4224
|
+
return state.original.existed ? Buffer.from(state.original.base64 ?? "", "base64") : void 0;
|
|
4225
|
+
}
|
|
4226
|
+
var SHARED_MESSAGE;
|
|
4227
|
+
var init_protocol2 = __esm({
|
|
4228
|
+
"packages/adapter-codex/dist/protocol.js"() {
|
|
4229
|
+
"use strict";
|
|
4230
|
+
init_dist();
|
|
4231
|
+
init_shared();
|
|
4232
|
+
SHARED_MESSAGE = "\u6B64 AGENTS.md \u534F\u8BAE\u5757\u7531 Codex \u4E0E dsh \u5171\u7528";
|
|
4233
|
+
}
|
|
4234
|
+
});
|
|
4235
|
+
|
|
3669
4236
|
// packages/adapter-codex/dist/index.js
|
|
3670
4237
|
var dist_exports2 = {};
|
|
3671
4238
|
__export(dist_exports2, {
|
|
3672
4239
|
HOOK_EVENTS: () => HOOK_EVENTS,
|
|
3673
4240
|
createAdapter: () => createAdapter2,
|
|
3674
|
-
feedbackSignals: () => feedbackSignalsCodex
|
|
4241
|
+
feedbackSignals: () => feedbackSignalsCodex,
|
|
4242
|
+
installProtocolCodex: () => installProtocolCodex,
|
|
4243
|
+
protocolStatusCodex: () => protocolStatusCodex,
|
|
4244
|
+
resolveNodeExecutable: () => resolveNodeExecutable,
|
|
4245
|
+
uninstallProtocolCodex: () => uninstallProtocolCodex
|
|
3675
4246
|
});
|
|
3676
4247
|
function createAdapter2(_options) {
|
|
3677
4248
|
return {
|
|
@@ -3691,6 +4262,7 @@ var init_dist4 = __esm({
|
|
|
3691
4262
|
init_hooks2();
|
|
3692
4263
|
init_scan2();
|
|
3693
4264
|
init_hooks2();
|
|
4265
|
+
init_protocol2();
|
|
3694
4266
|
}
|
|
3695
4267
|
});
|
|
3696
4268
|
|
|
@@ -4053,7 +4625,7 @@ var require_directives = __commonJS({
|
|
|
4053
4625
|
return tag[0] === "!" ? tag : `!<${tag}>`;
|
|
4054
4626
|
}
|
|
4055
4627
|
toString(doc) {
|
|
4056
|
-
const
|
|
4628
|
+
const lines2 = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : [];
|
|
4057
4629
|
const tagEntries = Object.entries(this.tags);
|
|
4058
4630
|
let tagNames;
|
|
4059
4631
|
if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) {
|
|
@@ -4069,9 +4641,9 @@ var require_directives = __commonJS({
|
|
|
4069
4641
|
if (handle2 === "!!" && prefix === "tag:yaml.org,2002:")
|
|
4070
4642
|
continue;
|
|
4071
4643
|
if (!doc || tagNames.some((tn) => tn.startsWith(prefix)))
|
|
4072
|
-
|
|
4644
|
+
lines2.push(`%TAG ${handle2} ${prefix}`);
|
|
4073
4645
|
}
|
|
4074
|
-
return
|
|
4646
|
+
return lines2.join("\n");
|
|
4075
4647
|
}
|
|
4076
4648
|
};
|
|
4077
4649
|
Directives.defaultYaml = { explicit: false, version: "1.2" };
|
|
@@ -5530,22 +6102,22 @@ var require_stringifyCollection = __commonJS({
|
|
|
5530
6102
|
const { indent, options: { commentString } } = ctx;
|
|
5531
6103
|
const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null });
|
|
5532
6104
|
let chompKeep = false;
|
|
5533
|
-
const
|
|
6105
|
+
const lines2 = [];
|
|
5534
6106
|
for (let i = 0; i < items.length; ++i) {
|
|
5535
6107
|
const item = items[i];
|
|
5536
6108
|
let comment2 = null;
|
|
5537
6109
|
if (identity.isNode(item)) {
|
|
5538
6110
|
if (!chompKeep && item.spaceBefore)
|
|
5539
|
-
|
|
5540
|
-
addCommentBefore(ctx,
|
|
6111
|
+
lines2.push("");
|
|
6112
|
+
addCommentBefore(ctx, lines2, item.commentBefore, chompKeep);
|
|
5541
6113
|
if (item.comment)
|
|
5542
6114
|
comment2 = item.comment;
|
|
5543
6115
|
} else if (identity.isPair(item)) {
|
|
5544
6116
|
const ik = identity.isNode(item.key) ? item.key : null;
|
|
5545
6117
|
if (ik) {
|
|
5546
6118
|
if (!chompKeep && ik.spaceBefore)
|
|
5547
|
-
|
|
5548
|
-
addCommentBefore(ctx,
|
|
6119
|
+
lines2.push("");
|
|
6120
|
+
addCommentBefore(ctx, lines2, ik.commentBefore, chompKeep);
|
|
5549
6121
|
}
|
|
5550
6122
|
}
|
|
5551
6123
|
chompKeep = false;
|
|
@@ -5554,15 +6126,15 @@ var require_stringifyCollection = __commonJS({
|
|
|
5554
6126
|
str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
|
|
5555
6127
|
if (chompKeep && comment2)
|
|
5556
6128
|
chompKeep = false;
|
|
5557
|
-
|
|
6129
|
+
lines2.push(blockItemPrefix + str2);
|
|
5558
6130
|
}
|
|
5559
6131
|
let str;
|
|
5560
|
-
if (
|
|
6132
|
+
if (lines2.length === 0) {
|
|
5561
6133
|
str = flowChars.start + flowChars.end;
|
|
5562
6134
|
} else {
|
|
5563
|
-
str =
|
|
5564
|
-
for (let i = 1; i <
|
|
5565
|
-
const line =
|
|
6135
|
+
str = lines2[0];
|
|
6136
|
+
for (let i = 1; i < lines2.length; ++i) {
|
|
6137
|
+
const line = lines2[i];
|
|
5566
6138
|
str += line ? `
|
|
5567
6139
|
${indent}${line}` : "\n";
|
|
5568
6140
|
}
|
|
@@ -5585,22 +6157,22 @@ ${indent}${line}` : "\n";
|
|
|
5585
6157
|
});
|
|
5586
6158
|
let reqNewline = false;
|
|
5587
6159
|
let linesAtValue = 0;
|
|
5588
|
-
const
|
|
6160
|
+
const lines2 = [];
|
|
5589
6161
|
for (let i = 0; i < items.length; ++i) {
|
|
5590
6162
|
const item = items[i];
|
|
5591
6163
|
let comment = null;
|
|
5592
6164
|
if (identity.isNode(item)) {
|
|
5593
6165
|
if (item.spaceBefore)
|
|
5594
|
-
|
|
5595
|
-
addCommentBefore(ctx,
|
|
6166
|
+
lines2.push("");
|
|
6167
|
+
addCommentBefore(ctx, lines2, item.commentBefore, false);
|
|
5596
6168
|
if (item.comment)
|
|
5597
6169
|
comment = item.comment;
|
|
5598
6170
|
} else if (identity.isPair(item)) {
|
|
5599
6171
|
const ik = identity.isNode(item.key) ? item.key : null;
|
|
5600
6172
|
if (ik) {
|
|
5601
6173
|
if (ik.spaceBefore)
|
|
5602
|
-
|
|
5603
|
-
addCommentBefore(ctx,
|
|
6174
|
+
lines2.push("");
|
|
6175
|
+
addCommentBefore(ctx, lines2, ik.commentBefore, false);
|
|
5604
6176
|
if (ik.comment)
|
|
5605
6177
|
reqNewline = true;
|
|
5606
6178
|
}
|
|
@@ -5617,12 +6189,12 @@ ${indent}${line}` : "\n";
|
|
|
5617
6189
|
if (comment)
|
|
5618
6190
|
reqNewline = true;
|
|
5619
6191
|
let str = stringify2.stringify(item, itemCtx, () => comment = null);
|
|
5620
|
-
reqNewline || (reqNewline =
|
|
6192
|
+
reqNewline || (reqNewline = lines2.length > linesAtValue || str.includes("\n"));
|
|
5621
6193
|
if (i < items.length - 1) {
|
|
5622
6194
|
str += ",";
|
|
5623
6195
|
} else if (ctx.options.trailingComma) {
|
|
5624
6196
|
if (ctx.options.lineWidth > 0) {
|
|
5625
|
-
reqNewline || (reqNewline =
|
|
6197
|
+
reqNewline || (reqNewline = lines2.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
|
|
5626
6198
|
}
|
|
5627
6199
|
if (reqNewline) {
|
|
5628
6200
|
str += ",";
|
|
@@ -5630,35 +6202,35 @@ ${indent}${line}` : "\n";
|
|
|
5630
6202
|
}
|
|
5631
6203
|
if (comment)
|
|
5632
6204
|
str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
|
|
5633
|
-
|
|
5634
|
-
linesAtValue =
|
|
6205
|
+
lines2.push(str);
|
|
6206
|
+
linesAtValue = lines2.length;
|
|
5635
6207
|
}
|
|
5636
6208
|
const { start, end } = flowChars;
|
|
5637
|
-
if (
|
|
6209
|
+
if (lines2.length === 0) {
|
|
5638
6210
|
return start + end;
|
|
5639
6211
|
} else {
|
|
5640
6212
|
if (!reqNewline) {
|
|
5641
|
-
const len =
|
|
6213
|
+
const len = lines2.reduce((sum, line) => sum + line.length + 2, 2);
|
|
5642
6214
|
reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth;
|
|
5643
6215
|
}
|
|
5644
6216
|
if (reqNewline) {
|
|
5645
6217
|
let str = start;
|
|
5646
|
-
for (const line of
|
|
6218
|
+
for (const line of lines2)
|
|
5647
6219
|
str += line ? `
|
|
5648
6220
|
${indentStep}${indent}${line}` : "\n";
|
|
5649
6221
|
return `${str}
|
|
5650
6222
|
${indent}${end}`;
|
|
5651
6223
|
} else {
|
|
5652
|
-
return `${start}${fcPadding}${
|
|
6224
|
+
return `${start}${fcPadding}${lines2.join(" ")}${fcPadding}${end}`;
|
|
5653
6225
|
}
|
|
5654
6226
|
}
|
|
5655
6227
|
}
|
|
5656
|
-
function addCommentBefore({ indent, options: { commentString } },
|
|
6228
|
+
function addCommentBefore({ indent, options: { commentString } }, lines2, comment, chompKeep) {
|
|
5657
6229
|
if (comment && chompKeep)
|
|
5658
6230
|
comment = comment.replace(/^\n+/, "");
|
|
5659
6231
|
if (comment) {
|
|
5660
6232
|
const ic = stringifyComment.indentComment(commentString(comment), indent);
|
|
5661
|
-
|
|
6233
|
+
lines2.push(ic.trimStart());
|
|
5662
6234
|
}
|
|
5663
6235
|
}
|
|
5664
6236
|
exports.stringifyCollection = stringifyCollection;
|
|
@@ -6296,11 +6868,11 @@ var require_binary = __commonJS({
|
|
|
6296
6868
|
if (type !== Scalar.Scalar.QUOTE_DOUBLE) {
|
|
6297
6869
|
const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth);
|
|
6298
6870
|
const n = Math.ceil(str.length / lineWidth);
|
|
6299
|
-
const
|
|
6871
|
+
const lines2 = new Array(n);
|
|
6300
6872
|
for (let i = 0, o = 0; i < n; ++i, o += lineWidth) {
|
|
6301
|
-
|
|
6873
|
+
lines2[i] = str.substr(o, lineWidth);
|
|
6302
6874
|
}
|
|
6303
|
-
str =
|
|
6875
|
+
str = lines2.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " ");
|
|
6304
6876
|
}
|
|
6305
6877
|
return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep);
|
|
6306
6878
|
}
|
|
@@ -6980,35 +7552,35 @@ var require_stringifyDocument = __commonJS({
|
|
|
6980
7552
|
var stringify2 = require_stringify();
|
|
6981
7553
|
var stringifyComment = require_stringifyComment();
|
|
6982
7554
|
function stringifyDocument(doc, options) {
|
|
6983
|
-
const
|
|
7555
|
+
const lines2 = [];
|
|
6984
7556
|
let hasDirectives = options.directives === true;
|
|
6985
7557
|
if (options.directives !== false && doc.directives) {
|
|
6986
7558
|
const dir = doc.directives.toString(doc);
|
|
6987
7559
|
if (dir) {
|
|
6988
|
-
|
|
7560
|
+
lines2.push(dir);
|
|
6989
7561
|
hasDirectives = true;
|
|
6990
7562
|
} else if (doc.directives.docStart)
|
|
6991
7563
|
hasDirectives = true;
|
|
6992
7564
|
}
|
|
6993
7565
|
if (hasDirectives)
|
|
6994
|
-
|
|
7566
|
+
lines2.push("---");
|
|
6995
7567
|
const ctx = stringify2.createStringifyContext(doc, options);
|
|
6996
7568
|
const { commentString } = ctx.options;
|
|
6997
7569
|
if (doc.commentBefore) {
|
|
6998
|
-
if (
|
|
6999
|
-
|
|
7570
|
+
if (lines2.length !== 1)
|
|
7571
|
+
lines2.unshift("");
|
|
7000
7572
|
const cs = commentString(doc.commentBefore);
|
|
7001
|
-
|
|
7573
|
+
lines2.unshift(stringifyComment.indentComment(cs, ""));
|
|
7002
7574
|
}
|
|
7003
7575
|
let chompKeep = false;
|
|
7004
7576
|
let contentComment = null;
|
|
7005
7577
|
if (doc.contents) {
|
|
7006
7578
|
if (identity.isNode(doc.contents)) {
|
|
7007
7579
|
if (doc.contents.spaceBefore && hasDirectives)
|
|
7008
|
-
|
|
7580
|
+
lines2.push("");
|
|
7009
7581
|
if (doc.contents.commentBefore) {
|
|
7010
7582
|
const cs = commentString(doc.contents.commentBefore);
|
|
7011
|
-
|
|
7583
|
+
lines2.push(stringifyComment.indentComment(cs, ""));
|
|
7012
7584
|
}
|
|
7013
7585
|
ctx.forceBlockIndent = !!doc.comment;
|
|
7014
7586
|
contentComment = doc.contents.comment;
|
|
@@ -7017,36 +7589,36 @@ var require_stringifyDocument = __commonJS({
|
|
|
7017
7589
|
let body = stringify2.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
|
|
7018
7590
|
if (contentComment)
|
|
7019
7591
|
body += stringifyComment.lineComment(body, "", commentString(contentComment));
|
|
7020
|
-
if ((body[0] === "|" || body[0] === ">") &&
|
|
7021
|
-
|
|
7592
|
+
if ((body[0] === "|" || body[0] === ">") && lines2[lines2.length - 1] === "---") {
|
|
7593
|
+
lines2[lines2.length - 1] = `--- ${body}`;
|
|
7022
7594
|
} else
|
|
7023
|
-
|
|
7595
|
+
lines2.push(body);
|
|
7024
7596
|
} else {
|
|
7025
|
-
|
|
7597
|
+
lines2.push(stringify2.stringify(doc.contents, ctx));
|
|
7026
7598
|
}
|
|
7027
7599
|
if (doc.directives?.docEnd) {
|
|
7028
7600
|
if (doc.comment) {
|
|
7029
7601
|
const cs = commentString(doc.comment);
|
|
7030
7602
|
if (cs.includes("\n")) {
|
|
7031
|
-
|
|
7032
|
-
|
|
7603
|
+
lines2.push("...");
|
|
7604
|
+
lines2.push(stringifyComment.indentComment(cs, ""));
|
|
7033
7605
|
} else {
|
|
7034
|
-
|
|
7606
|
+
lines2.push(`... ${cs}`);
|
|
7035
7607
|
}
|
|
7036
7608
|
} else {
|
|
7037
|
-
|
|
7609
|
+
lines2.push("...");
|
|
7038
7610
|
}
|
|
7039
7611
|
} else {
|
|
7040
7612
|
let dc = doc.comment;
|
|
7041
7613
|
if (dc && chompKeep)
|
|
7042
7614
|
dc = dc.replace(/^\n+/, "");
|
|
7043
7615
|
if (dc) {
|
|
7044
|
-
if ((!chompKeep || contentComment) &&
|
|
7045
|
-
|
|
7046
|
-
|
|
7616
|
+
if ((!chompKeep || contentComment) && lines2[lines2.length - 1] !== "")
|
|
7617
|
+
lines2.push("");
|
|
7618
|
+
lines2.push(stringifyComment.indentComment(commentString(dc), ""));
|
|
7047
7619
|
}
|
|
7048
7620
|
}
|
|
7049
|
-
return
|
|
7621
|
+
return lines2.join("\n") + "\n";
|
|
7050
7622
|
}
|
|
7051
7623
|
exports.stringifyDocument = stringifyDocument;
|
|
7052
7624
|
}
|
|
@@ -8108,17 +8680,17 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
8108
8680
|
if (!header)
|
|
8109
8681
|
return { value: "", type: null, comment: "", range: [start, start, start] };
|
|
8110
8682
|
const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL;
|
|
8111
|
-
const
|
|
8112
|
-
let chompStart =
|
|
8113
|
-
for (let i =
|
|
8114
|
-
const content =
|
|
8683
|
+
const lines2 = scalar.source ? splitLines(scalar.source) : [];
|
|
8684
|
+
let chompStart = lines2.length;
|
|
8685
|
+
for (let i = lines2.length - 1; i >= 0; --i) {
|
|
8686
|
+
const content = lines2[i][1];
|
|
8115
8687
|
if (content === "" || content === "\r")
|
|
8116
8688
|
chompStart = i;
|
|
8117
8689
|
else
|
|
8118
8690
|
break;
|
|
8119
8691
|
}
|
|
8120
8692
|
if (chompStart === 0) {
|
|
8121
|
-
const value2 = header.chomp === "+" &&
|
|
8693
|
+
const value2 = header.chomp === "+" && lines2.length > 0 ? "\n".repeat(Math.max(1, lines2.length - 1)) : "";
|
|
8122
8694
|
let end2 = start + header.length;
|
|
8123
8695
|
if (scalar.source)
|
|
8124
8696
|
end2 += scalar.source.length;
|
|
@@ -8128,7 +8700,7 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
8128
8700
|
let offset = scalar.offset + header.length;
|
|
8129
8701
|
let contentStart = 0;
|
|
8130
8702
|
for (let i = 0; i < chompStart; ++i) {
|
|
8131
|
-
const [indent, content] =
|
|
8703
|
+
const [indent, content] = lines2[i];
|
|
8132
8704
|
if (content === "" || content === "\r") {
|
|
8133
8705
|
if (header.indent === 0 && indent.length > trimIndent)
|
|
8134
8706
|
trimIndent = indent.length;
|
|
@@ -8148,17 +8720,17 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
8148
8720
|
}
|
|
8149
8721
|
offset += indent.length + content.length + 1;
|
|
8150
8722
|
}
|
|
8151
|
-
for (let i =
|
|
8152
|
-
if (
|
|
8723
|
+
for (let i = lines2.length - 1; i >= chompStart; --i) {
|
|
8724
|
+
if (lines2[i][0].length > trimIndent)
|
|
8153
8725
|
chompStart = i + 1;
|
|
8154
8726
|
}
|
|
8155
8727
|
let value = "";
|
|
8156
8728
|
let sep3 = "";
|
|
8157
8729
|
let prevMoreIndented = false;
|
|
8158
8730
|
for (let i = 0; i < contentStart; ++i)
|
|
8159
|
-
value +=
|
|
8731
|
+
value += lines2[i][0].slice(trimIndent) + "\n";
|
|
8160
8732
|
for (let i = contentStart; i < chompStart; ++i) {
|
|
8161
|
-
let [indent, content] =
|
|
8733
|
+
let [indent, content] = lines2[i];
|
|
8162
8734
|
offset += indent.length + content.length + 1;
|
|
8163
8735
|
const crlf = content[content.length - 1] === "\r";
|
|
8164
8736
|
if (crlf)
|
|
@@ -8195,8 +8767,8 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
8195
8767
|
case "-":
|
|
8196
8768
|
break;
|
|
8197
8769
|
case "+":
|
|
8198
|
-
for (let i = chompStart; i <
|
|
8199
|
-
value += "\n" +
|
|
8770
|
+
for (let i = chompStart; i < lines2.length; ++i)
|
|
8771
|
+
value += "\n" + lines2[i][0].slice(trimIndent);
|
|
8200
8772
|
if (value[value.length - 1] !== "\n")
|
|
8201
8773
|
value += "\n";
|
|
8202
8774
|
break;
|
|
@@ -8271,10 +8843,10 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
8271
8843
|
const first = split[0];
|
|
8272
8844
|
const m = first.match(/^( *)/);
|
|
8273
8845
|
const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first];
|
|
8274
|
-
const
|
|
8846
|
+
const lines2 = [line0];
|
|
8275
8847
|
for (let i = 1; i < split.length; i += 2)
|
|
8276
|
-
|
|
8277
|
-
return
|
|
8848
|
+
lines2.push([split[i], split[i + 1]]);
|
|
8849
|
+
return lines2;
|
|
8278
8850
|
}
|
|
8279
8851
|
exports.resolveBlockScalar = resolveBlockScalar;
|
|
8280
8852
|
}
|
|
@@ -11025,9 +11597,9 @@ var require_dist = __commonJS({
|
|
|
11025
11597
|
function parseHeader2(commentBefore) {
|
|
11026
11598
|
if (!commentBefore)
|
|
11027
11599
|
return void 0;
|
|
11028
|
-
const
|
|
11029
|
-
for (let i =
|
|
11030
|
-
const match =
|
|
11600
|
+
const lines2 = commentBefore.split("\n");
|
|
11601
|
+
for (let i = lines2.length - 1; i >= 0; i -= 1) {
|
|
11602
|
+
const match = lines2[i]?.match(/^\s*==\s*(.+?)\s*$/);
|
|
11031
11603
|
if (!match?.[1])
|
|
11032
11604
|
continue;
|
|
11033
11605
|
const text = match[1];
|
|
@@ -11230,32 +11802,32 @@ var init_dump_config = __esm({
|
|
|
11230
11802
|
// packages/adapter-dsh/dist/hooks.js
|
|
11231
11803
|
import { Buffer as Buffer3 } from "node:buffer";
|
|
11232
11804
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
11233
|
-
import { homedir as
|
|
11234
|
-
import { dirname as
|
|
11805
|
+
import { homedir as homedir9 } from "node:os";
|
|
11806
|
+
import { dirname as dirname11, join as join15 } from "node:path";
|
|
11235
11807
|
function feedbackSignalsDsh(_options) {
|
|
11236
11808
|
return { wiredEvents: [], feedback: "unavailable" };
|
|
11237
11809
|
}
|
|
11238
11810
|
function resolveDshHome(options) {
|
|
11239
11811
|
if (options?.homeDir !== void 0)
|
|
11240
|
-
return
|
|
11812
|
+
return join15(options.homeDir, ".dsh");
|
|
11241
11813
|
const envHome = process.env.DSH_HOME;
|
|
11242
11814
|
if (envHome !== void 0 && envHome !== "")
|
|
11243
11815
|
return envHome;
|
|
11244
|
-
return
|
|
11816
|
+
return join15(homedir9(), ".dsh");
|
|
11245
11817
|
}
|
|
11246
11818
|
function resolveDshHookPaths(options) {
|
|
11247
|
-
const home = options?.homeDir ??
|
|
11819
|
+
const home = options?.homeDir ?? homedir9();
|
|
11248
11820
|
const dshHome = resolveDshHome(options);
|
|
11249
|
-
const wingmanRoot2 =
|
|
11250
|
-
const hooksDir =
|
|
11821
|
+
const wingmanRoot2 = join15(home, ".wingman");
|
|
11822
|
+
const hooksDir = join15(wingmanRoot2, "dsh");
|
|
11251
11823
|
return {
|
|
11252
11824
|
dshHome,
|
|
11253
|
-
patchFile:
|
|
11825
|
+
patchFile: join15(dshHome, "cordis.patch.yml"),
|
|
11254
11826
|
wingmanRoot: wingmanRoot2,
|
|
11255
|
-
hooksConfigFile:
|
|
11256
|
-
forwarderFile:
|
|
11257
|
-
stateFile:
|
|
11258
|
-
backupsRoot:
|
|
11827
|
+
hooksConfigFile: join15(hooksDir, "hooks-claude-code.json"),
|
|
11828
|
+
forwarderFile: join15(hooksDir, "forward.mjs"),
|
|
11829
|
+
stateFile: join15(wingmanRoot2, "state", "dsh-hooks.json"),
|
|
11830
|
+
backupsRoot: join15(wingmanRoot2, "backups")
|
|
11259
11831
|
};
|
|
11260
11832
|
}
|
|
11261
11833
|
function yamlSingleQuote(value) {
|
|
@@ -11295,7 +11867,7 @@ async function readIfExists(file) {
|
|
|
11295
11867
|
throw error;
|
|
11296
11868
|
}
|
|
11297
11869
|
}
|
|
11298
|
-
function
|
|
11870
|
+
function decodeUtf8Strict3(buffer, file) {
|
|
11299
11871
|
const text = buffer.toString("utf8");
|
|
11300
11872
|
if (!buffer.equals(Buffer3.from(text, "utf8"))) {
|
|
11301
11873
|
throw new Error(`\u62D2\u5199:${file} \u4E0D\u662F UTF-8 \u6587\u672C,\u672A\u77E5\u683C\u5F0F\u4E0D\u731C`);
|
|
@@ -11328,10 +11900,10 @@ function tryParsePatchEntries(text, file) {
|
|
|
11328
11900
|
}
|
|
11329
11901
|
}
|
|
11330
11902
|
function stripLiveHooksBlocks(text, file) {
|
|
11331
|
-
const
|
|
11903
|
+
const lines2 = text.split("\n");
|
|
11332
11904
|
const kept = [];
|
|
11333
11905
|
let inBlock = false;
|
|
11334
|
-
for (const line of
|
|
11906
|
+
for (const line of lines2) {
|
|
11335
11907
|
const trimmed = line.trim();
|
|
11336
11908
|
if (!inBlock) {
|
|
11337
11909
|
if (trimmed.startsWith(HOOKS_MARKER_BEGIN_PREFIX)) {
|
|
@@ -11361,11 +11933,11 @@ function composeInstalledText(baseText, entryCount, block) {
|
|
|
11361
11933
|
return block;
|
|
11362
11934
|
let base = baseText;
|
|
11363
11935
|
if (entryCount === 0) {
|
|
11364
|
-
const
|
|
11365
|
-
const rootIndex =
|
|
11936
|
+
const lines2 = base.split("\n");
|
|
11937
|
+
const rootIndex = lines2.findIndex((line) => /^\s*\[\]\s*$/.test(line));
|
|
11366
11938
|
if (rootIndex !== -1) {
|
|
11367
|
-
|
|
11368
|
-
base =
|
|
11939
|
+
lines2.splice(rootIndex, 1);
|
|
11940
|
+
base = lines2.join("\n");
|
|
11369
11941
|
}
|
|
11370
11942
|
}
|
|
11371
11943
|
if (base === "")
|
|
@@ -11374,7 +11946,7 @@ function composeInstalledText(baseText, entryCount, block) {
|
|
|
11374
11946
|
base += "\n";
|
|
11375
11947
|
return base + block;
|
|
11376
11948
|
}
|
|
11377
|
-
async function
|
|
11949
|
+
async function readState5(paths) {
|
|
11378
11950
|
const buffer = await readIfExists(paths.stateFile);
|
|
11379
11951
|
if (buffer === void 0)
|
|
11380
11952
|
return void 0;
|
|
@@ -11383,19 +11955,19 @@ async function readState3(paths) {
|
|
|
11383
11955
|
throw new Error(`\u672A\u77E5\u7684\u63A5\u7EBF\u72B6\u6001\u7248\u672C:${String(state.version)},\u4E0D\u731C`);
|
|
11384
11956
|
return state;
|
|
11385
11957
|
}
|
|
11386
|
-
async function
|
|
11387
|
-
await mkdir(
|
|
11958
|
+
async function writeState5(paths, state) {
|
|
11959
|
+
await mkdir(dirname11(paths.stateFile), { recursive: true });
|
|
11388
11960
|
await writeFile(paths.stateFile, `${JSON.stringify(state, void 0, 2)}
|
|
11389
11961
|
`, "utf8");
|
|
11390
11962
|
}
|
|
11391
11963
|
async function backupPatchFile(paths, bytes) {
|
|
11392
|
-
const dir =
|
|
11964
|
+
const dir = join15(paths.backupsRoot, (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
|
|
11393
11965
|
await mkdir(dir, { recursive: true });
|
|
11394
|
-
await writeFile(
|
|
11966
|
+
await writeFile(join15(dir, "cordis.patch.yml"), bytes);
|
|
11395
11967
|
return dir;
|
|
11396
11968
|
}
|
|
11397
11969
|
async function writeOwnedFiles(paths, url) {
|
|
11398
|
-
await mkdir(
|
|
11970
|
+
await mkdir(dirname11(paths.hooksConfigFile), { recursive: true });
|
|
11399
11971
|
await writeFile(paths.forwarderFile, FORWARDER_SOURCE, "utf8");
|
|
11400
11972
|
await writeFile(paths.hooksConfigFile, buildHooksConfig(paths.forwarderFile, url), "utf8");
|
|
11401
11973
|
}
|
|
@@ -11407,7 +11979,7 @@ async function installDshHooks(port, options) {
|
|
|
11407
11979
|
const url = `http://127.0.0.1:${port}/hook?harness=dsh`;
|
|
11408
11980
|
const block = buildPatchBlock(paths.hooksConfigFile);
|
|
11409
11981
|
const currentBuffer = await readIfExists(paths.patchFile);
|
|
11410
|
-
const currentText = currentBuffer === void 0 ? void 0 :
|
|
11982
|
+
const currentText = currentBuffer === void 0 ? void 0 : decodeUtf8Strict3(currentBuffer, paths.patchFile);
|
|
11411
11983
|
const surgicalText = currentText === void 0 ? void 0 : stripLiveHooksBlocks(currentText, paths.patchFile);
|
|
11412
11984
|
const entriesBefore = surgicalText === void 0 ? [] : parsePatchEntries(surgicalText, paths.patchFile);
|
|
11413
11985
|
if (hasWingmanRow(entriesBefore)) {
|
|
@@ -11420,11 +11992,11 @@ async function installDshHooks(port, options) {
|
|
|
11420
11992
|
throw new Error(`\u62D2\u5199:${paths.patchFile} \u8FFD\u52A0\u540E\u5F62\u72B6\u6821\u9A8C\u5931\u8D25(\u539F\u6587\u4EF6\u5F62\u6001\u65E0\u6CD5\u5B89\u5168\u8FFD\u52A0),\u4E0D\u731C`);
|
|
11421
11993
|
}
|
|
11422
11994
|
await writeOwnedFiles(paths, url);
|
|
11423
|
-
const previousState = await
|
|
11995
|
+
const previousState = await readState5(paths);
|
|
11424
11996
|
if (composed === currentText) {
|
|
11425
11997
|
if (previousState === void 0) {
|
|
11426
11998
|
const recovered = surgicalText ?? "";
|
|
11427
|
-
await
|
|
11999
|
+
await writeState5(paths, {
|
|
11428
12000
|
version: 1,
|
|
11429
12001
|
port,
|
|
11430
12002
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -11438,7 +12010,7 @@ async function installDshHooks(port, options) {
|
|
|
11438
12010
|
if (currentBuffer !== void 0)
|
|
11439
12011
|
backupDir = await backupPatchFile(paths, currentBuffer);
|
|
11440
12012
|
const original = previousState?.original ?? (currentBuffer === void 0 ? { existed: false } : { existed: true, bytesBase64: currentBuffer.toString("base64") });
|
|
11441
|
-
await
|
|
12013
|
+
await writeState5(paths, {
|
|
11442
12014
|
version: 1,
|
|
11443
12015
|
port,
|
|
11444
12016
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -11451,10 +12023,10 @@ async function installDshHooks(port, options) {
|
|
|
11451
12023
|
}
|
|
11452
12024
|
async function uninstallDshHooks(options) {
|
|
11453
12025
|
const paths = resolveDshHookPaths(options);
|
|
11454
|
-
const state = await
|
|
12026
|
+
const state = await readState5(paths);
|
|
11455
12027
|
const currentBuffer = await readIfExists(paths.patchFile);
|
|
11456
12028
|
if (currentBuffer !== void 0) {
|
|
11457
|
-
const currentText =
|
|
12029
|
+
const currentText = decodeUtf8Strict3(currentBuffer, paths.patchFile);
|
|
11458
12030
|
if (currentText.includes(HOOKS_MARKER_BEGIN_PREFIX)) {
|
|
11459
12031
|
const surgical = stripLiveHooksBlocks(currentText, paths.patchFile);
|
|
11460
12032
|
const restored = resolveRestoredText(surgical, state, paths);
|
|
@@ -11543,18 +12115,18 @@ if (url !== undefined) {
|
|
|
11543
12115
|
import { Buffer as Buffer4 } from "node:buffer";
|
|
11544
12116
|
import { randomBytes } from "node:crypto";
|
|
11545
12117
|
import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
11546
|
-
import { dirname as
|
|
12118
|
+
import { dirname as dirname12, isAbsolute as isAbsolute2, join as join16, relative } from "node:path";
|
|
11547
12119
|
function buildToggleBlock(instanceId, flags) {
|
|
11548
|
-
const
|
|
12120
|
+
const lines2 = [`${TOGGLE_BEGIN_PREFIX}${instanceId}${TOGGLE_BEGIN_SUFFIX}`];
|
|
11549
12121
|
if (flags.fileCreated)
|
|
11550
|
-
|
|
12122
|
+
lines2.push(`${FLAG_PREFIX}${FLAG_FILE_CREATED}`);
|
|
11551
12123
|
if (flags.newlineAdded)
|
|
11552
|
-
|
|
12124
|
+
lines2.push(`${FLAG_PREFIX}${FLAG_NEWLINE_ADDED}`);
|
|
11553
12125
|
if (flags.emptyRootLine !== void 0) {
|
|
11554
|
-
|
|
12126
|
+
lines2.push(`${FLAG_PREFIX}${FLAG_EMPTY_ROOT_LINE}${flags.emptyRootLine}`);
|
|
11555
12127
|
}
|
|
11556
|
-
|
|
11557
|
-
return
|
|
12128
|
+
lines2.push(`- id: ${yamlSingleQuote(instanceId)}`, " disabled: true", `${TOGGLE_END_PREFIX}${instanceId}${TOGGLE_END_SUFFIX}`, "");
|
|
12129
|
+
return lines2.join("\n");
|
|
11558
12130
|
}
|
|
11559
12131
|
function parseBeginMarker(trimmed) {
|
|
11560
12132
|
if (!trimmed.startsWith(TOGGLE_BEGIN_PREFIX))
|
|
@@ -11655,10 +12227,10 @@ function stripAllToggleBlocks(text, blocks) {
|
|
|
11655
12227
|
return out + text.slice(cursor);
|
|
11656
12228
|
}
|
|
11657
12229
|
function stripHooksBlocks(text) {
|
|
11658
|
-
const
|
|
12230
|
+
const lines2 = text.split("\n");
|
|
11659
12231
|
const kept = [];
|
|
11660
12232
|
let inBlock = false;
|
|
11661
|
-
for (const line of
|
|
12233
|
+
for (const line of lines2) {
|
|
11662
12234
|
const trimmed = line.trim();
|
|
11663
12235
|
if (!inBlock) {
|
|
11664
12236
|
if (trimmed.startsWith(HOOKS_MARKER_BEGIN_PREFIX))
|
|
@@ -11679,8 +12251,8 @@ function referencesInstanceId(entries, instanceId) {
|
|
|
11679
12251
|
return Array.isArray(candidate.insert) && candidate.insert.some((row) => row.id === instanceId);
|
|
11680
12252
|
});
|
|
11681
12253
|
}
|
|
11682
|
-
function emptyRootLineIndex(
|
|
11683
|
-
return
|
|
12254
|
+
function emptyRootLineIndex(lines2) {
|
|
12255
|
+
return lines2.findIndex((line) => /^\s*\[\]\s*$/.test(line));
|
|
11684
12256
|
}
|
|
11685
12257
|
function composeToggleOff(current, instanceId, entryCount) {
|
|
11686
12258
|
if (current === void 0) {
|
|
@@ -11689,12 +12261,12 @@ function composeToggleOff(current, instanceId, entryCount) {
|
|
|
11689
12261
|
let base = current;
|
|
11690
12262
|
const flags = {};
|
|
11691
12263
|
if (entryCount === 0) {
|
|
11692
|
-
const
|
|
11693
|
-
const rootIndex = emptyRootLineIndex(
|
|
12264
|
+
const lines2 = base.split("\n");
|
|
12265
|
+
const rootIndex = emptyRootLineIndex(lines2);
|
|
11694
12266
|
if (rootIndex !== -1) {
|
|
11695
12267
|
flags.emptyRootLine = rootIndex;
|
|
11696
|
-
|
|
11697
|
-
base =
|
|
12268
|
+
lines2.splice(rootIndex, 1);
|
|
12269
|
+
base = lines2.join("\n");
|
|
11698
12270
|
}
|
|
11699
12271
|
}
|
|
11700
12272
|
if (base !== "" && !base.endsWith("\n")) {
|
|
@@ -11716,9 +12288,9 @@ function composeToggleOn(current, block, file) {
|
|
|
11716
12288
|
const entriesAfter = parsePatchEntries(removed, file);
|
|
11717
12289
|
if (block.emptyRootLine !== void 0) {
|
|
11718
12290
|
if (entriesAfter.length === 0) {
|
|
11719
|
-
const
|
|
11720
|
-
|
|
11721
|
-
removed =
|
|
12291
|
+
const lines2 = removed.split("\n");
|
|
12292
|
+
lines2.splice(Math.min(block.emptyRootLine, lines2.length), 0, "[]");
|
|
12293
|
+
removed = lines2.join("\n");
|
|
11722
12294
|
} else {
|
|
11723
12295
|
removed = transferEmptyRootFlag(removed, block.emptyRootLine, file);
|
|
11724
12296
|
}
|
|
@@ -11803,7 +12375,7 @@ async function planDshToggle(action, dump, options) {
|
|
|
11803
12375
|
}
|
|
11804
12376
|
const paths = resolveDshHookPaths(options);
|
|
11805
12377
|
const buffer = await readIfExists(paths.patchFile);
|
|
11806
|
-
const text = buffer === void 0 ? void 0 :
|
|
12378
|
+
const text = buffer === void 0 ? void 0 : decodeUtf8Strict3(buffer, paths.patchFile);
|
|
11807
12379
|
const blocks = text === void 0 ? [] : findToggleBlocks(text, paths.patchFile);
|
|
11808
12380
|
const myBlock = blocks.find((b) => b.instanceId === row.id);
|
|
11809
12381
|
const entriesAll = text === void 0 ? [] : parsePatchEntries(text, paths.patchFile);
|
|
@@ -11849,7 +12421,7 @@ async function planDshToggle(action, dump, options) {
|
|
|
11849
12421
|
harness: "dsh",
|
|
11850
12422
|
action: toggle,
|
|
11851
12423
|
writes: [change],
|
|
11852
|
-
backupDir:
|
|
12424
|
+
backupDir: join16(paths.backupsRoot, backupDirName()),
|
|
11853
12425
|
references: scanDshReferences(paths.patchFile, text, dump, row),
|
|
11854
12426
|
// 生效时机:patch 层在 boot 时组装(composeProfile),保证下次启动生效;钉定版本
|
|
11855
12427
|
// 还会 watchUserPatches 热重载家目录层,但该监听失败被静默吞掉(suppressShutdownError)
|
|
@@ -11909,12 +12481,12 @@ async function applyDshToggle(plan, options) {
|
|
|
11909
12481
|
}
|
|
11910
12482
|
await mkdir2(plan.backupDir, { recursive: true });
|
|
11911
12483
|
if (buffer !== void 0) {
|
|
11912
|
-
await writeFile2(
|
|
12484
|
+
await writeFile2(join16(plan.backupDir, "cordis.patch.yml"), buffer);
|
|
11913
12485
|
}
|
|
11914
12486
|
if (change.kind === "delete") {
|
|
11915
12487
|
await rm2(paths.patchFile);
|
|
11916
12488
|
} else {
|
|
11917
|
-
await mkdir2(
|
|
12489
|
+
await mkdir2(dirname12(paths.patchFile), { recursive: true });
|
|
11918
12490
|
await writeFile2(paths.patchFile, change.after ?? "", "utf8");
|
|
11919
12491
|
}
|
|
11920
12492
|
return { ok: true, backupDir: plan.backupDir };
|
|
@@ -11942,6 +12514,254 @@ var init_toggle = __esm({
|
|
|
11942
12514
|
}
|
|
11943
12515
|
});
|
|
11944
12516
|
|
|
12517
|
+
// packages/adapter-dsh/dist/protocol.js
|
|
12518
|
+
import { execFileSync } from "node:child_process";
|
|
12519
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync12, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "node:fs";
|
|
12520
|
+
import { homedir as homedir10 } from "node:os";
|
|
12521
|
+
import { basename as basename7, dirname as dirname13, join as join17, resolve as resolve7 } from "node:path";
|
|
12522
|
+
function protocolStatusDsh(options) {
|
|
12523
|
+
const paths = resolveOptions3(options);
|
|
12524
|
+
if (!existsSync11(paths.targetPath))
|
|
12525
|
+
return status2(paths.targetPath, "not_installed", options);
|
|
12526
|
+
try {
|
|
12527
|
+
const file = readAgents2(paths.targetPath);
|
|
12528
|
+
if (file.identity === void 0)
|
|
12529
|
+
return status2(paths.targetPath, "not_installed", options);
|
|
12530
|
+
const state = stateForIdentity3(file.identity);
|
|
12531
|
+
return status2(paths.targetPath, state, options, file.identity);
|
|
12532
|
+
} catch (error) {
|
|
12533
|
+
return status2(paths.targetPath, "invalid", options, void 0, error.message);
|
|
12534
|
+
}
|
|
12535
|
+
}
|
|
12536
|
+
function installProtocolDsh(options) {
|
|
12537
|
+
const paths = resolveOptions3(options);
|
|
12538
|
+
const existing = existsSync11(paths.targetPath) ? readAgents2(paths.targetPath) : void 0;
|
|
12539
|
+
const previousState = stateForIdentity3(existing?.identity);
|
|
12540
|
+
if (previousState === "installed") {
|
|
12541
|
+
return overlayLoadState(result3(paths.targetPath, "install", false, previousState, existing?.text ?? "", true), options);
|
|
12542
|
+
}
|
|
12543
|
+
if (previousState === "modified")
|
|
12544
|
+
throw modifiedError3(paths.targetPath, "\u5B89\u88C5");
|
|
12545
|
+
const before = existing?.text ?? "";
|
|
12546
|
+
const lineEnding = preferredMarkdownLineEnding(before);
|
|
12547
|
+
const block = buildPlanProtocolBlock().replaceAll("\n", lineEnding);
|
|
12548
|
+
const after = existing?.split.block ? existing.split.before + block + existing.split.after : appendBlock2(before, block, lineEnding);
|
|
12549
|
+
const installedRemainder = splitPlanProtocolBlock(after, paths.targetPath).remainder;
|
|
12550
|
+
const backupDir = nextBackupDir4(paths.homeDir);
|
|
12551
|
+
if (!paths.dryRun) {
|
|
12552
|
+
const prior = readState6(paths.statePath, paths.targetPath);
|
|
12553
|
+
backupTarget3(backupDir, paths.targetPath, existing?.bytes);
|
|
12554
|
+
writeState6(paths.statePath, {
|
|
12555
|
+
version: 1,
|
|
12556
|
+
projectDir: paths.projectDir,
|
|
12557
|
+
targetPath: paths.targetPath,
|
|
12558
|
+
protocolVersion: PLAN_PROTOCOL_VERSION,
|
|
12559
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12560
|
+
backupDir,
|
|
12561
|
+
installedRemainderSha256: sha256Hex(installedRemainder),
|
|
12562
|
+
original: prior?.original ?? (existing === void 0 ? { existed: false } : { existed: true, base64: existing.bytes.toString("base64") })
|
|
12563
|
+
});
|
|
12564
|
+
writeFileSync9(paths.targetPath, after);
|
|
12565
|
+
}
|
|
12566
|
+
return overlayLoadState({
|
|
12567
|
+
...result3(paths.targetPath, "install", true, previousState, before, true),
|
|
12568
|
+
state: "installed",
|
|
12569
|
+
version: PLAN_PROTOCOL_VERSION,
|
|
12570
|
+
after,
|
|
12571
|
+
backupDir,
|
|
12572
|
+
totalBytes: Buffer.byteLength(after)
|
|
12573
|
+
}, options);
|
|
12574
|
+
}
|
|
12575
|
+
function uninstallProtocolDsh(options) {
|
|
12576
|
+
const paths = resolveOptions3(options);
|
|
12577
|
+
if (!existsSync11(paths.targetPath)) {
|
|
12578
|
+
return result3(paths.targetPath, "uninstall", false, "not_installed", "", false);
|
|
12579
|
+
}
|
|
12580
|
+
const existing = readAgents2(paths.targetPath);
|
|
12581
|
+
if (existing.identity === void 0) {
|
|
12582
|
+
return result3(paths.targetPath, "uninstall", false, "not_installed", existing.text, true);
|
|
12583
|
+
}
|
|
12584
|
+
const previousState = stateForIdentity3(existing.identity);
|
|
12585
|
+
if (previousState === "modified")
|
|
12586
|
+
throw modifiedError3(paths.targetPath, "\u5378\u8F7D");
|
|
12587
|
+
const state = readState6(paths.statePath, paths.targetPath);
|
|
12588
|
+
const canRestore = state !== void 0 && sha256Hex(existing.split.remainder) === state.installedRemainderSha256;
|
|
12589
|
+
const restored = canRestore ? originalBytes2(state) : void 0;
|
|
12590
|
+
const deleteTarget = canRestore ? state?.original.existed === false : existing.split.remainder.trim() === "";
|
|
12591
|
+
const after = deleteTarget ? "" : restored?.toString("utf8") ?? existing.split.remainder;
|
|
12592
|
+
const backupDir = nextBackupDir4(paths.homeDir);
|
|
12593
|
+
if (!paths.dryRun) {
|
|
12594
|
+
backupTarget3(backupDir, paths.targetPath, existing.bytes);
|
|
12595
|
+
if (deleteTarget)
|
|
12596
|
+
rmSync6(paths.targetPath, { force: true });
|
|
12597
|
+
else
|
|
12598
|
+
writeFileSync9(paths.targetPath, restored ?? existing.split.remainder);
|
|
12599
|
+
rmSync6(paths.statePath, { force: true });
|
|
12600
|
+
}
|
|
12601
|
+
return {
|
|
12602
|
+
...result3(paths.targetPath, "uninstall", true, previousState, existing.text, !deleteTarget),
|
|
12603
|
+
state: "not_installed",
|
|
12604
|
+
message: canRestore ? `${SHARED_MESSAGE2}\uFF1B\u5378\u8F7D\u540C\u65F6\u5F71\u54CD Codex \u4E0E dsh` : `${SHARED_MESSAGE2}\uFF1B\u5378\u8F7D\u540C\u65F6\u5F71\u54CD Codex \u4E0E dsh\uFF1B\u5DF2\u4FDD\u7559\u7528\u6237\u6539\u52A8`,
|
|
12605
|
+
after,
|
|
12606
|
+
backupDir,
|
|
12607
|
+
totalBytes: Buffer.byteLength(after),
|
|
12608
|
+
restoredOriginal: canRestore
|
|
12609
|
+
};
|
|
12610
|
+
}
|
|
12611
|
+
function resolveOptions3(options) {
|
|
12612
|
+
const homeDir = resolve7(options?.homeDir ?? homedir10());
|
|
12613
|
+
const projectDir = resolve7(options?.projectDir ?? process.cwd());
|
|
12614
|
+
const targetPath2 = join17(projectDir, "AGENTS.md");
|
|
12615
|
+
return {
|
|
12616
|
+
homeDir,
|
|
12617
|
+
projectDir,
|
|
12618
|
+
targetPath: targetPath2,
|
|
12619
|
+
statePath: join17(homeDir, ".wingman", "state", `plan-protocol-agents-${sha256Hex(projectDir)}.json`),
|
|
12620
|
+
dryRun: options?.dryRun ?? false
|
|
12621
|
+
};
|
|
12622
|
+
}
|
|
12623
|
+
function readAgents2(targetPath2) {
|
|
12624
|
+
const bytes = readFileSync12(targetPath2);
|
|
12625
|
+
const text = decodeUtf8Strict(bytes, targetPath2);
|
|
12626
|
+
let split;
|
|
12627
|
+
try {
|
|
12628
|
+
split = splitPlanProtocolBlock(text, targetPath2);
|
|
12629
|
+
} catch (error) {
|
|
12630
|
+
throw new Error(`${targetPath2}: ${error.message}`);
|
|
12631
|
+
}
|
|
12632
|
+
return {
|
|
12633
|
+
bytes,
|
|
12634
|
+
text,
|
|
12635
|
+
split,
|
|
12636
|
+
...split.block !== void 0 ? { identity: inspectPlanProtocolBlock(split.block) } : {}
|
|
12637
|
+
};
|
|
12638
|
+
}
|
|
12639
|
+
function appendBlock2(text, block, lineEnding) {
|
|
12640
|
+
if (text === "")
|
|
12641
|
+
return block;
|
|
12642
|
+
let base = text;
|
|
12643
|
+
if (!base.endsWith("\n"))
|
|
12644
|
+
base += lineEnding;
|
|
12645
|
+
if (!base.endsWith(`${lineEnding}${lineEnding}`))
|
|
12646
|
+
base += lineEnding;
|
|
12647
|
+
return base + block;
|
|
12648
|
+
}
|
|
12649
|
+
function stateForIdentity3(identity) {
|
|
12650
|
+
if (identity === void 0)
|
|
12651
|
+
return "not_installed";
|
|
12652
|
+
if (identity.kind === "current")
|
|
12653
|
+
return "installed";
|
|
12654
|
+
if (identity.kind === "legacy")
|
|
12655
|
+
return "outdated";
|
|
12656
|
+
return "modified";
|
|
12657
|
+
}
|
|
12658
|
+
function status2(targetPath2, state, options, identity, reason) {
|
|
12659
|
+
const disabled = state === "installed" ? agentInstructionsDisabled(options) : void 0;
|
|
12660
|
+
const loadedState = disabled === true ? "installed_not_loaded" : state;
|
|
12661
|
+
return {
|
|
12662
|
+
harness: "dsh",
|
|
12663
|
+
state: loadedState,
|
|
12664
|
+
targetPath: targetPath2,
|
|
12665
|
+
shared: true,
|
|
12666
|
+
...disabled !== void 0 ? { pluginDisabled: disabled } : {},
|
|
12667
|
+
message: disabled === true ? `${SHARED_MESSAGE2}\uFF1B${DISABLED_MESSAGE}` : SHARED_MESSAGE2,
|
|
12668
|
+
...identity?.version !== void 0 ? { version: identity.version } : {},
|
|
12669
|
+
...reason !== void 0 ? { reason } : {}
|
|
12670
|
+
};
|
|
12671
|
+
}
|
|
12672
|
+
function result3(targetPath2, operation, changed, previousState, before, afterExists) {
|
|
12673
|
+
return {
|
|
12674
|
+
harness: "dsh",
|
|
12675
|
+
state: previousState,
|
|
12676
|
+
targetPath: targetPath2,
|
|
12677
|
+
shared: true,
|
|
12678
|
+
message: SHARED_MESSAGE2,
|
|
12679
|
+
operation,
|
|
12680
|
+
changed,
|
|
12681
|
+
previousState,
|
|
12682
|
+
before,
|
|
12683
|
+
after: before,
|
|
12684
|
+
afterExists,
|
|
12685
|
+
effectiveAt: "next_session",
|
|
12686
|
+
totalBytes: Buffer.byteLength(before)
|
|
12687
|
+
};
|
|
12688
|
+
}
|
|
12689
|
+
function overlayLoadState(resultValue, options) {
|
|
12690
|
+
const disabled = agentInstructionsDisabled(options);
|
|
12691
|
+
if (disabled === void 0)
|
|
12692
|
+
return resultValue;
|
|
12693
|
+
return {
|
|
12694
|
+
...resultValue,
|
|
12695
|
+
state: disabled ? "installed_not_loaded" : "installed",
|
|
12696
|
+
pluginDisabled: disabled,
|
|
12697
|
+
message: disabled ? `${SHARED_MESSAGE2}\uFF1B${DISABLED_MESSAGE}` : SHARED_MESSAGE2
|
|
12698
|
+
};
|
|
12699
|
+
}
|
|
12700
|
+
function agentInstructionsDisabled(options) {
|
|
12701
|
+
let text;
|
|
12702
|
+
try {
|
|
12703
|
+
if (options?.dumpConfigText !== void 0)
|
|
12704
|
+
text = options.dumpConfigText;
|
|
12705
|
+
else if (options?.dumpConfigPath !== void 0)
|
|
12706
|
+
text = readFileSync12(options.dumpConfigPath, "utf8");
|
|
12707
|
+
else {
|
|
12708
|
+
text = execFileSync("dsh", ["--profile", options?.profile ?? "web", "--dump-config"], {
|
|
12709
|
+
encoding: "utf8",
|
|
12710
|
+
timeout: 1e4,
|
|
12711
|
+
maxBuffer: 16 * 1024 * 1024
|
|
12712
|
+
});
|
|
12713
|
+
}
|
|
12714
|
+
const row = parseDumpConfig(text).find((entry) => entry.id === "agent-instructions" && entry.name === "@deepseek-ai/dsh-agent-instructions");
|
|
12715
|
+
return row?.disabled === true;
|
|
12716
|
+
} catch {
|
|
12717
|
+
return void 0;
|
|
12718
|
+
}
|
|
12719
|
+
}
|
|
12720
|
+
function modifiedError3(targetPath2, operation) {
|
|
12721
|
+
return new Error(`${targetPath2}: \u53D7\u7BA1\u5757\u5DF2\u88AB\u4FEE\u6539\uFF0C\u62D2\u7EDD${operation}\u5E76\u4FDD\u7559\u7528\u6237\u5185\u5BB9`);
|
|
12722
|
+
}
|
|
12723
|
+
function nextBackupDir4(homeDir) {
|
|
12724
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
12725
|
+
const root = join17(homeDir, ".wingman", "backups");
|
|
12726
|
+
let dir = join17(root, stamp);
|
|
12727
|
+
for (let n = 2; existsSync11(dir); n += 1)
|
|
12728
|
+
dir = join17(root, `${stamp}-${n}`);
|
|
12729
|
+
return dir;
|
|
12730
|
+
}
|
|
12731
|
+
function backupTarget3(backupDir, targetPath2, bytes) {
|
|
12732
|
+
mkdirSync8(backupDir, { recursive: true });
|
|
12733
|
+
if (bytes !== void 0)
|
|
12734
|
+
writeFileSync9(join17(backupDir, basename7(targetPath2)), bytes);
|
|
12735
|
+
}
|
|
12736
|
+
function readState6(statePath2, targetPath2) {
|
|
12737
|
+
if (!existsSync11(statePath2))
|
|
12738
|
+
return void 0;
|
|
12739
|
+
try {
|
|
12740
|
+
const value = JSON.parse(readFileSync12(statePath2, "utf8"));
|
|
12741
|
+
return value.version === 1 && value.targetPath === targetPath2 ? value : void 0;
|
|
12742
|
+
} catch {
|
|
12743
|
+
return void 0;
|
|
12744
|
+
}
|
|
12745
|
+
}
|
|
12746
|
+
function writeState6(statePath2, state) {
|
|
12747
|
+
mkdirSync8(dirname13(statePath2), { recursive: true });
|
|
12748
|
+
writeFileSync9(statePath2, `${JSON.stringify(state, null, 2)}
|
|
12749
|
+
`);
|
|
12750
|
+
}
|
|
12751
|
+
function originalBytes2(state) {
|
|
12752
|
+
return state.original.existed ? Buffer.from(state.original.base64 ?? "", "base64") : void 0;
|
|
12753
|
+
}
|
|
12754
|
+
var SHARED_MESSAGE2, DISABLED_MESSAGE;
|
|
12755
|
+
var init_protocol3 = __esm({
|
|
12756
|
+
"packages/adapter-dsh/dist/protocol.js"() {
|
|
12757
|
+
"use strict";
|
|
12758
|
+
init_dist();
|
|
12759
|
+
init_dump_config();
|
|
12760
|
+
SHARED_MESSAGE2 = "\u6B64 AGENTS.md \u534F\u8BAE\u5757\u7531 Codex \u4E0E dsh \u5171\u7528";
|
|
12761
|
+
DISABLED_MESSAGE = "dsh \u6307\u4EE4\u52A0\u8F7D\u63D2\u4EF6\u5DF2\u7981\u7528\uFF0C\u89C1\u63A7\u5236\u9875\u5F00\u5173";
|
|
12762
|
+
}
|
|
12763
|
+
});
|
|
12764
|
+
|
|
11945
12765
|
// packages/adapter-dsh/dist/index.js
|
|
11946
12766
|
var dist_exports3 = {};
|
|
11947
12767
|
__export(dist_exports3, {
|
|
@@ -11949,20 +12769,23 @@ __export(dist_exports3, {
|
|
|
11949
12769
|
createAdapter: () => createAdapter3,
|
|
11950
12770
|
detectDshLiveWiring: () => detectDshLiveWiring,
|
|
11951
12771
|
feedbackSignals: () => feedbackSignalsDsh,
|
|
12772
|
+
installProtocolDsh: () => installProtocolDsh,
|
|
11952
12773
|
parseDumpConfig: () => parseDumpConfig,
|
|
12774
|
+
protocolStatusDsh: () => protocolStatusDsh,
|
|
11953
12775
|
resolveDshHome: () => resolveDshHome,
|
|
11954
|
-
resolveDshHookPaths: () => resolveDshHookPaths
|
|
12776
|
+
resolveDshHookPaths: () => resolveDshHookPaths,
|
|
12777
|
+
uninstallProtocolDsh: () => uninstallProtocolDsh
|
|
11955
12778
|
});
|
|
11956
12779
|
import { execFile } from "node:child_process";
|
|
11957
|
-
import { existsSync as
|
|
12780
|
+
import { existsSync as existsSync12, readFileSync as readFileSync13 } from "node:fs";
|
|
11958
12781
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
11959
12782
|
import { createRequire } from "node:module";
|
|
11960
|
-
import { join as
|
|
12783
|
+
import { join as join18 } from "node:path";
|
|
11961
12784
|
import { promisify } from "node:util";
|
|
11962
12785
|
function resolvePinnedDshVersion() {
|
|
11963
12786
|
try {
|
|
11964
12787
|
const require2 = createRequire(import.meta.url);
|
|
11965
|
-
const pkg = JSON.parse(
|
|
12788
|
+
const pkg = JSON.parse(readFileSync13(require2.resolve("@deepseek-ai/dsh/package.json"), "utf8"));
|
|
11966
12789
|
return typeof pkg.version === "string" ? pkg.version : void 0;
|
|
11967
12790
|
} catch {
|
|
11968
12791
|
return void 0;
|
|
@@ -12019,7 +12842,7 @@ function createAdapter3(options = {}) {
|
|
|
12019
12842
|
// 版本尽力:钉定 devDependency 可解析时用其版本(仓库开发形态);发布产物无此依赖
|
|
12020
12843
|
// → 省略,不跑命令探版本。
|
|
12021
12844
|
async detect(options2) {
|
|
12022
|
-
if (!
|
|
12845
|
+
if (!existsSync12(resolveDshHome(options2)))
|
|
12023
12846
|
return { installed: false };
|
|
12024
12847
|
const version = resolvePinnedDshVersion();
|
|
12025
12848
|
return version !== void 0 ? { installed: true, version } : { installed: true };
|
|
@@ -12052,7 +12875,7 @@ function createAdapter3(options = {}) {
|
|
|
12052
12875
|
},
|
|
12053
12876
|
async planAction(action, options2) {
|
|
12054
12877
|
const text = await loadDumpText();
|
|
12055
|
-
const file = dumpConfigPath ??
|
|
12878
|
+
const file = dumpConfigPath ?? join18(resolveDshHookPaths(options2).dshHome, `dump-config(${profile})`);
|
|
12056
12879
|
return await planDshToggle(action, { text, file }, options2);
|
|
12057
12880
|
},
|
|
12058
12881
|
async applyAction(plan, options2) {
|
|
@@ -12075,14 +12898,16 @@ var init_dist5 = __esm({
|
|
|
12075
12898
|
init_toggle();
|
|
12076
12899
|
init_dump_config();
|
|
12077
12900
|
init_hooks3();
|
|
12901
|
+
init_protocol3();
|
|
12078
12902
|
execFileAsync = promisify(execFile);
|
|
12079
12903
|
}
|
|
12080
12904
|
});
|
|
12081
12905
|
|
|
12082
12906
|
// apps/cli/dist/main.js
|
|
12083
12907
|
init_dist();
|
|
12084
|
-
import { readFileSync as
|
|
12085
|
-
import {
|
|
12908
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
12909
|
+
import { homedir as homedir13 } from "node:os";
|
|
12910
|
+
import { dirname as dirname16, resolve as resolve11 } from "node:path";
|
|
12086
12911
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12087
12912
|
|
|
12088
12913
|
// packages/fixtures/dist/index.js
|
|
@@ -12210,7 +13035,7 @@ function isHookPayload(value) {
|
|
|
12210
13035
|
}
|
|
12211
13036
|
var HOOK_BODY_LIMIT = 1024 * 1024;
|
|
12212
13037
|
function readBody(req) {
|
|
12213
|
-
return new Promise((
|
|
13038
|
+
return new Promise((resolve12, reject) => {
|
|
12214
13039
|
const chunks = [];
|
|
12215
13040
|
let size = 0;
|
|
12216
13041
|
req.on("data", (chunk) => {
|
|
@@ -12222,16 +13047,16 @@ function readBody(req) {
|
|
|
12222
13047
|
}
|
|
12223
13048
|
chunks.push(chunk);
|
|
12224
13049
|
});
|
|
12225
|
-
req.on("end", () =>
|
|
13050
|
+
req.on("end", () => resolve12(Buffer.concat(chunks).toString("utf8")));
|
|
12226
13051
|
req.on("error", reject);
|
|
12227
13052
|
});
|
|
12228
13053
|
}
|
|
12229
|
-
function sendJson(res,
|
|
12230
|
-
res.writeHead(
|
|
13054
|
+
function sendJson(res, status3, body) {
|
|
13055
|
+
res.writeHead(status3, { "content-type": "application/json; charset=utf-8" });
|
|
12231
13056
|
res.end(JSON.stringify(body));
|
|
12232
13057
|
}
|
|
12233
|
-
function sendText(res,
|
|
12234
|
-
res.writeHead(
|
|
13058
|
+
function sendText(res, status3, body) {
|
|
13059
|
+
res.writeHead(status3, { "content-type": "text/plain; charset=utf-8" });
|
|
12235
13060
|
res.end(body);
|
|
12236
13061
|
}
|
|
12237
13062
|
|
|
@@ -12246,8 +13071,8 @@ function checkLicense(options = {}) {
|
|
|
12246
13071
|
const publicKeyPem = options.publicKeyPem ?? PROD_PUBLIC_KEY_PEM;
|
|
12247
13072
|
if (publicKeyPem === "")
|
|
12248
13073
|
return true;
|
|
12249
|
-
const
|
|
12250
|
-
return
|
|
13074
|
+
const status3 = licenseStatus({ ...options, publicKeyPem });
|
|
13075
|
+
return status3.state === "active" || status3.state === "grace";
|
|
12251
13076
|
}
|
|
12252
13077
|
function licenseStatus(options = {}) {
|
|
12253
13078
|
const home = options.homeDir ?? homedir();
|
|
@@ -12336,9 +13161,9 @@ function installLicense(raw, options = {}) {
|
|
|
12336
13161
|
}
|
|
12337
13162
|
const publicKeyPem = options.publicKeyPem ?? PROD_PUBLIC_KEY_PEM;
|
|
12338
13163
|
if (publicKeyPem !== "") {
|
|
12339
|
-
const
|
|
12340
|
-
if (
|
|
12341
|
-
return { ok: false, error:
|
|
13164
|
+
const status3 = verifyEntitlement(raw, options.now ?? /* @__PURE__ */ new Date(), publicKeyPem);
|
|
13165
|
+
if (status3.state !== "active" && status3.state !== "grace") {
|
|
13166
|
+
return { ok: false, error: status3.detail };
|
|
12342
13167
|
}
|
|
12343
13168
|
}
|
|
12344
13169
|
const dir = join2(options.homeDir ?? homedir(), ".wingman");
|
|
@@ -12429,11 +13254,11 @@ async function refreshLicense(options = {}) {
|
|
|
12429
13254
|
`;
|
|
12430
13255
|
const publicKeyPem = options.publicKeyPem ?? PROD_PUBLIC_KEY_PEM;
|
|
12431
13256
|
if (publicKeyPem !== "") {
|
|
12432
|
-
const
|
|
12433
|
-
if (
|
|
13257
|
+
const status3 = verifyEntitlement(next, options.now ?? /* @__PURE__ */ new Date(), publicKeyPem);
|
|
13258
|
+
if (status3.state !== "active" && status3.state !== "grace") {
|
|
12434
13259
|
return {
|
|
12435
13260
|
ok: false,
|
|
12436
|
-
message: `\u7B7E\u53D1\u7AEF\u8FD4\u56DE\u7684 license \u65E0\u6548:${
|
|
13261
|
+
message: `\u7B7E\u53D1\u7AEF\u8FD4\u56DE\u7684 license \u65E0\u6548:${status3.detail};\u539F license \u672A\u52A8`,
|
|
12437
13262
|
reason: "invalid-response"
|
|
12438
13263
|
};
|
|
12439
13264
|
}
|
|
@@ -12478,15 +13303,18 @@ function expSuffix(token) {
|
|
|
12478
13303
|
}
|
|
12479
13304
|
|
|
12480
13305
|
// apps/cli/dist/narrative-service.js
|
|
13306
|
+
init_dist2();
|
|
13307
|
+
init_dist4();
|
|
13308
|
+
init_dist5();
|
|
12481
13309
|
init_dist();
|
|
12482
13310
|
import { readdir } from "node:fs/promises";
|
|
12483
|
-
import { join as
|
|
13311
|
+
import { isAbsolute as isAbsolute4, join as join20, resolve as resolve9 } from "node:path";
|
|
12484
13312
|
|
|
12485
13313
|
// packages/narrative/dist/attribute.js
|
|
12486
|
-
function extractSteps(
|
|
13314
|
+
function extractSteps(lines2) {
|
|
12487
13315
|
const steps = [];
|
|
12488
13316
|
const byToolUseId = /* @__PURE__ */ new Map();
|
|
12489
|
-
for (const line of
|
|
13317
|
+
for (const line of lines2) {
|
|
12490
13318
|
if (line.kind === "assistant") {
|
|
12491
13319
|
for (const block of line.blocks) {
|
|
12492
13320
|
if (block.kind !== "tool_use")
|
|
@@ -12505,21 +13333,21 @@ function extractSteps(lines) {
|
|
|
12505
13333
|
byToolUseId.set(block.id, step);
|
|
12506
13334
|
}
|
|
12507
13335
|
} else if (line.kind === "user") {
|
|
12508
|
-
for (const
|
|
12509
|
-
const step = byToolUseId.get(
|
|
13336
|
+
for (const result4 of line.toolResults) {
|
|
13337
|
+
const step = byToolUseId.get(result4.toolUseId);
|
|
12510
13338
|
if (step === void 0)
|
|
12511
13339
|
continue;
|
|
12512
13340
|
if (line.timestamp !== void 0)
|
|
12513
13341
|
step.endTs = line.timestamp;
|
|
12514
|
-
if (
|
|
13342
|
+
if (result4.isError)
|
|
12515
13343
|
step.isError = true;
|
|
12516
13344
|
}
|
|
12517
13345
|
}
|
|
12518
13346
|
}
|
|
12519
13347
|
return steps;
|
|
12520
13348
|
}
|
|
12521
|
-
function deriveSubagentIntervals(
|
|
12522
|
-
const agentSteps = extractSteps(
|
|
13349
|
+
function deriveSubagentIntervals(lines2) {
|
|
13350
|
+
const agentSteps = extractSteps(lines2).filter((step) => step.toolName === "Agent");
|
|
12523
13351
|
return agentSteps.map((step) => {
|
|
12524
13352
|
const interval = { agentId: step.toolUseId };
|
|
12525
13353
|
if (step.ts !== void 0)
|
|
@@ -12683,17 +13511,17 @@ function parseResponseItem(payload, timestamp) {
|
|
|
12683
13511
|
return call;
|
|
12684
13512
|
}
|
|
12685
13513
|
if (payload !== void 0 && (payloadType === "function_call_output" || payloadType === "custom_tool_call_output")) {
|
|
12686
|
-
const
|
|
12687
|
-
assignString(
|
|
12688
|
-
assignString(
|
|
12689
|
-
assignString(
|
|
12690
|
-
assignString(
|
|
13514
|
+
const result4 = { kind: "tool_result", resultType: payloadType };
|
|
13515
|
+
assignString(result4, "timestamp", timestamp);
|
|
13516
|
+
assignString(result4, "callId", optionalString(payload.call_id));
|
|
13517
|
+
assignString(result4, "itemId", optionalString(payload.id));
|
|
13518
|
+
assignString(result4, "turnId", extractTurnId(payload));
|
|
12691
13519
|
if (payload.output !== void 0) {
|
|
12692
|
-
|
|
13520
|
+
result4.output = payload.output;
|
|
12693
13521
|
if (codexToolResultIsError(payload.output))
|
|
12694
|
-
|
|
13522
|
+
result4.isError = true;
|
|
12695
13523
|
}
|
|
12696
|
-
return
|
|
13524
|
+
return result4;
|
|
12697
13525
|
}
|
|
12698
13526
|
return otherLine("response_item", payload, timestamp);
|
|
12699
13527
|
}
|
|
@@ -12872,13 +13700,13 @@ function parseUser(raw, common) {
|
|
|
12872
13700
|
if (!isRecord3(block))
|
|
12873
13701
|
continue;
|
|
12874
13702
|
if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
|
|
12875
|
-
const
|
|
13703
|
+
const result4 = {
|
|
12876
13704
|
toolUseId: block.tool_use_id,
|
|
12877
13705
|
// 实测:成功结果常无 is_error 字段;仅显式 true 视为失败
|
|
12878
13706
|
isError: block.is_error === true,
|
|
12879
13707
|
content: block.content
|
|
12880
13708
|
};
|
|
12881
|
-
line.toolResults.push(
|
|
13709
|
+
line.toolResults.push(result4);
|
|
12882
13710
|
} else if (block.type === "text" && typeof block.text === "string") {
|
|
12883
13711
|
texts.push(block.text);
|
|
12884
13712
|
}
|
|
@@ -13011,8 +13839,283 @@ function consolidatePhases(steps) {
|
|
|
13011
13839
|
return nodes;
|
|
13012
13840
|
}
|
|
13013
13841
|
|
|
13014
|
-
// packages/narrative/dist/
|
|
13015
|
-
|
|
13842
|
+
// packages/narrative/dist/project-plan.js
|
|
13843
|
+
var PROJECT_PLAN_MAX_BYTES = 1024 * 1024;
|
|
13844
|
+
var PROJECT_PLAN_MAX_LINES = 2e4;
|
|
13845
|
+
var PROJECT_PLAN_MAX_NODES = 2e3;
|
|
13846
|
+
var VERSION_LINE = /^<!-- wingman-plan: (\d+) -->$/u;
|
|
13847
|
+
var H1_LINE = /^#\s+(.+?)\s*$/u;
|
|
13848
|
+
var GOAL_LINE = /^(?:目标|Goal):\s*(.+?)\s*$/u;
|
|
13849
|
+
var STEP_LINE = /^([ \t]*)[-*+]\s+\[([ /xX?-])\]\s+(.+?)\s*$/u;
|
|
13850
|
+
var VERIFY_LINE = /^([ \t]+)(?:验收|Verify):\s*(.+?)\s*$/u;
|
|
13851
|
+
var FENCE_LINE = /^\s*(```|~~~)/u;
|
|
13852
|
+
function parseProjectPlan(input, facts = {}) {
|
|
13853
|
+
const fileProblem = validateFileFacts(facts);
|
|
13854
|
+
if (fileProblem !== void 0)
|
|
13855
|
+
return unavailable(fileProblem);
|
|
13856
|
+
const byteLength = typeof input === "string" ? new TextEncoder().encode(input).byteLength : input.byteLength;
|
|
13857
|
+
if (byteLength > PROJECT_PLAN_MAX_BYTES)
|
|
13858
|
+
return unavailable("\u8BA1\u5212\u6587\u4EF6\u8D85\u8FC7 1 MiB");
|
|
13859
|
+
let text;
|
|
13860
|
+
try {
|
|
13861
|
+
text = typeof input === "string" ? input : new TextDecoder("utf-8", { fatal: true }).decode(input);
|
|
13862
|
+
} catch {
|
|
13863
|
+
return unavailable("\u8BA1\u5212\u6587\u4EF6\u4E0D\u662F\u4E25\u683C UTF-8");
|
|
13864
|
+
}
|
|
13865
|
+
if (text.charCodeAt(0) === 65279)
|
|
13866
|
+
text = text.slice(1);
|
|
13867
|
+
text = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
|
13868
|
+
const lines2 = text.split("\n");
|
|
13869
|
+
if (lines2.at(-1) === "")
|
|
13870
|
+
lines2.pop();
|
|
13871
|
+
if (lines2.length > PROJECT_PLAN_MAX_LINES) {
|
|
13872
|
+
return unavailable("\u8BA1\u5212\u6587\u4EF6\u8D85\u8FC7 20000 \u884C", PROJECT_PLAN_MAX_LINES + 1);
|
|
13873
|
+
}
|
|
13874
|
+
const versionMatch = VERSION_LINE.exec(lines2[0] ?? "");
|
|
13875
|
+
if (versionMatch !== null && Number(versionMatch[1]) > 1) {
|
|
13876
|
+
return unavailable("\u683C\u5F0F\u7248\u672C\u4E0D\u53D7\u652F\u6301", 1);
|
|
13877
|
+
}
|
|
13878
|
+
for (const [index, line] of lines2.entries()) {
|
|
13879
|
+
if (line.includes("<<<<<<<") || line.includes(">>>>>>>")) {
|
|
13880
|
+
return unavailable("\u8BA1\u5212\u6587\u4EF6\u542B git \u51B2\u7A81\u6807\u8BB0", index + 1);
|
|
13881
|
+
}
|
|
13882
|
+
}
|
|
13883
|
+
let title;
|
|
13884
|
+
let goal;
|
|
13885
|
+
let ignoredLineCount = 0;
|
|
13886
|
+
let fence;
|
|
13887
|
+
let stepCount = 0;
|
|
13888
|
+
let lastStep;
|
|
13889
|
+
const nodes = [];
|
|
13890
|
+
const stack = [];
|
|
13891
|
+
for (const [index, line] of lines2.entries()) {
|
|
13892
|
+
const lineNumber = index + 1;
|
|
13893
|
+
const fenceMatch = FENCE_LINE.exec(line);
|
|
13894
|
+
if (fence !== void 0) {
|
|
13895
|
+
ignoredLineCount += 1;
|
|
13896
|
+
if (fenceMatch?.[1] === fence)
|
|
13897
|
+
fence = void 0;
|
|
13898
|
+
lastStep = void 0;
|
|
13899
|
+
continue;
|
|
13900
|
+
}
|
|
13901
|
+
if (fenceMatch !== null) {
|
|
13902
|
+
fence = fenceMatch[1];
|
|
13903
|
+
ignoredLineCount += 1;
|
|
13904
|
+
lastStep = void 0;
|
|
13905
|
+
continue;
|
|
13906
|
+
}
|
|
13907
|
+
if (lineNumber === 1 && versionMatch !== null)
|
|
13908
|
+
continue;
|
|
13909
|
+
const headingMatch = H1_LINE.exec(line);
|
|
13910
|
+
if (headingMatch !== null) {
|
|
13911
|
+
if (title === void 0)
|
|
13912
|
+
title = headingMatch[1];
|
|
13913
|
+
else
|
|
13914
|
+
ignoredLineCount += 1;
|
|
13915
|
+
lastStep = void 0;
|
|
13916
|
+
continue;
|
|
13917
|
+
}
|
|
13918
|
+
const goalMatch = GOAL_LINE.exec(line);
|
|
13919
|
+
if (stepCount === 0 && goal === void 0 && goalMatch !== null) {
|
|
13920
|
+
goal = goalMatch[1];
|
|
13921
|
+
lastStep = void 0;
|
|
13922
|
+
continue;
|
|
13923
|
+
}
|
|
13924
|
+
const verifyMatch = VERIFY_LINE.exec(line);
|
|
13925
|
+
if (verifyMatch !== null && lastStep !== void 0 && lastStep.line === lineNumber - 1 && indentColumns(verifyMatch[1] ?? "") > lastStep.indent) {
|
|
13926
|
+
lastStep.node.verification = verifyMatch[2] ?? "";
|
|
13927
|
+
continue;
|
|
13928
|
+
}
|
|
13929
|
+
const stepMatch = STEP_LINE.exec(line);
|
|
13930
|
+
if (stepMatch !== null) {
|
|
13931
|
+
stepCount += 1;
|
|
13932
|
+
if (stepCount > PROJECT_PLAN_MAX_NODES) {
|
|
13933
|
+
return unavailable("\u8BA1\u5212\u8282\u70B9\u8D85\u8FC7 2000 \u4E2A", lineNumber);
|
|
13934
|
+
}
|
|
13935
|
+
const indent = indentColumns(stepMatch[1] ?? "");
|
|
13936
|
+
const node = {
|
|
13937
|
+
key: String(lineNumber),
|
|
13938
|
+
text: stepMatch[3] ?? "",
|
|
13939
|
+
status: statusForMark(stepMatch[2] ?? " "),
|
|
13940
|
+
line: lineNumber,
|
|
13941
|
+
children: []
|
|
13942
|
+
};
|
|
13943
|
+
while (stack.length > 0 && (stack.at(-1)?.indent ?? -1) >= indent)
|
|
13944
|
+
stack.pop();
|
|
13945
|
+
const parent = stack.at(-1)?.node;
|
|
13946
|
+
if (parent === void 0)
|
|
13947
|
+
nodes.push(node);
|
|
13948
|
+
else
|
|
13949
|
+
parent.children.push(node);
|
|
13950
|
+
stack.push({ indent, node });
|
|
13951
|
+
lastStep = { indent, line: lineNumber, node };
|
|
13952
|
+
continue;
|
|
13953
|
+
}
|
|
13954
|
+
ignoredLineCount += 1;
|
|
13955
|
+
lastStep = void 0;
|
|
13956
|
+
}
|
|
13957
|
+
assignNodeKeys(nodes);
|
|
13958
|
+
return {
|
|
13959
|
+
availability: "available",
|
|
13960
|
+
version: 1,
|
|
13961
|
+
...title === void 0 ? {} : { title },
|
|
13962
|
+
...goal === void 0 ? {} : { goal },
|
|
13963
|
+
ignoredLineCount,
|
|
13964
|
+
nodes
|
|
13965
|
+
};
|
|
13966
|
+
}
|
|
13967
|
+
function projectPlanStructureKey(nodes) {
|
|
13968
|
+
return JSON.stringify(structureOf(nodes));
|
|
13969
|
+
}
|
|
13970
|
+
function structureOf(nodes) {
|
|
13971
|
+
return nodes.map((node) => [node.text, structureOf(node.children)]);
|
|
13972
|
+
}
|
|
13973
|
+
function deriveProjectPlanSummary(nodes) {
|
|
13974
|
+
const currentPoints = [];
|
|
13975
|
+
const checkpoints = [];
|
|
13976
|
+
const leaves = [];
|
|
13977
|
+
const contradictions = [];
|
|
13978
|
+
visitNodes(nodes, [], (node, path) => {
|
|
13979
|
+
if (node.children.length === 0)
|
|
13980
|
+
leaves.push(node);
|
|
13981
|
+
if (node.status === "checkpoint")
|
|
13982
|
+
checkpoints.push(pathFact(node, path));
|
|
13983
|
+
if (node.status === "in_progress" && !hasStatus(node.children, "in_progress")) {
|
|
13984
|
+
currentPoints.push(pathFact(node, path));
|
|
13985
|
+
}
|
|
13986
|
+
const contradiction = contradictionFor(node);
|
|
13987
|
+
if (contradiction !== void 0)
|
|
13988
|
+
contradictions.push({ key: node.key, text: contradiction });
|
|
13989
|
+
});
|
|
13990
|
+
const counts = {
|
|
13991
|
+
pending: leaves.filter((node) => node.status === "pending").length,
|
|
13992
|
+
inProgress: leaves.filter((node) => node.status === "in_progress").length,
|
|
13993
|
+
completed: leaves.filter((node) => node.status === "completed").length,
|
|
13994
|
+
cancelled: leaves.filter((node) => node.status === "cancelled").length,
|
|
13995
|
+
checkpoint: leaves.filter((node) => node.status === "checkpoint").length
|
|
13996
|
+
};
|
|
13997
|
+
const allLeavesDone = leaves.length > 0 && leaves.every((node) => node.status === "completed" || node.status === "cancelled");
|
|
13998
|
+
return {
|
|
13999
|
+
currentPointLabel: currentPoints.length === 0 ? "\u8BA1\u5212\u672A\u6807\u8BB0\u8FDB\u884C\u4E2D\u6B65\u9AA4" : currentPoints.length === 1 ? "\u5F53\u524D\u70B9" : `\u5E76\u884C ${currentPoints.length} \u9879`,
|
|
14000
|
+
currentPoints,
|
|
14001
|
+
checkpoints,
|
|
14002
|
+
counts,
|
|
14003
|
+
allLeavesDone,
|
|
14004
|
+
...allLeavesDone ? { completionLabel: "Agent \u6807\u8BB0\u9879\u76EE\u6B65\u9AA4\u5168\u90E8\u5B8C\u6210" } : {},
|
|
14005
|
+
contradictions
|
|
14006
|
+
};
|
|
14007
|
+
}
|
|
14008
|
+
function deriveProjectReconciliationFacts(nodes, sessions, now) {
|
|
14009
|
+
const facts = [];
|
|
14010
|
+
const nodesByKey = /* @__PURE__ */ new Map();
|
|
14011
|
+
visitNodes(nodes, [], (node) => nodesByKey.set(node.key, node));
|
|
14012
|
+
if (sessions.some((session) => session.sessionPlanAllCompleted === true && session.attachedTo !== void 0 && nodesByKey.get(session.attachedTo)?.status === "in_progress")) {
|
|
14013
|
+
facts.push("\u4F1A\u8BDD\u8BA1\u5212\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u9879\u76EE\u6B65\u9AA4\u4ECD\u4E3A\u8FDB\u884C\u4E2D");
|
|
14014
|
+
}
|
|
14015
|
+
const activeAttachments = new Set(sessions.filter((session) => !session.ended && session.attachedTo !== void 0).map((session) => session.attachedTo));
|
|
14016
|
+
visitNodes(nodes, [], (node) => {
|
|
14017
|
+
if (node.status === "in_progress" && !activeAttachments.has(node.key)) {
|
|
14018
|
+
facts.push(`\u300C${node.text}\u300D\u6B65\u9AA4\u4E0B\u5F53\u524D\u6CA1\u6709\u89C2\u5BDF\u5230\u6D3B\u8DC3\u4F1A\u8BDD`);
|
|
14019
|
+
}
|
|
14020
|
+
});
|
|
14021
|
+
const nowMs = Date.parse(now);
|
|
14022
|
+
const recentWriters = new Set(sessions.filter((session) => {
|
|
14023
|
+
if (session.wrotePlanAt === void 0)
|
|
14024
|
+
return false;
|
|
14025
|
+
const age = nowMs - Date.parse(session.wrotePlanAt);
|
|
14026
|
+
return Number.isFinite(age) && age >= 0 && age <= 6e4;
|
|
14027
|
+
}).map((session) => session.id));
|
|
14028
|
+
if (recentWriters.size > 1) {
|
|
14029
|
+
facts.push(`\u6700\u8FD1 60 \u79D2\u5185 ${recentWriters.size} \u4E2A\u4F1A\u8BDD\u5199\u5165\u6B64\u6587\u4EF6`);
|
|
14030
|
+
}
|
|
14031
|
+
return facts;
|
|
14032
|
+
}
|
|
14033
|
+
function validateFileFacts(facts) {
|
|
14034
|
+
if (facts.isRegularFile === false)
|
|
14035
|
+
return "\u8BA1\u5212\u6587\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6";
|
|
14036
|
+
if (facts.resolvedInsideRoot === false)
|
|
14037
|
+
return "\u8F6F\u94FE\u89E3\u6790\u5230\u9879\u76EE\u6839\u4E4B\u5916";
|
|
14038
|
+
return void 0;
|
|
14039
|
+
}
|
|
14040
|
+
function unavailable(reason, line) {
|
|
14041
|
+
return { availability: "unavailable", reason, ...line === void 0 ? {} : { line } };
|
|
14042
|
+
}
|
|
14043
|
+
function indentColumns(indent) {
|
|
14044
|
+
let columns = 0;
|
|
14045
|
+
for (const character of indent)
|
|
14046
|
+
columns += character === " " ? 4 : 1;
|
|
14047
|
+
return columns;
|
|
14048
|
+
}
|
|
14049
|
+
function statusForMark(mark) {
|
|
14050
|
+
if (mark === "/")
|
|
14051
|
+
return "in_progress";
|
|
14052
|
+
if (mark === "x" || mark === "X")
|
|
14053
|
+
return "completed";
|
|
14054
|
+
if (mark === "-")
|
|
14055
|
+
return "cancelled";
|
|
14056
|
+
if (mark === "?")
|
|
14057
|
+
return "checkpoint";
|
|
14058
|
+
return "pending";
|
|
14059
|
+
}
|
|
14060
|
+
function assignNodeKeys(nodes, parentPath = []) {
|
|
14061
|
+
const siblingCounts = /* @__PURE__ */ new Map();
|
|
14062
|
+
for (const node of nodes) {
|
|
14063
|
+
const normalizedText = node.text.trim().replace(/\s+/gu, " ");
|
|
14064
|
+
const occurrence = (siblingCounts.get(normalizedText) ?? 0) + 1;
|
|
14065
|
+
siblingCounts.set(normalizedText, occurrence);
|
|
14066
|
+
const segment = occurrence === 1 ? normalizedText : `${normalizedText}#${occurrence}`;
|
|
14067
|
+
const path = [...parentPath, segment];
|
|
14068
|
+
node.key = path.join(" > ");
|
|
14069
|
+
assignNodeKeys(node.children, path);
|
|
14070
|
+
}
|
|
14071
|
+
}
|
|
14072
|
+
function visitNodes(nodes, parentPath, visit) {
|
|
14073
|
+
for (const node of nodes) {
|
|
14074
|
+
const path = [...parentPath, node.text];
|
|
14075
|
+
visit(node, path);
|
|
14076
|
+
visitNodes(node.children, path, visit);
|
|
14077
|
+
}
|
|
14078
|
+
}
|
|
14079
|
+
function pathFact(node, path) {
|
|
14080
|
+
return { key: node.key, path, text: node.text };
|
|
14081
|
+
}
|
|
14082
|
+
function hasStatus(nodes, status3) {
|
|
14083
|
+
return nodes.some((node) => node.status === status3 || hasStatus(node.children, status3));
|
|
14084
|
+
}
|
|
14085
|
+
function contradictionFor(node) {
|
|
14086
|
+
if (node.status === "completed") {
|
|
14087
|
+
const child = node.children.find((candidate) => candidate.status === "pending" || candidate.status === "in_progress");
|
|
14088
|
+
if (child !== void 0) {
|
|
14089
|
+
return `\u7236\u6B65\u9AA4\u6807\u8BB0\u5B8C\u6210\uFF0C\u4F46\u540E\u4EE3\u4ECD\u6807\u8BB0\u4E3A${statusLabel(child.status)}`;
|
|
14090
|
+
}
|
|
14091
|
+
}
|
|
14092
|
+
if (node.status === "cancelled") {
|
|
14093
|
+
const child = node.children.find((candidate) => candidate.status === "in_progress");
|
|
14094
|
+
if (child !== void 0)
|
|
14095
|
+
return "\u7236\u6B65\u9AA4\u5DF2\u53D6\u6D88\uFF0C\u4F46\u540E\u4EE3\u4ECD\u6807\u8BB0\u4E3A\u8FDB\u884C\u4E2D";
|
|
14096
|
+
}
|
|
14097
|
+
if (node.status === "pending") {
|
|
14098
|
+
const child = node.children.find((candidate) => candidate.status === "in_progress" || candidate.status === "completed");
|
|
14099
|
+
if (child !== void 0) {
|
|
14100
|
+
return `\u7236\u6B65\u9AA4\u4ECD\u5F85\u505A\uFF0C\u4F46\u540E\u4EE3\u5DF2\u6807\u8BB0${statusLabel(child.status)}`;
|
|
14101
|
+
}
|
|
14102
|
+
}
|
|
14103
|
+
return void 0;
|
|
14104
|
+
}
|
|
14105
|
+
function statusLabel(status3) {
|
|
14106
|
+
if (status3 === "pending")
|
|
14107
|
+
return "\u5F85\u505A";
|
|
14108
|
+
if (status3 === "in_progress")
|
|
14109
|
+
return "\u8FDB\u884C\u4E2D";
|
|
14110
|
+
if (status3 === "completed")
|
|
14111
|
+
return "\u5B8C\u6210";
|
|
14112
|
+
if (status3 === "cancelled")
|
|
14113
|
+
return "\u5DF2\u53D6\u6D88";
|
|
14114
|
+
return "\u68C0\u67E5\u70B9";
|
|
14115
|
+
}
|
|
14116
|
+
|
|
14117
|
+
// packages/narrative/dist/tail.js
|
|
14118
|
+
import { open, stat } from "node:fs/promises";
|
|
13016
14119
|
var NEWLINE = 10;
|
|
13017
14120
|
function followTranscript(path, onLine, options = {}) {
|
|
13018
14121
|
const intervalMs = options.intervalMs ?? 500;
|
|
@@ -13087,11 +14190,11 @@ var TODO_STATUSES = ["pending", "in_progress", "completed"];
|
|
|
13087
14190
|
function isTodoStatus(value) {
|
|
13088
14191
|
return typeof value === "string" && TODO_STATUSES.includes(value);
|
|
13089
14192
|
}
|
|
13090
|
-
function extractTodoSnapshots(
|
|
14193
|
+
function extractTodoSnapshots(lines2) {
|
|
13091
14194
|
const snapshots = [];
|
|
13092
14195
|
const tasks = /* @__PURE__ */ new Map();
|
|
13093
|
-
const createResultIds = buildTaskCreateResultIndex(
|
|
13094
|
-
for (const line of
|
|
14196
|
+
const createResultIds = buildTaskCreateResultIndex(lines2);
|
|
14197
|
+
for (const line of lines2) {
|
|
13095
14198
|
if (line.kind !== "assistant")
|
|
13096
14199
|
continue;
|
|
13097
14200
|
for (const block of line.blocks) {
|
|
@@ -13111,16 +14214,16 @@ function extractTodoSnapshots(lines) {
|
|
|
13111
14214
|
}
|
|
13112
14215
|
return snapshots;
|
|
13113
14216
|
}
|
|
13114
|
-
function buildTaskCreateResultIndex(
|
|
14217
|
+
function buildTaskCreateResultIndex(lines2) {
|
|
13115
14218
|
const index = /* @__PURE__ */ new Map();
|
|
13116
|
-
for (const line of
|
|
14219
|
+
for (const line of lines2) {
|
|
13117
14220
|
if (line.kind !== "user")
|
|
13118
14221
|
continue;
|
|
13119
|
-
const
|
|
13120
|
-
if (!isRecord3(
|
|
14222
|
+
const result4 = line.toolUseResult;
|
|
14223
|
+
if (!isRecord3(result4) || !isRecord3(result4.task) || typeof result4.task.id !== "string")
|
|
13121
14224
|
continue;
|
|
13122
14225
|
for (const toolResult of line.toolResults)
|
|
13123
|
-
index.set(toolResult.toolUseId,
|
|
14226
|
+
index.set(toolResult.toolUseId, result4.task.id);
|
|
13124
14227
|
}
|
|
13125
14228
|
return index;
|
|
13126
14229
|
}
|
|
@@ -13235,8 +14338,8 @@ function deriveTodoTimeline(snapshots) {
|
|
|
13235
14338
|
}
|
|
13236
14339
|
return [...entries.values()];
|
|
13237
14340
|
}
|
|
13238
|
-
function newSpan(
|
|
13239
|
-
const span = { status };
|
|
14341
|
+
function newSpan(status3, from) {
|
|
14342
|
+
const span = { status: status3 };
|
|
13240
14343
|
if (from !== void 0)
|
|
13241
14344
|
span.from = from;
|
|
13242
14345
|
return span;
|
|
@@ -13254,7 +14357,7 @@ function turnUsage(line) {
|
|
|
13254
14357
|
if (inputTokens === void 0 || cacheCreationInputTokens === void 0 || cacheReadInputTokens === void 0 || outputTokens === void 0) {
|
|
13255
14358
|
return void 0;
|
|
13256
14359
|
}
|
|
13257
|
-
const
|
|
14360
|
+
const result4 = {
|
|
13258
14361
|
inputTokens,
|
|
13259
14362
|
cacheCreationInputTokens,
|
|
13260
14363
|
cacheReadInputTokens,
|
|
@@ -13269,11 +14372,11 @@ function turnUsage(line) {
|
|
|
13269
14372
|
const fiveMinute = finiteNumber(breakdown.ephemeral_5m_input_tokens);
|
|
13270
14373
|
const oneHour = finiteNumber(breakdown.ephemeral_1h_input_tokens);
|
|
13271
14374
|
if (fiveMinute !== void 0)
|
|
13272
|
-
|
|
14375
|
+
result4.cacheCreation5mTokens = fiveMinute;
|
|
13273
14376
|
if (oneHour !== void 0)
|
|
13274
|
-
|
|
14377
|
+
result4.cacheCreation1hTokens = oneHour;
|
|
13275
14378
|
}
|
|
13276
|
-
return
|
|
14379
|
+
return result4;
|
|
13277
14380
|
}
|
|
13278
14381
|
function finiteNumber(value) {
|
|
13279
14382
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
@@ -13337,9 +14440,9 @@ function derivePlanRevisions(observations) {
|
|
|
13337
14440
|
}
|
|
13338
14441
|
return revisions;
|
|
13339
14442
|
}
|
|
13340
|
-
function deriveTaskPlan(harness,
|
|
14443
|
+
function deriveTaskPlan(harness, lines2 = [], codexLines = []) {
|
|
13341
14444
|
if (harness === "cc")
|
|
13342
|
-
return deriveClaudeTaskPlan(
|
|
14445
|
+
return deriveClaudeTaskPlan(lines2);
|
|
13343
14446
|
if (harness === "codex")
|
|
13344
14447
|
return deriveCodexTaskPlan(codexLines);
|
|
13345
14448
|
return {
|
|
@@ -13348,16 +14451,62 @@ function deriveTaskPlan(harness, lines = [], codexLines = []) {
|
|
|
13348
14451
|
reason: "dsh \u6CA1\u6709\u5411 Wingman hooks \u66B4\u9732\u7A33\u5B9A\u7684\u672C\u5730 todo projection"
|
|
13349
14452
|
};
|
|
13350
14453
|
}
|
|
13351
|
-
function
|
|
14454
|
+
function observeTranscriptVersion(lines2) {
|
|
14455
|
+
let version;
|
|
14456
|
+
for (const line of lines2) {
|
|
14457
|
+
if (line.kind === "unknown")
|
|
14458
|
+
continue;
|
|
14459
|
+
const candidate = line.raw.version;
|
|
14460
|
+
if (typeof candidate === "string" && /^\d+\.\d+\.\d+$/u.test(candidate)) {
|
|
14461
|
+
version = candidate;
|
|
14462
|
+
}
|
|
14463
|
+
}
|
|
14464
|
+
return version;
|
|
14465
|
+
}
|
|
14466
|
+
function observeAssistantModel(lines2) {
|
|
14467
|
+
let model;
|
|
14468
|
+
for (const line of lines2) {
|
|
14469
|
+
if (line.kind === "assistant" && line.model !== void 0 && line.model !== "<synthetic>") {
|
|
14470
|
+
model = line.model;
|
|
14471
|
+
}
|
|
14472
|
+
}
|
|
14473
|
+
return model;
|
|
14474
|
+
}
|
|
14475
|
+
function compareVersions(a, b) {
|
|
14476
|
+
const left = a.split(".").map(Number);
|
|
14477
|
+
const right = b.split(".").map(Number);
|
|
14478
|
+
for (let index = 0; index < 3; index += 1) {
|
|
14479
|
+
if ((left[index] ?? 0) > (right[index] ?? 0))
|
|
14480
|
+
return 1;
|
|
14481
|
+
if ((left[index] ?? 0) < (right[index] ?? 0))
|
|
14482
|
+
return -1;
|
|
14483
|
+
}
|
|
14484
|
+
return 0;
|
|
14485
|
+
}
|
|
14486
|
+
function planToolGateFor(lines2) {
|
|
14487
|
+
const harnessVersion = observeTranscriptVersion(lines2);
|
|
14488
|
+
if (harnessVersion === void 0 || compareVersions(harnessVersion, "2.1.233") < 0) {
|
|
14489
|
+
return void 0;
|
|
14490
|
+
}
|
|
14491
|
+
const model = observeAssistantModel(lines2);
|
|
14492
|
+
return {
|
|
14493
|
+
harnessVersion,
|
|
14494
|
+
...model === void 0 ? {} : { model },
|
|
14495
|
+
affected: model !== void 0 && modelHasGatedPlanTools(model) ? "known" : "unknown",
|
|
14496
|
+
since: "2.1.233",
|
|
14497
|
+
enable: "CLAUDE_CODE_ENABLE_TODO_TOOLS=1"
|
|
14498
|
+
};
|
|
14499
|
+
}
|
|
14500
|
+
function deriveClaudeTaskPlan(lines2) {
|
|
13352
14501
|
const source = "Claude Code TaskCreate/TaskUpdate/TodoWrite";
|
|
13353
|
-
if (latestClaudePlanPayloadValid(
|
|
14502
|
+
if (latestClaudePlanPayloadValid(lines2) === false) {
|
|
13354
14503
|
return {
|
|
13355
14504
|
availability: "unavailable",
|
|
13356
14505
|
source,
|
|
13357
14506
|
reason: "Claude Code \u8BA1\u5212\u5DE5\u5177\u53C2\u6570\u65E0\u6CD5\u89E3\u6790"
|
|
13358
14507
|
};
|
|
13359
14508
|
}
|
|
13360
|
-
const observations = extractTodoSnapshots(
|
|
14509
|
+
const observations = extractTodoSnapshots(lines2).map((snapshot) => ({
|
|
13361
14510
|
...snapshot.ts === void 0 ? {} : { observedAt: snapshot.ts },
|
|
13362
14511
|
steps: snapshot.todos.map((todo) => ({
|
|
13363
14512
|
text: todo.content,
|
|
@@ -13365,13 +14514,28 @@ function deriveClaudeTaskPlan(lines) {
|
|
|
13365
14514
|
status: todo.status
|
|
13366
14515
|
}))
|
|
13367
14516
|
}));
|
|
13368
|
-
if (observations.length === 0)
|
|
13369
|
-
|
|
14517
|
+
if (observations.length === 0) {
|
|
14518
|
+
const toolGate = planToolGateFor(lines2);
|
|
14519
|
+
return {
|
|
14520
|
+
availability: "missing",
|
|
14521
|
+
source,
|
|
14522
|
+
...toolGate === void 0 ? {} : { toolGate }
|
|
14523
|
+
};
|
|
14524
|
+
}
|
|
13370
14525
|
return availablePlan(source, derivePlanRevisions(observations));
|
|
13371
14526
|
}
|
|
13372
|
-
function
|
|
14527
|
+
function modelHasGatedPlanTools(model) {
|
|
14528
|
+
const match = /^claude-(opus|sonnet|haiku|fable|mythos)-(\d+)(?:-(\d+))?/u.exec(model);
|
|
14529
|
+
if (match === null)
|
|
14530
|
+
return false;
|
|
14531
|
+
const family = match[1];
|
|
14532
|
+
const major = Number(match[2]);
|
|
14533
|
+
const minor = Number(match[3] ?? 0);
|
|
14534
|
+
return major >= 5 || family === "opus" && major === 4 && minor >= 8;
|
|
14535
|
+
}
|
|
14536
|
+
function latestClaudePlanPayloadValid(lines2) {
|
|
13373
14537
|
let latest;
|
|
13374
|
-
for (const line of
|
|
14538
|
+
for (const line of lines2) {
|
|
13375
14539
|
if (line.kind !== "assistant")
|
|
13376
14540
|
continue;
|
|
13377
14541
|
for (const block of line.blocks) {
|
|
@@ -13388,9 +14552,9 @@ function latestClaudePlanPayloadValid(lines) {
|
|
|
13388
14552
|
}
|
|
13389
14553
|
return latest;
|
|
13390
14554
|
}
|
|
13391
|
-
function deriveCodexTaskPlan(
|
|
14555
|
+
function deriveCodexTaskPlan(lines2) {
|
|
13392
14556
|
const source = "Codex update_plan";
|
|
13393
|
-
const calls =
|
|
14557
|
+
const calls = lines2.filter((line) => line.kind === "tool_call" && line.name === "update_plan");
|
|
13394
14558
|
if (calls.length === 0)
|
|
13395
14559
|
return { availability: "missing", source };
|
|
13396
14560
|
const observations = [];
|
|
@@ -13447,6 +14611,345 @@ function isRecord4(value) {
|
|
|
13447
14611
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13448
14612
|
}
|
|
13449
14613
|
|
|
14614
|
+
// apps/cli/dist/project-attribution.js
|
|
14615
|
+
function extractToolObservations(harness, records) {
|
|
14616
|
+
const observations = [];
|
|
14617
|
+
for (const value of records) {
|
|
14618
|
+
if (!isRecord5(value))
|
|
14619
|
+
continue;
|
|
14620
|
+
if (value.hook_event_name === "PostToolUse") {
|
|
14621
|
+
const tool = toolObservation(harness, value.tool_name, value.tool_input, "hook", atOf(value));
|
|
14622
|
+
if (tool !== void 0)
|
|
14623
|
+
observations.push(tool);
|
|
14624
|
+
continue;
|
|
14625
|
+
}
|
|
14626
|
+
if (harness === "codex") {
|
|
14627
|
+
const payload = isRecord5(value.payload) ? value.payload : void 0;
|
|
14628
|
+
if (payload !== void 0 && (payload.type === "function_call" || payload.type === "custom_tool_call")) {
|
|
14629
|
+
const input = payload.input ?? payload.arguments;
|
|
14630
|
+
const tool = toolObservation(harness, payload.name, input, "transcript", atOf(value));
|
|
14631
|
+
if (tool !== void 0)
|
|
14632
|
+
observations.push(tool);
|
|
14633
|
+
}
|
|
14634
|
+
continue;
|
|
14635
|
+
}
|
|
14636
|
+
const message = isRecord5(value.message) ? value.message : void 0;
|
|
14637
|
+
if (message === void 0 || !Array.isArray(message.content))
|
|
14638
|
+
continue;
|
|
14639
|
+
for (const block of message.content) {
|
|
14640
|
+
if (!isRecord5(block) || block.type !== "tool_use")
|
|
14641
|
+
continue;
|
|
14642
|
+
const tool = toolObservation(harness, block.name, block.input, "transcript", atOf(value));
|
|
14643
|
+
if (tool !== void 0)
|
|
14644
|
+
observations.push(tool);
|
|
14645
|
+
}
|
|
14646
|
+
}
|
|
14647
|
+
return observations;
|
|
14648
|
+
}
|
|
14649
|
+
function toolObservation(harness, name, input, source, at) {
|
|
14650
|
+
if (typeof name !== "string")
|
|
14651
|
+
return void 0;
|
|
14652
|
+
const planWrites = fromTool(harness, name, input, source, at);
|
|
14653
|
+
const serializedInput = JSON.stringify(input);
|
|
14654
|
+
return {
|
|
14655
|
+
method: name,
|
|
14656
|
+
source,
|
|
14657
|
+
planWrites,
|
|
14658
|
+
planFileAccess: planWrites.length > 0 || typeof serializedInput === "string" && serializedInput.includes(".wingman") && serializedInput.includes("plan.md"),
|
|
14659
|
+
...at === void 0 ? {} : { at }
|
|
14660
|
+
};
|
|
14661
|
+
}
|
|
14662
|
+
function attributePlanWrite(before, after, observation) {
|
|
14663
|
+
if (observation.kind === "shell")
|
|
14664
|
+
return { reason: "\u901A\u8FC7 shell \u5199\u5165\u65E0\u6CD5\u786E\u8BA4" };
|
|
14665
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
14666
|
+
const beforeByKey = new Map(flatten(before.nodes).map(({ node }) => [node.key, node]));
|
|
14667
|
+
for (const { node } of flatten(after.nodes)) {
|
|
14668
|
+
const previous = beforeByKey.get(node.key);
|
|
14669
|
+
if (node.status === "in_progress" && previous !== void 0 && previous.status !== "in_progress") {
|
|
14670
|
+
candidates.add(node.key);
|
|
14671
|
+
}
|
|
14672
|
+
}
|
|
14673
|
+
if (!observation.wholeFile) {
|
|
14674
|
+
const beforeByPosition = new Map(flatten(before.nodes).map(({ node, position }) => [position, node]));
|
|
14675
|
+
for (const { node, position } of flatten(after.nodes)) {
|
|
14676
|
+
const previous = beforeByPosition.get(position);
|
|
14677
|
+
if (node.status === "in_progress" && previous?.status === "in_progress" && nodeStructure(previous) !== nodeStructure(node)) {
|
|
14678
|
+
candidates.add(node.key);
|
|
14679
|
+
}
|
|
14680
|
+
}
|
|
14681
|
+
}
|
|
14682
|
+
if (candidates.size === 0)
|
|
14683
|
+
return { reason: "\u672A\u89C2\u5BDF\u5230\u53EF\u5F52\u5C5E\u7684\u8FDB\u884C\u4E2D\u6B65\u9AA4\u53D8\u66F4" };
|
|
14684
|
+
if (candidates.size > 1)
|
|
14685
|
+
return { reason: "\u4E00\u6B21\u5199\u5165\u6D89\u53CA\u591A\u4E2A\u8FDB\u884C\u4E2D\u6B65\u9AA4\uFF0C\u65E0\u6CD5\u552F\u4E00\u5F52\u5C5E" };
|
|
14686
|
+
const attachedTo = [...candidates][0];
|
|
14687
|
+
return {
|
|
14688
|
+
attachedTo,
|
|
14689
|
+
attachedBy: {
|
|
14690
|
+
kind: "plan_file_write",
|
|
14691
|
+
node: attachedTo,
|
|
14692
|
+
method: observation.method,
|
|
14693
|
+
...observation.at === void 0 ? {} : { at: observation.at }
|
|
14694
|
+
}
|
|
14695
|
+
};
|
|
14696
|
+
}
|
|
14697
|
+
function fromTool(harness, name, input, source, at) {
|
|
14698
|
+
if (typeof name !== "string")
|
|
14699
|
+
return [];
|
|
14700
|
+
if (harness === "codex" && name === "exec") {
|
|
14701
|
+
const inputText = toolText(input, ["input"]);
|
|
14702
|
+
const paths = codexExecPatchPaths(inputText);
|
|
14703
|
+
if (paths.length > 0) {
|
|
14704
|
+
return paths.map((path) => fileObservation(path, "codex apply_patch via exec", source, false, at));
|
|
14705
|
+
}
|
|
14706
|
+
}
|
|
14707
|
+
if (name === "Bash" || name === "exec") {
|
|
14708
|
+
const command = toolText(input, ["command", "input"]);
|
|
14709
|
+
return planPaths(command).map((path) => ({
|
|
14710
|
+
kind: "shell",
|
|
14711
|
+
path,
|
|
14712
|
+
method: name,
|
|
14713
|
+
source,
|
|
14714
|
+
wholeFile: false,
|
|
14715
|
+
...at === void 0 ? {} : { at },
|
|
14716
|
+
reason: "\u901A\u8FC7 shell \u5199\u5165\u65E0\u6CD5\u786E\u8BA4"
|
|
14717
|
+
}));
|
|
14718
|
+
}
|
|
14719
|
+
if (harness === "cc" && (name === "Write" || name === "Edit" || name === "MultiEdit")) {
|
|
14720
|
+
const path = recordString(input, "file_path");
|
|
14721
|
+
return path === void 0 ? [] : [fileObservation(path, name, source, name === "Write", at)];
|
|
14722
|
+
}
|
|
14723
|
+
if (harness === "dsh" && (name === "write" || name === "edit")) {
|
|
14724
|
+
const path = recordString(input, "path");
|
|
14725
|
+
return path === void 0 ? [] : [fileObservation(path, name, source, name === "write", at)];
|
|
14726
|
+
}
|
|
14727
|
+
if (harness === "codex" && name === "apply_patch") {
|
|
14728
|
+
const patch = toolText(input, ["patch", "input"]);
|
|
14729
|
+
return patchPaths(patch).map((path) => fileObservation(path, name, source, false, at));
|
|
14730
|
+
}
|
|
14731
|
+
return [];
|
|
14732
|
+
}
|
|
14733
|
+
function fileObservation(path, method, source, wholeFile, at) {
|
|
14734
|
+
return {
|
|
14735
|
+
kind: "file_editor",
|
|
14736
|
+
path,
|
|
14737
|
+
method,
|
|
14738
|
+
source,
|
|
14739
|
+
wholeFile,
|
|
14740
|
+
...at === void 0 ? {} : { at }
|
|
14741
|
+
};
|
|
14742
|
+
}
|
|
14743
|
+
function patchPaths(patch) {
|
|
14744
|
+
if (patch === void 0)
|
|
14745
|
+
return [];
|
|
14746
|
+
const paths = [];
|
|
14747
|
+
for (const line of patch.split("\n")) {
|
|
14748
|
+
const match = /^\*\*\* (?:Update|Add) File: (.+?)\s*$/u.exec(line);
|
|
14749
|
+
if (match?.[1] !== void 0 && isPlanPath(match[1]))
|
|
14750
|
+
paths.push(match[1]);
|
|
14751
|
+
}
|
|
14752
|
+
return paths;
|
|
14753
|
+
}
|
|
14754
|
+
function codexExecPatchPaths(input) {
|
|
14755
|
+
if (input === void 0 || !input.includes("*** Begin Patch"))
|
|
14756
|
+
return [];
|
|
14757
|
+
const literal = /\bconst\s+patch\s*=\s*("(?:\\.|[^"\\])*")/su.exec(input)?.[1];
|
|
14758
|
+
if (literal !== void 0) {
|
|
14759
|
+
try {
|
|
14760
|
+
return patchPaths(JSON.parse(literal));
|
|
14761
|
+
} catch {
|
|
14762
|
+
return [];
|
|
14763
|
+
}
|
|
14764
|
+
}
|
|
14765
|
+
return patchPaths(input);
|
|
14766
|
+
}
|
|
14767
|
+
function planPaths(text) {
|
|
14768
|
+
if (text === void 0)
|
|
14769
|
+
return [];
|
|
14770
|
+
return [...text.matchAll(/[^\s"'`]*\.wingman[\\/]plan\.md/gu)].map((match) => match[0]);
|
|
14771
|
+
}
|
|
14772
|
+
function toolText(input, keys) {
|
|
14773
|
+
if (typeof input === "string")
|
|
14774
|
+
return input;
|
|
14775
|
+
if (!isRecord5(input))
|
|
14776
|
+
return void 0;
|
|
14777
|
+
for (const key of keys) {
|
|
14778
|
+
const value = input[key];
|
|
14779
|
+
if (typeof value === "string")
|
|
14780
|
+
return value;
|
|
14781
|
+
}
|
|
14782
|
+
return void 0;
|
|
14783
|
+
}
|
|
14784
|
+
function recordString(value, key) {
|
|
14785
|
+
if (!isRecord5(value))
|
|
14786
|
+
return void 0;
|
|
14787
|
+
const field = value[key];
|
|
14788
|
+
return typeof field === "string" && isPlanPath(field) ? field : void 0;
|
|
14789
|
+
}
|
|
14790
|
+
function isPlanPath(path) {
|
|
14791
|
+
return /(?:^|[\\/])\.wingman[\\/]plan\.md$/u.test(path);
|
|
14792
|
+
}
|
|
14793
|
+
function atOf(record) {
|
|
14794
|
+
return typeof record.timestamp === "string" ? record.timestamp : typeof record.received_at === "string" ? record.received_at : void 0;
|
|
14795
|
+
}
|
|
14796
|
+
function flatten(nodes, parentPosition = "") {
|
|
14797
|
+
const flattened = [];
|
|
14798
|
+
nodes.forEach((node, index) => {
|
|
14799
|
+
const position = parentPosition === "" ? String(index) : `${parentPosition}.${index}`;
|
|
14800
|
+
flattened.push({ node, position }, ...flatten(node.children, position));
|
|
14801
|
+
});
|
|
14802
|
+
return flattened;
|
|
14803
|
+
}
|
|
14804
|
+
function nodeStructure(node) {
|
|
14805
|
+
return JSON.stringify([node.text, node.children.map(nodeStructure)]);
|
|
14806
|
+
}
|
|
14807
|
+
function isRecord5(value) {
|
|
14808
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14809
|
+
}
|
|
14810
|
+
|
|
14811
|
+
// apps/cli/dist/project-index.js
|
|
14812
|
+
import { lstat, readFile as readFile3, realpath, stat as stat2 } from "node:fs/promises";
|
|
14813
|
+
import { dirname as dirname14, isAbsolute as isAbsolute3, join as join19, relative as relative2, resolve as resolve8 } from "node:path";
|
|
14814
|
+
var PROJECT_ROOT_MAX_DIRECTORIES = 8;
|
|
14815
|
+
var PROJECT_PLAN_POLL_MS = 2e3;
|
|
14816
|
+
async function resolveProjectRoot(cwd, options) {
|
|
14817
|
+
const root = resolve8(cwd);
|
|
14818
|
+
const homeDir = resolve8(options.homeDir);
|
|
14819
|
+
const boundaryDir = options.boundaryDir === void 0 ? void 0 : resolve8(options.boundaryDir);
|
|
14820
|
+
const [rootIdentity, homeIdentity, boundaryIdentity] = await Promise.all([
|
|
14821
|
+
pathIdentity(root),
|
|
14822
|
+
pathIdentity(homeDir),
|
|
14823
|
+
boundaryDir === void 0 ? void 0 : pathIdentity(boundaryDir)
|
|
14824
|
+
]);
|
|
14825
|
+
const wingmanHomeIdentity = await pathIdentity(join19(homeIdentity, ".wingman"));
|
|
14826
|
+
const planPathIdentity = await pathIdentity(join19(rootIdentity, ".wingman", "plan.md"));
|
|
14827
|
+
if (rootIdentity === homeIdentity || rootIdentity === wingmanHomeIdentity || boundaryIdentity !== void 0 && !isWithin(boundaryIdentity, rootIdentity)) {
|
|
14828
|
+
return { root, rootIdentity, planPathIdentity, lookedUp: [], higherPlanPaths: [] };
|
|
14829
|
+
}
|
|
14830
|
+
const lookedUp = [];
|
|
14831
|
+
const found = [];
|
|
14832
|
+
let current = root;
|
|
14833
|
+
const maxDirectories = options.maxDirectories ?? PROJECT_ROOT_MAX_DIRECTORIES;
|
|
14834
|
+
for (let checked = 0; checked < maxDirectories; checked += 1) {
|
|
14835
|
+
const currentIdentity = await pathIdentity(current);
|
|
14836
|
+
if (currentIdentity === homeIdentity || currentIdentity === wingmanHomeIdentity)
|
|
14837
|
+
break;
|
|
14838
|
+
if (boundaryIdentity !== void 0 && !isWithin(boundaryIdentity, currentIdentity))
|
|
14839
|
+
break;
|
|
14840
|
+
const planPath = join19(current, ".wingman", "plan.md");
|
|
14841
|
+
lookedUp.push(planPath);
|
|
14842
|
+
try {
|
|
14843
|
+
await lstat(planPath);
|
|
14844
|
+
found.push({
|
|
14845
|
+
root: current,
|
|
14846
|
+
rootIdentity: currentIdentity,
|
|
14847
|
+
planPath,
|
|
14848
|
+
planPathIdentity: await pathIdentity(planPath)
|
|
14849
|
+
});
|
|
14850
|
+
} catch {
|
|
14851
|
+
}
|
|
14852
|
+
const parent = dirname14(current);
|
|
14853
|
+
if (parent === current)
|
|
14854
|
+
break;
|
|
14855
|
+
current = parent;
|
|
14856
|
+
}
|
|
14857
|
+
const nearest = found[0];
|
|
14858
|
+
return {
|
|
14859
|
+
root: nearest?.root ?? root,
|
|
14860
|
+
rootIdentity: nearest?.rootIdentity ?? rootIdentity,
|
|
14861
|
+
...nearest === void 0 ? {} : { planPath: nearest.planPath },
|
|
14862
|
+
planPathIdentity: nearest?.planPathIdentity ?? planPathIdentity,
|
|
14863
|
+
lookedUp,
|
|
14864
|
+
higherPlanPaths: found.slice(1).map((item) => item.planPath)
|
|
14865
|
+
};
|
|
14866
|
+
}
|
|
14867
|
+
function watchProjectPlan(planPath, options) {
|
|
14868
|
+
const intervalMs = options.intervalMs ?? PROJECT_PLAN_POLL_MS;
|
|
14869
|
+
let previousSignature;
|
|
14870
|
+
let polling = false;
|
|
14871
|
+
let closed = false;
|
|
14872
|
+
const poll = async () => {
|
|
14873
|
+
if (closed || polling)
|
|
14874
|
+
return;
|
|
14875
|
+
polling = true;
|
|
14876
|
+
try {
|
|
14877
|
+
const linkInfo = await lstat(planPath);
|
|
14878
|
+
const resolvedPath = await realpath(planPath);
|
|
14879
|
+
const fileInfo = await stat2(planPath);
|
|
14880
|
+
const resolvedInsideRoot = options.projectRoot === void 0 || isWithin(await pathIdentity(options.projectRoot), normalizedPath(resolvedPath));
|
|
14881
|
+
const info = {
|
|
14882
|
+
mtime: fileInfo.mtime.toISOString(),
|
|
14883
|
+
size: fileInfo.size,
|
|
14884
|
+
isRegularFile: fileInfo.isFile(),
|
|
14885
|
+
resolvedPath,
|
|
14886
|
+
resolvedInsideRoot
|
|
14887
|
+
};
|
|
14888
|
+
const signature = [
|
|
14889
|
+
linkInfo.mtimeMs,
|
|
14890
|
+
fileInfo.mtimeMs,
|
|
14891
|
+
fileInfo.size,
|
|
14892
|
+
info.isRegularFile,
|
|
14893
|
+
resolvedPath
|
|
14894
|
+
].join(":");
|
|
14895
|
+
if (signature === previousSignature)
|
|
14896
|
+
return;
|
|
14897
|
+
previousSignature = signature;
|
|
14898
|
+
if (!info.isRegularFile) {
|
|
14899
|
+
options.onUnavailable?.("\u8BA1\u5212\u6587\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6", info);
|
|
14900
|
+
return;
|
|
14901
|
+
}
|
|
14902
|
+
if (!info.resolvedInsideRoot) {
|
|
14903
|
+
options.onUnavailable?.("\u8F6F\u94FE\u89E3\u6790\u5230\u9879\u76EE\u6839\u4E4B\u5916", info);
|
|
14904
|
+
return;
|
|
14905
|
+
}
|
|
14906
|
+
if (info.size > PROJECT_PLAN_MAX_BYTES) {
|
|
14907
|
+
options.onUnavailable?.("\u8BA1\u5212\u6587\u4EF6\u8D85\u8FC7 1 MiB", info);
|
|
14908
|
+
return;
|
|
14909
|
+
}
|
|
14910
|
+
options.onRead(await readFile3(planPath), info);
|
|
14911
|
+
} catch {
|
|
14912
|
+
if (previousSignature !== "missing") {
|
|
14913
|
+
previousSignature = "missing";
|
|
14914
|
+
options.onUnavailable?.("\u8BA1\u5212\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
14915
|
+
}
|
|
14916
|
+
} finally {
|
|
14917
|
+
polling = false;
|
|
14918
|
+
}
|
|
14919
|
+
};
|
|
14920
|
+
const timer = setInterval(() => void poll(), intervalMs);
|
|
14921
|
+
void poll();
|
|
14922
|
+
return {
|
|
14923
|
+
close: () => {
|
|
14924
|
+
closed = true;
|
|
14925
|
+
clearInterval(timer);
|
|
14926
|
+
}
|
|
14927
|
+
};
|
|
14928
|
+
}
|
|
14929
|
+
async function pathIdentity(path) {
|
|
14930
|
+
const resolved = resolve8(path);
|
|
14931
|
+
try {
|
|
14932
|
+
return normalizedPath(await realpath(resolved));
|
|
14933
|
+
} catch {
|
|
14934
|
+
return normalizedPath(resolved);
|
|
14935
|
+
}
|
|
14936
|
+
}
|
|
14937
|
+
async function pathsReferToSameLocation(left, right) {
|
|
14938
|
+
const [leftIdentity, rightIdentity] = await Promise.all([
|
|
14939
|
+
pathIdentity(left),
|
|
14940
|
+
pathIdentity(right)
|
|
14941
|
+
]);
|
|
14942
|
+
return relative2(leftIdentity, rightIdentity) === "";
|
|
14943
|
+
}
|
|
14944
|
+
function normalizedPath(path) {
|
|
14945
|
+
const resolved = resolve8(path);
|
|
14946
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
14947
|
+
}
|
|
14948
|
+
function isWithin(parent, child) {
|
|
14949
|
+
const pathFromParent = relative2(parent, child);
|
|
14950
|
+
return pathFromParent === "" || !pathFromParent.startsWith("..") && !isAbsolute3(pathFromParent);
|
|
14951
|
+
}
|
|
14952
|
+
|
|
13450
14953
|
// apps/cli/dist/narrative-service.js
|
|
13451
14954
|
var MAX_SESSIONS = 8;
|
|
13452
14955
|
var ENDED_AFTER_STOP_MS = 10 * 6e4;
|
|
@@ -13461,6 +14964,8 @@ function createNarrativeService(options = {}) {
|
|
|
13461
14964
|
const harnesses = /* @__PURE__ */ new Map();
|
|
13462
14965
|
const listeners = /* @__PURE__ */ new Set();
|
|
13463
14966
|
const pendingNotifications = /* @__PURE__ */ new Set();
|
|
14967
|
+
const projectWatchers = /* @__PURE__ */ new Map();
|
|
14968
|
+
const projectFiles = /* @__PURE__ */ new Map();
|
|
13464
14969
|
let notificationQueued = false;
|
|
13465
14970
|
let closed = false;
|
|
13466
14971
|
function notify(harness) {
|
|
@@ -13500,6 +15005,9 @@ function createNarrativeService(options = {}) {
|
|
|
13500
15005
|
generation: 0,
|
|
13501
15006
|
lines: [],
|
|
13502
15007
|
codexLines: [],
|
|
15008
|
+
pendingPlanWrites: [],
|
|
15009
|
+
confirmedPlanWrites: [],
|
|
15010
|
+
nonPlanToolSteps: 0,
|
|
13503
15011
|
follows: /* @__PURE__ */ new Map(),
|
|
13504
15012
|
hookIntervals: [],
|
|
13505
15013
|
waiting: null,
|
|
@@ -13516,6 +15024,167 @@ function createNarrativeService(options = {}) {
|
|
|
13516
15024
|
clearInterval(session.subagentTimer);
|
|
13517
15025
|
session.subagentTimer = void 0;
|
|
13518
15026
|
}
|
|
15027
|
+
function anchorCwd(harness, session, cwd) {
|
|
15028
|
+
if (session.cwd !== void 0)
|
|
15029
|
+
return;
|
|
15030
|
+
session.cwd = cwd;
|
|
15031
|
+
if (options.homeDir === void 0)
|
|
15032
|
+
return;
|
|
15033
|
+
void resolveProjectRoot(cwd, {
|
|
15034
|
+
homeDir: options.homeDir,
|
|
15035
|
+
...options.projectBoundaryDir === void 0 ? {} : { boundaryDir: options.projectBoundaryDir }
|
|
15036
|
+
}).then((resolution) => {
|
|
15037
|
+
if (closed || session.cwd !== cwd)
|
|
15038
|
+
return;
|
|
15039
|
+
session.projectResolution = resolution;
|
|
15040
|
+
watchResolution(resolution);
|
|
15041
|
+
session.dirty = true;
|
|
15042
|
+
notify(harness);
|
|
15043
|
+
});
|
|
15044
|
+
}
|
|
15045
|
+
function watchResolution(resolution) {
|
|
15046
|
+
if (resolution.lookedUp.length === 0)
|
|
15047
|
+
return;
|
|
15048
|
+
const planPath = resolution.planPath ?? join20(resolution.root, ".wingman", "plan.md");
|
|
15049
|
+
const planPathIdentity = resolution.planPathIdentity;
|
|
15050
|
+
if (projectWatchers.has(planPathIdentity))
|
|
15051
|
+
return;
|
|
15052
|
+
projectWatchers.set(planPathIdentity, watchProjectPlan(planPath, {
|
|
15053
|
+
...options.projectPollIntervalMs === void 0 ? {} : { intervalMs: options.projectPollIntervalMs },
|
|
15054
|
+
projectRoot: resolution.root,
|
|
15055
|
+
onRead: (content, info) => {
|
|
15056
|
+
const existing = projectFiles.get(planPathIdentity);
|
|
15057
|
+
const parsed = parseProjectPlan(content, {
|
|
15058
|
+
isRegularFile: info.isRegularFile,
|
|
15059
|
+
resolvedInsideRoot: info.resolvedInsideRoot
|
|
15060
|
+
});
|
|
15061
|
+
let revision = existing?.revision ?? 0;
|
|
15062
|
+
let structureKey = existing?.structureKey;
|
|
15063
|
+
let lastGood = existing?.lastGood;
|
|
15064
|
+
if (parsed.availability === "available") {
|
|
15065
|
+
const nextStructureKey = projectPlanStructureKey(parsed.nodes);
|
|
15066
|
+
if (nextStructureKey !== structureKey)
|
|
15067
|
+
revision += 1;
|
|
15068
|
+
structureKey = nextStructureKey;
|
|
15069
|
+
lastGood = {
|
|
15070
|
+
revision,
|
|
15071
|
+
mtime: info.mtime,
|
|
15072
|
+
...parsed.title === void 0 ? {} : { title: parsed.title },
|
|
15073
|
+
...parsed.goal === void 0 ? {} : { goal: parsed.goal },
|
|
15074
|
+
ignoredLineCount: parsed.ignoredLineCount,
|
|
15075
|
+
nodes: parsed.nodes
|
|
15076
|
+
};
|
|
15077
|
+
}
|
|
15078
|
+
projectFiles.set(planPathIdentity, {
|
|
15079
|
+
...existing?.current === void 0 ? {} : { previous: existing.current },
|
|
15080
|
+
current: { content, info },
|
|
15081
|
+
parsed,
|
|
15082
|
+
revision,
|
|
15083
|
+
...structureKey === void 0 ? {} : { structureKey },
|
|
15084
|
+
...lastGood === void 0 ? {} : { lastGood },
|
|
15085
|
+
missing: false
|
|
15086
|
+
});
|
|
15087
|
+
projectChanged(planPathIdentity);
|
|
15088
|
+
},
|
|
15089
|
+
onUnavailable: (reason) => {
|
|
15090
|
+
const existing = projectFiles.get(planPathIdentity);
|
|
15091
|
+
const missing = reason === "\u8BA1\u5212\u6587\u4EF6\u4E0D\u5B58\u5728";
|
|
15092
|
+
projectFiles.set(planPathIdentity, {
|
|
15093
|
+
...existing,
|
|
15094
|
+
...missing ? {} : { parsed: { availability: "unavailable", reason } },
|
|
15095
|
+
revision: existing?.revision ?? 0,
|
|
15096
|
+
missing
|
|
15097
|
+
});
|
|
15098
|
+
projectChanged(planPathIdentity);
|
|
15099
|
+
}
|
|
15100
|
+
}));
|
|
15101
|
+
}
|
|
15102
|
+
function projectChanged(planPathIdentity) {
|
|
15103
|
+
for (const [harness, sessions] of harnesses) {
|
|
15104
|
+
let changed = false;
|
|
15105
|
+
for (const session of sessions.values()) {
|
|
15106
|
+
const resolution = session.projectResolution;
|
|
15107
|
+
if (resolution?.planPathIdentity === planPathIdentity) {
|
|
15108
|
+
const planPath = resolution.planPath ?? join20(resolution.root, ".wingman", "plan.md");
|
|
15109
|
+
void applyPendingPlanWrites(session, planPath, planPathIdentity).then(() => {
|
|
15110
|
+
session.dirty = true;
|
|
15111
|
+
notify(harness);
|
|
15112
|
+
});
|
|
15113
|
+
session.dirty = true;
|
|
15114
|
+
changed = true;
|
|
15115
|
+
}
|
|
15116
|
+
}
|
|
15117
|
+
if (changed)
|
|
15118
|
+
notify(harness);
|
|
15119
|
+
}
|
|
15120
|
+
}
|
|
15121
|
+
function recordToolObservations(harness, session, tools) {
|
|
15122
|
+
for (const tool of tools) {
|
|
15123
|
+
if (tool.planWrites.length === 0) {
|
|
15124
|
+
if (!tool.planFileAccess && !TODO_TOOLS.has(tool.method))
|
|
15125
|
+
session.nonPlanToolSteps += 1;
|
|
15126
|
+
continue;
|
|
15127
|
+
}
|
|
15128
|
+
recordPlanWrites(harness, session, tool.planWrites, session.nonPlanToolSteps);
|
|
15129
|
+
}
|
|
15130
|
+
}
|
|
15131
|
+
function recordPlanWrites(harness, session, observations, nonPlanStepsBefore) {
|
|
15132
|
+
for (const observation of observations) {
|
|
15133
|
+
if (observation.kind === "shell") {
|
|
15134
|
+
session.planUnattachedReason = observation.reason;
|
|
15135
|
+
continue;
|
|
15136
|
+
}
|
|
15137
|
+
const same = (candidate) => candidate.path === observation.path && candidate.method === observation.method;
|
|
15138
|
+
if (observation.source === "hook" && session.pendingPlanWrites.some((candidate) => candidate.observation.source === "transcript" && same(candidate.observation))) {
|
|
15139
|
+
continue;
|
|
15140
|
+
}
|
|
15141
|
+
if (observation.source === "transcript") {
|
|
15142
|
+
session.pendingPlanWrites = session.pendingPlanWrites.filter((candidate) => candidate.observation.source !== "hook" || !same(candidate.observation));
|
|
15143
|
+
}
|
|
15144
|
+
session.pendingPlanWrites.push({ observation, nonPlanStepsBefore });
|
|
15145
|
+
}
|
|
15146
|
+
const resolution = session.projectResolution;
|
|
15147
|
+
if (resolution !== void 0) {
|
|
15148
|
+
const planPath = resolution.planPath ?? join20(resolution.root, ".wingman", "plan.md");
|
|
15149
|
+
void applyPendingPlanWrites(session, planPath, resolution.planPathIdentity).then(() => {
|
|
15150
|
+
session.dirty = true;
|
|
15151
|
+
notify(harness);
|
|
15152
|
+
});
|
|
15153
|
+
}
|
|
15154
|
+
}
|
|
15155
|
+
async function applyPendingPlanWrites(session, planPath, planPathIdentity) {
|
|
15156
|
+
const versions = projectFiles.get(planPathIdentity);
|
|
15157
|
+
if (versions?.previous === void 0 || versions.current === void 0)
|
|
15158
|
+
return;
|
|
15159
|
+
const before = parseProjectPlan(versions.previous.content);
|
|
15160
|
+
const after = parseProjectPlan(versions.current.content);
|
|
15161
|
+
if (before.availability !== "available" || after.availability !== "available")
|
|
15162
|
+
return;
|
|
15163
|
+
const remaining = [];
|
|
15164
|
+
for (const pending of session.pendingPlanWrites) {
|
|
15165
|
+
const { observation } = pending;
|
|
15166
|
+
const observedPath = resolveObservedPath(observation.path, session.cwd);
|
|
15167
|
+
if (observedPath === void 0 || !await pathsReferToSameLocation(observedPath, planPath)) {
|
|
15168
|
+
remaining.push(pending);
|
|
15169
|
+
continue;
|
|
15170
|
+
}
|
|
15171
|
+
session.confirmedPlanWrites.push({
|
|
15172
|
+
method: observation.method,
|
|
15173
|
+
path: observedPath,
|
|
15174
|
+
source: observation.source,
|
|
15175
|
+
at: observation.at ?? versions.current.info.mtime,
|
|
15176
|
+
nonPlanStepsBefore: pending.nonPlanStepsBefore
|
|
15177
|
+
});
|
|
15178
|
+
const result4 = attributePlanWrite(before, after, observation);
|
|
15179
|
+
if ("attachedTo" in result4) {
|
|
15180
|
+
session.planAttachment = result4;
|
|
15181
|
+
session.planUnattachedReason = void 0;
|
|
15182
|
+
} else {
|
|
15183
|
+
session.planUnattachedReason = result4.reason;
|
|
15184
|
+
}
|
|
15185
|
+
}
|
|
15186
|
+
session.pendingPlanWrites = remaining;
|
|
15187
|
+
}
|
|
13519
15188
|
function evictOne(sessions, nowIso) {
|
|
13520
15189
|
let victimId;
|
|
13521
15190
|
let victimAt = "\uFFFF";
|
|
@@ -13569,6 +15238,7 @@ function createNarrativeService(options = {}) {
|
|
|
13569
15238
|
session.codexLines.push(parseCodexRolloutLine(line));
|
|
13570
15239
|
else
|
|
13571
15240
|
session.lines.push(parseTranscriptLine(line));
|
|
15241
|
+
recordToolObservations(harness, session, extractRawToolObservations(harness, line));
|
|
13572
15242
|
session.dirty = true;
|
|
13573
15243
|
notify(harness);
|
|
13574
15244
|
}, { intervalMs }));
|
|
@@ -13577,7 +15247,7 @@ function createNarrativeService(options = {}) {
|
|
|
13577
15247
|
const sessionDir = transcriptPath.replace(/\.jsonl$/, "");
|
|
13578
15248
|
if (sessionDir === transcriptPath)
|
|
13579
15249
|
return;
|
|
13580
|
-
const subagentsDir =
|
|
15250
|
+
const subagentsDir = join20(sessionDir, "subagents");
|
|
13581
15251
|
const generation = session.generation;
|
|
13582
15252
|
const poll = async () => {
|
|
13583
15253
|
try {
|
|
@@ -13587,7 +15257,7 @@ function createNarrativeService(options = {}) {
|
|
|
13587
15257
|
}
|
|
13588
15258
|
for (const entry of entries) {
|
|
13589
15259
|
if (/^agent-.*\.jsonl$/.test(entry)) {
|
|
13590
|
-
startFollowing(harness, session,
|
|
15260
|
+
startFollowing(harness, session, join20(subagentsDir, entry), generation);
|
|
13591
15261
|
}
|
|
13592
15262
|
}
|
|
13593
15263
|
} catch {
|
|
@@ -13608,10 +15278,12 @@ function createNarrativeService(options = {}) {
|
|
|
13608
15278
|
session.lastActivity = event.received_at;
|
|
13609
15279
|
const cwd = typeof payload.cwd === "string" ? payload.cwd : void 0;
|
|
13610
15280
|
if (cwd !== void 0) {
|
|
15281
|
+
anchorCwd(event.harness, session, cwd);
|
|
13611
15282
|
const base = pathBasename(cwd);
|
|
13612
15283
|
if (base !== "")
|
|
13613
15284
|
session.label = base;
|
|
13614
15285
|
}
|
|
15286
|
+
recordToolObservations(event.harness, session, extractToolObservations(event.harness, [{ ...payload, received_at: event.received_at }]));
|
|
13615
15287
|
if (follow && transcriptPath !== void 0) {
|
|
13616
15288
|
if (session.transcriptPath !== transcriptPath) {
|
|
13617
15289
|
resetTranscript(session, transcriptPath);
|
|
@@ -13706,9 +15378,15 @@ function createNarrativeService(options = {}) {
|
|
|
13706
15378
|
const session2 = sessionOf(harness, id2);
|
|
13707
15379
|
session2.lines = [];
|
|
13708
15380
|
session2.codexLines = codexLines;
|
|
15381
|
+
session2.pendingPlanWrites = [];
|
|
15382
|
+
session2.confirmedPlanWrites = [];
|
|
15383
|
+
session2.nonPlanToolSteps = 0;
|
|
15384
|
+
for (const line of sourceLines)
|
|
15385
|
+
recordToolObservations(harness, session2, extractRawToolObservations(harness, line));
|
|
13709
15386
|
session2.mockNote = mockNote;
|
|
13710
15387
|
const cwd = meta?.cwd ?? codexLines.find((line) => line.kind === "turn_context")?.cwd;
|
|
13711
15388
|
if (cwd !== void 0) {
|
|
15389
|
+
anchorCwd(harness, session2, cwd);
|
|
13712
15390
|
const base = pathBasename(cwd);
|
|
13713
15391
|
if (base !== "")
|
|
13714
15392
|
session2.label = base;
|
|
@@ -13717,18 +15395,25 @@ function createNarrativeService(options = {}) {
|
|
|
13717
15395
|
notify(harness);
|
|
13718
15396
|
return;
|
|
13719
15397
|
}
|
|
13720
|
-
const
|
|
13721
|
-
const sessionLine =
|
|
15398
|
+
const lines2 = sourceLines.map((line) => parseTranscriptLine(line));
|
|
15399
|
+
const sessionLine = lines2.find((l) => l.kind !== "unknown" && typeof l.sessionId === "string");
|
|
13722
15400
|
const id = sessionLine?.sessionId ?? "__fixture__";
|
|
13723
15401
|
const session = sessionOf(harness, id);
|
|
13724
|
-
session.lines =
|
|
15402
|
+
session.lines = lines2;
|
|
15403
|
+
session.pendingPlanWrites = [];
|
|
15404
|
+
session.confirmedPlanWrites = [];
|
|
15405
|
+
session.nonPlanToolSteps = 0;
|
|
15406
|
+
for (const line of sourceLines) {
|
|
15407
|
+
recordToolObservations(harness, session, extractRawToolObservations(harness, line));
|
|
15408
|
+
}
|
|
13725
15409
|
session.codexLines = [];
|
|
13726
15410
|
session.mockNote = mockNote;
|
|
13727
|
-
for (const line of
|
|
15411
|
+
for (const line of lines2) {
|
|
13728
15412
|
if (line.kind === "unknown")
|
|
13729
15413
|
continue;
|
|
13730
15414
|
const cwd = line.raw.cwd;
|
|
13731
15415
|
if (typeof cwd === "string") {
|
|
15416
|
+
anchorCwd(harness, session, cwd);
|
|
13732
15417
|
const base = pathBasename(cwd);
|
|
13733
15418
|
if (base !== "")
|
|
13734
15419
|
session.label = base;
|
|
@@ -13757,6 +15442,7 @@ function createNarrativeService(options = {}) {
|
|
|
13757
15442
|
const infos = [...sessions.values()].map((session) => ({
|
|
13758
15443
|
id: session.id,
|
|
13759
15444
|
label: session.label ?? session.id.slice(0, 8),
|
|
15445
|
+
...session.cwd === void 0 ? {} : { cwd: session.cwd },
|
|
13760
15446
|
lastActivity: lastActivityOf(session),
|
|
13761
15447
|
waiting: session.waiting !== null,
|
|
13762
15448
|
ended: isEnded(session, nowIso),
|
|
@@ -13785,12 +15471,198 @@ function createNarrativeService(options = {}) {
|
|
|
13785
15471
|
}
|
|
13786
15472
|
return emptyState(harness, now().toISOString());
|
|
13787
15473
|
}
|
|
15474
|
+
function installedProtocolStatus(harness, projectDir) {
|
|
15475
|
+
if (options.protocolStatus !== void 0)
|
|
15476
|
+
return options.protocolStatus(harness, projectDir);
|
|
15477
|
+
const common = options.homeDir === void 0 ? { projectDir } : { homeDir: options.homeDir, projectDir };
|
|
15478
|
+
switch (harness) {
|
|
15479
|
+
case "cc":
|
|
15480
|
+
return protocolStatusCc(common);
|
|
15481
|
+
case "codex":
|
|
15482
|
+
return protocolStatusCodex(common);
|
|
15483
|
+
case "dsh":
|
|
15484
|
+
return protocolStatusDsh(common);
|
|
15485
|
+
}
|
|
15486
|
+
}
|
|
15487
|
+
function projectProtocolStatus(harness, projectDir, planMtime) {
|
|
15488
|
+
const observed = installedProtocolStatus(harness, projectDir);
|
|
15489
|
+
let state;
|
|
15490
|
+
if (observed.state === "installed_not_loaded")
|
|
15491
|
+
state = "installed_not_loaded";
|
|
15492
|
+
else if (observed.state === "installed") {
|
|
15493
|
+
state = planMtime === void 0 ? "installed" : "plan_present";
|
|
15494
|
+
} else
|
|
15495
|
+
state = "not_installed";
|
|
15496
|
+
return {
|
|
15497
|
+
state,
|
|
15498
|
+
targetPath: observed.targetPath,
|
|
15499
|
+
...observed.version === void 0 ? {} : { version: observed.version },
|
|
15500
|
+
...state === "plan_present" && planMtime !== void 0 ? { planMtime } : {},
|
|
15501
|
+
...!("message" in observed) || observed.message === void 0 ? {} : { message: observed.message },
|
|
15502
|
+
...observed.reason === void 0 ? {} : { reason: observed.reason }
|
|
15503
|
+
};
|
|
15504
|
+
}
|
|
15505
|
+
function complianceFor(session, installed) {
|
|
15506
|
+
const writeCount = session.confirmedPlanWrites.length;
|
|
15507
|
+
const canAttributeProtocol = installed.state === "installed" || installed.state === "plan_present";
|
|
15508
|
+
const level = canAttributeProtocol && writeCount > 0 ? "session_wrote_plan" : installed.state;
|
|
15509
|
+
const latest = session.confirmedPlanWrites.at(-1);
|
|
15510
|
+
const first = session.confirmedPlanWrites[0];
|
|
15511
|
+
let timing;
|
|
15512
|
+
if (first !== void 0) {
|
|
15513
|
+
if (first.nonPlanStepsBefore > 0) {
|
|
15514
|
+
timing = {
|
|
15515
|
+
relation: "after",
|
|
15516
|
+
nonPlanToolStep: first.nonPlanStepsBefore,
|
|
15517
|
+
text: `\u8BA1\u5212\u6587\u4EF6\u9996\u6B21\u5199\u5165\u53D1\u751F\u5728\u7B2C ${first.nonPlanStepsBefore} \u4E2A\u975E\u8BA1\u5212\u5DE5\u5177\u6B65\u9AA4\u4E4B\u540E`
|
|
15518
|
+
};
|
|
15519
|
+
} else if (session.nonPlanToolSteps > 0) {
|
|
15520
|
+
timing = {
|
|
15521
|
+
relation: "before",
|
|
15522
|
+
nonPlanToolStep: 1,
|
|
15523
|
+
text: "\u8BA1\u5212\u6587\u4EF6\u9996\u6B21\u5199\u5165\u53D1\u751F\u5728\u7B2C 1 \u4E2A\u975E\u8BA1\u5212\u5DE5\u5177\u6B65\u9AA4\u4E4B\u524D"
|
|
15524
|
+
};
|
|
15525
|
+
}
|
|
15526
|
+
}
|
|
15527
|
+
return {
|
|
15528
|
+
level,
|
|
15529
|
+
writeCount,
|
|
15530
|
+
...latest === void 0 ? {} : { latestWriteAt: latest.at },
|
|
15531
|
+
...latest === void 0 ? {} : { writeSummary: `\u672C\u4F1A\u8BDD\u5199\u5165 ${writeCount} \u6B21 \xB7 \u6700\u8FD1 ${latest.at}` },
|
|
15532
|
+
...timing === void 0 ? {} : { timing }
|
|
15533
|
+
};
|
|
15534
|
+
}
|
|
15535
|
+
function getProjects() {
|
|
15536
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
15537
|
+
for (const [harness, sessions] of harnesses) {
|
|
15538
|
+
for (const session of sessions.values()) {
|
|
15539
|
+
if (session.cwd === void 0)
|
|
15540
|
+
continue;
|
|
15541
|
+
const rootIdentity = session.projectResolution?.rootIdentity ?? resolve9(session.cwd);
|
|
15542
|
+
const group = grouped.get(rootIdentity);
|
|
15543
|
+
if (group === void 0)
|
|
15544
|
+
grouped.set(rootIdentity, [{ harness, session }]);
|
|
15545
|
+
else
|
|
15546
|
+
group.push({ harness, session });
|
|
15547
|
+
}
|
|
15548
|
+
}
|
|
15549
|
+
const overviews = /* @__PURE__ */ new Map();
|
|
15550
|
+
for (const harness of harnesses.keys())
|
|
15551
|
+
overviews.set(harness, getOverview(harness));
|
|
15552
|
+
const projects = [];
|
|
15553
|
+
for (const members of grouped.values()) {
|
|
15554
|
+
const resolution = members.find((member) => member.session.projectResolution !== void 0)?.session.projectResolution;
|
|
15555
|
+
const root = resolution?.root ?? resolve9(members[0]?.session.cwd ?? "");
|
|
15556
|
+
const planPath = resolution?.planPath ?? join20(root, ".wingman", "plan.md");
|
|
15557
|
+
const runtime = projectFiles.get(resolution?.planPathIdentity ?? planPath);
|
|
15558
|
+
const parsed = runtime?.missing === true ? void 0 : runtime?.parsed;
|
|
15559
|
+
const planMtime = parsed?.availability === "available" ? runtime?.current?.info.mtime : void 0;
|
|
15560
|
+
const protocol = {
|
|
15561
|
+
cc: projectProtocolStatus("cc", root, planMtime),
|
|
15562
|
+
codex: projectProtocolStatus("codex", root, planMtime),
|
|
15563
|
+
dsh: projectProtocolStatus("dsh", root, planMtime)
|
|
15564
|
+
};
|
|
15565
|
+
const sessionViews = members.map(({ harness, session }) => {
|
|
15566
|
+
const overview = overviews.get(harness);
|
|
15567
|
+
const state = overview?.states[session.id];
|
|
15568
|
+
const info = overview?.sessions.find((candidate) => candidate.id === session.id);
|
|
15569
|
+
return {
|
|
15570
|
+
harness,
|
|
15571
|
+
id: session.id,
|
|
15572
|
+
label: session.label ?? session.id.slice(0, 8),
|
|
15573
|
+
ended: info?.ended ?? false,
|
|
15574
|
+
compliance: complianceFor(session, protocol[harness]),
|
|
15575
|
+
...session.planAttachment === void 0 ? {} : {
|
|
15576
|
+
attachedTo: session.planAttachment.attachedTo,
|
|
15577
|
+
attachedBy: session.planAttachment.attachedBy
|
|
15578
|
+
},
|
|
15579
|
+
...session.planAttachment !== void 0 ? {} : {
|
|
15580
|
+
unattachedReason: session.planUnattachedReason ?? "\u672A\u89C2\u5BDF\u5230\u8BE5\u4F1A\u8BDD\u901A\u8FC7\u6587\u4EF6\u7F16\u8F91\u5DE5\u5177\u5199\u5165 plan.md"
|
|
15581
|
+
},
|
|
15582
|
+
...state?.taskWorkflow === void 0 ? {} : {
|
|
15583
|
+
sessionPlan: state.taskWorkflow.plan,
|
|
15584
|
+
feedback: state.taskWorkflow.feedback
|
|
15585
|
+
}
|
|
15586
|
+
};
|
|
15587
|
+
}).sort((left, right) => {
|
|
15588
|
+
const harnessOrder = HARNESS_IDS.indexOf(left.harness) - HARNESS_IDS.indexOf(right.harness);
|
|
15589
|
+
return harnessOrder === 0 ? left.id.localeCompare(right.id) : harnessOrder;
|
|
15590
|
+
});
|
|
15591
|
+
for (const session of sessionViews) {
|
|
15592
|
+
if (session.compliance.level === "session_wrote_plan") {
|
|
15593
|
+
protocol[session.harness] = {
|
|
15594
|
+
...protocol[session.harness],
|
|
15595
|
+
state: "session_wrote_plan"
|
|
15596
|
+
};
|
|
15597
|
+
}
|
|
15598
|
+
}
|
|
15599
|
+
const summary = parsed?.availability === "available" ? deriveProjectPlanSummary(parsed.nodes) : deriveProjectPlanSummary([]);
|
|
15600
|
+
const reconciliation = deriveProjectReconciliationFacts(parsed?.availability === "available" ? parsed.nodes : [], sessionViews.map((session) => ({
|
|
15601
|
+
id: `${session.harness}:${session.id}`,
|
|
15602
|
+
...session.attachedTo === void 0 ? {} : { attachedTo: session.attachedTo },
|
|
15603
|
+
ended: session.ended,
|
|
15604
|
+
...session.sessionPlan?.availability === "available" && session.sessionPlan.steps.length > 0 ? {
|
|
15605
|
+
sessionPlanAllCompleted: session.sessionPlan.steps.every((step) => step.status === "completed")
|
|
15606
|
+
} : {},
|
|
15607
|
+
...session.attachedBy?.at === void 0 ? {} : { wrotePlanAt: session.attachedBy.at }
|
|
15608
|
+
})), now().toISOString());
|
|
15609
|
+
const lastWriter = [...sessionViews].filter((session) => typeof session.attachedBy?.at === "string").sort((left, right) => right.attachedBy.at.localeCompare(left.attachedBy.at))[0];
|
|
15610
|
+
const titleText = parsed?.availability === "available" && parsed.title !== void 0 ? parsed.title : pathBasename(root) || root;
|
|
15611
|
+
const titleSource = parsed?.availability === "available" && parsed.title !== void 0 ? ".wingman/plan.md H1" : "\u5DE5\u4F5C\u76EE\u5F55";
|
|
15612
|
+
let plan;
|
|
15613
|
+
if (parsed?.availability === "available") {
|
|
15614
|
+
plan = {
|
|
15615
|
+
availability: "available",
|
|
15616
|
+
ignoredLineCount: parsed.ignoredLineCount,
|
|
15617
|
+
nodes: parsed.nodes,
|
|
15618
|
+
higherPlanPaths: resolution?.higherPlanPaths ?? []
|
|
15619
|
+
};
|
|
15620
|
+
} else if (parsed?.availability === "unavailable") {
|
|
15621
|
+
plan = {
|
|
15622
|
+
availability: "unavailable",
|
|
15623
|
+
reason: parsed.reason,
|
|
15624
|
+
...parsed.line === void 0 ? {} : { line: parsed.line },
|
|
15625
|
+
...runtime?.lastGood === void 0 ? {} : { lastGood: runtime.lastGood }
|
|
15626
|
+
};
|
|
15627
|
+
} else {
|
|
15628
|
+
plan = {
|
|
15629
|
+
availability: "missing",
|
|
15630
|
+
lookedUp: resolution?.lookedUp ?? [planPath]
|
|
15631
|
+
};
|
|
15632
|
+
}
|
|
15633
|
+
projects.push({
|
|
15634
|
+
root,
|
|
15635
|
+
title: { text: titleText, source: titleSource },
|
|
15636
|
+
...parsed?.availability === "available" && parsed.goal !== void 0 ? { goal: parsed.goal } : {},
|
|
15637
|
+
plan,
|
|
15638
|
+
...runtime !== void 0 && runtime.revision > 0 ? { revision: runtime.revision } : {},
|
|
15639
|
+
...runtime?.current?.info.mtime === void 0 ? {} : { mtime: runtime.current.info.mtime },
|
|
15640
|
+
...lastWriter?.attachedBy.at === void 0 ? {} : {
|
|
15641
|
+
lastWriter: {
|
|
15642
|
+
harness: lastWriter.harness,
|
|
15643
|
+
id: lastWriter.id,
|
|
15644
|
+
label: lastWriter.label,
|
|
15645
|
+
at: lastWriter.attachedBy.at
|
|
15646
|
+
}
|
|
15647
|
+
},
|
|
15648
|
+
...summary,
|
|
15649
|
+
facts: reconciliation,
|
|
15650
|
+
protocol,
|
|
15651
|
+
sessions: sessionViews
|
|
15652
|
+
});
|
|
15653
|
+
}
|
|
15654
|
+
projects.sort((left, right) => left.root.localeCompare(right.root));
|
|
15655
|
+
return { projects };
|
|
15656
|
+
}
|
|
13788
15657
|
function close() {
|
|
13789
15658
|
closed = true;
|
|
13790
15659
|
for (const sessions of harnesses.values()) {
|
|
13791
15660
|
for (const session of sessions.values())
|
|
13792
15661
|
closeSession(session);
|
|
13793
15662
|
}
|
|
15663
|
+
for (const watcher of projectWatchers.values())
|
|
15664
|
+
watcher.close();
|
|
15665
|
+
projectWatchers.clear();
|
|
13794
15666
|
pendingNotifications.clear();
|
|
13795
15667
|
listeners.clear();
|
|
13796
15668
|
}
|
|
@@ -13801,7 +15673,7 @@ function createNarrativeService(options = {}) {
|
|
|
13801
15673
|
listeners.add(listener);
|
|
13802
15674
|
return () => listeners.delete(listener);
|
|
13803
15675
|
}
|
|
13804
|
-
return { handleEvent, getState, getOverview, seedFixture, subscribe, close };
|
|
15676
|
+
return { handleEvent, getState, getOverview, getProjects, seedFixture, subscribe, close };
|
|
13805
15677
|
}
|
|
13806
15678
|
function lastActivityOf(session) {
|
|
13807
15679
|
if (session.lastActivity !== void 0)
|
|
@@ -13853,6 +15725,18 @@ function pathBasename(path) {
|
|
|
13853
15725
|
const parts = path.split(/[\\/]/).filter((p) => p !== "");
|
|
13854
15726
|
return parts[parts.length - 1] ?? "";
|
|
13855
15727
|
}
|
|
15728
|
+
function extractRawToolObservations(harness, rawLine) {
|
|
15729
|
+
try {
|
|
15730
|
+
return extractToolObservations(harness, [JSON.parse(rawLine)]);
|
|
15731
|
+
} catch {
|
|
15732
|
+
return [];
|
|
15733
|
+
}
|
|
15734
|
+
}
|
|
15735
|
+
function resolveObservedPath(path, cwd) {
|
|
15736
|
+
if (isAbsolute4(path))
|
|
15737
|
+
return resolve9(path);
|
|
15738
|
+
return cwd === void 0 ? void 0 : resolve9(cwd, path);
|
|
15739
|
+
}
|
|
13856
15740
|
function agentIdOf(payload) {
|
|
13857
15741
|
const id = payload.agent_id ?? payload.agentId;
|
|
13858
15742
|
return typeof id === "string" && id !== "" ? id : void 0;
|
|
@@ -13905,13 +15789,13 @@ function feedbackView(harness, capability, state) {
|
|
|
13905
15789
|
}
|
|
13906
15790
|
function derive(harness, session, nowIso) {
|
|
13907
15791
|
const codexLines = sortedCodexByTimestamp(session.codexLines);
|
|
13908
|
-
const
|
|
13909
|
-
const timeline = deriveTodoTimeline(extractTodoSnapshots(
|
|
13910
|
-
const allSteps = extractSteps(
|
|
15792
|
+
const lines2 = harness === "codex" ? adaptCodexNarrativeLines(codexLines) : sortedByTimestamp(session.lines);
|
|
15793
|
+
const timeline = deriveTodoTimeline(extractTodoSnapshots(lines2));
|
|
15794
|
+
const allSteps = extractSteps(lines2);
|
|
13911
15795
|
const steps = allSteps.filter((s) => !TODO_TOOLS.has(s.toolName));
|
|
13912
|
-
const intervals = [...deriveSubagentIntervals(
|
|
15796
|
+
const intervals = [...deriveSubagentIntervals(lines2), ...session.hookIntervals];
|
|
13913
15797
|
const attributed = attributeSteps(steps, timeline, intervals);
|
|
13914
|
-
const results = collectResults(
|
|
15798
|
+
const results = collectResults(lines2);
|
|
13915
15799
|
const buckets = /* @__PURE__ */ new Map();
|
|
13916
15800
|
for (const { step, attribution } of attributed) {
|
|
13917
15801
|
const key = attribution.kind === "todo" ? attribution.todoId : null;
|
|
@@ -13989,9 +15873,9 @@ function derive(harness, session, nowIso) {
|
|
|
13989
15873
|
agents: [...agentsByTodo.get(entry.id) ?? []]
|
|
13990
15874
|
};
|
|
13991
15875
|
});
|
|
13992
|
-
const textTodos = todos.length === 0 ? deriveTextTodos(
|
|
15876
|
+
const textTodos = todos.length === 0 ? deriveTextTodos(lines2) : void 0;
|
|
13993
15877
|
const currentAction = deriveCurrentAction(steps, session.lastEvent, session.pendingHookTool);
|
|
13994
|
-
const lastPrompt = deriveLastPrompt(
|
|
15878
|
+
const lastPrompt = deriveLastPrompt(lines2, session.hookPrompt, session.hookPromptAt);
|
|
13995
15879
|
const workflowAction = currentAction === null ? void 0 : currentAction.kind === "tool" ? {
|
|
13996
15880
|
text: [currentAction.toolName, currentAction.detail].filter((part) => part !== "").join(": "),
|
|
13997
15881
|
source: "transcript / hook tool event",
|
|
@@ -13999,7 +15883,7 @@ function derive(harness, session, nowIso) {
|
|
|
13999
15883
|
} : { text: currentAction.name, source: "hook event", observedAt: currentAction.at };
|
|
14000
15884
|
const taskWorkflow = deriveTaskWorkflowView({
|
|
14001
15885
|
harness,
|
|
14002
|
-
lines,
|
|
15886
|
+
lines: lines2,
|
|
14003
15887
|
codexLines,
|
|
14004
15888
|
...lastPrompt === null ? {} : {
|
|
14005
15889
|
latestInstruction: {
|
|
@@ -14018,14 +15902,21 @@ function derive(harness, session, nowIso) {
|
|
|
14018
15902
|
harness,
|
|
14019
15903
|
sessionId: session.id,
|
|
14020
15904
|
transcriptPath: session.transcriptPath ?? null,
|
|
15905
|
+
...session.cwd === void 0 ? {} : { cwd: session.cwd },
|
|
15906
|
+
...session.cwd === void 0 ? {} : {
|
|
15907
|
+
project: {
|
|
15908
|
+
root: session.projectResolution?.root ?? resolve9(session.cwd),
|
|
15909
|
+
...session.planAttachment === void 0 ? {} : { attachedTo: session.planAttachment.attachedTo }
|
|
15910
|
+
}
|
|
15911
|
+
},
|
|
14021
15912
|
...session.mockNote !== void 0 ? { __mock_note: session.mockNote } : {},
|
|
14022
15913
|
todos,
|
|
14023
15914
|
...textTodos !== void 0 && textTodos.length > 0 ? { textTodos } : {},
|
|
14024
15915
|
traces,
|
|
14025
|
-
turnUsages: harness === "codex" ? deriveCodexTurnUsages(codexLines) : deriveTurnUsages(
|
|
15916
|
+
turnUsages: harness === "codex" ? deriveCodexTurnUsages(codexLines) : deriveTurnUsages(lines2),
|
|
14026
15917
|
currentAction,
|
|
14027
15918
|
waiting: session.waiting,
|
|
14028
|
-
agentQuote: deriveAgentQuote(
|
|
15919
|
+
agentQuote: deriveAgentQuote(lines2),
|
|
14029
15920
|
// transcript 是会话事实源;hook prompt 只在 tail 尚未读到/格式暂不可读时兜底。
|
|
14030
15921
|
lastPrompt,
|
|
14031
15922
|
taskWorkflow,
|
|
@@ -14050,9 +15941,9 @@ function insertByStartTs(nodes, node) {
|
|
|
14050
15941
|
}
|
|
14051
15942
|
nodes.push(node);
|
|
14052
15943
|
}
|
|
14053
|
-
function deriveTextTodos(
|
|
14054
|
-
for (let i =
|
|
14055
|
-
const line =
|
|
15944
|
+
function deriveTextTodos(lines2) {
|
|
15945
|
+
for (let i = lines2.length - 1; i >= 0; i--) {
|
|
15946
|
+
const line = lines2[i];
|
|
14056
15947
|
if (line === void 0 || line.kind !== "assistant" || line.agentId !== void 0)
|
|
14057
15948
|
continue;
|
|
14058
15949
|
for (let j = line.blocks.length - 1; j >= 0; j--) {
|
|
@@ -14066,15 +15957,15 @@ function deriveTextTodos(lines) {
|
|
|
14066
15957
|
}
|
|
14067
15958
|
return void 0;
|
|
14068
15959
|
}
|
|
14069
|
-
function sortedByTimestamp(
|
|
14070
|
-
return sortByInheritedTimestamp(
|
|
15960
|
+
function sortedByTimestamp(lines2) {
|
|
15961
|
+
return sortByInheritedTimestamp(lines2, (line) => line.kind === "unknown" ? void 0 : line.timestamp, (line) => line.kind === "unknown" ? "" : line.agentId ?? "");
|
|
14071
15962
|
}
|
|
14072
|
-
function sortedCodexByTimestamp(
|
|
14073
|
-
return sortByInheritedTimestamp(
|
|
15963
|
+
function sortedCodexByTimestamp(lines2) {
|
|
15964
|
+
return sortByInheritedTimestamp(lines2, (line) => line.kind === "unknown" ? void 0 : line.timestamp);
|
|
14074
15965
|
}
|
|
14075
|
-
function sortByInheritedTimestamp(
|
|
15966
|
+
function sortByInheritedTimestamp(lines2, timestampOf, sourceOf = () => "") {
|
|
14076
15967
|
const previousBySource = /* @__PURE__ */ new Map();
|
|
14077
|
-
return
|
|
15968
|
+
return lines2.map((line, index) => {
|
|
14078
15969
|
const source = sourceOf(line);
|
|
14079
15970
|
const timestamp = timestampOf(line) ?? previousBySource.get(source);
|
|
14080
15971
|
if (timestamp !== void 0)
|
|
@@ -14088,17 +15979,17 @@ function sortByInheritedTimestamp(lines, timestampOf, sourceOf = () => "") {
|
|
|
14088
15979
|
return a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : a.index - b.index;
|
|
14089
15980
|
}).map(({ line }) => line);
|
|
14090
15981
|
}
|
|
14091
|
-
function adaptCodexNarrativeLines(
|
|
14092
|
-
const responseTexts = new Set(
|
|
15982
|
+
function adaptCodexNarrativeLines(lines2) {
|
|
15983
|
+
const responseTexts = new Set(lines2.flatMap((line) => line.kind === "assistant" && line.source === "response_item" && line.text !== void 0 ? [line.text] : []));
|
|
14093
15984
|
const modelByTurn = /* @__PURE__ */ new Map();
|
|
14094
|
-
for (const line of
|
|
15985
|
+
for (const line of lines2) {
|
|
14095
15986
|
if (line.kind === "turn_context" && line.turnId !== void 0 && line.model !== void 0) {
|
|
14096
15987
|
modelByTurn.set(line.turnId, line.model);
|
|
14097
15988
|
}
|
|
14098
15989
|
}
|
|
14099
15990
|
const adapted = [];
|
|
14100
15991
|
let activeTurnId;
|
|
14101
|
-
for (const line of
|
|
15992
|
+
for (const line of lines2) {
|
|
14102
15993
|
if (line.kind === "task_started") {
|
|
14103
15994
|
activeTurnId = line.turnId;
|
|
14104
15995
|
continue;
|
|
@@ -14182,23 +16073,23 @@ function codexToolInput(value) {
|
|
|
14182
16073
|
}
|
|
14183
16074
|
return value === void 0 ? {} : { input: value };
|
|
14184
16075
|
}
|
|
14185
|
-
function collectResults(
|
|
16076
|
+
function collectResults(lines2) {
|
|
14186
16077
|
const results = /* @__PURE__ */ new Map();
|
|
14187
|
-
for (const line of
|
|
16078
|
+
for (const line of lines2) {
|
|
14188
16079
|
if (line.kind !== "user")
|
|
14189
16080
|
continue;
|
|
14190
|
-
for (const
|
|
14191
|
-
if (
|
|
16081
|
+
for (const result4 of line.toolResults) {
|
|
16082
|
+
if (result4.content === void 0)
|
|
14192
16083
|
continue;
|
|
14193
16084
|
const info = {};
|
|
14194
16085
|
try {
|
|
14195
|
-
info.bytes = Buffer.byteLength(JSON.stringify(
|
|
16086
|
+
info.bytes = Buffer.byteLength(JSON.stringify(result4.content), "utf8");
|
|
14196
16087
|
} catch {
|
|
14197
16088
|
}
|
|
14198
|
-
const brief = errorBrief(
|
|
16089
|
+
const brief = errorBrief(result4.content);
|
|
14199
16090
|
if (brief !== void 0)
|
|
14200
16091
|
info.brief = brief;
|
|
14201
|
-
results.set(
|
|
16092
|
+
results.set(result4.toolUseId, info);
|
|
14202
16093
|
}
|
|
14203
16094
|
}
|
|
14204
16095
|
return results;
|
|
@@ -14273,10 +16164,10 @@ function hookToolDetail(input) {
|
|
|
14273
16164
|
return "";
|
|
14274
16165
|
return inputDetail(input);
|
|
14275
16166
|
}
|
|
14276
|
-
function deriveTurnUsages(
|
|
16167
|
+
function deriveTurnUsages(lines2) {
|
|
14277
16168
|
const byMessage = /* @__PURE__ */ new Map();
|
|
14278
16169
|
let anonymous = 0;
|
|
14279
|
-
for (const line of
|
|
16170
|
+
for (const line of lines2) {
|
|
14280
16171
|
if (line.kind !== "assistant")
|
|
14281
16172
|
continue;
|
|
14282
16173
|
const usage = turnUsage(line);
|
|
@@ -14313,9 +16204,9 @@ var ZERO_CODEX_USAGE = {
|
|
|
14313
16204
|
reasoningOutputTokens: 0,
|
|
14314
16205
|
totalTokens: 0
|
|
14315
16206
|
};
|
|
14316
|
-
function deriveCodexTurnUsages(
|
|
16207
|
+
function deriveCodexTurnUsages(lines2) {
|
|
14317
16208
|
const modelByTurn = /* @__PURE__ */ new Map();
|
|
14318
|
-
for (const line of
|
|
16209
|
+
for (const line of lines2) {
|
|
14319
16210
|
if (line.kind === "turn_context" && line.turnId !== void 0 && line.model !== void 0) {
|
|
14320
16211
|
modelByTurn.set(line.turnId, line.model);
|
|
14321
16212
|
}
|
|
@@ -14356,7 +16247,7 @@ function deriveCodexTurnUsages(lines) {
|
|
|
14356
16247
|
view.totalTokens = delta.totalTokens;
|
|
14357
16248
|
turns.push(view);
|
|
14358
16249
|
}
|
|
14359
|
-
for (const line of
|
|
16250
|
+
for (const line of lines2) {
|
|
14360
16251
|
if (line.kind === "task_started") {
|
|
14361
16252
|
if (active !== void 0 && active.terminal === void 0)
|
|
14362
16253
|
active.terminal = "incomplete";
|
|
@@ -14450,9 +16341,9 @@ function deriveCurrentAction(steps, lastEvent, pendingHookTool) {
|
|
|
14450
16341
|
return { kind: "event", name: lastEvent.name, at: lastEvent.at };
|
|
14451
16342
|
return null;
|
|
14452
16343
|
}
|
|
14453
|
-
function deriveAgentQuote(
|
|
14454
|
-
for (let i =
|
|
14455
|
-
const line =
|
|
16344
|
+
function deriveAgentQuote(lines2) {
|
|
16345
|
+
for (let i = lines2.length - 1; i >= 0; i--) {
|
|
16346
|
+
const line = lines2[i];
|
|
14456
16347
|
if (line === void 0 || line.kind !== "assistant" || line.agentId !== void 0)
|
|
14457
16348
|
continue;
|
|
14458
16349
|
for (let j = line.blocks.length - 1; j >= 0; j--) {
|
|
@@ -14464,9 +16355,9 @@ function deriveAgentQuote(lines) {
|
|
|
14464
16355
|
}
|
|
14465
16356
|
return null;
|
|
14466
16357
|
}
|
|
14467
|
-
function deriveLastPrompt(
|
|
14468
|
-
for (let i =
|
|
14469
|
-
const line =
|
|
16358
|
+
function deriveLastPrompt(lines2, hookPrompt, hookPromptAt) {
|
|
16359
|
+
for (let i = lines2.length - 1; i >= 0; i--) {
|
|
16360
|
+
const line = lines2[i];
|
|
14470
16361
|
if (line === void 0 || line.kind !== "user" || line.agentId !== void 0)
|
|
14471
16362
|
continue;
|
|
14472
16363
|
if (line.toolResults.length > 0)
|
|
@@ -14498,9 +16389,146 @@ function errorBrief(content) {
|
|
|
14498
16389
|
return void 0;
|
|
14499
16390
|
}
|
|
14500
16391
|
|
|
16392
|
+
// apps/cli/dist/protocol-cli.js
|
|
16393
|
+
init_dist2();
|
|
16394
|
+
init_dist4();
|
|
16395
|
+
init_dist5();
|
|
16396
|
+
import { createInterface } from "node:readline/promises";
|
|
16397
|
+
var ProtocolUsageError = class extends Error {
|
|
16398
|
+
};
|
|
16399
|
+
async function runProtocolCli(args) {
|
|
16400
|
+
const parsed = parseProtocolArgs(args);
|
|
16401
|
+
if (parsed.action === "status") {
|
|
16402
|
+
printStatus(getStatus(parsed.harness, parsed.projectDir));
|
|
16403
|
+
return 0;
|
|
16404
|
+
}
|
|
16405
|
+
const preview = mutate(parsed.action, parsed.harness, parsed.projectDir, true);
|
|
16406
|
+
printPreview(preview);
|
|
16407
|
+
if (!preview.changed)
|
|
16408
|
+
return 0;
|
|
16409
|
+
if (parsed.dryRun)
|
|
16410
|
+
return 0;
|
|
16411
|
+
if (!parsed.yes && !await confirmWrite()) {
|
|
16412
|
+
process.stdout.write("wingman:\u5DF2\u53D6\u6D88\uFF0C\u672A\u5199\u5165\u4EFB\u4F55\u6587\u4EF6\u3002\n");
|
|
16413
|
+
return 0;
|
|
16414
|
+
}
|
|
16415
|
+
const applied = mutate(parsed.action, parsed.harness, parsed.projectDir, false);
|
|
16416
|
+
process.stdout.write(`wingman:${parsed.harness} protocol ${parsed.action} \u5B8C\u6210
|
|
16417
|
+
state: ${applied.state}
|
|
16418
|
+
backupDir: ${applied.backupDir ?? "(no backup)"}
|
|
16419
|
+
effectiveAt: ${applied.effectiveAt}
|
|
16420
|
+
totalBytes: ${applied.totalBytes}
|
|
16421
|
+
${applied.message ?? ""}${applied.message === void 0 ? "" : "\n"}`);
|
|
16422
|
+
return 0;
|
|
16423
|
+
}
|
|
16424
|
+
function parseProtocolArgs(args) {
|
|
16425
|
+
const [actionText, harnessText, ...tail] = args;
|
|
16426
|
+
if (actionText !== "install" && actionText !== "uninstall" && actionText !== "status") {
|
|
16427
|
+
throw new ProtocolUsageError("protocol \u7528\u6CD5:wingman protocol install|uninstall|status <cc|codex|dsh> [--project <dir>] [--dry-run] [--yes]");
|
|
16428
|
+
}
|
|
16429
|
+
if (harnessText !== "cc" && harnessText !== "codex" && harnessText !== "dsh") {
|
|
16430
|
+
throw new ProtocolUsageError(`protocol \u9700\u8981 harness \u2208 cc|codex|dsh,\u5F97\u5230 ${harnessText ?? "(\u7F3A\u53C2)"}`);
|
|
16431
|
+
}
|
|
16432
|
+
let projectDir = process.cwd();
|
|
16433
|
+
let dryRun = false;
|
|
16434
|
+
let yes = false;
|
|
16435
|
+
const rest = [...tail];
|
|
16436
|
+
while (rest.length > 0) {
|
|
16437
|
+
const arg = rest.shift();
|
|
16438
|
+
if (arg === "--project") {
|
|
16439
|
+
const value = rest.shift();
|
|
16440
|
+
if (value === void 0)
|
|
16441
|
+
throw new ProtocolUsageError("--project \u9700\u8981\u76EE\u5F55\u8DEF\u5F84,\u5F97\u5230(\u7F3A\u53C2)");
|
|
16442
|
+
projectDir = value;
|
|
16443
|
+
} else if (arg === "--dry-run") {
|
|
16444
|
+
dryRun = true;
|
|
16445
|
+
} else if (arg === "--yes") {
|
|
16446
|
+
yes = true;
|
|
16447
|
+
} else {
|
|
16448
|
+
throw new ProtocolUsageError(`protocol \u4E0D\u8BA4\u8BC6\u53C2\u6570:${arg}`);
|
|
16449
|
+
}
|
|
16450
|
+
}
|
|
16451
|
+
return { action: actionText, harness: harnessText, projectDir, dryRun, yes };
|
|
16452
|
+
}
|
|
16453
|
+
function getStatus(harness, projectDir) {
|
|
16454
|
+
switch (harness) {
|
|
16455
|
+
case "cc":
|
|
16456
|
+
return protocolStatusCc({ projectDir });
|
|
16457
|
+
case "codex":
|
|
16458
|
+
return protocolStatusCodex({ projectDir });
|
|
16459
|
+
case "dsh":
|
|
16460
|
+
return protocolStatusDsh({ projectDir });
|
|
16461
|
+
}
|
|
16462
|
+
}
|
|
16463
|
+
function mutate(action, harness, projectDir, dryRun) {
|
|
16464
|
+
switch (harness) {
|
|
16465
|
+
case "cc":
|
|
16466
|
+
return action === "install" ? installProtocolCc({ projectDir, dryRun }) : uninstallProtocolCc({ projectDir, dryRun });
|
|
16467
|
+
case "codex":
|
|
16468
|
+
return action === "install" ? installProtocolCodex({ projectDir, dryRun }) : uninstallProtocolCodex({ projectDir, dryRun });
|
|
16469
|
+
case "dsh":
|
|
16470
|
+
return action === "install" ? installProtocolDsh({ projectDir, dryRun }) : uninstallProtocolDsh({ projectDir, dryRun });
|
|
16471
|
+
}
|
|
16472
|
+
}
|
|
16473
|
+
function printStatus(value) {
|
|
16474
|
+
process.stdout.write(`harness: ${value.harness}
|
|
16475
|
+
target: ${value.targetPath}
|
|
16476
|
+
state: ${value.state}
|
|
16477
|
+
${value.version === void 0 ? "" : `version: ${value.version}
|
|
16478
|
+
`}${value.message === void 0 ? "" : `${value.message}
|
|
16479
|
+
`}${value.reason === void 0 ? "" : `reason: ${value.reason}
|
|
16480
|
+
`}`);
|
|
16481
|
+
}
|
|
16482
|
+
function printPreview(value) {
|
|
16483
|
+
process.stdout.write(`${unifiedDiff(value.targetPath, value.before, value.after)}
|
|
16484
|
+
target: ${value.targetPath}
|
|
16485
|
+
state: ${value.state}
|
|
16486
|
+
backupDir: ${value.backupDir ?? "(no backup)"}
|
|
16487
|
+
effectiveAt: ${value.effectiveAt}
|
|
16488
|
+
totalBytes: ${value.totalBytes}
|
|
16489
|
+
${value.message === void 0 ? "" : `${value.message}
|
|
16490
|
+
`}`);
|
|
16491
|
+
if (value.targetPath.endsWith("AGENTS.md") && value.afterExists && value.totalBytes >= 31 * 1024) {
|
|
16492
|
+
process.stdout.write("\u8B66\u544A:Codex \u53EF\u80FD\u622A\u65AD\u672C\u5757\uFF1BAGENTS.md \u88C5\u540E\u5DF2\u63A5\u8FD1\u6216\u8D85\u8FC7\u9ED8\u8BA4 32 KiB project_doc_max_bytes\u3002\n");
|
|
16493
|
+
}
|
|
16494
|
+
}
|
|
16495
|
+
function unifiedDiff(path, before, after) {
|
|
16496
|
+
if (before === after)
|
|
16497
|
+
return `--- ${path}
|
|
16498
|
+
+++ ${path}
|
|
16499
|
+
(no changes)`;
|
|
16500
|
+
const beforeLines = lines(before);
|
|
16501
|
+
const afterLines = lines(after);
|
|
16502
|
+
return [
|
|
16503
|
+
`--- ${path}`,
|
|
16504
|
+
`+++ ${path}`,
|
|
16505
|
+
`@@ -1,${beforeLines.length} +1,${afterLines.length} @@`,
|
|
16506
|
+
...beforeLines.map((line) => `-${line}`),
|
|
16507
|
+
...afterLines.map((line) => `+${line}`)
|
|
16508
|
+
].join("\n");
|
|
16509
|
+
}
|
|
16510
|
+
function lines(text) {
|
|
16511
|
+
if (text === "")
|
|
16512
|
+
return [];
|
|
16513
|
+
const normalized = text.replaceAll("\r\n", "\n");
|
|
16514
|
+
const values = normalized.split("\n");
|
|
16515
|
+
if (values.at(-1) === "")
|
|
16516
|
+
values.pop();
|
|
16517
|
+
return values;
|
|
16518
|
+
}
|
|
16519
|
+
async function confirmWrite() {
|
|
16520
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
16521
|
+
try {
|
|
16522
|
+
const answer = await prompt.question("\u786E\u8BA4\u5199\u5165? [y/N] ");
|
|
16523
|
+
return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
|
|
16524
|
+
} finally {
|
|
16525
|
+
prompt.close();
|
|
16526
|
+
}
|
|
16527
|
+
}
|
|
16528
|
+
|
|
14501
16529
|
// apps/cli/dist/registry.js
|
|
14502
|
-
import { existsSync as
|
|
14503
|
-
import { homedir as
|
|
16530
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
16531
|
+
import { homedir as homedir11 } from "node:os";
|
|
14504
16532
|
async function importHarnessModule(harness) {
|
|
14505
16533
|
const mod = await importAdapterModule(harness);
|
|
14506
16534
|
if (typeof mod.createAdapter !== "function") {
|
|
@@ -14522,7 +16550,7 @@ function importAdapterModule(harness) {
|
|
|
14522
16550
|
}
|
|
14523
16551
|
}
|
|
14524
16552
|
function fixtureScanOptions(harness) {
|
|
14525
|
-
if (!
|
|
16553
|
+
if (!existsSync13(fixturePath())) {
|
|
14526
16554
|
throw new Error("--fixtures \u9700\u8981\u4ED3\u5E93\u5185\u7684 packages/fixtures \u6570\u636E;\u53D1\u5E03\u5B89\u88C5\u5305/npx \u4EA7\u7269\u4E0D\u5305\u542B\u6837\u4F8B");
|
|
14527
16555
|
}
|
|
14528
16556
|
switch (harness) {
|
|
@@ -14540,16 +16568,16 @@ async function loadHarness(harness, mode) {
|
|
|
14540
16568
|
return {
|
|
14541
16569
|
harness,
|
|
14542
16570
|
adapter,
|
|
14543
|
-
scanOptions: mode.fixtures ? fixtureScanOptions(harness) : { homeDir:
|
|
16571
|
+
scanOptions: mode.fixtures ? fixtureScanOptions(harness) : { homeDir: homedir11() },
|
|
14544
16572
|
feedbackSignals
|
|
14545
16573
|
};
|
|
14546
16574
|
}
|
|
14547
16575
|
|
|
14548
16576
|
// apps/cli/dist/runtime-state.js
|
|
14549
16577
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
14550
|
-
import { closeSync, mkdirSync as
|
|
14551
|
-
import { homedir as
|
|
14552
|
-
import { dirname as
|
|
16578
|
+
import { closeSync, mkdirSync as mkdirSync9, openSync, readFileSync as readFileSync14, renameSync as renameSync3, rmSync as rmSync7, writeFileSync as writeFileSync10 } from "node:fs";
|
|
16579
|
+
import { homedir as homedir12 } from "node:os";
|
|
16580
|
+
import { dirname as dirname15, join as join21 } from "node:path";
|
|
14553
16581
|
function createHookToken() {
|
|
14554
16582
|
return randomBytes2(32).toString("base64url");
|
|
14555
16583
|
}
|
|
@@ -14560,15 +16588,15 @@ function writeServeRuntimeState(port, token, options = {}) {
|
|
|
14560
16588
|
const pid = options.pid ?? process.pid;
|
|
14561
16589
|
const suffix = randomBytes2(8).toString("base64url");
|
|
14562
16590
|
const owner = `serve-${startedAtMs}-${pid}-${suffix}`;
|
|
14563
|
-
const path =
|
|
14564
|
-
const tempPath =
|
|
16591
|
+
const path = join21(options.homeDir ?? homedir12(), ".wingman", "state", "serve", `${owner}.json`);
|
|
16592
|
+
const tempPath = join21(dirname15(path), `.${owner}.tmp`);
|
|
14565
16593
|
const contents = Buffer.from(`${JSON.stringify({ version: 2, owner, pid, port, startedAtMs, token })}
|
|
14566
16594
|
`, "utf8");
|
|
14567
16595
|
let fd;
|
|
14568
16596
|
try {
|
|
14569
|
-
|
|
16597
|
+
mkdirSync9(dirname15(path), { recursive: true, mode: 448 });
|
|
14570
16598
|
fd = openSync(tempPath, "wx", 384);
|
|
14571
|
-
|
|
16599
|
+
writeFileSync10(fd, contents);
|
|
14572
16600
|
closeSync(fd);
|
|
14573
16601
|
fd = void 0;
|
|
14574
16602
|
renameSync3(tempPath, path);
|
|
@@ -14581,7 +16609,7 @@ function writeServeRuntimeState(port, token, options = {}) {
|
|
|
14581
16609
|
}
|
|
14582
16610
|
}
|
|
14583
16611
|
try {
|
|
14584
|
-
|
|
16612
|
+
rmSync7(tempPath, { force: true });
|
|
14585
16613
|
} catch {
|
|
14586
16614
|
}
|
|
14587
16615
|
warn(`Codex hook \u8FD0\u884C\u6001\u5199\u5165\u5931\u8D25,\u9762\u677F\u7EE7\u7EED\u8FD0\u884C:${errorMessage(error)}`);
|
|
@@ -14592,9 +16620,9 @@ function removeServeRuntimeState(registration) {
|
|
|
14592
16620
|
if (registration === void 0)
|
|
14593
16621
|
return;
|
|
14594
16622
|
try {
|
|
14595
|
-
const current =
|
|
16623
|
+
const current = readFileSync14(registration.path);
|
|
14596
16624
|
if (current.equals(registration.contents))
|
|
14597
|
-
|
|
16625
|
+
rmSync7(registration.path, { force: true });
|
|
14598
16626
|
} catch {
|
|
14599
16627
|
}
|
|
14600
16628
|
}
|
|
@@ -14617,9 +16645,9 @@ async function scanValidated(adapter, options) {
|
|
|
14617
16645
|
// apps/cli/dist/server.js
|
|
14618
16646
|
init_dist();
|
|
14619
16647
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
14620
|
-
import { readFile as
|
|
16648
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
14621
16649
|
import { createServer } from "node:http";
|
|
14622
|
-
import { extname, resolve as
|
|
16650
|
+
import { extname, resolve as resolve10, sep as sep2 } from "node:path";
|
|
14623
16651
|
|
|
14624
16652
|
// apps/cli/dist/suggest.js
|
|
14625
16653
|
init_dist();
|
|
@@ -14887,6 +16915,14 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
14887
16915
|
sendJson2(res, 200, publicLicenseStatus(licenseStatus(options.licenseOptions)));
|
|
14888
16916
|
return;
|
|
14889
16917
|
}
|
|
16918
|
+
if (url.pathname === "/api/projects") {
|
|
16919
|
+
if (!options.narrative) {
|
|
16920
|
+
sendText2(res, 404, "not found");
|
|
16921
|
+
return;
|
|
16922
|
+
}
|
|
16923
|
+
sendJson2(res, 200, options.narrative.getProjects());
|
|
16924
|
+
return;
|
|
16925
|
+
}
|
|
14890
16926
|
if (url.pathname === "/api/narrative") {
|
|
14891
16927
|
if (!options.narrative) {
|
|
14892
16928
|
sendText2(res, 404, "not found");
|
|
@@ -15073,11 +17109,11 @@ async function handleActionApply(req, res, options, runtime) {
|
|
|
15073
17109
|
return;
|
|
15074
17110
|
}
|
|
15075
17111
|
try {
|
|
15076
|
-
const
|
|
15077
|
-
if (
|
|
17112
|
+
const result4 = await loaded.adapter.applyAction(plan, loaded.scanOptions);
|
|
17113
|
+
if (result4.ok) {
|
|
15078
17114
|
runtime.anatomy = null;
|
|
15079
17115
|
}
|
|
15080
|
-
sendJson2(res, 200,
|
|
17116
|
+
sendJson2(res, 200, result4);
|
|
15081
17117
|
} catch (error) {
|
|
15082
17118
|
sendJson2(res, 400, { error: errorMessage2(error) });
|
|
15083
17119
|
}
|
|
@@ -15089,12 +17125,12 @@ async function handleLicenseInstall(req, res, options) {
|
|
|
15089
17125
|
sendJson2(res, 400, { error: "license \u6587\u4EF6\u8D85\u8FC7 64KB \u4E0A\u9650\u6216\u8BFB\u53D6\u5931\u8D25" });
|
|
15090
17126
|
return;
|
|
15091
17127
|
}
|
|
15092
|
-
const
|
|
15093
|
-
if (!
|
|
15094
|
-
sendJson2(res, 400, { error:
|
|
17128
|
+
const result4 = installLicense(raw, options.licenseOptions);
|
|
17129
|
+
if (!result4.ok) {
|
|
17130
|
+
sendJson2(res, 400, { error: result4.error });
|
|
15095
17131
|
return;
|
|
15096
17132
|
}
|
|
15097
|
-
sendJson2(res, 200, publicLicenseStatus(
|
|
17133
|
+
sendJson2(res, 200, publicLicenseStatus(result4.status));
|
|
15098
17134
|
}
|
|
15099
17135
|
async function handleLicenseRefresh(res, options) {
|
|
15100
17136
|
const homeDir = options.licenseRefreshOptions?.homeDir ?? options.licenseOptions?.homeDir;
|
|
@@ -15106,11 +17142,11 @@ async function handleLicenseRefresh(res, options) {
|
|
|
15106
17142
|
...publicKeyPem === void 0 ? {} : { publicKeyPem },
|
|
15107
17143
|
...now === void 0 ? {} : { now }
|
|
15108
17144
|
};
|
|
15109
|
-
const
|
|
15110
|
-
if (!
|
|
17145
|
+
const result4 = await refreshLicense(refreshOptions);
|
|
17146
|
+
if (!result4.ok) {
|
|
15111
17147
|
sendJson2(res, 400, {
|
|
15112
|
-
error:
|
|
15113
|
-
canSubscribe:
|
|
17148
|
+
error: result4.message,
|
|
17149
|
+
canSubscribe: result4.reason === "inactive"
|
|
15114
17150
|
});
|
|
15115
17151
|
return;
|
|
15116
17152
|
}
|
|
@@ -15122,14 +17158,14 @@ async function handleLicenseRefresh(res, options) {
|
|
|
15122
17158
|
};
|
|
15123
17159
|
sendJson2(res, 200, {
|
|
15124
17160
|
...publicLicenseStatus(licenseStatus(statusOptions)),
|
|
15125
|
-
message:
|
|
17161
|
+
message: result4.message
|
|
15126
17162
|
});
|
|
15127
17163
|
}
|
|
15128
|
-
function publicLicenseStatus(
|
|
15129
|
-
const body = { state:
|
|
15130
|
-
if (
|
|
15131
|
-
body.plan =
|
|
15132
|
-
body.exp =
|
|
17164
|
+
function publicLicenseStatus(status3) {
|
|
17165
|
+
const body = { state: status3.state, detail: status3.detail };
|
|
17166
|
+
if (status3.claims) {
|
|
17167
|
+
body.plan = status3.claims.plan;
|
|
17168
|
+
body.exp = status3.claims.exp;
|
|
15133
17169
|
}
|
|
15134
17170
|
return body;
|
|
15135
17171
|
}
|
|
@@ -15185,8 +17221,8 @@ function readJsonBody(req) {
|
|
|
15185
17221
|
req.on("error", () => resolveBody(void 0));
|
|
15186
17222
|
});
|
|
15187
17223
|
}
|
|
15188
|
-
function sendJson2(res,
|
|
15189
|
-
res.writeHead(
|
|
17224
|
+
function sendJson2(res, status3, body) {
|
|
17225
|
+
res.writeHead(status3, { "content-type": "application/json; charset=utf-8" });
|
|
15190
17226
|
res.end(JSON.stringify(body));
|
|
15191
17227
|
}
|
|
15192
17228
|
async function serveStatic(pathname, res, panelDistDir) {
|
|
@@ -15198,14 +17234,14 @@ async function serveStatic(pathname, res, panelDistDir) {
|
|
|
15198
17234
|
return;
|
|
15199
17235
|
}
|
|
15200
17236
|
const rel = decoded === "/" ? "/index.html" : decoded;
|
|
15201
|
-
const distDir =
|
|
15202
|
-
const filePath =
|
|
17237
|
+
const distDir = resolve10(panelDistDir);
|
|
17238
|
+
const filePath = resolve10(distDir, `.${rel}`);
|
|
15203
17239
|
if (filePath !== distDir && !filePath.startsWith(distDir + sep2)) {
|
|
15204
17240
|
sendText2(res, 403, "forbidden");
|
|
15205
17241
|
return;
|
|
15206
17242
|
}
|
|
15207
17243
|
try {
|
|
15208
|
-
const content = await
|
|
17244
|
+
const content = await readFile4(filePath);
|
|
15209
17245
|
res.writeHead(200, {
|
|
15210
17246
|
"content-type": CONTENT_TYPES[extname(filePath)] ?? "application/octet-stream"
|
|
15211
17247
|
});
|
|
@@ -15218,8 +17254,8 @@ async function serveStatic(pathname, res, panelDistDir) {
|
|
|
15218
17254
|
}
|
|
15219
17255
|
}
|
|
15220
17256
|
}
|
|
15221
|
-
function sendText2(res,
|
|
15222
|
-
res.writeHead(
|
|
17257
|
+
function sendText2(res, status3, body) {
|
|
17258
|
+
res.writeHead(status3, { "content-type": "text/plain; charset=utf-8" });
|
|
15223
17259
|
res.end(body);
|
|
15224
17260
|
}
|
|
15225
17261
|
function errorMessage2(error) {
|
|
@@ -15235,6 +17271,8 @@ var HELP = `wingman \u2014 Not your copilot. Your wingman.
|
|
|
15235
17271
|
[--panel-dist <dir>] \u9762\u677F\u9759\u6001\u4EA7\u7269\u76EE\u5F55(\u9ED8\u8BA4\u4ED3\u5E93\u5E03\u5C40 apps/panel/dist)
|
|
15236
17272
|
wingman hook install <harness> [--port 7333] \u5B9E\u51B5\u63A5\u7EBF(\u5199\u524D\u5907\u4EFD,\u5E42\u7B49)
|
|
15237
17273
|
wingman hook uninstall <harness> \u89E3\u7EBF(\u914D\u7F6E\u6062\u590D,\u5E42\u7B49)
|
|
17274
|
+
wingman protocol install|uninstall|status <harness> \u6309\u9879\u76EE\u7BA1\u7406 Plan Protocol
|
|
17275
|
+
[--project <dir>] [--dry-run] [--yes]
|
|
15238
17276
|
wingman license status \u67E5\u770B\u8BA2\u9605\u72B6\u6001(\u5199\u52A8\u4F5C\u7684\u95F8)
|
|
15239
17277
|
wingman license refresh \u8054\u7F51\u5411\u7B7E\u53D1\u7AEF\u6362\u65B0 license(\u7EED\u671F)
|
|
15240
17278
|
wingman help \u663E\u793A\u672C\u5E2E\u52A9
|
|
@@ -15256,21 +17294,23 @@ async function main(argv) {
|
|
|
15256
17294
|
return runServe(rest);
|
|
15257
17295
|
case "hook":
|
|
15258
17296
|
return runHook(rest);
|
|
17297
|
+
case "protocol":
|
|
17298
|
+
return runProtocolCli(rest);
|
|
15259
17299
|
case "license": {
|
|
15260
17300
|
if (rest[0] === "status") {
|
|
15261
|
-
const
|
|
15262
|
-
process.stdout.write(`${
|
|
17301
|
+
const status3 = licenseStatus();
|
|
17302
|
+
process.stdout.write(`${status3.state}:${status3.detail}
|
|
15263
17303
|
`);
|
|
15264
17304
|
return 0;
|
|
15265
17305
|
}
|
|
15266
17306
|
if (rest[0] === "refresh") {
|
|
15267
|
-
const
|
|
15268
|
-
process.stdout.write(`${
|
|
17307
|
+
const result4 = await refreshLicense();
|
|
17308
|
+
process.stdout.write(`${result4.message}
|
|
15269
17309
|
`);
|
|
15270
|
-
if (!
|
|
17310
|
+
if (!result4.ok)
|
|
15271
17311
|
return 1;
|
|
15272
|
-
const
|
|
15273
|
-
process.stdout.write(`${
|
|
17312
|
+
const status3 = licenseStatus();
|
|
17313
|
+
process.stdout.write(`${status3.state}:${status3.detail}
|
|
15274
17314
|
`);
|
|
15275
17315
|
return 0;
|
|
15276
17316
|
}
|
|
@@ -15317,9 +17357,11 @@ async function runServe(args) {
|
|
|
15317
17357
|
`);
|
|
15318
17358
|
}
|
|
15319
17359
|
}
|
|
15320
|
-
const panelDistDir = panelDist === void 0 ?
|
|
17360
|
+
const panelDistDir = panelDist === void 0 ? resolve11(dirname16(fileURLToPath2(import.meta.url)), "../../panel/dist") : resolve11(panelDist);
|
|
15321
17361
|
const narrative = createNarrativeService({
|
|
15322
17362
|
followTranscripts: !fixtures,
|
|
17363
|
+
homeDir: fixtures ? fixturesRoot : homedir13(),
|
|
17364
|
+
...fixtures ? { projectBoundaryDir: fixturesRoot } : {},
|
|
15323
17365
|
feedbackSignals: (harness) => {
|
|
15324
17366
|
const loaded = harnesses.find((candidate) => candidate.harness === harness);
|
|
15325
17367
|
return loaded?.feedbackSignals(loaded.scanOptions).feedback;
|
|
@@ -15327,7 +17369,7 @@ async function runServe(args) {
|
|
|
15327
17369
|
});
|
|
15328
17370
|
if (fixtures) {
|
|
15329
17371
|
try {
|
|
15330
|
-
const raw =
|
|
17372
|
+
const raw = readFileSync15(fixturePath("cc", "sessions", "session-todos.jsonl"), "utf8");
|
|
15331
17373
|
narrative.seedFixture("cc", raw.split("\n"), "MOCK \u2014 fixtures \u6F14\u793A:\u53D9\u4E8B\u6570\u636E\u6765\u81EA packages/fixtures \u7684 session-todos.jsonl \u6837\u4F8B\u4F1A\u8BDD,\u975E\u672C\u673A\u771F\u5B9E transcript\u3002");
|
|
15332
17374
|
} catch (error) {
|
|
15333
17375
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -15460,9 +17502,10 @@ main(process.argv.slice(2)).then((code) => {
|
|
|
15460
17502
|
const message = error instanceof Error ? error.message : String(error);
|
|
15461
17503
|
process.stderr.write(`wingman:${message}
|
|
15462
17504
|
`);
|
|
15463
|
-
if (error instanceof UsageError)
|
|
17505
|
+
if (error instanceof UsageError || error instanceof ProtocolUsageError) {
|
|
15464
17506
|
process.stderr.write(`
|
|
15465
17507
|
${HELP}`);
|
|
17508
|
+
}
|
|
15466
17509
|
process.exitCode = 1;
|
|
15467
17510
|
});
|
|
15468
17511
|
/*! Bundled license information:
|