@astrosheep/keiyaku 0.1.76 → 0.1.78
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/README.md +88 -96
- package/build/.tsbuildinfo +1 -1
- package/build/config/apply-argument-descriptions.js +1 -1
- package/build/config/base-rules.js +14 -7
- package/build/config/dotenv.js +17 -11
- package/build/config/keiyaku-home.js +9 -0
- package/build/config/settings.js +41 -24
- package/build/config/term-presets/resolver.js +0 -3
- package/build/errno.js +3 -0
- package/build/flow-error.js +2 -0
- package/build/generated/version.js +1 -1
- package/build/git/diff/constants.js +1 -0
- package/build/git/diff/filter.js +3 -18
- package/build/git/diff/parsers.js +149 -61
- package/build/git/diff/preview.js +16 -2
- package/build/git/diff/read.js +32 -20
- package/build/git/snapshot.js +5 -24
- package/build/git/worktree.js +5 -4
- package/build/mcp/responses.js +3 -2
- package/build/mcp/server.js +61 -69
- package/build/protocol/draft-artifacts.js +2 -1
- package/build/protocol/file-guards.js +2 -1
- package/build/protocol/markdown/lex.js +52 -14
- package/build/protocol/markdown/normalization.js +3 -2
- package/build/protocol/markdown/parser.js +2 -2
- package/build/protocol/response-history.js +44 -5
- package/build/protocol/status-previews.js +20 -8
- package/build/protocol/summon-draft.js +3 -2
- package/build/protocol/summon-input.js +1 -0
- package/build/protocol/trace.js +1 -1
- package/build/tools/amend/index.js +11 -21
- package/build/tools/ask/index.js +11 -18
- package/build/tools/ask/persist.js +60 -37
- package/build/tools/ask/run.js +17 -7
- package/build/tools/create-handler.js +31 -0
- package/build/tools/drive/index.js +11 -24
- package/build/tools/drive/run.js +9 -5
- package/build/tools/petition/claim-gates.js +38 -9
- package/build/tools/petition/claim.js +20 -2
- package/build/tools/petition/forfeit.js +4 -1
- package/build/tools/petition/index.js +43 -58
- package/build/tools/petition/run.js +12 -0
- package/build/tools/round/head-guard.js +10 -0
- package/build/tools/round/incremental-diff.js +6 -2
- package/build/tools/round/report.js +24 -2
- package/build/tools/round/run.js +6 -0
- package/build/tools/round/worktree.js +6 -2
- package/build/tools/status/index.js +11 -24
- package/build/tools/status/read.js +6 -4
- package/build/tools/summon/index.js +17 -27
- package/build/tools/summon/run.js +21 -18
- package/package.json +6 -6
- package/build/git/diff/stat.js +0 -9
package/build/mcp/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { resolveOath } from "../protocol/oath.js";
|
|
3
|
-
import { amendToolSchema,
|
|
4
|
-
import { getAvailableNamesForPreset,
|
|
3
|
+
import { amendToolSchema, askToolSchema, summonToolSchema, driveToolSchema, petitionToolSchema, helpToolSchema, statusToolSchema, } from "../tools/schema.js";
|
|
4
|
+
import { getAvailableNamesForPreset, resolveTermPreset } from "../config/term-presets/resolver.js";
|
|
5
5
|
import { renderPreset } from "../config/render-template.js";
|
|
6
6
|
import { applyArgumentDescriptions } from "../config/apply-argument-descriptions.js";
|
|
7
7
|
import { createAmendHandler } from "../tools/amend/index.js";
|
|
@@ -13,9 +13,6 @@ import { createHelpHandler } from "../tools/help.js";
|
|
|
13
13
|
import { createStatusHandler } from "../tools/status/index.js";
|
|
14
14
|
function registerTools(server, packageVersion) {
|
|
15
15
|
const preset = resolveTermPreset();
|
|
16
|
-
const presetIdentities = listTermPresets()
|
|
17
|
-
.map((item) => `${item.id}=${item.identity}`)
|
|
18
|
-
.join(", ");
|
|
19
16
|
const availableNames = getAvailableNamesForPreset(preset).join(", ");
|
|
20
17
|
const currentOath = resolveOath();
|
|
21
18
|
const renderedPreset = renderPreset(preset, {
|
|
@@ -25,73 +22,68 @@ function registerTools(server, packageVersion) {
|
|
|
25
22
|
amend: preset.tools.amend.name,
|
|
26
23
|
close: preset.tools.close.name,
|
|
27
24
|
identity: preset.identity,
|
|
28
|
-
preset_identities: presetIdentities,
|
|
29
25
|
available_names: availableNames,
|
|
30
26
|
oath_text: `'${currentOath}'`,
|
|
31
27
|
});
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
title: statusPreset.title,
|
|
92
|
-
description: statusPreset.description,
|
|
93
|
-
inputSchema: dynamicStatusSchema,
|
|
94
|
-
}, createStatusHandler());
|
|
28
|
+
const tools = [
|
|
29
|
+
{
|
|
30
|
+
key: "start",
|
|
31
|
+
schema: applyArgumentDescriptions(summonToolSchema, {
|
|
32
|
+
...renderedPreset.tools.start.args,
|
|
33
|
+
}),
|
|
34
|
+
handler: createSummonHandler(),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
key: "drive",
|
|
38
|
+
schema: applyArgumentDescriptions(driveToolSchema, {
|
|
39
|
+
...renderedPreset.tools.drive.args,
|
|
40
|
+
}),
|
|
41
|
+
handler: createDriveHandler(),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: "ask",
|
|
45
|
+
schema: applyArgumentDescriptions(askToolSchema, {
|
|
46
|
+
...renderedPreset.tools.ask.args,
|
|
47
|
+
}),
|
|
48
|
+
handler: createAskHandler(),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: "amend",
|
|
52
|
+
schema: applyArgumentDescriptions(amendToolSchema, {
|
|
53
|
+
...renderedPreset.tools.amend.args,
|
|
54
|
+
}),
|
|
55
|
+
handler: createAmendHandler(),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
key: "close",
|
|
59
|
+
schema: applyArgumentDescriptions(petitionToolSchema, {
|
|
60
|
+
...renderedPreset.tools.close.args,
|
|
61
|
+
}),
|
|
62
|
+
handler: createPetitionHandler(),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: "help",
|
|
66
|
+
schema: applyArgumentDescriptions(helpToolSchema, {
|
|
67
|
+
...renderedPreset.tools.help.args,
|
|
68
|
+
}),
|
|
69
|
+
handler: createHelpHandler(renderedPreset, packageVersion),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: "status",
|
|
73
|
+
schema: applyArgumentDescriptions(statusToolSchema, {
|
|
74
|
+
...renderedPreset.tools.status.args,
|
|
75
|
+
}),
|
|
76
|
+
handler: createStatusHandler(),
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
for (const tool of tools) {
|
|
80
|
+
const toolPreset = renderedPreset.tools[tool.key];
|
|
81
|
+
server.registerTool(toolPreset.name, {
|
|
82
|
+
title: toolPreset.title,
|
|
83
|
+
description: toolPreset.description,
|
|
84
|
+
inputSchema: tool.schema,
|
|
85
|
+
}, tool.handler);
|
|
86
|
+
}
|
|
95
87
|
}
|
|
96
88
|
export function createServer(packageVersion) {
|
|
97
89
|
const server = new McpServer({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { DRAFT_ARTIFACT_NOTICE, KEIYAKU_DRAFT_DIR } from "../keiyaku.js";
|
|
4
|
+
import { isErrnoException } from "../errno.js";
|
|
4
5
|
const DRAFT_SLUG_FALLBACK = "keiyaku-draft";
|
|
5
6
|
function normalizeDraftRelativePath(relativePath) {
|
|
6
7
|
return relativePath.split(path.sep).join("/");
|
|
@@ -63,7 +64,7 @@ export async function listDraftArtifacts(cwd) {
|
|
|
63
64
|
.sort((a, b) => b.localeCompare(a));
|
|
64
65
|
}
|
|
65
66
|
catch (error) {
|
|
66
|
-
if (error
|
|
67
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
67
68
|
return [];
|
|
68
69
|
}
|
|
69
70
|
throw error;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { KEIYAKU_FILE, TRACE_FILE } from "../keiyaku.js";
|
|
4
|
+
import { isErrnoException } from "../errno.js";
|
|
4
5
|
import { FlowError } from "../flow-error.js";
|
|
5
6
|
async function isMissingFile(filePath) {
|
|
6
7
|
try {
|
|
@@ -8,7 +9,7 @@ async function isMissingFile(filePath) {
|
|
|
8
9
|
return false;
|
|
9
10
|
}
|
|
10
11
|
catch (error) {
|
|
11
|
-
if (error
|
|
12
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
12
13
|
return true;
|
|
13
14
|
}
|
|
14
15
|
throw error;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const FENCE_DELIMITERS = ["`", "~"];
|
|
1
2
|
function countLeadingSpaces(line) {
|
|
2
3
|
let idx = 0;
|
|
3
4
|
while (idx < line.length && line[idx] === " ") {
|
|
@@ -6,15 +7,17 @@ function countLeadingSpaces(line) {
|
|
|
6
7
|
return idx;
|
|
7
8
|
}
|
|
8
9
|
function parseFence(trimmedLine) {
|
|
9
|
-
|
|
10
|
+
const delimiter = FENCE_DELIMITERS.find((candidate) => trimmedLine.startsWith(candidate.repeat(3)));
|
|
11
|
+
if (!delimiter)
|
|
10
12
|
return null;
|
|
11
13
|
let idx = 0;
|
|
12
|
-
while (idx < trimmedLine.length && trimmedLine[idx] ===
|
|
14
|
+
while (idx < trimmedLine.length && trimmedLine[idx] === delimiter) {
|
|
13
15
|
idx += 1;
|
|
14
16
|
}
|
|
15
17
|
if (idx < 3)
|
|
16
18
|
return null;
|
|
17
19
|
return {
|
|
20
|
+
delimiter,
|
|
18
21
|
length: idx,
|
|
19
22
|
info: trimmedLine.slice(idx).trim(),
|
|
20
23
|
};
|
|
@@ -39,6 +42,15 @@ function parseHeader(trimmedLine) {
|
|
|
39
42
|
return null;
|
|
40
43
|
return { level, text };
|
|
41
44
|
}
|
|
45
|
+
function parseSetextUnderline(trimmedLine) {
|
|
46
|
+
if (/^=+$/.test(trimmedLine)) {
|
|
47
|
+
return { level: 1 };
|
|
48
|
+
}
|
|
49
|
+
if (/^-+$/.test(trimmedLine)) {
|
|
50
|
+
return { level: 2 };
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
42
54
|
function parseBlockquote(line) {
|
|
43
55
|
let idx = 0;
|
|
44
56
|
while (idx < line.length && line[idx] === " ") {
|
|
@@ -164,17 +176,19 @@ export function isSectionHeaderToken(token) {
|
|
|
164
176
|
return token.type === "header" && (token.level === 1 || token.level === 2);
|
|
165
177
|
}
|
|
166
178
|
export function isFenceClosingToken(token, fence) {
|
|
167
|
-
return token.type === "fence" && token.length >= fence.length;
|
|
179
|
+
return token.type === "fence" && token.delimiter === fence.delimiter && token.length >= fence.length;
|
|
168
180
|
}
|
|
169
181
|
export function lexMarkdown(content) {
|
|
170
182
|
const lines = stripUtf8Bom(content).split(/\r?\n/);
|
|
171
183
|
const tokens = [];
|
|
172
184
|
const frontMatter = consumeLeadingYamlFrontMatter(lines);
|
|
173
185
|
const contentLines = frontMatter.contentLines;
|
|
186
|
+
let activeFence = null;
|
|
174
187
|
if (frontMatter.token) {
|
|
175
188
|
tokens.push(frontMatter.token);
|
|
176
189
|
}
|
|
177
|
-
for (
|
|
190
|
+
for (let index = 0; index < contentLines.length; index += 1) {
|
|
191
|
+
const line = contentLines[index] ?? "";
|
|
178
192
|
const leadingSpaces = countLeadingSpaces(line);
|
|
179
193
|
if (leadingSpaces <= 3) {
|
|
180
194
|
const trimmedLine = line.trimStart();
|
|
@@ -184,21 +198,45 @@ export function lexMarkdown(content) {
|
|
|
184
198
|
type: "fence",
|
|
185
199
|
raw: line,
|
|
186
200
|
leadingSpaces,
|
|
201
|
+
delimiter: fence.delimiter,
|
|
187
202
|
length: fence.length,
|
|
188
203
|
info: fence.info,
|
|
189
204
|
});
|
|
205
|
+
if (!activeFence) {
|
|
206
|
+
activeFence = { delimiter: fence.delimiter, length: fence.length };
|
|
207
|
+
}
|
|
208
|
+
else if (activeFence.delimiter === fence.delimiter && fence.length >= activeFence.length) {
|
|
209
|
+
activeFence = null;
|
|
210
|
+
}
|
|
190
211
|
continue;
|
|
191
212
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
213
|
+
if (!activeFence) {
|
|
214
|
+
const header = parseHeader(trimmedLine);
|
|
215
|
+
if (header) {
|
|
216
|
+
tokens.push({
|
|
217
|
+
type: "header",
|
|
218
|
+
raw: line,
|
|
219
|
+
leadingSpaces,
|
|
220
|
+
level: header.level,
|
|
221
|
+
text: header.text,
|
|
222
|
+
});
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const nextLine = contentLines[index + 1];
|
|
226
|
+
if (trimmedLine.trim().length > 0 && nextLine !== undefined && countLeadingSpaces(nextLine) <= 3) {
|
|
227
|
+
const setext = parseSetextUnderline(nextLine.trim());
|
|
228
|
+
if (setext) {
|
|
229
|
+
tokens.push({
|
|
230
|
+
type: "header",
|
|
231
|
+
raw: line,
|
|
232
|
+
leadingSpaces,
|
|
233
|
+
level: setext.level,
|
|
234
|
+
text: trimmedLine.trim(),
|
|
235
|
+
});
|
|
236
|
+
index += 1;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
202
240
|
}
|
|
203
241
|
}
|
|
204
242
|
const marker = parseListMarker(line);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { parseToAST
|
|
1
|
+
import { parseToAST } from "./parser.js";
|
|
2
|
+
import { renderNodeContent } from "./render.js";
|
|
2
3
|
export function computeHeadingDelta(shallowest, minLevel) {
|
|
3
4
|
if (shallowest === null || shallowest >= minLevel) {
|
|
4
5
|
return 0;
|
|
@@ -61,7 +62,7 @@ export function renderMarkdownSections(items) {
|
|
|
61
62
|
// Keiyaku uses H2 as contract section delimiters. Keep payload headings at H3+.
|
|
62
63
|
const normalized = demoteMarkdownHeadings(item, 3);
|
|
63
64
|
const trimmed = normalized.trimStart();
|
|
64
|
-
if (/^[-*+]\s+/.test(trimmed) || /^\d
|
|
65
|
+
if (/^[-*+]\s+/.test(trimmed) || /^\d+[.)]\s+/.test(trimmed)) {
|
|
65
66
|
return normalized;
|
|
66
67
|
}
|
|
67
68
|
return `- ${normalized}`;
|
|
@@ -84,7 +84,7 @@ class MarkdownParser {
|
|
|
84
84
|
throw new KeiyakuParseError("invalid markdown parse state: expected code fence");
|
|
85
85
|
}
|
|
86
86
|
const lines = [opener.raw];
|
|
87
|
-
const fence = { length: opener.length };
|
|
87
|
+
const fence = { delimiter: opener.delimiter, length: opener.length };
|
|
88
88
|
while (!this.isEof()) {
|
|
89
89
|
const token = this.consume();
|
|
90
90
|
if (!token)
|
|
@@ -192,7 +192,7 @@ class MarkdownParser {
|
|
|
192
192
|
break;
|
|
193
193
|
lines.push(consumed.raw);
|
|
194
194
|
if (consumed.type === "fence") {
|
|
195
|
-
itemFence = { length: consumed.length };
|
|
195
|
+
itemFence = { delimiter: consumed.delimiter, length: consumed.length };
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
const normalizedLines = stripTrailingBlankLines(lines);
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { RESPONSE_ARTIFACT_NOTICE, RESPONSE_HISTORY_DIR } from "../keiyaku.js";
|
|
4
|
+
import { isErrnoException } from "../errno.js";
|
|
4
5
|
import { FlowError, requireText } from "../flow-error.js";
|
|
5
6
|
import { parseToAST } from "./markdown/index.js";
|
|
6
7
|
const RESPONSE_HISTORY_SLUG_MAX_LENGTH = 24;
|
|
7
|
-
const
|
|
8
|
+
export const RESPONSE_ARTIFACT_HEADINGS = {
|
|
9
|
+
ask: "# Ask",
|
|
10
|
+
summon: "# Summon",
|
|
11
|
+
drive: "# Drive",
|
|
12
|
+
review: "# Review",
|
|
13
|
+
};
|
|
14
|
+
const RESUMABLE_RESPONSE_HEADINGS = Object.values(RESPONSE_ARTIFACT_HEADINGS);
|
|
8
15
|
function toTimestampParts(now) {
|
|
9
16
|
const year = String(now.getFullYear());
|
|
10
17
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
@@ -12,8 +19,9 @@ function toTimestampParts(now) {
|
|
|
12
19
|
const hour = String(now.getHours()).padStart(2, "0");
|
|
13
20
|
const minute = String(now.getMinutes()).padStart(2, "0");
|
|
14
21
|
const second = String(now.getSeconds()).padStart(2, "0");
|
|
22
|
+
const millisecond = String(now.getMilliseconds()).padStart(3, "0");
|
|
15
23
|
return {
|
|
16
|
-
fileStamp: `${year}${month}${day}-${hour}${minute}${second}`,
|
|
24
|
+
fileStamp: `${year}${month}${day}-${hour}${minute}${second}${millisecond}`,
|
|
17
25
|
iso: now.toISOString(),
|
|
18
26
|
};
|
|
19
27
|
}
|
|
@@ -133,6 +141,28 @@ export async function persistResponseHistory(input) {
|
|
|
133
141
|
await fs.writeFile(responsePath, markdown, "utf-8");
|
|
134
142
|
return path.relative(input.cwd, responsePath);
|
|
135
143
|
}
|
|
144
|
+
function detectArtifactHeading(document) {
|
|
145
|
+
const lines = document.split("\n");
|
|
146
|
+
let index = 0;
|
|
147
|
+
if (lines[0] === "---") {
|
|
148
|
+
index = 1;
|
|
149
|
+
while (index < lines.length && lines[index] !== "---") {
|
|
150
|
+
index += 1;
|
|
151
|
+
}
|
|
152
|
+
if (index === lines.length) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
index += 1;
|
|
156
|
+
}
|
|
157
|
+
for (; index < lines.length; index += 1) {
|
|
158
|
+
const line = lines[index].trim();
|
|
159
|
+
if (line.length === 0) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
return RESUMABLE_RESPONSE_HEADINGS.find((heading) => heading === line) ?? null;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
136
166
|
export async function resolveAskResponsePath(input) {
|
|
137
167
|
const responsePath = requireText("responsePath", input.responsePath);
|
|
138
168
|
const resolvedPath = path.isAbsolute(responsePath) ? responsePath : path.resolve(input.cwd, responsePath);
|
|
@@ -141,8 +171,7 @@ export async function resolveAskResponsePath(input) {
|
|
|
141
171
|
document = await fs.readFile(resolvedPath, "utf-8");
|
|
142
172
|
}
|
|
143
173
|
catch (error) {
|
|
144
|
-
|
|
145
|
-
if (code === "ENOENT" || code === "EISDIR") {
|
|
174
|
+
if (isErrnoException(error) && (error.code === "ENOENT" || error.code === "EISDIR")) {
|
|
146
175
|
throw new FlowError("INVALID_RESPONSE_PATH", `Response path file not found: '${responsePath}'.`);
|
|
147
176
|
}
|
|
148
177
|
throw error;
|
|
@@ -155,9 +184,19 @@ export async function resolveAskResponsePath(input) {
|
|
|
155
184
|
if (frontmatter.notice !== RESPONSE_ARTIFACT_NOTICE) {
|
|
156
185
|
throw new FlowError("INVALID_RESPONSE_PATH", `Response path file '${responsePath}' is not a Keiyaku response artifact.`);
|
|
157
186
|
}
|
|
158
|
-
|
|
187
|
+
const artifactHeading = detectArtifactHeading(document);
|
|
188
|
+
if (!artifactHeading) {
|
|
159
189
|
throw new FlowError("INVALID_RESPONSE_PATH", `Response path file '${responsePath}' is not a supported Keiyaku response artifact.`);
|
|
160
190
|
}
|
|
191
|
+
if (input.allowedTools?.length) {
|
|
192
|
+
const allowedHeadings = input.allowedTools.map((tool) => RESPONSE_ARTIFACT_HEADINGS[tool]);
|
|
193
|
+
if (!allowedHeadings.includes(artifactHeading)) {
|
|
194
|
+
if (input.onArtifactTypeMismatch === "return-null") {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
throw new FlowError("INVALID_RESPONSE_PATH", `Response path file '${responsePath}' is not a supported ${input.allowedTools.join("/")} response artifact.`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
161
200
|
const sessionId = frontmatter.session_id?.trim();
|
|
162
201
|
if (!sessionId) {
|
|
163
202
|
throw new FlowError("INVALID_RESPONSE_PATH", `Response path file '${responsePath}' does not include 'session_id'; this conversation cannot be resumed.`);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { parseToAST, renderNodeContent } from "./markdown/index.js";
|
|
2
|
-
import { parseRoundSummary } from "../tools/round/report.js";
|
|
1
|
+
import { parseMarkdownSections, parseToAST, renderNodeContent } from "./markdown/index.js";
|
|
3
2
|
const PREVIEW_SECTION_TITLES = ["goal", "context"];
|
|
4
3
|
const PREVIEW_SECTION_TITLE_SET = new Set(PREVIEW_SECTION_TITLES);
|
|
5
4
|
const STATUS_PREVIEW_MAX_LINES = 2;
|
|
@@ -49,6 +48,22 @@ function extractRoundRawSummary(roundSection) {
|
|
|
49
48
|
const summary = renderNodesForPreview(extractHeadingBody(roundSection, "summary"));
|
|
50
49
|
return summary.length > 0 ? summary : undefined;
|
|
51
50
|
}
|
|
51
|
+
function extractOutcomeFromStructuredSummary(rawSummary) {
|
|
52
|
+
for (const sectionMarkdown of parseMarkdownSections(rawSummary)) {
|
|
53
|
+
const lines = sectionMarkdown.split(/\r?\n/);
|
|
54
|
+
const headerLine = lines[0]?.trim();
|
|
55
|
+
if (!headerLine)
|
|
56
|
+
continue;
|
|
57
|
+
const headerMatch = /^#{2,3}\s+(.+?)\s*$/.exec(headerLine);
|
|
58
|
+
if (!headerMatch)
|
|
59
|
+
continue;
|
|
60
|
+
if (normalizeHeading(headerMatch[1]) !== "outcome")
|
|
61
|
+
continue;
|
|
62
|
+
const body = lines.slice(1).join("\n").trim();
|
|
63
|
+
return body.length > 0 ? body : undefined;
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
52
67
|
export function extractLastRoundSummary(traceContent) {
|
|
53
68
|
const ast = parseToAST(traceContent, { allowSections: true });
|
|
54
69
|
let lastRoundSection;
|
|
@@ -69,12 +84,9 @@ export function extractLastRoundSummary(traceContent) {
|
|
|
69
84
|
const rawSummary = extractRoundRawSummary(lastRoundSection)?.trim();
|
|
70
85
|
if (!rawSummary)
|
|
71
86
|
return undefined;
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
if (outcomeBody) {
|
|
76
|
-
return outcomeBody;
|
|
77
|
-
}
|
|
87
|
+
const outcomeBody = extractOutcomeFromStructuredSummary(rawSummary);
|
|
88
|
+
if (outcomeBody) {
|
|
89
|
+
return outcomeBody;
|
|
78
90
|
}
|
|
79
91
|
return rawSummary;
|
|
80
92
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { KEIYAKU_DRAFT_DIR } from "../keiyaku.js";
|
|
4
|
+
import { isErrnoException } from "../errno.js";
|
|
4
5
|
import { FlowError, isFlowError, requireText, wrapFlowError } from "../flow-error.js";
|
|
5
6
|
import { resolveTermPreset } from "../config/term-presets/resolver.js";
|
|
6
7
|
import { writeDraftArtifact, listDraftArtifacts } from "./draft-artifacts.js";
|
|
@@ -74,7 +75,7 @@ export async function resolveSummonInput(cwd, input) {
|
|
|
74
75
|
content = await fs.readFile(draftPath, "utf-8");
|
|
75
76
|
}
|
|
76
77
|
catch (error) {
|
|
77
|
-
if (error
|
|
78
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
78
79
|
throw new FlowError("FROM_DRAFT_NOT_FOUND", `from_draft path does not exist: ${fromDraft}`);
|
|
79
80
|
}
|
|
80
81
|
throw error;
|
|
@@ -115,7 +116,7 @@ export async function readRollbackDraftSnapshot(cwd, candidatePath) {
|
|
|
115
116
|
return { relativePath, content };
|
|
116
117
|
}
|
|
117
118
|
catch (error) {
|
|
118
|
-
if (error
|
|
119
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
119
120
|
return null;
|
|
120
121
|
}
|
|
121
122
|
throw error;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/build/protocol/trace.js
CHANGED
|
@@ -187,7 +187,7 @@ export function renderReviewSection(review) {
|
|
|
187
187
|
`${REVIEW_RESULT_PREFIX} ${review.result}`,
|
|
188
188
|
`${REVIEW_HISTORY_POINTER_PREFIX} ${historyPath}`,
|
|
189
189
|
];
|
|
190
|
-
if (findings
|
|
190
|
+
if (findings) {
|
|
191
191
|
lines.push("", findings);
|
|
192
192
|
}
|
|
193
193
|
lines.push("");
|
|
@@ -1,28 +1,18 @@
|
|
|
1
|
-
import { appendDebugLog } from "../../telemetry/debug-log.js";
|
|
2
1
|
import { buildAmendResponse } from "../../mcp/responses.js";
|
|
3
|
-
import { handleToolError } from "../../mcp/tool-errors.js";
|
|
4
2
|
import { amendKeiyaku } from "./run.js";
|
|
3
|
+
import { createToolHandler } from "../create-handler.js";
|
|
5
4
|
export function createAmendHandler() {
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
cwd
|
|
5
|
+
return createToolHandler({
|
|
6
|
+
logLabel: "tool amend",
|
|
7
|
+
doneLog: (outcome) => `tool amend success branch=${outcome.currentBranch} amendment=${outcome.amendmentNumber}`,
|
|
8
|
+
async run({ amendment, cwd }) {
|
|
9
|
+
return amendKeiyaku({
|
|
10
|
+
cwd,
|
|
12
11
|
amendment,
|
|
13
12
|
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
section: "script",
|
|
17
|
-
});
|
|
13
|
+
},
|
|
14
|
+
buildResponse(outcome) {
|
|
18
15
|
return buildAmendResponse(outcome);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return handleToolError({
|
|
22
|
-
error: err,
|
|
23
|
-
cwd: workingDir,
|
|
24
|
-
logLabel: "tool amend",
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
};
|
|
16
|
+
},
|
|
17
|
+
});
|
|
28
18
|
}
|
package/build/tools/ask/index.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { appendDebugLog } from "../../telemetry/debug-log.js";
|
|
2
1
|
import { runAskAndPersist } from "./persist.js";
|
|
3
2
|
import { buildAskResponse } from "../../mcp/responses.js";
|
|
4
|
-
import {
|
|
3
|
+
import { createToolHandler } from "../create-handler.js";
|
|
5
4
|
export function createAskHandler() {
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
cwd
|
|
5
|
+
return createToolHandler({
|
|
6
|
+
logLabel: "tool ask",
|
|
7
|
+
doneLog: "tool ask success",
|
|
8
|
+
async run({ title, request, context, tier, responsePath, cwd }, extra) {
|
|
9
|
+
return runAskAndPersist({
|
|
10
|
+
cwd,
|
|
12
11
|
title,
|
|
13
12
|
request,
|
|
14
13
|
context,
|
|
@@ -16,18 +15,12 @@ export function createAskHandler() {
|
|
|
16
15
|
tier,
|
|
17
16
|
signal: extra.signal,
|
|
18
17
|
});
|
|
19
|
-
|
|
18
|
+
},
|
|
19
|
+
buildResponse(outcome, { tier }) {
|
|
20
20
|
return buildAskResponse(outcome.result, {
|
|
21
21
|
tier,
|
|
22
22
|
responsePath: outcome.responsePath,
|
|
23
23
|
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return handleToolError({
|
|
27
|
-
error: err,
|
|
28
|
-
cwd: workingDir,
|
|
29
|
-
logLabel: "tool ask",
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
};
|
|
24
|
+
},
|
|
25
|
+
});
|
|
33
26
|
}
|