@astrosheep/keiyaku 0.1.9 → 0.1.11

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.
@@ -19,14 +19,14 @@ 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.**\n\n## Workflow\n`(ask) -> summon` -> [`drive` x N] -> `present`\n`ask` (stateless dispatch / utility at any point)',
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.**\n\n## Workflow\n`ask` (anytime) | `summon` -> [`drive` | `ask`]* -> `present`',
23
23
  nextHints: {
24
24
  start: [
25
25
  'Keiyaku signed. The Servant is bound to this branch until release.',
26
26
  'Review [Diff]: Confirm the scaffold aligns with the stated Architecture.',
27
27
  'Next: Issue your first ${drive}. One directive, one focus.',
28
28
  'If—and only if—the work already meets every criterion with absolute certainty, you may ${close}.',
29
- 'Otherwise, do not even think about present yet.',
29
+ 'Otherwise, do not even think about ${close} yet.',
30
30
  ],
31
31
  drive: [
32
32
  'Step complete. Review [Diff] for exactly what changed.',
@@ -42,12 +42,12 @@ export const DEFAULT_PRESET = {
42
42
  closeClaim: [
43
43
  'Contract fulfilled. Branch merged.',
44
44
  'You are back on base.',
45
- 'Next assignment: ${start} when ready.',
45
+ 'Next assignment: ${start} or ${ask} when ready.',
46
46
  ],
47
47
  closeDrop: [
48
48
  'Contract forfeited. Branch discarded. Work erased.',
49
49
  'No penalty beyond the lost effort. Clean slate.',
50
- 'Redefine with ${start}, or walk away.',
50
+ 'Redefine with ${start}, scout with ${ask}, or walk away.',
51
51
  ],
52
52
  },
53
53
  availableNames: DEFAULT_AVAILABLE_NAMES,
@@ -55,7 +55,7 @@ export const DEFAULT_PRESET = {
55
55
  start: {
56
56
  name: 'summon',
57
57
  title: 'Sign Keiyaku',
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: summon → [drive x N] → present',
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
60
  title: 'REQUIRED. A concise codename for this hunt.',
61
61
  goal: 'REQUIRED. The Kill Condition. State exactly what success looks like for the servant to achieve.',
@@ -63,29 +63,29 @@ export const DEFAULT_PRESET = {
63
63
  context: 'REQUIRED. Mission Intel. The complete knowledge base for the servant: current vs. expected behavior, relevant file paths, error logs, and any critical background info.',
64
64
  constraints: 'REQUIRED. Non-negotiable Rules. Architectural and stylistic boundaries the servant must obey.',
65
65
  criteria: 'REQUIRED. Acceptance Criteria. Verifiable checks to prove the servant has finished the job.',
66
- name: 'Optional ${IDENTITY} profile to execute this mission. Available: ${AVAILABLE_NAMES}.',
66
+ name: 'Optional ${identity} profile to execute this mission. Available: ${available_names}.',
67
67
  cwd: "Optional repository path. Defaults to the server's current working directory.",
68
68
  },
69
69
  },
70
70
  drive: {
71
71
  name: 'drive',
72
72
  title: 'Iterate',
73
- description: "Issue a Directive. Command the Servant to execute the next phase of work.\nWhether scaffolding, implementing, or refining, this is the primary engine of progress.\nMANDATORY: Validate the output (git diff) before proceeding. Drive until the Goal is fully realized.\n\nFlow: summon → [drive x N] → present",
73
+ description: "Issue a Directive. Command the Servant to execute the next phase of work.\nWhether scaffolding, implementing, or refining, this is the primary engine of progress.\nMANDATORY: Validate the output (git diff) before proceeding. Drive until the Goal is fully realized.\n\nFlow: ${start} → [${drive} x N] → ${close}",
74
74
  args: {
75
75
  directive: 'REQUIRED. The Next Order. Precise instructions for the servant. Can be a correction ("fix the leak") or a continuation ("now add the tests").',
76
76
  context: 'Optional. New Intel. New error logs or details discovered after the servant\'s last strike.',
77
- name: 'Optional ${IDENTITY} profile to process this turn. Available: ${AVAILABLE_NAMES}.',
77
+ name: 'Optional ${identity} profile to process this turn. Available: ${available_names}.',
78
78
  cwd: "Optional repository path. Defaults to the server's current working directory.",
79
79
  },
80
80
  },
81
81
  ask: {
82
82
  name: 'ask',
83
83
  title: 'Ask',
84
- description: 'Dispatch a temporary Servant for a stateless task.\nUse for quick reconnaissance, code generation, or isolated queries.\nNo contract signed. No branch created. Pure utility.',
84
+ 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
85
  args: {
86
86
  request: 'REQUIRED. The task, question, or mission to delegate to the servant.',
87
87
  context: 'REQUIRED. Relevant background or data the servant needs to execute the request.',
88
- name: 'Optional ${IDENTITY} profile to perform this task. Available: ${AVAILABLE_NAMES}.',
88
+ name: 'Optional ${identity} profile to perform this task. Available: ${available_names}.',
89
89
  cwd: "Optional repository path. Defaults to the server's current working directory.",
90
90
  },
91
91
  },
@@ -100,18 +100,18 @@ export const DEFAULT_PRESET = {
100
100
  'The Contract rejected. Coldly. Completely.\n' +
101
101
  'What happened next... no one speaks of it.\n\n' +
102
102
  'Do not call this to "finish." Call this when the work genuinely meets every criterion.\n' +
103
- 'If uncertain, return to ${DRIVE_TOOL_NAME}. Premature claims are not forgiven.\n\n' +
104
- 'You present. The Contract decides.\n\n' +
105
- 'Flow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}',
103
+ 'If uncertain, return to ${drive}. Premature claims are not forgiven.\n\n' +
104
+ 'You call ${close}. The Contract decides.\n\n' +
105
+ 'Flow: ${start} → [${drive} x N] → ${close}',
106
106
  args: {
107
- petition: 'REQUIRED. CLAIM declares fulfillment; FORFEIT concedes failure.\nREQUIRES AN ACTIVE KEIYAKU (started via ${START_TOOL_NAME}).\nIf any score wavers, do not claim—return to ${DRIVE_TOOL_NAME}.',
107
+ 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}.',
108
108
  criteriaChecks: 'REQUIRED. For CLAIM: evidence that each criterion is met. For FORFEIT: honest account of what remains unfinished.',
109
109
  score_precise: 'REQUIRED (0-5). Architectural placement. 5 = exact layer, exact boundary, zero misplacement.',
110
110
  score_minimal: 'REQUIRED (0-5). Economy of change. 5 = no avoidable lines, no speculative edits, no hidden bloat.',
111
111
  score_isolated: 'REQUIRED (0-5). Surgical containment. 5 = zero unrelated files, zero opportunistic cleanup, zero collateral.',
112
112
  score_idiomatic: 'REQUIRED (0-5). Native fluency. 5 = naming, structure, style indistinguishable from the codebase.',
113
113
  score_cohesive: 'REQUIRED (0-5). Single responsibility. 5 = each unit does one thing, boundaries intact.',
114
- oath: 'Required for CLAIM. Your binding word. The Contract holds you to it.\nVerbatim: ${OATH_TEXT}',
114
+ oath: 'Required for CLAIM. Your binding word. The Contract holds you to it.\nVerbatim: ${oath_text}',
115
115
  cwd: "Optional repository path. Defaults to the server's current working directory.",
116
116
  },
117
117
  },
@@ -129,7 +129,7 @@ export const POCKET_PRESET = {
129
129
  id: 'pocket',
130
130
  identity: 'Critter',
131
131
  verdict: DEFAULT_VERDICT_CONFIG,
132
- usageGuide: "## Pocket Battle Guide\n\n**grub** — Basic Fighter 🐛\n- Weak but free. Use for Tackle and String Shot (small tasks).\n- Don't expect it to defeat the Elite Four.\n\n**sparky** — Reliable Partner ⚡\n- Good for most battles. Thunderbolt gets the job done.\n- Has some personality, but still follows orders.\n\n**titan** — Legendary Power 🔮\n- Costly. Dangerous. Overpowered.\n- Use only when the gym leader is cheating.\n\n## Workflow\n`choose_you` -> [`command` x N] -> `capture`\n`dex` (optional, read-only analysis)",
132
+ usageGuide: "## Pocket Battle Guide\n\n**grub** — Basic Fighter 🐛\n- Weak but free. Use for Tackle and String Shot (small tasks).\n- Don't expect it to defeat the Elite Four.\n\n**sparky** — Reliable Partner ⚡\n- Good for most battles. Thunderbolt gets the job done.\n- Has some personality, but still follows orders.\n\n**titan** — Legendary Power 🔮\n- Costly. Dangerous. Overpowered.\n- Use only when the gym leader is cheating.\n\n## Workflow\n`dex` (scan/test) | `choose_you` -> [`command` | `dex`]* -> `capture`",
133
133
  nextHints: {
134
134
  start: [
135
135
  'Battle Started: The [Diff] section shows the opening moves.',
@@ -149,11 +149,11 @@ export const POCKET_PRESET = {
149
149
  ],
150
150
  closeClaim: [
151
151
  'Critter Captured! The entry is logged in the PC (base branch).',
152
- "Heal up. Use '${start}' to challenge the next opponent.",
152
+ "Heal up. Use '${start}' to challenge the next opponent, or '${ask}' to scan for another.",
153
153
  ],
154
154
  closeDrop: [
155
155
  'Ran Away Safely. The encounter is reset.',
156
- "Back to the map. Use '${start}' to find a wild Critter.",
156
+ "Back to the map. Use '${start}' to find a wild Critter, or '${ask}' to scan the tall grass.",
157
157
  ],
158
158
  },
159
159
  availableNames: ['grub', 'sparky', 'titan'],
@@ -161,7 +161,7 @@ export const POCKET_PRESET = {
161
161
  start: {
162
162
  name: 'choose_you',
163
163
  title: 'I Choose You!',
164
- 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: choose_you → [command x N] → capture',
164
+ 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}',
165
165
  args: {
166
166
  title: 'REQUIRED. Battle card title for this encounter.',
167
167
  goal: 'REQUIRED. Victory condition. Define exactly what winning this battle means.',
@@ -169,45 +169,45 @@ export const POCKET_PRESET = {
169
169
  context: 'REQUIRED. Battle background: key code paths, symptoms, logs, and repro clues.',
170
170
  constraints: 'REQUIRED. Battle rules that cannot be broken while fighting.',
171
171
  criteria: 'REQUIRED. Gym badges for completion: concrete checks proving victory.',
172
- name: 'Optional ${IDENTITY} to send into battle. Available: ${AVAILABLE_NAMES}.',
172
+ name: 'Optional ${identity} to send into battle. Available: ${available_names}.',
173
173
  cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
174
174
  },
175
175
  },
176
176
  drive: {
177
177
  name: 'command',
178
178
  title: 'Issue Command',
179
- description: 'Execute the next Strategic Move. Order the Critter to advance the battle state.\nWhether Attacking (coding) or Defending (testing), every command counts as a Turn.\nMANDATORY: Check the Battle Log (git diff) before the next move. Command until Victory is certain.\n\nFlow: choose_you → [command x N] → capture',
179
+ description: 'Execute the next Strategic Move. Order the Critter to advance the battle state.\nWhether Attacking (coding) or Defending (testing), every command counts as a Turn.\nMANDATORY: Check the Battle Log (git diff) before the next move. Command until Victory is certain.\n\nFlow: ${start} → [${drive} x N] → ${close}',
180
180
  args: {
181
181
  directive: 'REQUIRED. The specific move or tactic to execute next.',
182
182
  context: 'Optional new battle intel discovered after the previous turn.',
183
- name: 'Optional ${IDENTITY} to execute this turn. Available: ${AVAILABLE_NAMES}.',
183
+ name: 'Optional ${identity} to execute this turn. Available: ${available_names}.',
184
184
  cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
185
185
  },
186
186
  },
187
187
  ask: {
188
188
  name: 'dex',
189
189
  title: 'Dex',
190
- description: 'Scan the Environment. A stateless look-up for intel.\nUse for analyzing the codebase or checking type advantages (docs).\nNo PP cost. No turn used. Pure data.',
190
+ 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.',
191
191
  args: {
192
- request: 'REQUIRED. What should the Dex analyze, compare, or explain.',
193
- context: 'REQUIRED. Context entries so the analysis targets the right ecosystem.',
194
- name: 'Optional ${IDENTITY} doing the scan. Available: ${AVAILABLE_NAMES}.',
192
+ request: 'REQUIRED. What should the Dex analyze, compare, or execute.',
193
+ context: 'REQUIRED. Context entries so the action targets the right ecosystem.',
194
+ name: 'Optional ${identity} doing the work. Available: ${available_names}.',
195
195
  cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
196
196
  },
197
197
  },
198
198
  close: {
199
199
  name: 'capture',
200
200
  title: 'End Battle',
201
- description: 'Attempt Capture. Present the weakened target for League Inspection.\nWARNING: The League (System) checks every Badge. If you attempt to `CLAIM` with fainted code, Disqualification (FORFEIT) is immediate.\nOnly throw the Ball when the target is 100% ready.\n\nFlow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}',
201
+ description: 'Attempt Capture. Present the weakened target for League Inspection.\nWARNING: The League (System) checks every Badge. If you attempt to `CLAIM` with fainted code, Disqualification (FORFEIT) is immediate.\nOnly throw the Ball when the target is 100% ready.\n\nFlow: ${start} → [${drive} x N] → ${close}',
202
202
  args: {
203
- petition: 'REQUIRED. CLAIM seeks Badge; FORFEIT forfeits the match.\nREQUIRES AN ACTIVE BATTLE (started via ${START_TOOL_NAME}).\nIf stats are low, continue with ${DRIVE_TOOL_NAME}.',
203
+ petition: 'REQUIRED. CLAIM seeks Badge; FORFEIT forfeits the match.\nREQUIRES AN ACTIVE BATTLE (started via ${start}).\nIf stats are low, continue with ${drive}.',
204
204
  criteriaChecks: 'REQUIRED. Badge-by-badge proof for CLAIM, or reason for Forfeit.',
205
205
  score_precise: 'REQUIRED score (0-5). 5 means a Critical Hit: exact layer, exact target, zero meaningful misplacement.',
206
206
  score_minimal: 'REQUIRED score (0-5). 5 means Max Efficiency: no wasted PP, no extra motion.',
207
207
  score_isolated: 'REQUIRED score (0-5). 5 means 1v1 Focus: zero side-quests, zero unrelated damage.',
208
208
  score_idiomatic: "REQUIRED score (0-5). 5 means STAB (Same Type Attack Bonus): perfectly matches the codebase style.",
209
209
  score_cohesive: 'REQUIRED score (0-5). 5 means Team Synergy: action serves one purpose with clean boundaries.',
210
- oath: "Trainer's Honor Code. Required for CLAIM. Verbatim: ${OATH_TEXT}",
210
+ oath: "Trainer's Honor Code. Required for CLAIM. Verbatim: ${oath_text}",
211
211
  cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
212
212
  },
213
213
  },
@@ -225,7 +225,7 @@ export const MISCHIEF_PRESET = {
225
225
  id: 'mischief',
226
226
  identity: 'minion',
227
227
  verdict: DEFAULT_VERDICT_CONFIG,
228
- usageGuide: '## Minion Manual\n\n**imp** — Disposable Grunt 😈\n- Expendable. Send it into the trap first.\n\n**minion** — Standard Henchman 👹\n- Can carry out complex evil plans. mostly.\n\n**mastermind** — The Boss ??? 🧠\n- Wait, why are you commanding the boss?\n- Extremely expensive consulting fee.\n\n## Workflow\n`oi` -> [`neh` x N] -> `yoshi`\n`eeto` (optional, read-only contemplation)',
228
+ usageGuide: '## Minion Manual\n\n**imp** — Disposable Grunt 😈\n- Expendable. Send it into the trap first.\n\n**minion** — Standard Henchman 👹\n- Can carry out complex evil plans. mostly.\n\n**mastermind** — The Boss ??? 🧠\n- Wait, why are you commanding the boss?\n- Extremely expensive consulting fee.\n\n## Workflow\n`nn` (Nn——! disposable/experiment) | `oi` -> [`neh` | `nn`]* -> `dayaa` (Dayaa!)',
229
229
  nextHints: {
230
230
  start: [
231
231
  "Inspect the Minion's Work: The [Diff] section shows the first step of the plan.",
@@ -241,15 +241,15 @@ export const MISCHIEF_PRESET = {
241
241
  ],
242
242
  ask: [
243
243
  "Intel Stolen: Use '${start}' to launch the scheme, or '${drive}' to exploit this weakness.",
244
- "Still Puzzled? Send the spy ('${ask}') out again.",
244
+ "Still Puzzled? Send the disposable ('${ask}') out again.",
245
245
  ],
246
246
  closeClaim: [
247
247
  'Scheme Successful. The Lair is merged. The Minion is fed.',
248
- "Plotting something new? Use '${start}' for the next conquest.",
248
+ "Plotting something new? Use '${start}' for the next conquest, or '${ask}' to scout for vulnerabilities.",
249
249
  ],
250
250
  closeDrop: [
251
251
  'Plan Aborted. Evidence destroyed. The Minion was disposed of.',
252
- "Back to the drawing board. Use '${start}' to hatch a new plan.",
252
+ "Back to the drawing board. Use '${start}' to hatch a new plan, or '${ask}' to spy on enemies.",
253
253
  ],
254
254
  },
255
255
  availableNames: ['imp', 'minion', 'mastermind'],
@@ -257,7 +257,7 @@ export const MISCHIEF_PRESET = {
257
257
  start: {
258
258
  name: 'oi',
259
259
  title: 'Oi!',
260
- 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: oi → [neh x N] → yoshi",
260
+ 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}",
261
261
  args: {
262
262
  title: 'REQUIRED. Operation codename for your grand scheme.',
263
263
  goal: 'REQUIRED. The conquest objective. Define the exact end-state your minion must deliver.',
@@ -265,45 +265,45 @@ export const MISCHIEF_PRESET = {
265
265
  context: 'REQUIRED. Briefing dossier: relevant file paths, current failures, logs, and repro clues.',
266
266
  constraints: 'REQUIRED. Absolute decrees. Architectural and stylistic limits your minion must obey.',
267
267
  criteria: 'REQUIRED. Triumph conditions that can be verified without debate.',
268
- name: 'Optional ${IDENTITY} to command this operation. Available: ${AVAILABLE_NAMES}.',
268
+ name: 'Optional ${identity} to command this operation. Available: ${available_names}.',
269
269
  cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
270
270
  },
271
271
  },
272
272
  drive: {
273
273
  name: 'neh',
274
274
  title: 'Neh...',
275
- description: 'Crack the Whip. Advance the Plot. Execute the next stage of the Plan.\nDon\'t just fix mistakes; force the scheme forward. The Minion obeys your every word.\nMANDATORY: Inspect the work (git diff) before the next order. Drive them until the World is yours.\n\nFlow: oi → [neh x N] → yoshi',
275
+ description: 'Crack the Whip. Advance the Plot. Execute the next stage of the Plan.\nDon\'t just fix mistakes; force the scheme forward. The Minion obeys your every word.\nMANDATORY: Inspect the work (git diff) before the next order. Drive them until the World is yours.\n\nFlow: ${start} → [${drive} x N] → ${close}',
276
276
  args: {
277
277
  directive: 'REQUIRED. Precise order for the next phase of the scheme.',
278
278
  context: 'Optional newly uncovered secrets or updated briefing.',
279
- name: 'Optional ${IDENTITY} to execute this phase. Available: ${AVAILABLE_NAMES}.',
279
+ name: 'Optional ${identity} to execute this phase. Available: ${available_names}.',
280
280
  cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
281
281
  },
282
282
  },
283
283
  ask: {
284
- name: 'eeto',
285
- title: 'Eeto...',
286
- description: 'Send a Spy. A stateless reconnaissance mission.\nUse this to scout the enemy territory (codebase) or steal documents (docs).\nThe spy reports back and vanishes. No traces left.',
284
+ name: 'nn',
285
+ title: 'Nn——!',
286
+ 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.',
287
287
  args: {
288
- request: 'REQUIRED. The intel to gather or the strategy to formulate.',
289
- context: 'REQUIRED. World-state details needed for a sharp analysis.',
290
- name: 'Optional ${IDENTITY} to contemplate this puzzle. Available: ${AVAILABLE_NAMES}.',
288
+ request: 'REQUIRED. The intel to gather or the dirty work to execute.',
289
+ context: 'REQUIRED. World-state details needed for a sharp strike.',
290
+ name: 'Optional ${identity} to handle this business. Available: ${available_names}.',
291
291
  cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
292
292
  },
293
293
  },
294
294
  close: {
295
- name: 'yoshi',
296
- title: 'Yoshi!',
297
- description: 'The Final Reveal. Present your Masterpiece to the Dark Council (System).\nWARNING: The Council destroys failure. If you `CLAIM` with weak plans, the Self-Destruct (FORFEIT) will trigger.\nOnly reveal the Doomsday Device when it is fully operational.\n\nFlow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}',
295
+ name: 'dayaa',
296
+ title: 'Dayaa!',
297
+ description: 'The Final Reveal. Present your Masterpiece to the Dark Council (System).\nWARNING: The Council destroys failure. If you `CLAIM` with weak plans, you will be eaten (FORFEIT).\nOnly throw the switch when the Doomsday Device is 100% operational.\n\nFlow: ${start} → [${drive} x N] → ${close}',
298
298
  args: {
299
- petition: 'REQUIRED. CLAIM demands rule; FORFEIT admits defeat.\nREQUIRES AN ACTIVE SCHEME (started via ${START_TOOL_NAME}).\nIf the plan is weak, improve it with ${DRIVE_TOOL_NAME}.',
299
+ petition: 'REQUIRED. CLAIM demands rule; FORFEIT admits defeat.\nREQUIRES AN ACTIVE SCHEME (started via ${start}).\nIf the plan is weak, improve it with ${drive}.',
300
300
  criteriaChecks: 'REQUIRED. Proof of conquest for CLAIM, or reason for self-destruct.',
301
- score_precise: 'REQUIRED score (0-5). 5 means Laser Focus: exact cut, zero meaningful drift.',
302
- score_minimal: 'REQUIRED score (0-5). 5 means Ruthless Efficiency: no wasted movement, no excess.',
303
- score_isolated: 'REQUIRED score (0-5). 5 means Perfect Containment: no collateral damage.',
304
- score_idiomatic: 'REQUIRED score (0-5). 5 means Native Tongue: matches the lair\'s dialect perfectly.',
305
- score_cohesive: 'REQUIRED score (0-5). 5 means Absolute Loyalty: each unit serves the master plan.',
306
- oath: "Mastermind's Vow. Required for CLAIM. Verbatim: ${OATH_TEXT}",
301
+ score_precise: 'REQUIRED (0-5). Architectural placement. 5 = exact layer, exact boundary, zero misplacement.',
302
+ score_minimal: 'REQUIRED (0-5). Economy of change. 5 = no avoidable lines, no speculative edits, no hidden bloat.',
303
+ score_isolated: 'REQUIRED (0-5). Surgical containment. 5 = zero unrelated files, zero opportunistic cleanup, zero collateral.',
304
+ score_idiomatic: 'REQUIRED (0-5). Native fluency. 5 = naming, structure, style indistinguishable from the codebase.',
305
+ score_cohesive: 'REQUIRED (0-5). Single responsibility. 5 = each unit does one thing, boundaries intact.',
306
+ oath: "Mastermind's Vow. Required for CLAIM. Verbatim: ${oath_text}",
307
307
  cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
308
308
  },
309
309
  },
package/build/index.js CHANGED
@@ -27,13 +27,14 @@ function registerTools(server) {
27
27
  const availableNames = getAvailableNamesForPreset(preset).join(", ");
28
28
  const currentOath = resolveOath();
29
29
  const renderedPreset = renderPreset(preset, {
30
- IDENTITY: preset.identity,
31
- PRESET_IDENTITIES: presetIdentities,
32
- AVAILABLE_NAMES: availableNames,
33
- START_TOOL_NAME: preset.tools.start.name,
34
- DRIVE_TOOL_NAME: preset.tools.drive.name,
35
- CLOSE_TOOL_NAME: preset.tools.close.name,
36
- OATH_TEXT: `'${currentOath}'`,
30
+ start: preset.tools.start.name,
31
+ drive: preset.tools.drive.name,
32
+ ask: preset.tools.ask.name,
33
+ close: preset.tools.close.name,
34
+ identity: preset.identity,
35
+ preset_identities: presetIdentities,
36
+ available_names: availableNames,
37
+ oath_text: `'${currentOath}'`,
37
38
  });
38
39
  const startPreset = renderedPreset.tools.start;
39
40
  const drivePreset = renderedPreset.tools.drive;
@@ -160,20 +160,42 @@ export async function commit(cwd, message) {
160
160
  throw wrapGitError(args.join(" "), err, cwd);
161
161
  }
162
162
  }
163
- export async function merge(cwd, branchName, message) {
163
+ export async function merge(cwd, branchName, message, options = {}) {
164
164
  const git = createGit(cwd);
165
+ const { squash = true } = options;
165
166
  const skipVerify = readBooleanEnv("KEIYAKU_GIT_NO_VERIFY", true);
166
167
  const noGpgSign = readBooleanEnv("KEIYAKU_GIT_NO_GPG_SIGN", true);
167
- const options = [branchName, "--no-ff", "--no-edit", "-m", message];
168
+ if (!squash) {
169
+ const mergeOptions = [branchName, "--no-ff", "--no-edit", "-m", message];
170
+ if (skipVerify)
171
+ mergeOptions.push("--no-verify");
172
+ if (noGpgSign)
173
+ mergeOptions.push("--no-gpg-sign");
174
+ try {
175
+ await git.merge(mergeOptions);
176
+ return;
177
+ }
178
+ catch (err) {
179
+ throw wrapGitError(`merge ${mergeOptions.join(" ")}`, err, cwd);
180
+ }
181
+ }
182
+ const squashArgs = ["merge", "--squash", branchName];
183
+ try {
184
+ await git.raw(squashArgs);
185
+ }
186
+ catch (err) {
187
+ throw wrapGitError(squashArgs.join(" "), err, cwd);
188
+ }
189
+ const commitArgs = ["commit", "--allow-empty", "-m", message];
168
190
  if (skipVerify)
169
- options.push("--no-verify");
191
+ commitArgs.push("--no-verify");
170
192
  if (noGpgSign)
171
- options.push("--no-gpg-sign");
193
+ commitArgs.push("--no-gpg-sign");
172
194
  try {
173
- await git.merge(options);
195
+ await git.raw(commitArgs);
174
196
  }
175
197
  catch (err) {
176
- throw wrapGitError(`merge ${options.join(" ")}`, err, cwd);
198
+ throw wrapGitError(commitArgs.join(" "), err, cwd);
177
199
  }
178
200
  }
179
201
  export async function getUnmergedFiles(cwd) {
@@ -27,11 +27,8 @@ export async function appendReview(cwd, round, reason) {
27
27
  const block = ["", `## Review ${round}`, "### Reason", reason, ""].join("\n");
28
28
  await fs.appendFile(tracePath, block, "utf-8");
29
29
  }
30
- function normalizeTraceText(text) {
31
- return text.replace(/\s+/g, " ").trim().slice(0, 500) || "Unknown error.";
32
- }
33
30
  export async function appendRoundReport(cwd, report) {
34
- const summaryText = normalizeTraceText(report.summary);
31
+ const summaryText = report.summary.trim() || "Unknown error.";
35
32
  const fileLines = report.filesModified.length > 0 ? report.filesModified.map((file) => `- ${file}`) : ["(none)"];
36
33
  const tracePath = path.join(cwd, TRACE_FILE);
37
34
  const lines = [
@@ -49,7 +46,7 @@ export async function appendRoundReport(cwd, report) {
49
46
  ...fileLines,
50
47
  ];
51
48
  if (report.errorMessage) {
52
- lines.push("### Error", normalizeTraceText(report.errorMessage));
49
+ lines.push("### Error", report.errorMessage);
53
50
  }
54
51
  lines.push("");
55
52
  const block = lines.join("\n");
@@ -71,7 +68,7 @@ export async function appendRoundSystemNote(cwd, round, note) {
71
68
  "### Files Modified",
72
69
  "(none)",
73
70
  "### Error",
74
- normalizeTraceText(note),
71
+ note,
75
72
  "",
76
73
  ].join("\n");
77
74
  await fs.appendFile(tracePath, block, "utf-8");
@@ -146,7 +146,7 @@ function buildMergeMessage(title, keiyakuContent, reportContent) {
146
146
  return `keiyaku(${title}): done\n\n---\n${keiyakuContent}\n---\n${reportContent}\n---\n`;
147
147
  }
148
148
  function summarizeForTrace(message) {
149
- return message.replace(/\s+/g, " ").trim().slice(0, 500) || "Unknown error.";
149
+ return message.length > 0 ? message : "Unknown error.";
150
150
  }
151
151
  function isAbortError(error) {
152
152
  return error instanceof Error && error.name === "AbortError";
@@ -226,6 +226,18 @@ function formatSubagentFailureMessage(failure) {
226
226
  `message=${summarizeForTrace(failure.message)}`,
227
227
  ].join("; ");
228
228
  }
229
+ function filterInternalSections(text) {
230
+ const sections = parseMarkdownHeaders(text);
231
+ if (sections.length === 0)
232
+ return text.trim();
233
+ return sections
234
+ .filter((section) => {
235
+ const firstLine = section.split(/\r?\n/, 1)[0] ?? "";
236
+ return !/^#{2,3}\s+(?:criteria|constraints)\s+check\s*$/i.test(firstLine.trim());
237
+ })
238
+ .join("\n\n")
239
+ .trim();
240
+ }
229
241
  async function appendRoundResult(cwd, round, summary, failureMessage) {
230
242
  const dirtyPaths = await git.getDirtyPaths(cwd);
231
243
  const filesModified = collectRoundFiles(dirtyPaths);
@@ -269,7 +281,7 @@ async function runAndRecordRound(cwd, titleToken, round, prompt, options) {
269
281
  console.error(failureStderrLog);
270
282
  appendDebugBlock("subagent failure stderr", failure.stderrSnippet, { cwd, section: "codex-stderr" });
271
283
  }
272
- failureMessage = summarizeForTrace(failure.message);
284
+ failureMessage = failure.message.trim() || "Unknown error.";
273
285
  summary =
274
286
  options.failureMode === "round_specific"
275
287
  ? `Round ${round} completed with subagent execution failure recorded in trace.`
@@ -354,11 +366,12 @@ export async function handleStart(input) {
354
366
  await git.addFiles(cwd, [KEIYAKU_FILE, TRACE_FILE]);
355
367
  await git.commit(cwd, `keiyaku(${branchToken}): open`);
356
368
  const prompt = buildStartPrompt(finalTitle, finalGoal, directive);
357
- const { summary } = await runAndRecordRound(cwd, branchToken, 1, prompt, {
369
+ const { summary: rawSummary } = await runAndRecordRound(cwd, branchToken, 1, prompt, {
358
370
  signal,
359
371
  name,
360
372
  failureMode: "standard",
361
373
  });
374
+ const summary = filterInternalSections(rawSummary);
362
375
  const trace = await fs.readFile(path.join(cwd, TRACE_FILE), "utf-8");
363
376
  const diff = await git.getIncrementalDiff(cwd);
364
377
  return {
@@ -399,7 +412,21 @@ export async function handleAsk(input) {
399
412
  const prompt = buildAskPrompt(request, context);
400
413
  // TODO: enforce read-only access and persist summary to .keiyaku/notes/.
401
414
  const summary = await runSubagent(selectSubagent(name), prompt, cwd, 0, signal);
402
- return { summary };
415
+ let branch;
416
+ let diffStats;
417
+ try {
418
+ branch = await git.getCurrentBranch(cwd);
419
+ if (branch && branch.startsWith("keiyaku/")) {
420
+ const baseBranch = await git.getKeiyakuBase(cwd, branch);
421
+ if (baseBranch) {
422
+ diffStats = await git.getDiffPreviewText(cwd, baseBranch);
423
+ }
424
+ }
425
+ }
426
+ catch {
427
+ branch = undefined;
428
+ }
429
+ return { summary, branch, diffStats };
403
430
  }
404
431
  export async function handleClose(input) {
405
432
  const { cwd } = input;
@@ -503,7 +530,7 @@ export async function handleClose(input) {
503
530
  const invokeFinalizeLog = `[CLAIM] Deleting merged branch '${keiyakuBranch}' and clearing metadata`;
504
531
  console.error(invokeFinalizeLog);
505
532
  appendDebugLog(invokeFinalizeLog, { cwd, section: "script" });
506
- await git.deleteBranch(cwd, keiyakuBranch);
533
+ await git.deleteBranch(cwd, keiyakuBranch, true);
507
534
  await git.clearKeiyakuBase(cwd, keiyakuBranch);
508
535
  const round = computeTraceState(traceContent).maxRound;
509
536
  return {
@@ -549,7 +576,7 @@ export async function handleDrive(input) {
549
576
  await appendReview(cwd, plan.targetRound, plan.reviewReason);
550
577
  await git.addFiles(cwd, TRACE_FILE);
551
578
  await git.commit(cwd, `keiyaku(${title}): iterate round ${plan.targetRound}`);
552
- const { summary } = await runAndRecordRound(cwd, title, plan.targetRound, plan.prompt, {
579
+ const { summary: rawSummary } = await runAndRecordRound(cwd, title, plan.targetRound, plan.prompt, {
553
580
  signal,
554
581
  name,
555
582
  failureMode: "round_specific",
@@ -559,6 +586,7 @@ export async function handleDrive(input) {
559
586
  await git.commit(cwd, `keiyaku(${title}): round ${plan.targetRound} cancelled`);
560
587
  },
561
588
  });
589
+ const summary = filterInternalSections(rawSummary);
562
590
  const trace = await fs.readFile(path.join(cwd, TRACE_FILE), "utf-8");
563
591
  const diff = await git.getIncrementalDiff(cwd);
564
592
  return {
@@ -16,17 +16,31 @@ Round: 1 (initial implementation).
16
16
  - Do not violate constraints.
17
17
  - Meet all criteria.
18
18
  - Match existing structure.
19
- 6. End with 4 sections:
20
- - Summary: outcome.
21
- - Changes: files, logic delta, evidence.
22
- - Aesthetics Gap
23
- Self-critique.
24
- precise — right place, layer, abstraction
25
- minimal smallest diff that solves it
26
- isolated no unrelated changes riding along
27
- idiomatic — fits the codebase's voice
28
- cohesive each unit does one thing
29
- - Blindspots: untested paths/assumptions.
19
+ 6. End with exactly these 6 sections using this strict Markdown template:
20
+
21
+ ## Outcome
22
+ [One sentence summarizing the outcome]
23
+
24
+ ## Changes
25
+ - [File path]: [Brief description of change]
26
+ - [Logic delta]: [Key architectural or logic shift]
27
+
28
+ ## Aesthetics Gap
29
+ Self-critique.
30
+ - precise: [score/reasoning]
31
+ - minimal: [score/reasoning]
32
+ - isolated: [score/reasoning]
33
+ - idiomatic: [score/reasoning]
34
+ - cohesive: [score/reasoning]
35
+
36
+ ## Blindspots
37
+ - [Unintentional side effects or untested paths]
38
+
39
+ ## Criteria Check
40
+ - [List each criterion and its status: MET/PARTIAL/TODO]
41
+
42
+ ## Constraints Check
43
+ - [List each constraint and confirm compliance: COMPLIANT/VIOLATED]
30
44
 
31
45
  No git commands. Do not edit KEIYAKU_TRACE.md.`;
32
46
  }
@@ -43,17 +57,31 @@ ${reason}
43
57
  4. Fix review reason first.
44
58
  5. No drift from constraints/criteria.
45
59
  6. Keep diff small.
46
- 7. End with 4 sections:
47
- - Summary: outcome.
48
- - Changes: files, logic delta, evidence.
49
- - Aesthetics Gap
50
- Self-critique.
51
- precise — right place, layer, abstraction
52
- minimal smallest diff that solves it
53
- isolated no unrelated changes riding along
54
- idiomatic — fits the codebase's voice
55
- cohesive each unit does one thing
56
- - Blindspots: untested paths/assumptions.
60
+ 7. End with exactly these 6 sections using this strict Markdown template:
61
+
62
+ ## Outcome
63
+ [One sentence summarizing the outcome]
64
+
65
+ ## Changes
66
+ - [File path]: [Brief description of change]
67
+ - [Logic delta]: [Key architectural or logic shift]
68
+
69
+ ## Aesthetics Gap
70
+ Self-critique.
71
+ - precise: [score/reasoning]
72
+ - minimal: [score/reasoning]
73
+ - isolated: [score/reasoning]
74
+ - idiomatic: [score/reasoning]
75
+ - cohesive: [score/reasoning]
76
+
77
+ ## Blindspots
78
+ - [Unintentional side effects or untested paths]
79
+
80
+ ## Criteria Check
81
+ - [List each criterion and its status: MET/PARTIAL/TODO]
82
+
83
+ ## Constraints Check
84
+ - [List each constraint and confirm compliance: COMPLIANT/VIOLATED]
57
85
 
58
86
  No git commands. Do not edit KEIYAKU_TRACE.md.`;
59
87
  }
@@ -66,5 +94,5 @@ ${request}
66
94
  Context:
67
95
  ${context}
68
96
 
69
- Provide a clear reasoning summary or code generation in the response.`;
97
+ Provide a clear final response.`;
70
98
  }
@@ -31,8 +31,38 @@ function buildSection(title, content) {
31
31
  return "";
32
32
  return `[${title}]\n${validLines.join("\n")}`;
33
33
  }
34
+ function supportsHeaderColor() {
35
+ if (process.env.NO_COLOR !== undefined)
36
+ return false;
37
+ if (process.env.FORCE_COLOR === "0")
38
+ return false;
39
+ if (process.env.FORCE_COLOR !== undefined)
40
+ return true;
41
+ if (!process.stdout?.isTTY)
42
+ return false;
43
+ const term = process.env.TERM?.toLowerCase();
44
+ return term !== undefined && term !== "dumb";
45
+ }
46
+ function colorizeHeaderTitle(titleLabel) {
47
+ return `\x1b[1;38;5;15;48;5;23m${titleLabel}\x1b[0m`;
48
+ }
49
+ function colorizeErrorStatus(status) {
50
+ if (!supportsHeaderColor())
51
+ return status;
52
+ return `\x1b[1;38;5;15;48;5;196m${status}\x1b[0m`;
53
+ }
34
54
  function formatHeader(title) {
35
- return `\n── ${title.toUpperCase()} ${"─".repeat(Math.max(0, 50 - title.length))}`;
55
+ const headerWidth = 60;
56
+ const normalizedTitle = title.trim().toUpperCase();
57
+ const titleLabel = ` ${normalizedTitle} `;
58
+ if (!supportsHeaderColor()) {
59
+ const plainPrefix = `──${titleLabel}`;
60
+ const divider = "─".repeat(Math.max(0, headerWidth - plainPrefix.length));
61
+ return divider.length > 0 ? `\n${plainPrefix}${divider}` : `\n${plainPrefix}`;
62
+ }
63
+ const coloredTitle = colorizeHeaderTitle(titleLabel);
64
+ const divider = "─".repeat(Math.max(0, headerWidth - titleLabel.length - 1));
65
+ return divider.length > 0 ? `\n${coloredTitle} ${divider}` : `\n${coloredTitle}`;
36
66
  }
37
67
  function assembleResponse(status, summary, sections, nextHints, infoLines = []) {
38
68
  // Main Status Block
@@ -60,7 +90,7 @@ ${summary}
60
90
  : "";
61
91
  // Next Steps Block
62
92
  const nextBlock = nextHints.length > 0
63
- ? `${formatHeader("Next Steps")}\n${nextHints.map(h => `› ${h}`).join("\n")}`
93
+ ? `${formatHeader("Hints")}\n${nextHints.map(h => `› ${h}`).join("\n")}`
64
94
  : "";
65
95
  return [
66
96
  statusBlock,
@@ -126,7 +156,7 @@ export function buildKeiyakuSuccessResponse(result, input) {
126
156
  ...formatMaybe("Current Branch", result.branch, 200),
127
157
  ...formatMaybe("Base Branch", result.baseBranch, 200),
128
158
  ];
129
- const text = assembleResponse(`Started (Round ${result.round})`, `Created branch '${result.branch}' (base: '${result.baseBranch}'). ${result.summary}`, [inputSection, diffSection], nextHints, infoLines);
159
+ const text = assembleResponse(`Started (Round ${result.round})`, `Created branch '${result.branch}' (base: '${result.baseBranch}').\n\n${result.summary}`, [inputSection, diffSection], nextHints, infoLines);
130
160
  return {
131
161
  content: [{ type: "text", text }],
132
162
  structuredContent: buildSuccessStructuredContent(getStartToolName(), {
@@ -153,7 +183,7 @@ export function buildDriveResponse(result, input) {
153
183
  ...formatMaybe("Current Branch", result.branch, 200),
154
184
  ...formatMaybe("Base Branch", result.baseBranch, 200),
155
185
  ];
156
- const text = assembleResponse(`Driven (Round ${result.round})`, `Updated branch '${result.branch}'. ${result.summary}`, [inputSection, diffSection], nextHints, infoLines);
186
+ const text = assembleResponse(`Driven (Round ${result.round})`, `Updated branch '${result.branch}'.\n\n${result.summary}`, [inputSection, diffSection], nextHints, infoLines);
157
187
  return {
158
188
  content: [{ type: "text", text }],
159
189
  structuredContent: buildSuccessStructuredContent(getDriveToolName(), {
@@ -173,7 +203,11 @@ export function buildAskResponse(result, input) {
173
203
  ];
174
204
  const nextHints = renderHints(resolveTermPreset().nextHints.ask);
175
205
  const inputSection = buildSection("Input", inputEcho);
176
- const infoLines = [...formatMaybe("CWD", input.cwd, 300)];
206
+ const infoLines = [
207
+ ...formatMaybe("CWD", input.cwd, 300),
208
+ ...formatMaybe("Current Branch", result.branch, 200),
209
+ ...formatMaybe("Diff Stats", result.diffStats, 1000),
210
+ ];
177
211
  const text = assembleResponse("Answered", result.summary, [inputSection], nextHints, infoLines);
178
212
  return {
179
213
  content: [{ type: "text", text }],
@@ -244,7 +278,7 @@ export function buildCloseDropResponse(result, input) {
244
278
  export function buildToolErrorResponse(input) {
245
279
  const inputEcho = (input.inputEcho ?? []).map((line) => truncateForDisplay(line, 800));
246
280
  const shouldRaiseProtocolError = input.errorType === "runtime_error";
247
- const text = assembleResponse("Failed", input.message, [buildSection("Input Context", inputEcho)], [input.hint]);
281
+ const text = assembleResponse(colorizeErrorStatus("!! FAILED !!"), input.message, [buildSection("Input Context", inputEcho)], [input.hint]);
248
282
  const response = {
249
283
  content: [{ type: "text", text }],
250
284
  structuredContent: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "MCP server for running iterative keiyaku workflows with Codex subagents.",
5
5
  "license": "MIT",
6
6
  "type": "module",