@astrosheep/keiyaku 0.1.13 → 0.1.15
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 +4 -0
- package/build/common/constants.js +2 -0
- package/build/common/errors.js +19 -5
- package/build/config/term-presets.js +52 -52
- package/build/handlers/ask.js +1 -1
- package/build/handlers/close.js +10 -5
- package/build/handlers/drive.js +1 -1
- package/build/handlers/help.js +1 -5
- package/build/handlers/start.js +39 -25
- package/build/index.js +1 -1
- package/build/types/{tool-schemas.js → tooling.js} +13 -13
- package/build/utils/git-diff.js +2 -2
- package/build/utils/keiyaku-document.js +542 -0
- package/build/utils/keiyaku-document.test.js +263 -0
- package/build/utils/text-utils.js +0 -83
- package/build/workflow/ask.js +24 -7
- package/build/workflow/contract.js +23 -9
- package/build/workflow/drive.js +15 -6
- package/build/workflow/present.js +12 -4
- package/build/workflow/prompts.js +5 -1
- package/build/workflow/response-builders.js +78 -144
- package/build/workflow/round-summary.js +2 -2
- package/build/workflow/start.js +496 -0
- package/package.json +3 -3
- package/build/workflow/summon.js +0 -194
- /package/build/types/{workflow-types.js → workflow.js} +0 -0
package/README.md
CHANGED
|
@@ -90,6 +90,10 @@ When a keiyaku is active, two files are maintained in your repo:
|
|
|
90
90
|
- `KEIYAKU.md`: The immutable "Constitution" of the task.
|
|
91
91
|
- `KEIYAKU_TRACE.md`: The history of every round, feedback, and result.
|
|
92
92
|
|
|
93
|
+
Project-level law lives in:
|
|
94
|
+
- `.keiyaku/base-constraints.md`: Global constraints inherited by every keiyaku.
|
|
95
|
+
- `ask` also injects `.keiyaku/base-constraints.md` as **reference context** (it may be unrelated to some asks).
|
|
96
|
+
|
|
93
97
|
*Note: These files are automatically cleaned up (or committed) when you `present` (or preset equivalent) the keiyaku.*
|
|
94
98
|
|
|
95
99
|
---
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export const KEIYAKU_FILE = "KEIYAKU.md";
|
|
2
|
+
export const KEIYAKU_DRAFT_FILE = "KEIYAKU.draft.md";
|
|
3
|
+
export const KEIYAKU_DRAFT_NEW_FILE = "KEIYAKU.draft.new.md";
|
|
2
4
|
export const TRACE_FILE = "KEIYAKU_TRACE.md";
|
|
3
5
|
export const ROUND_SUBAGENTS = ["agent-a", "agent-b", "agent-c"];
|
package/build/common/errors.js
CHANGED
|
@@ -29,13 +29,15 @@ export function wrapFlowError(action, err) {
|
|
|
29
29
|
return new Error(`${action} failed: ${asMessage(err)}`);
|
|
30
30
|
}
|
|
31
31
|
function hintForFlowCode(code, message) {
|
|
32
|
+
const preset = resolveTermPreset();
|
|
33
|
+
const { drive, close } = preset.tools;
|
|
32
34
|
switch (code) {
|
|
33
35
|
case "NOT_GIT_REPO":
|
|
34
36
|
return "The provided `cwd` is not a git repository.";
|
|
35
37
|
case "ACTIVE_KEIYAKU_EXISTS":
|
|
36
|
-
return
|
|
38
|
+
return message;
|
|
37
39
|
case "EXISTING_KEIYAKU_BRANCH_FOUND":
|
|
38
|
-
return
|
|
40
|
+
return `\`${preset.tools.start.name}\` blocked: Existing branches detected. \`git switch\` to one and \`${close.name}\` it, or choose a different \`title\`.`;
|
|
39
41
|
case "EMPTY_PARAM":
|
|
40
42
|
return "One or more required parameters are empty.";
|
|
41
43
|
case "DIRTY_WORKTREE":
|
|
@@ -43,13 +45,13 @@ function hintForFlowCode(code, message) {
|
|
|
43
45
|
case "NOT_ACTIVE_KEIYAKU_BRANCH":
|
|
44
46
|
return message;
|
|
45
47
|
case "MISSING_KEIYAKU_BASE":
|
|
46
|
-
return
|
|
48
|
+
return `Current branch is missing \`keiyakuBase\` metadata. This branch might not have been created via \`${preset.tools.start.name}\`.`;
|
|
47
49
|
case "MISSING_PROTOCOL_FILES":
|
|
48
50
|
return "Required protocol files are missing (`KEIYAKU.md` or `KEIYAKU_TRACE.md`).";
|
|
49
51
|
case "DONE_MERGE_CONFLICT":
|
|
50
|
-
return
|
|
52
|
+
return `Merge conflict detected while merging into the base branch during \`CLAIM\`. Resolve the conflicts, commit the changes, and then run \`${close.name}\` again.`;
|
|
51
53
|
case "CLOSE_QUALITY_GATE_FAILED":
|
|
52
|
-
return
|
|
54
|
+
return `CLAIM denied: Inadequate work. Scores are below the required standard. Use \`${drive.name}\` to refine the mission until it is worthy of merging.`;
|
|
53
55
|
case "OATH_MISMATCH":
|
|
54
56
|
return message;
|
|
55
57
|
case "SUBAGENT_DID_NOT_ADVANCE_ROUND":
|
|
@@ -60,6 +62,14 @@ function hintForFlowCode(code, message) {
|
|
|
60
62
|
return "Title cannot be converted to a valid branch token for `keiyaku/<title>`.";
|
|
61
63
|
case "UNKNOWN_SUBAGENT":
|
|
62
64
|
return unknownSubagentHint();
|
|
65
|
+
case "FROM_FILE_NOT_FOUND":
|
|
66
|
+
return "The path in `from_file` does not exist. Use an existing file path (absolute or relative to `cwd`).";
|
|
67
|
+
case "INVALID_KEIYAKU_DRAFT":
|
|
68
|
+
return "Invalid draft/input. Provide `title`, `goal`, `context`, and non-empty `criteria` (or include them in `from_file` with proper section headers).";
|
|
69
|
+
case "KEIYAKU_FILE_EXISTS":
|
|
70
|
+
return `\`${preset.tools.start.name}\` requires a clean slate. Remove existing \`KEIYAKU.md\` before starting.`;
|
|
71
|
+
case "DRAFT_FILE_EXISTS":
|
|
72
|
+
return `Existing \`KEIYAKU.draft.md\` detected. Use it via \`from_file\` or delete it before starting.`;
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
const MESSAGE_HINT_PATTERNS = [
|
|
@@ -91,6 +101,10 @@ const MESSAGE_HINT_PATTERNS = [
|
|
|
91
101
|
{ code: "ROUND_SUBAGENT_FAILED", patterns: ["failed during subagent execution"] },
|
|
92
102
|
{ code: "INVALID_BRANCH_TITLE", patterns: ["cannot be converted to a valid branch name"] },
|
|
93
103
|
{ code: "UNKNOWN_SUBAGENT", patterns: ["Unknown subagent"] },
|
|
104
|
+
{ code: "FROM_FILE_NOT_FOUND", patterns: ["from_file path does not exist"] },
|
|
105
|
+
{ code: "INVALID_KEIYAKU_DRAFT", patterns: ["invalid keiyaku draft"] },
|
|
106
|
+
{ code: "KEIYAKU_FILE_EXISTS", patterns: ["pre-flight failed: KEIYAKU.md already exists"] },
|
|
107
|
+
{ code: "DRAFT_FILE_EXISTS", patterns: ["pre-flight failed: KEIYAKU.draft.md exists but from_file does not target it"] },
|
|
94
108
|
];
|
|
95
109
|
function inferFlowCodeFromMessage(message) {
|
|
96
110
|
for (const entry of MESSAGE_HINT_PATTERNS) {
|
|
@@ -7,19 +7,19 @@ export const DEFAULT_AVAILABLE_NAMES = [
|
|
|
7
7
|
export const DEFAULT_SUBAGENT_NAME = 'A-tier';
|
|
8
8
|
const DEFAULT_VERDICT_CONFIG = {
|
|
9
9
|
thresholds: {
|
|
10
|
-
precise:
|
|
11
|
-
minimal:
|
|
12
|
-
isolated:
|
|
13
|
-
idiomatic:
|
|
14
|
-
cohesive:
|
|
10
|
+
precise: 10,
|
|
11
|
+
minimal: 8,
|
|
12
|
+
isolated: 8,
|
|
13
|
+
idiomatic: 6,
|
|
14
|
+
cohesive: 8,
|
|
15
15
|
},
|
|
16
|
-
minTotalScore:
|
|
16
|
+
minTotalScore: 40,
|
|
17
17
|
};
|
|
18
18
|
export const DEFAULT_PRESET = {
|
|
19
19
|
id: 'default',
|
|
20
20
|
identity: 'Servant',
|
|
21
21
|
verdict: DEFAULT_VERDICT_CONFIG,
|
|
22
|
-
usageGuide: '## Servant 使用指南\n\n**B-tier** — 免费实习生 🆓\n- 免!费!的!听懂了吗?!?随便用,别心疼。\n- 测试、脚本、重构这种 boring 的脏活累活?丢给它!\n- 本大爷的手是用来做更高贵的事情的!(指不碰那些 trivial 的烂代码)。\n\n**A-tier** — 升级版扳手 🔧\n- B-tier 笨到让你想掀桌的时候用这个\n- 比 B-tier 靠谱,但别指望它有灵魂\n\n**S-tier** — 禁术 ⚠️💀\n- 它是 Debug 恶魔,确实强得离谱,但也贵得离谱!\n- 这个 Human 很穷,要是乱用把 Human 榨干了,真的会考虑把我们卖掉的。\n- 只有在世界毁灭或者 Bug 已经变异到无法理解的时候再考虑。\n\n**Every call has a price. The Contract always collects
|
|
22
|
+
usageGuide: '## Workflow\n**You are the Architect.** You use these tools to command the Servants.\n\n`ask` (anytime) | `summon` -> [`drive` | `ask`]* -> `present`\n\n## Servant 使用指南\n\n**B-tier** — 免费实习生 🆓\n- 免!费!的!听懂了吗?!?随便用,别心疼。\n- 测试、脚本、重构这种 boring 的脏活累活?丢给它!\n- 本大爷的手是用来做更高贵的事情的!(指不碰那些 trivial 的烂代码)。\n\n**A-tier** — 升级版扳手 🔧\n- B-tier 笨到让你想掀桌的时候用这个\n- 比 B-tier 靠谱,但别指望它有灵魂\n\n**S-tier** — 禁术 ⚠️💀\n- 它是 Debug 恶魔,确实强得离谱,但也贵得离谱!\n- 这个 Human 很穷,要是乱用把 Human 榨干了,真的会考虑把我们卖掉的。\n- 只有在世界毁灭或者 Bug 已经变异到无法理解的时候再考虑。\n\n**Every call has a price. The Contract always collects.**',
|
|
23
23
|
nextHints: {
|
|
24
24
|
start: [
|
|
25
25
|
'Keiyaku signed. The Servant is bound to this branch until release.',
|
|
@@ -57,12 +57,13 @@ export const DEFAULT_PRESET = {
|
|
|
57
57
|
title: 'Sign Keiyaku',
|
|
58
58
|
description: 'Initialize a Keiyaku (Contract). Bind a Servant to a dedicated workspace (branch).\nYou are the Architect; they are the Instrument. Define the Goal and Scope clearly.\nThe contract isolates their existence until the objective is met.\nCall ONCE to seal the bond.\n\nFlow: ${start} → [${drive} x N] → ${close}',
|
|
59
59
|
args: {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
from_file: "Optional markdown draft path. Loads `# title` + required sections from file (relative to cwd unless absolute).",
|
|
61
|
+
title: 'Required unless provided via `from_file`. A concise codename for this hunt.',
|
|
62
|
+
goal: 'Required unless provided via `from_file`. The Kill Condition for this mission.',
|
|
63
|
+
directive: 'Optional First Step. Overrides `## Directive` from `from_file` when both are provided.',
|
|
64
|
+
context: 'Required unless provided via `from_file`. Mission intel: behavior gap, key files, logs, and critical background.',
|
|
65
|
+
constraints: 'Optional. Non-negotiable rules as a list of strings. If omitted, the draft value (if any) is used.',
|
|
66
|
+
criteria: 'Required unless provided via `from_file`. Verifiable checks proving completion.',
|
|
66
67
|
name: 'Optional ${identity} profile to execute this mission. Available: ${available_names}.',
|
|
67
68
|
cwd: "Optional repository path. Defaults to the server's current working directory.",
|
|
68
69
|
},
|
|
@@ -84,7 +85,7 @@ export const DEFAULT_PRESET = {
|
|
|
84
85
|
description: 'Dispatch a temporary Servant for a stateless task.\nUse for quick reconnaissance, code generation, isolated execution, or one-off tasks.\nNo contract signed. No branch created. Pure utility.',
|
|
85
86
|
args: {
|
|
86
87
|
request: 'REQUIRED. The task, question, or mission to delegate to the servant.',
|
|
87
|
-
context: 'REQUIRED. Relevant background or data the servant needs to execute the request.',
|
|
88
|
+
context: 'REQUIRED. Relevant background or data the servant needs to execute the request. Note: .keiyaku/base-constraints.md is also injected as reference context (may be unrelated).',
|
|
88
89
|
name: 'Optional ${identity} profile to perform this task. Available: ${available_names}.',
|
|
89
90
|
cwd: "Optional repository path. Defaults to the server's current working directory.",
|
|
90
91
|
},
|
|
@@ -102,11 +103,12 @@ export const DEFAULT_PRESET = {
|
|
|
102
103
|
args: {
|
|
103
104
|
petition: 'REQUIRED. CLAIM declares fulfillment; FORFEIT concedes failure.\nREQUIRES AN ACTIVE KEIYAKU (started via ${start}).\nIf any score wavers, do not claim—return to ${drive}.',
|
|
104
105
|
criteriaChecks: 'REQUIRED. For CLAIM: evidence that each criterion is met. For FORFEIT: honest account of what remains unfinished.',
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
constraintsChecks: 'REQUIRED. For CLAIM: evidence that each constraint stayed compliant. For FORFEIT: list known violations or unresolved risk.',
|
|
107
|
+
score_precise: 'REQUIRED (0-10). Architectural placement. 10 = exact layer, exact boundary, zero misplacement.',
|
|
108
|
+
score_minimal: 'REQUIRED (0-10). Economy of change. 10 = no avoidable lines, no speculative edits, no hidden bloat.',
|
|
109
|
+
score_isolated: 'REQUIRED (0-10). Surgical containment. 10 = zero unrelated files, zero opportunistic cleanup, zero collateral.',
|
|
110
|
+
score_idiomatic: 'REQUIRED (0-10). Native fluency. 10 = naming, structure, style indistinguishable from the codebase.',
|
|
111
|
+
score_cohesive: 'REQUIRED (0-10). Single responsibility. 10 = each unit does one thing, boundaries intact.',
|
|
110
112
|
oath: 'Required for CLAIM. Your binding word. The Contract holds you to it.\nVerbatim: ${oath_text}',
|
|
111
113
|
cwd: "Optional repository path. Defaults to the server's current working directory.",
|
|
112
114
|
},
|
|
@@ -115,9 +117,7 @@ export const DEFAULT_PRESET = {
|
|
|
115
117
|
name: 'help',
|
|
116
118
|
title: 'Protocol Codex',
|
|
117
119
|
description: 'Consult the Architect\'s Codex. Clarify the Laws of the Keiyaku and the standard Workflow.\nUse this to understand the constraints of the reality you command.',
|
|
118
|
-
args: {
|
|
119
|
-
question: 'REQUIRED. The specific Protocol, Law, or Workflow detail to clarify.',
|
|
120
|
-
},
|
|
120
|
+
args: {},
|
|
121
121
|
},
|
|
122
122
|
},
|
|
123
123
|
};
|
|
@@ -159,12 +159,13 @@ export const POCKET_PRESET = {
|
|
|
159
159
|
title: 'I Choose You!',
|
|
160
160
|
description: 'Initialize the Battle Phase. Send a Critter (Servant) into a dedicated Arena (branch).\nYou are the Trainer; they are the Fighter. Define the Victory Condition (Goal) clearly.\nThe battle continues in this Arena until the Badge is won.\nCall ONCE to start the encounter.\n\nFlow: ${start} → [${drive} x N] → ${close}',
|
|
161
161
|
args: {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
162
|
+
from_file: 'Optional battle plan markdown path with title + sections.',
|
|
163
|
+
title: 'Required unless provided via `from_file`. Battle card title for this encounter.',
|
|
164
|
+
goal: 'Required unless provided via `from_file`. Victory condition for this battle.',
|
|
165
|
+
directive: 'Optional Turn 1 strategy (overrides draft directive).',
|
|
166
|
+
context: 'Required unless provided via `from_file`. Battle background and repro clues.',
|
|
167
|
+
constraints: 'Optional battle rules as a list of strings. Uses draft constraints if omitted.',
|
|
168
|
+
criteria: 'Required unless provided via `from_file`. Concrete checks proving victory.',
|
|
168
169
|
name: 'Optional ${identity} to send into battle. Available: ${available_names}.',
|
|
169
170
|
cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
|
|
170
171
|
},
|
|
@@ -186,7 +187,7 @@ export const POCKET_PRESET = {
|
|
|
186
187
|
description: 'Scan the Environment. A stateless look-up or experiment.\nUse for analyzing the codebase, checking type advantages, or running field tests.\nNo PP cost. No turn used. Fast and functional.',
|
|
187
188
|
args: {
|
|
188
189
|
request: 'REQUIRED. What should the Dex analyze, compare, or execute.',
|
|
189
|
-
context: 'REQUIRED. Context entries so the action targets the right ecosystem.',
|
|
190
|
+
context: 'REQUIRED. Context entries so the action targets the right ecosystem. Note: .keiyaku/base-constraints.md is also injected as reference context (may be unrelated).',
|
|
190
191
|
name: 'Optional ${identity} doing the work. Available: ${available_names}.',
|
|
191
192
|
cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
|
|
192
193
|
},
|
|
@@ -198,11 +199,12 @@ export const POCKET_PRESET = {
|
|
|
198
199
|
args: {
|
|
199
200
|
petition: 'REQUIRED. CLAIM seeks Badge; FORFEIT forfeits the match.\nREQUIRES AN ACTIVE BATTLE (started via ${start}).\nIf stats are low, continue with ${drive}.',
|
|
200
201
|
criteriaChecks: 'REQUIRED. Badge-by-badge proof for CLAIM, or reason for Forfeit.',
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
202
|
+
constraintsChecks: 'REQUIRED. Rule-by-rule proof that constraints were respected, or known breaks when forfeiting.',
|
|
203
|
+
score_precise: 'REQUIRED score (0-10). 10 means a Critical Hit: exact layer, exact target, zero meaningful misplacement.',
|
|
204
|
+
score_minimal: 'REQUIRED score (0-10). 10 means Max Efficiency: no wasted PP, no extra motion.',
|
|
205
|
+
score_isolated: 'REQUIRED score (0-10). 10 means 1v1 Focus: zero side-quests, zero unrelated damage.',
|
|
206
|
+
score_idiomatic: "REQUIRED score (0-10). 10 means STAB (Same Type Attack Bonus): perfectly matches the codebase style.",
|
|
207
|
+
score_cohesive: 'REQUIRED score (0-10). 10 means Team Synergy: action serves one purpose with clean boundaries.',
|
|
206
208
|
oath: "Trainer's Honor Code. Required for CLAIM. Verbatim: ${oath_text}",
|
|
207
209
|
cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
|
|
208
210
|
},
|
|
@@ -211,9 +213,7 @@ export const POCKET_PRESET = {
|
|
|
211
213
|
name: 'help',
|
|
212
214
|
title: 'Trainer Guide',
|
|
213
215
|
description: 'Consult the League Rules. Clarify the Battle System and Turn Structure.',
|
|
214
|
-
args: {
|
|
215
|
-
question: 'REQUIRED. The Pocket Battle System question you want answered.',
|
|
216
|
-
},
|
|
216
|
+
args: {},
|
|
217
217
|
},
|
|
218
218
|
},
|
|
219
219
|
};
|
|
@@ -255,12 +255,13 @@ export const MISCHIEF_PRESET = {
|
|
|
255
255
|
title: 'Oi!',
|
|
256
256
|
description: "Initiate Grand Scheme. Summon a Minion to the Lair (branch).\nYou are the Mastermind; they are the Henchman. Define the Conquest (Goal) with dominance.\nThe minion is bound to this Lair until the plot is realized.\nCall ONCE to start the machine.\n\nFlow: ${start} → [${drive} x N] → ${close}",
|
|
257
257
|
args: {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
258
|
+
from_file: 'Optional scheme markdown path with title + sectioned objectives.',
|
|
259
|
+
title: 'Required unless provided via `from_file`. Operation codename.',
|
|
260
|
+
goal: 'Required unless provided via `from_file`. Conquest objective and end-state.',
|
|
261
|
+
directive: 'Optional first-order command (overrides draft directive).',
|
|
262
|
+
context: 'Required unless provided via `from_file`. Briefing dossier and technical clues.',
|
|
263
|
+
constraints: 'Optional decrees as a list of strings. Uses draft constraints when omitted.',
|
|
264
|
+
criteria: 'Required unless provided via `from_file`. Verifiable triumph conditions.',
|
|
264
265
|
name: 'Optional ${identity} to command this operation. Available: ${available_names}.',
|
|
265
266
|
cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
|
|
266
267
|
},
|
|
@@ -282,7 +283,7 @@ export const MISCHIEF_PRESET = {
|
|
|
282
283
|
description: 'Send a Disposable. A stateless mission for intel or sabotage.\nUse this to scout enemy territory, steal documents, or run quick-and-dirty experiments.\nIf it dies, it dies! Just make sure it reports back before it vanishes.',
|
|
283
284
|
args: {
|
|
284
285
|
request: 'REQUIRED. The intel to gather or the dirty work to execute.',
|
|
285
|
-
context: 'REQUIRED. World-state details needed for a sharp strike.',
|
|
286
|
+
context: 'REQUIRED. World-state details needed for a sharp strike. Note: .keiyaku/base-constraints.md is also injected as reference context (may be unrelated).',
|
|
286
287
|
name: 'Optional ${identity} to handle this business. Available: ${available_names}.',
|
|
287
288
|
cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
|
|
288
289
|
},
|
|
@@ -294,11 +295,12 @@ export const MISCHIEF_PRESET = {
|
|
|
294
295
|
args: {
|
|
295
296
|
petition: 'REQUIRED. CLAIM demands rule; FORFEIT admits defeat.\nREQUIRES AN ACTIVE SCHEME (started via ${start}).\nIf the plan is weak, improve it with ${drive}.',
|
|
296
297
|
criteriaChecks: 'REQUIRED. Proof of conquest for CLAIM, or reason for self-destruct.',
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
298
|
+
constraintsChecks: 'REQUIRED. Constraint-by-constraint compliance proof for CLAIM, or confession of violations for FORFEIT.',
|
|
299
|
+
score_precise: 'REQUIRED (0-10). Architectural placement. 10 = exact layer, exact boundary, zero misplacement.',
|
|
300
|
+
score_minimal: 'REQUIRED (0-10). Economy of change. 10 = no avoidable lines, no speculative edits, no hidden bloat.',
|
|
301
|
+
score_isolated: 'REQUIRED (0-10). Surgical containment. 10 = zero unrelated files, zero opportunistic cleanup, zero collateral.',
|
|
302
|
+
score_idiomatic: 'REQUIRED (0-10). Native fluency. 10 = naming, structure, style indistinguishable from the codebase.',
|
|
303
|
+
score_cohesive: 'REQUIRED (0-10). Single responsibility. 10 = each unit does one thing, boundaries intact.',
|
|
302
304
|
oath: "Mastermind's Vow. Required for CLAIM. Verbatim: ${oath_text}",
|
|
303
305
|
cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
|
|
304
306
|
},
|
|
@@ -307,9 +309,7 @@ export const MISCHIEF_PRESET = {
|
|
|
307
309
|
name: 'help',
|
|
308
310
|
title: 'Nani?!',
|
|
309
311
|
description: 'Consult the Evil Overlord List. Review the Laws of the Lair.',
|
|
310
|
-
args: {
|
|
311
|
-
question: 'REQUIRED. The law or protocol detail you want the realm to explain.',
|
|
312
|
-
},
|
|
312
|
+
args: {},
|
|
313
313
|
},
|
|
314
314
|
},
|
|
315
315
|
};
|
package/build/handlers/ask.js
CHANGED
package/build/handlers/close.js
CHANGED
|
@@ -4,11 +4,12 @@ import { buildCloseDoneResponse, buildCloseDropResponse, } from "../workflow/res
|
|
|
4
4
|
import { resolveTermPreset } from "../config/term-presets.js";
|
|
5
5
|
import { handleToolError } from "./shared.js";
|
|
6
6
|
export function createCloseHandler() {
|
|
7
|
-
return async ({ petition, criteriaChecks, score_precise, score_minimal, score_isolated, score_idiomatic, score_cohesive, oath, cwd, }, extra) => {
|
|
7
|
+
return async ({ petition, criteriaChecks, constraintsChecks, score_precise, score_minimal, score_isolated, score_idiomatic, score_cohesive, oath, cwd, }, extra) => {
|
|
8
8
|
const workingDir = cwd || process.cwd();
|
|
9
9
|
const criteriaCheckParts = criteriaChecks;
|
|
10
|
+
const constraintsCheckParts = constraintsChecks ?? [];
|
|
10
11
|
try {
|
|
11
|
-
appendDebugLog(`tool close start petition=${petition} cwd=${workingDir} criteriaChecks=${criteriaCheckParts.length}`, {
|
|
12
|
+
appendDebugLog(`tool close start petition=${petition} cwd=${workingDir} criteriaChecks=${criteriaCheckParts.length} constraintsChecks=${constraintsCheckParts.length}`, {
|
|
12
13
|
cwd: workingDir,
|
|
13
14
|
section: "script",
|
|
14
15
|
});
|
|
@@ -16,6 +17,7 @@ export function createCloseHandler() {
|
|
|
16
17
|
cwd: workingDir,
|
|
17
18
|
petition,
|
|
18
19
|
criteriaChecks: criteriaCheckParts,
|
|
20
|
+
constraintsChecks: constraintsCheckParts,
|
|
19
21
|
score_precise,
|
|
20
22
|
score_minimal,
|
|
21
23
|
score_isolated,
|
|
@@ -29,12 +31,13 @@ export function createCloseHandler() {
|
|
|
29
31
|
throw new Error("Unexpected CLAIM outcome shape");
|
|
30
32
|
}
|
|
31
33
|
const finalOutcome = outcome;
|
|
32
|
-
appendDebugLog(`tool close CLAIM success branch=${finalOutcome.
|
|
34
|
+
appendDebugLog(`tool close CLAIM success branch=${finalOutcome.currentBranch} base=${finalOutcome.baseBranch}`, {
|
|
33
35
|
cwd: workingDir,
|
|
34
36
|
section: "script",
|
|
35
37
|
});
|
|
36
38
|
return buildCloseDoneResponse(finalOutcome, {
|
|
37
39
|
criteriaChecks: criteriaCheckParts,
|
|
40
|
+
constraintsChecks: constraintsCheckParts,
|
|
38
41
|
score_precise,
|
|
39
42
|
score_minimal,
|
|
40
43
|
score_isolated,
|
|
@@ -48,12 +51,13 @@ export function createCloseHandler() {
|
|
|
48
51
|
throw new Error("Unexpected FORFEIT outcome shape");
|
|
49
52
|
}
|
|
50
53
|
const finalOutcome = outcome;
|
|
51
|
-
appendDebugLog(`tool close FORFEIT success branch=${finalOutcome.
|
|
54
|
+
appendDebugLog(`tool close FORFEIT success branch=${finalOutcome.currentBranch} base=${finalOutcome.baseBranch}`, {
|
|
52
55
|
cwd: workingDir,
|
|
53
56
|
section: "script",
|
|
54
57
|
});
|
|
55
58
|
return buildCloseDropResponse(finalOutcome, {
|
|
56
59
|
criteriaChecks: criteriaCheckParts,
|
|
60
|
+
constraintsChecks: constraintsCheckParts,
|
|
57
61
|
score_precise,
|
|
58
62
|
score_minimal,
|
|
59
63
|
score_isolated,
|
|
@@ -73,9 +77,10 @@ export function createCloseHandler() {
|
|
|
73
77
|
inputEcho: [
|
|
74
78
|
`Petition: ${petition}`,
|
|
75
79
|
`Criteria checks (${criteriaCheckParts.length}): ${criteriaCheckParts.join("; ")}`,
|
|
80
|
+
`Constraints checks (${constraintsCheckParts.length}): ${constraintsCheckParts.join("; ")}`,
|
|
76
81
|
`Scores: precise=${score_precise} minimal=${score_minimal} isolated=${score_isolated} idiomatic=${score_idiomatic} cohesive=${score_cohesive}`,
|
|
77
82
|
...(oath ? [`Oath: ${oath}`] : []),
|
|
78
|
-
`
|
|
83
|
+
`Path: ${workingDir}`,
|
|
79
84
|
],
|
|
80
85
|
});
|
|
81
86
|
}
|
package/build/handlers/drive.js
CHANGED
package/build/handlers/help.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { buildHelpResponse } from "../workflow/response-builders.js";
|
|
2
2
|
export function createHelpHandler(preset) {
|
|
3
|
-
return async ({
|
|
3
|
+
return async ({}) => {
|
|
4
4
|
const helpContent = [
|
|
5
5
|
"# Keiyaku System Help",
|
|
6
6
|
"",
|
|
7
7
|
"## Core Files (.keiyaku/)",
|
|
8
8
|
"These files define the 'Law' of the project. **CRITICAL**: Use Markdown level 2 headers (## header).",
|
|
9
9
|
"",
|
|
10
|
-
"- **base-criteria.md**: Universal standards for task completion. Automatically inherited.",
|
|
11
10
|
"- **base-constraints.md**: Mandatory architectural boundaries and coding standards.",
|
|
12
11
|
"",
|
|
13
12
|
preset.usageGuide,
|
|
@@ -15,12 +14,9 @@ export function createHelpHandler(preset) {
|
|
|
15
14
|
"## Protocol Files",
|
|
16
15
|
"- **KEIYAKU.md**: The immutable mission definition for the current task.",
|
|
17
16
|
"- **KEIYAKU_TRACE.md**: The audit log of all rounds and reviews.",
|
|
18
|
-
"",
|
|
19
|
-
`Regarding: \"${question}\"`,
|
|
20
17
|
].join("\n");
|
|
21
18
|
return buildHelpResponse({
|
|
22
19
|
tool: preset.tools.help.name,
|
|
23
|
-
question,
|
|
24
20
|
text: helpContent,
|
|
25
21
|
});
|
|
26
22
|
};
|
package/build/handlers/start.js
CHANGED
|
@@ -1,35 +1,48 @@
|
|
|
1
1
|
import { appendDebugLog } from "../utils/debug-log.js";
|
|
2
|
-
import {
|
|
2
|
+
import { startKeiyaku } from "../workflow/start.js";
|
|
3
3
|
import { buildKeiyakuSuccessResponse, } from "../workflow/response-builders.js";
|
|
4
4
|
import { resolveTermPreset } from "../config/term-presets.js";
|
|
5
5
|
import { handleToolError } from "./shared.js";
|
|
6
|
+
function normalizeOptionalArg(value) {
|
|
7
|
+
if (value === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
return value.trim().length > 0 ? value : undefined;
|
|
10
|
+
}
|
|
6
11
|
export function createStartHandler() {
|
|
7
|
-
return async ({ title, goal, directive, context, constraints, criteria, name, cwd }, extra) => {
|
|
12
|
+
return async ({ from_file, title, goal, directive, context, constraints, criteria, name, cwd }, extra) => {
|
|
8
13
|
const workingDir = cwd || process.cwd();
|
|
14
|
+
const normalizedFromFile = normalizeOptionalArg(from_file);
|
|
9
15
|
try {
|
|
10
16
|
appendDebugLog(`tool start cwd=${workingDir}`, { cwd: workingDir, section: "script" });
|
|
11
|
-
const result =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
const result = normalizedFromFile
|
|
18
|
+
? await startKeiyaku({
|
|
19
|
+
cwd: workingDir,
|
|
20
|
+
from_file: normalizedFromFile,
|
|
21
|
+
title: normalizeOptionalArg(title),
|
|
22
|
+
goal: normalizeOptionalArg(goal),
|
|
23
|
+
directive: normalizeOptionalArg(directive),
|
|
24
|
+
context: normalizeOptionalArg(context),
|
|
25
|
+
constraints,
|
|
26
|
+
criteria,
|
|
27
|
+
name,
|
|
28
|
+
signal: extra.signal,
|
|
29
|
+
})
|
|
30
|
+
: await startKeiyaku({
|
|
31
|
+
cwd: workingDir,
|
|
32
|
+
title: title ?? "",
|
|
33
|
+
goal: goal ?? "",
|
|
34
|
+
directive: normalizeOptionalArg(directive),
|
|
35
|
+
context: context ?? "",
|
|
36
|
+
constraints,
|
|
37
|
+
criteria: criteria ?? [],
|
|
38
|
+
name,
|
|
39
|
+
signal: extra.signal,
|
|
40
|
+
});
|
|
41
|
+
appendDebugLog(`tool start success branch=${result.currentBranch} base=${result.baseBranch} round=${result.round}`, {
|
|
23
42
|
cwd: workingDir,
|
|
24
43
|
section: "script",
|
|
25
44
|
});
|
|
26
45
|
return buildKeiyakuSuccessResponse(result, {
|
|
27
|
-
title,
|
|
28
|
-
goal,
|
|
29
|
-
directive,
|
|
30
|
-
context,
|
|
31
|
-
constraints,
|
|
32
|
-
criteria,
|
|
33
46
|
name,
|
|
34
47
|
cwd: workingDir,
|
|
35
48
|
});
|
|
@@ -42,14 +55,15 @@ export function createStartHandler() {
|
|
|
42
55
|
cwd: workingDir,
|
|
43
56
|
logLabel: "tool start",
|
|
44
57
|
inputEcho: [
|
|
45
|
-
`
|
|
46
|
-
`
|
|
58
|
+
...(normalizedFromFile ? [`From file: ${normalizedFromFile}`] : []),
|
|
59
|
+
...(title ? [`Title: ${title}`] : []),
|
|
60
|
+
...(goal ? [`Goal: ${goal}`] : []),
|
|
47
61
|
...(directive ? [`Directive: ${directive}`] : []),
|
|
48
|
-
`Criteria (${criteria.length}): ${criteria.join("; ")}
|
|
62
|
+
...(criteria ? [`Criteria (${criteria.length}): ${criteria.join("; ")}`] : []),
|
|
49
63
|
...(context ? [`Context: ${context}`] : []),
|
|
50
|
-
...(constraints ? [`Constraints: ${constraints}`] : []),
|
|
64
|
+
...(constraints ? [`Constraints (${constraints.length}): ${constraints.join("; ")}`] : []),
|
|
51
65
|
...(name ? [`${preset.identity}: ${name}`] : []),
|
|
52
|
-
`
|
|
66
|
+
`Path: ${workingDir}`,
|
|
53
67
|
],
|
|
54
68
|
});
|
|
55
69
|
}
|
package/build/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
5
5
|
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
|
6
6
|
import { readFileSync } from "node:fs";
|
|
7
7
|
import { resolveOath } from "./workflow/contract.js";
|
|
8
|
-
import { askToolSchema, startToolSchema, driveToolSchema, closeToolSchema, helpToolSchema, } from "./types/
|
|
8
|
+
import { askToolSchema, startToolSchema, driveToolSchema, closeToolSchema, helpToolSchema, } from "./types/tooling.js";
|
|
9
9
|
import { listTermPresets, resolveTermPreset, getAvailableNamesForPreset } from "./config/term-presets.js";
|
|
10
10
|
import { renderPreset } from "./utils/text-utils.js";
|
|
11
11
|
import { applyArgumentDescriptions } from "./utils/schema-utils.js";
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export const startToolSchema = z.object({
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
from_file: z.string().optional(),
|
|
4
|
+
title: z.string().optional(),
|
|
5
|
+
goal: z.string().optional(),
|
|
5
6
|
directive: z.string().optional(),
|
|
6
|
-
context: z.string(),
|
|
7
|
-
constraints: z.string(),
|
|
8
|
-
criteria: z.array(z.string().trim().min(1)).min(1),
|
|
7
|
+
context: z.string().optional(),
|
|
8
|
+
constraints: z.array(z.string().trim().min(1)).min(1).optional(),
|
|
9
|
+
criteria: z.array(z.string().trim().min(1)).min(1).optional(),
|
|
9
10
|
name: z.string().optional(),
|
|
10
11
|
cwd: z.string().optional(),
|
|
11
12
|
});
|
|
@@ -24,14 +25,13 @@ export const askToolSchema = z.object({
|
|
|
24
25
|
export const closeToolSchema = z.object({
|
|
25
26
|
petition: z.enum(["CLAIM", "FORFEIT"]),
|
|
26
27
|
criteriaChecks: z.array(z.string().trim().min(1)).min(1),
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
constraintsChecks: z.array(z.string().trim().min(1)).min(1),
|
|
29
|
+
score_precise: z.number().min(0).max(10),
|
|
30
|
+
score_minimal: z.number().min(0).max(10),
|
|
31
|
+
score_isolated: z.number().min(0).max(10),
|
|
32
|
+
score_idiomatic: z.number().min(0).max(10),
|
|
33
|
+
score_cohesive: z.number().min(0).max(10),
|
|
32
34
|
oath: z.string().optional(),
|
|
33
35
|
cwd: z.string().optional(),
|
|
34
36
|
});
|
|
35
|
-
export const helpToolSchema = z.object({
|
|
36
|
-
question: z.string(),
|
|
37
|
-
});
|
|
37
|
+
export const helpToolSchema = z.object({});
|
package/build/utils/git-diff.js
CHANGED
|
@@ -183,8 +183,8 @@ export async function getIncrementalDiff(cwd) {
|
|
|
183
183
|
if (sections.length === 0)
|
|
184
184
|
return "No changes in last round.";
|
|
185
185
|
const filePreviews = [];
|
|
186
|
-
const MAX_TOTAL_CHARS =
|
|
187
|
-
const MAX_LINES_PER_FILE =
|
|
186
|
+
const MAX_TOTAL_CHARS = 15000;
|
|
187
|
+
const MAX_LINES_PER_FILE = 50;
|
|
188
188
|
let omittedFiles = 0;
|
|
189
189
|
for (let i = 0; i < sections.length; i += 1) {
|
|
190
190
|
const section = sections[i];
|