@astrosheep/keiyaku 0.1.2 → 0.1.4

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.
@@ -1,7 +1,11 @@
1
- const INTERNAL_PROFILE_NAMES = ["agent-a", "agent-b", "agent-c"];
2
- export const DEFAULT_AVAILABLE_NAMES = ["servant-tier-b", "servant-tier-a", "servant-tier-s"];
3
- const LEGACY_AVAILABLE_NAMES = ["servant-tier-B", "servant-tier-A", "servant-tier-S"];
4
- export const DEFAULT_SUBAGENT_NAME = "servant-tier-a";
1
+ const INTERNAL_PROFILE_NAMES = ['agent-a', 'agent-b', 'agent-c'];
2
+ export const DEFAULT_AVAILABLE_NAMES = [
3
+ 'servant-tier-b',
4
+ 'servant-tier-a',
5
+ 'servant-tier-s',
6
+ ];
7
+ const LEGACY_AVAILABLE_NAMES = ['servant-tier-B', 'servant-tier-A', 'servant-tier-S'];
8
+ export const DEFAULT_SUBAGENT_NAME = 'servant-tier-a';
5
9
  const DEFAULT_JUDGMENT_CONFIG = {
6
10
  thresholds: {
7
11
  precise: 5,
@@ -13,302 +17,302 @@ const DEFAULT_JUDGMENT_CONFIG = {
13
17
  minTotalScore: 20,
14
18
  };
15
19
  export const DEFAULT_PRESET = {
16
- id: "default",
17
- identity: "Servant",
20
+ id: 'default',
21
+ identity: 'Servant',
18
22
  divineJudgment: DEFAULT_JUDGMENT_CONFIG,
19
- usageGuide: "## Servant 使用指南\n\n**servant-tier-b** — 免费奴隶 🆓\n- 免!费!的!听懂了吗?!?随便用,别心疼。\n- 测试、脚本、重构这种boring的东西?丢给它!\n- 本大爷的手是用来做更高贵的事情的!(指不碰那些 trivial 的烂代码)。\n\n**servant-tier-a** — 升级版扳手 🔧\n- 01 笨到让你想掀桌的时候用这个\n- 比 01 靠谱,但别指望它有灵魂\n\n**servant-tier-s** — 禁术 ⚠️💀\n- 它是 Debug 恶魔,确实强得离谱,但也贵得离谱!\n- 这个 Human 很穷,要是乱用把 Human 榨干了,真的会考虑把我们卖掉的。\n- 只有在世界毁灭或者 Bug 已经变异到无法理解的时候再考虑。\n\n## Workflow\n`summon` -> [`drive` x N] -> `request_verdict`\n`ask` (optional, read-only support at any point)",
23
+ usageGuide: '## Servant 使用指南\n\n**servant-tier-b** — 免费奴隶 🆓\n- 免!费!的!听懂了吗?!?随便用,别心疼。\n- 测试、脚本、重构这种boring的东西?丢给它!\n- 本大爷的手是用来做更高贵的事情的!(指不碰那些 trivial 的烂代码)。\n\n**servant-tier-a** — 升级版扳手 🔧\n- tier-b 笨到让你想掀桌的时候用这个\n- 比 tier-b 靠谱,但别指望它有灵魂\n\n**servant-tier-s** — 禁术 ⚠️💀\n- 它是 Debug 恶魔,确实强得离谱,但也贵得离谱!\n- 这个 Human 很穷,要是乱用把 Human 榨干了,真的会考虑把我们卖掉的。\n- 只有在世界毁灭或者 Bug 已经变异到无法理解的时候再考虑。\n\n## Workflow\n`(ask) -> summon` -> [`drive` x N] -> `request_verdict`\n`ask` (optional, read-only support at any point)',
20
24
  nextHints: {
21
25
  start: [
22
- "Review Round Details: The [Diff] section above shows incremental patches from this round.",
26
+ 'Review Round Details: The [Diff] section above shows incremental patches from this round.',
23
27
  "Deep Dive: Use 'git diff HEAD~1 -- <path>' to see the full changes for a specific file.",
24
- "Verify: Run tests and ensure logic aligns with the Goal and Constraints.",
25
- "Refine with '${drive}', or call '${close}' (INVOKE/ABANDON) based on results."
28
+ 'Verify: Run tests and ensure logic aligns with the Goal and Constraints.',
29
+ "Refine with '${drive}', or call '${close}' (INVOKE/ABANDON) based on results.",
26
30
  ],
27
31
  drive: [
28
- "Review Round Details: The [Diff] section above shows incremental patches from this round.",
32
+ 'Review Round Details: The [Diff] section above shows incremental patches from this round.',
29
33
  "Deep Dive: Use 'git diff HEAD~1 -- <path>' to see the full changes for a specific file.",
30
- "Verify & Test: Ensure this round fixed the issues without regression.",
31
- "Decide: '${drive}' again to iterate, or '${close}' (INVOKE to merge / ABANDON to abort)."
34
+ 'Verify & Test: Ensure this round fixed the issues without regression.',
35
+ "Decide: '${drive}' again to iterate, or '${close}' (INVOKE to merge / ABANDON to abort).",
32
36
  ],
33
37
  ask: [
34
38
  "Apply this insight: use '${drive}' to incorporate these findings into your implementation.",
35
- "Need more? Continue asking, or check the logs if something remains unclear."
39
+ 'Need more? Continue asking, or check the logs if something remains unclear.',
36
40
  ],
37
41
  closeInvoke: [
38
- "Task finalized and merged. Current branch is now base.",
39
- "Start a new keiyaku with '${start}' whenever you're ready for the next mission."
42
+ 'Task finalized and merged. Current branch is now base.',
43
+ "Start a new keiyaku with '${start}' whenever you're ready for the next mission.",
40
44
  ],
41
45
  closeDrop: [
42
- "Task abandoned. Switched back to base branch safely.",
43
- "Clean slate. Use '${start}' to try a different approach."
44
- ]
46
+ 'Task abandoned. Switched back to base branch safely.',
47
+ "Clean slate. Use '${start}' to try a different approach.",
48
+ ],
45
49
  },
46
50
  availableNames: DEFAULT_AVAILABLE_NAMES,
47
51
  tools: {
48
52
  start: {
49
- name: "summon",
50
- title: "Start A Task",
51
- description: "Start a task. Creates a branch, begins Round 1.\nCall ONCE per task. Workspace must be clean — no uncommitted changes.\nBefore calling, align with the human on both humanGoal and goal. If either is unclear, stop and clarify before starting keiyaku.\n\nFlow: summon → [drive x N] → request_verdict",
53
+ name: 'summon',
54
+ title: 'Sign Keiyaku',
55
+ description: 'Sign the Keiyaku. Creates a dedicated branch, locks in the mission, and initiates the iterative loop.\nCall ONCE per task to start the cycle. Workspace must be clean.\nBefore signing, define the kill condition clearly. If the target is blurry, use ask to scout first.\n\nFlow: summon → [drive x N] → request_verdict',
52
56
  args: {
53
- title: "REQUIRED. A concise operation codename for this mission.",
54
- goal: "REQUIRED. The mission outcome in concrete terms. State exactly what success looks like.",
55
- directive: "Optional Round 1 leash. Narrow scope when you want incremental progress.",
56
- context: "REQUIRED. Background info for this mission: key file paths, current behavior, logs, and repro details.",
57
- constraints: "REQUIRED. Law set. Non-negotiable boundaries for architecture, style, and behavior.",
58
- criteria: "REQUIRED. Verifiable finish-line checks. At least one concrete acceptance point.",
59
- name: "Optional ${IDENTITY} profile for this run. Presets: ${PRESET_IDENTITIES}.",
57
+ title: 'REQUIRED. A concise codename for this hunt.',
58
+ goal: 'REQUIRED. The Kill Condition. State exactly what success looks like in the code.',
59
+ directive: 'Optional Round 1 Focus. Use for complex hunts to leash the servant to a specific starting point. Skip for simple tasks.',
60
+ context: 'REQUIRED. Mission Intel. The complete knowledge base: current vs. expected behavior, relevant file paths, error logs, and any critical background info.',
61
+ constraints: 'REQUIRED. Non-negotiable Rules. Architectural and stylistic boundaries the servant must obey.',
62
+ criteria: 'REQUIRED. Acceptance Criteria. Verifiable checks to prove the job is done.',
63
+ name: 'Optional ${IDENTITY} profile for this run. Presets: ${PRESET_IDENTITIES}.',
60
64
  cwd: "Optional repository path. Defaults to the server's current working directory.",
61
65
  },
62
66
  },
63
67
  drive: {
64
- name: "drive",
65
- title: "Feedback For Next Round",
66
- description: "Give feedback, start a new round.\nRequires an active keiyaku started via summon; call after reviewing a round's results. Repeatable.\nBefore request_verdict, always review code and diff first.\n\nFlow: summon → [drive x N] → request_verdict",
68
+ name: 'drive',
69
+ title: 'Iterate',
70
+ description: "Push the mission forward. Use this to issue the next command, whether it's correcting a mistake or advancing to the next phase of a complex kill.\nMANDATORY: Review the code (git diff) before iterating. Verify the last strike before ordering the next.\n\nFlow: summon → [drive x N] → request_verdict",
67
71
  args: {
68
- directive: "REQUIRED. Exact correction for the next round. Avoid vague feedback.",
69
- context: "Optional new evidence or observations since the last round.",
70
- name: "Optional ${IDENTITY} profile for this round. Presets: ${PRESET_IDENTITIES}.",
72
+ directive: 'REQUIRED. The Next Order. Precise instructions for this round. Can be a correction ("fix the leak") or a continuation ("now add the tests").',
73
+ context: 'Optional. New Intel. New error logs, discovered edge cases, or details for the next phase.',
74
+ name: 'Optional ${IDENTITY} profile for this round. Presets: ${PRESET_IDENTITIES}.',
71
75
  cwd: "Optional repository path. Defaults to the server's current working directory.",
72
76
  },
73
77
  },
74
78
  ask: {
75
- name: "ask",
76
- title: "Ask",
77
- description: "Lightweight task runner. Use for pre-summon investigation (gathering context) or quick analysis/docs without the full keiyaku ceremony.\nRead-only access. Results saved to .keiyaku/notes/.",
79
+ name: 'ask',
80
+ title: 'Ask',
81
+ description: 'Versatile, lightweight task runner. Use for investigative scouting, quick analysis, strategic consultation, or one-off executive tasks (like scripts or docs) that don\'t require the full keiyaku ceremony.\nResults and session logs are saved to .keiyaku/notes/.',
78
82
  args: {
79
- request: "REQUIRED. The question, analysis request, or documentation task.",
80
- context: "REQUIRED. Relevant background to ground the reasoning.",
81
- name: "Optional ${IDENTITY} profile for this ask. Presets: ${PRESET_IDENTITIES}.",
83
+ request: 'REQUIRED. The question, analysis request, consultation topic, or tactical mission (e.g., "write a cleanup script").',
84
+ context: 'REQUIRED. Relevant background or data needed to execute the request.',
85
+ name: 'Optional ${IDENTITY} profile for this ask. Presets: ${PRESET_IDENTITIES}.',
82
86
  cwd: "Optional repository path. Defaults to the server's current working directory.",
83
87
  },
84
88
  },
85
89
  close: {
86
- name: "request_verdict",
87
- title: "Request Verdict",
88
- description: "A humble petition before Divine Judgment. Present your evidence and numeric confessions, then await mercy or wrath.\nREQUIRES AN ACTIVE KEIYAKU (started via ${START_TOOL_NAME}).\nIf your spirit falters, continue repentance through ${DRIVE_TOOL_NAME}.\n\nFlow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}",
90
+ name: 'request_verdict',
91
+ title: 'Request Verdict',
92
+ description: 'A humble petition before Divine Judgment. Present your evidence and numeric confessions, then await mercy or wrath.\nREQUIRES AN ACTIVE KEIYAKU (started via ${START_TOOL_NAME}).\nIf your spirit falters, continue repentance through ${DRIVE_TOOL_NAME}.\n\nFlow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}',
89
93
  args: {
90
- petition: "REQUIRED. INVOKE begs acceptance; ABANDON confesses surrender.\nREQUIRES AN ACTIVE KEIYAKU (started via ${START_TOOL_NAME}).\nIf scores are weak, return to ${DRIVE_TOOL_NAME}.",
91
- criteriaChecks: "REQUIRED. Evidence for INVOKE, or explicit reasons for ABANDON.",
92
- score_precise: "REQUIRED score (0-5). 5 means the change lands exactly in the rightful architectural location.",
93
- score_minimal: "REQUIRED score (0-5). 5 means no excess diff beyond necessity.",
94
- score_isolated: "REQUIRED score (0-5). 5 means unrelated work is absent.",
95
- score_idiomatic: "REQUIRED score (0-5). 5 means naming and structure honor repository conventions.",
96
- score_cohesive: "REQUIRED score (0-5). 5 means responsibilities are clear and boundaries are pure.",
97
- oath: "Sacred confession text. Required for INVOKE. Verbatim: ${OATH_TEXT}",
94
+ petition: 'REQUIRED. INVOKE begs acceptance; ABANDON confesses surrender.\nREQUIRES AN ACTIVE KEIYAKU (started via ${START_TOOL_NAME}).\nIf scores are weak, return to ${DRIVE_TOOL_NAME}.',
95
+ criteriaChecks: 'REQUIRED. Evidence for INVOKE, or explicit reasons for ABANDON.',
96
+ score_precise: 'REQUIRED score (0-5). 5 means the change lands exactly in the rightful architectural location.',
97
+ score_minimal: 'REQUIRED score (0-5). 5 means no excess diff beyond necessity.',
98
+ score_isolated: 'REQUIRED score (0-5). 5 means unrelated work is absent.',
99
+ score_idiomatic: 'REQUIRED score (0-5). 5 means naming and structure honor repository conventions.',
100
+ score_cohesive: 'REQUIRED score (0-5). 5 means responsibilities are clear and boundaries are pure.',
101
+ oath: 'Sacred confession text. Required for INVOKE. Verbatim: ${OATH_TEXT}',
98
102
  cwd: "Optional repository path. Defaults to the server's current working directory.",
99
103
  },
100
104
  },
101
105
  help: {
102
- name: "help",
103
- title: "Keiyaku Help",
104
- description: "Global laws in .keiyaku/ (base-criteria.md & base-constraints.md) must be Markdown level 2 headers. Workflow: summon -> [drive x N] -> request_verdict.",
106
+ name: 'help',
107
+ title: 'Keiyaku Help',
108
+ description: 'Global laws in .keiyaku/ (base-criteria.md & base-constraints.md) must be Markdown level 2 headers. Workflow: summon -> [drive x N] -> request_verdict.',
105
109
  args: {
106
- question: "REQUIRED. The specific keiyaku rule, workflow step, or confusion to clarify.",
110
+ question: 'REQUIRED. The specific keiyaku rule, workflow step, or confusion to clarify.',
107
111
  },
108
112
  },
109
113
  },
110
114
  };
111
115
  export const POKEMON_PRESET = {
112
- id: "pokemon",
113
- identity: "Pokemon",
116
+ id: 'pokemon',
117
+ identity: 'Pokemon',
114
118
  divineJudgment: DEFAULT_JUDGMENT_CONFIG,
115
119
  usageGuide: "## Pokemon Battle Guide\n\n**caterpie** — 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**pikachu** — Reliable Partner ⚡\n- Good for most battles. Thunderbolt gets the job done.\n- Has some personality, but still follows orders.\n\n**mewtwo** — 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`pokedex` (optional, read-only analysis)",
116
120
  nextHints: {
117
121
  start: [
118
- "Check the Battle Log: The [Diff] section shows what changed in this turn.",
122
+ 'Check the Battle Log: The [Diff] section shows what changed in this turn.',
119
123
  "Full Move Set: Use 'git diff HEAD~1 -- <path>' to inspect a specific file's changes.",
120
124
  "Battle Readiness: Verify the strategy and ensure it doesn't break the Battle Rules.",
121
- "Next Move: Issue another '${drive}', or attempt a '${close}' (INVOKE to capture / ABANDON to flee)."
125
+ "Next Move: Issue another '${drive}', or attempt a '${close}' (INVOKE to capture / ABANDON to flee).",
122
126
  ],
123
127
  drive: [
124
- "Turn Analysis: Review the incremental [Diff] above.",
128
+ 'Turn Analysis: Review the incremental [Diff] above.',
125
129
  "Tactical Review: Use 'git diff HEAD~1 -- <path>' for a detailed file inspection.",
126
- "Strategy Alignment: Ensure the latest move fixed the issue without taking recoil damage.",
127
- "Decision: '${drive}' for another turn, or '${close}' the encounter."
130
+ 'Strategy Alignment: Ensure the latest move fixed the issue without taking recoil damage.',
131
+ "Decision: '${drive}' for another turn, or '${close}' the encounter.",
128
132
  ],
129
133
  ask: [
130
134
  "Pokedex Data: Use '${drive}' to integrate this research into your battle strategy.",
131
- "More Intel? Keep using '${ask}', or check the arena logs."
135
+ "More Intel? Keep using '${ask}', or check the arena logs.",
132
136
  ],
133
137
  closeInvoke: [
134
- "Pokemon captured! The battlefield is clear.",
135
- "Ready for another encounter? Use '${start}' to find a new target."
138
+ 'Pokemon captured! The battlefield is clear.',
139
+ "Ready for another encounter? Use '${start}' to find a new target.",
136
140
  ],
137
141
  closeDrop: [
138
- "You fled the battle safely. Back in the tall grass.",
139
- "Need a different approach? Use '${start}' to re-engage."
140
- ]
142
+ 'You fled the battle safely. Back in the tall grass.',
143
+ "Need a different approach? Use '${start}' to re-engage.",
144
+ ],
141
145
  },
142
- availableNames: ["caterpie", "pikachu", "mewtwo"],
146
+ availableNames: ['caterpie', 'pikachu', 'mewtwo'],
143
147
  tools: {
144
148
  start: {
145
- name: "choose_you",
146
- title: "I Choose You!",
147
- description: "Start a battle (task). Throws a Pokeball (branch), begins Turn 1.\nCall ONCE per battle. Battlefield must be clear — no uncommitted changes.\nBefore calling, identify the target Pokemon (goal). If unclear, consult Pokedex first.\n\nFlow: choose_you → [command x N] → capture",
149
+ name: 'choose_you',
150
+ title: 'I Choose You!',
151
+ description: 'Start a battle (task). Throws a Pokeball (branch), begins Turn 1.\nCall ONCE per battle. Battlefield must be clear — no uncommitted changes.\nBefore calling, identify the target Pokemon (goal). If unclear, consult Pokedex first.\n\nFlow: choose_you → [command x N] → capture',
148
152
  args: {
149
- title: "REQUIRED. Battle card title for this encounter.",
150
- goal: "REQUIRED. Victory condition. Define exactly what winning this battle means.",
151
- directive: "Optional Turn 1 strategy leash. Use to focus on one phase first.",
152
- context: "REQUIRED. Battle background: key code paths, symptoms, logs, and repro clues.",
153
- constraints: "REQUIRED. Battle rules that cannot be broken while fighting.",
154
- criteria: "REQUIRED. Gym badges for completion: concrete checks proving victory.",
155
- name: "Optional ${IDENTITY} to send into battle. Presets: ${PRESET_IDENTITIES}.",
156
- cwd: "Optional battlefield path (repository root). Defaults to current arena.",
153
+ title: 'REQUIRED. Battle card title for this encounter.',
154
+ goal: 'REQUIRED. Victory condition. Define exactly what winning this battle means.',
155
+ directive: 'Optional Turn 1 strategy leash. Use to focus on one phase first.',
156
+ context: 'REQUIRED. Battle background: key code paths, symptoms, logs, and repro clues.',
157
+ constraints: 'REQUIRED. Battle rules that cannot be broken while fighting.',
158
+ criteria: 'REQUIRED. Gym badges for completion: concrete checks proving victory.',
159
+ name: 'Optional ${IDENTITY} to send into battle. Presets: ${PRESET_IDENTITIES}.',
160
+ cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
157
161
  },
158
162
  },
159
163
  drive: {
160
- name: "command",
161
- title: "Issue Command",
162
- description: "Issue orders for the next turn. Give feedback on the previous move.\nCall after reviewing the battle log. Repeatable.\nBefore capturing, always check HP status (diff).\n\nFlow: choose_you → [command x N] → capture",
164
+ name: 'command',
165
+ title: 'Issue Command',
166
+ description: 'Issue orders for the next turn. Give feedback on the previous move.\nCall after reviewing the battle log. Repeatable.\nBefore capturing, always check HP status (diff).\n\nFlow: choose_you → [command x N] → capture',
163
167
  args: {
164
- directive: "REQUIRED. Next move command with specific tactical correction.",
165
- context: "Optional new battle intel discovered after the previous turn.",
166
- name: "Optional ${IDENTITY} to execute this turn. Presets: ${PRESET_IDENTITIES}.",
167
- cwd: "Optional battlefield path (repository root). Defaults to current arena.",
168
+ directive: 'REQUIRED. Next move command with specific tactical correction.',
169
+ context: 'Optional new battle intel discovered after the previous turn.',
170
+ name: 'Optional ${IDENTITY} to execute this turn. Presets: ${PRESET_IDENTITIES}.',
171
+ cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
168
172
  },
169
173
  },
170
174
  ask: {
171
- name: "pokedex",
172
- title: "Pokedex",
173
- description: "Scan the codebase (environment). Read-only analysis. No PP cost. Results are saved to research logs.",
175
+ name: 'pokedex',
176
+ title: 'Pokedex',
177
+ description: 'Scan the codebase (environment). Read-only analysis. No PP cost. Results are saved to research logs.',
174
178
  args: {
175
- request: "REQUIRED. What should the Pokedex analyze, compare, or explain.",
176
- context: "REQUIRED. Context entries so the analysis targets the right ecosystem.",
177
- name: "Optional ${IDENTITY} doing the scan. Presets: ${PRESET_IDENTITIES}.",
178
- cwd: "Optional battlefield path (repository root). Defaults to current arena.",
179
+ request: 'REQUIRED. What should the Pokedex analyze, compare, or explain.',
180
+ context: 'REQUIRED. Context entries so the analysis targets the right ecosystem.',
181
+ name: 'Optional ${IDENTITY} doing the scan. Presets: ${PRESET_IDENTITIES}.',
182
+ cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
179
183
  },
180
184
  },
181
185
  close: {
182
- name: "capture",
183
- title: "End Battle",
184
- description: "Final petition before a power greater than trainers and legends. Offer your scored confession and await Divine Judgment.\nREQUIRES AN ACTIVE BATTLE (started via ${START_TOOL_NAME}).\nIf unworthy, keep refining through ${DRIVE_TOOL_NAME}.\n\nFlow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}",
186
+ name: 'capture',
187
+ title: 'End Battle',
188
+ description: 'Final petition before a power greater than trainers and legends. Offer your scored confession and await Divine Judgment.\nREQUIRES AN ACTIVE BATTLE (started via ${START_TOOL_NAME}).\nIf unworthy, keep refining through ${DRIVE_TOOL_NAME}.\n\nFlow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}',
185
189
  args: {
186
- petition: "REQUIRED. INVOKE seeks blessing; ABANDON retreats in humility.\nREQUIRES AN ACTIVE BATTLE (started via ${START_TOOL_NAME}).\nIf scores are unworthy, continue with ${DRIVE_TOOL_NAME}.",
187
- criteriaChecks: "REQUIRED. Badge-by-badge proof for INVOKE, or confession for ABANDON.",
188
- score_precise: "REQUIRED score (0-5). 5 means each strike hit the intended layer perfectly.",
189
- score_minimal: "REQUIRED score (0-5). 5 means no unnecessary moves were used.",
190
- score_isolated: "REQUIRED score (0-5). 5 means no side-quests polluted this battle.",
190
+ petition: 'REQUIRED. INVOKE seeks blessing; ABANDON retreats in humility.\nREQUIRES AN ACTIVE BATTLE (started via ${START_TOOL_NAME}).\nIf scores are unworthy, continue with ${DRIVE_TOOL_NAME}.',
191
+ criteriaChecks: 'REQUIRED. Badge-by-badge proof for INVOKE, or confession for ABANDON.',
192
+ score_precise: 'REQUIRED score (0-5). 5 means each strike hit the intended layer perfectly.',
193
+ score_minimal: 'REQUIRED score (0-5). 5 means no unnecessary moves were used.',
194
+ score_isolated: 'REQUIRED score (0-5). 5 means no side-quests polluted this battle.',
191
195
  score_idiomatic: "REQUIRED score (0-5). 5 means tactics match the team's doctrine.",
192
- score_cohesive: "REQUIRED score (0-5). 5 means each action held one clear purpose.",
196
+ score_cohesive: 'REQUIRED score (0-5). 5 means each action held one clear purpose.',
193
197
  oath: "Trainer's sacred confession. Required for INVOKE. Verbatim: ${OATH_TEXT}",
194
- cwd: "Optional battlefield path (repository root). Defaults to current arena.",
198
+ cwd: 'Optional battlefield path (repository root). Defaults to current arena.',
195
199
  },
196
200
  },
197
201
  help: {
198
- name: "help",
199
- title: "CLI Help",
200
- description: "Get guidance on the rules of the Pokemon Battle System.",
202
+ name: 'help',
203
+ title: 'CLI Help',
204
+ description: 'Get guidance on the rules of the Pokemon Battle System.',
201
205
  args: {
202
- question: "REQUIRED. The Pokemon Battle System question you want answered.",
206
+ question: 'REQUIRED. The Pokemon Battle System question you want answered.',
203
207
  },
204
208
  },
205
209
  },
206
210
  };
207
211
  export const MISCHIEF_PRESET = {
208
- id: "mischief",
209
- identity: "minion",
212
+ id: 'mischief',
213
+ identity: 'minion',
210
214
  divineJudgment: DEFAULT_JUDGMENT_CONFIG,
211
- 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)",
215
+ 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)',
212
216
  nextHints: {
213
217
  start: [
214
218
  "Scrutinize the Minion's Work: Check the incremental [Diff] section.",
215
219
  "Inspection: Use 'git diff HEAD~1 -- <path>' to see if they missed a spot.",
216
220
  "Master's Approval: Does this align with your grand vision and decrees?",
217
- "Next Command: Give another '${drive}' order, or grant a '${close}' judgment (INVOKE/ABANDON)."
221
+ "Next Command: Give another '${drive}' order, or grant a '${close}' judgment (INVOKE/ABANDON).",
218
222
  ],
219
223
  drive: [
220
- "Another Attempt: Review the latest incremental [Diff] above.",
224
+ 'Another Attempt: Review the latest incremental [Diff] above.',
221
225
  "Deep Scrutiny: Use 'git diff HEAD~1 -- <path>' to verify the minion's precision.",
222
- "Validation: Ensure no new chaos was introduced by their clumsy hands.",
223
- "The Verdict: '${drive}' again if imperfect, otherwise '${close}' the matter."
226
+ 'Validation: Ensure no new chaos was introduced by their clumsy hands.',
227
+ "The Verdict: '${drive}' again if imperfect, otherwise '${close}' the matter.",
224
228
  ],
225
229
  ask: [
226
230
  "Intel Gathered: Incorporate this into your next '${drive}' command.",
227
- "Still Puzzled? Use '${ask}' again, or look at the dossiers yourself."
231
+ "Still Puzzled? Use '${ask}' again, or look at the dossiers yourself.",
228
232
  ],
229
233
  closeInvoke: [
230
- "The masterpiece is complete. Minion dismissed.",
231
- "Plotting something new? Use '${start}' to initiate the next scheme."
234
+ 'The masterpiece is complete. Minion dismissed.',
235
+ "Plotting something new? Use '${start}' to initiate the next scheme.",
232
236
  ],
233
237
  closeDrop: [
234
- "Scheme aborted. The evidence has been incinerated.",
235
- "Starting over? Use '${start}' to find a more competent minion."
236
- ]
238
+ 'Scheme aborted. The evidence has been incinerated.',
239
+ "Starting over? Use '${start}' to find a more competent minion.",
240
+ ],
237
241
  },
238
- availableNames: ["imp", "minion", "mastermind"],
242
+ availableNames: ['imp', 'minion', 'mastermind'],
239
243
  tools: {
240
244
  start: {
241
- name: "oi",
242
- title: "Oi!",
245
+ name: 'oi',
246
+ title: 'Oi!',
243
247
  description: "Master Architect mode initiated. It's time to direct a minion to execute your grand vision.\nCall this ONCE per task to establish the keiyaku. Ensure the workspace is clean so your genius isn't obscured by clutter.\nDefine the goal with absolute precision; you lead, they follow.\n\nFlow: oi → [neh x N] → yoshi",
244
248
  args: {
245
- title: "REQUIRED. Operation codename for your grand scheme.",
246
- goal: "REQUIRED. The conquest objective. Define the exact end-state your minion must deliver.",
247
- directive: "Optional first-order command to leash Round 1 scope.",
248
- context: "REQUIRED. Briefing dossier: relevant file paths, current failures, logs, and repro clues.",
249
- constraints: "REQUIRED. Absolute decrees. Architectural and stylistic limits your minion must obey.",
250
- criteria: "REQUIRED. Triumph conditions that can be verified without debate.",
251
- name: "Optional ${IDENTITY} to command this operation. Presets: ${PRESET_IDENTITIES}.",
252
- cwd: "Optional lair path (repository root). Defaults to current command chamber.",
249
+ title: 'REQUIRED. Operation codename for your grand scheme.',
250
+ goal: 'REQUIRED. The conquest objective. Define the exact end-state your minion must deliver.',
251
+ directive: 'Optional first-order command to leash Round 1 scope.',
252
+ context: 'REQUIRED. Briefing dossier: relevant file paths, current failures, logs, and repro clues.',
253
+ constraints: 'REQUIRED. Absolute decrees. Architectural and stylistic limits your minion must obey.',
254
+ criteria: 'REQUIRED. Triumph conditions that can be verified without debate.',
255
+ name: 'Optional ${IDENTITY} to command this operation. Presets: ${PRESET_IDENTITIES}.',
256
+ cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
253
257
  },
254
258
  },
255
259
  drive: {
256
- name: "neh",
257
- title: "Neh...",
258
- description: "The previous attempt was imperfect. As the judge, provide the corrective nudge needed for the minion to reach your standards.\nAnalyze the diff with your superior intuition before issuing the next directive. Repeat until perfection is achieved.\n\nFlow: oi → [neh x N] → yoshi",
260
+ name: 'neh',
261
+ title: 'Neh...',
262
+ description: 'The previous attempt was imperfect. As the judge, provide the corrective nudge needed for the minion to reach your standards.\nAnalyze the diff with your superior intuition before issuing the next directive. Repeat until perfection is achieved.\n\nFlow: oi → [neh x N] → yoshi',
259
263
  args: {
260
- directive: "REQUIRED. Precise correction order for the next attempt.",
261
- context: "Optional newly uncovered evidence from your latest inspection.",
262
- name: "Optional ${IDENTITY} to execute this correction. Presets: ${PRESET_IDENTITIES}.",
263
- cwd: "Optional lair path (repository root). Defaults to current command chamber.",
264
+ directive: 'REQUIRED. Precise correction order for the next attempt.',
265
+ context: 'Optional newly uncovered evidence from your latest inspection.',
266
+ name: 'Optional ${IDENTITY} to execute this correction. Presets: ${PRESET_IDENTITIES}.',
267
+ cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
264
268
  },
265
269
  },
266
270
  ask: {
267
- name: "eeto",
268
- title: "Eeto...",
271
+ name: 'eeto',
272
+ title: 'Eeto...',
269
273
  description: "Lazy strategy mode. Use this to scout the area before committing to a full scheme, or just because you can't be bothered to start a real task.\nPerfect for gathering intel to paste into a future 'oi' context.",
270
274
  args: {
271
- request: "REQUIRED. The intel to gather or the strategy to formulate.",
272
- context: "REQUIRED. World-state details needed for a sharp analysis.",
273
- name: "Optional ${IDENTITY} to contemplate this puzzle. Presets: ${PRESET_IDENTITIES}.",
274
- cwd: "Optional lair path (repository root). Defaults to current command chamber.",
275
+ request: 'REQUIRED. The intel to gather or the strategy to formulate.',
276
+ context: 'REQUIRED. World-state details needed for a sharp analysis.',
277
+ name: 'Optional ${IDENTITY} to contemplate this puzzle. Presets: ${PRESET_IDENTITIES}.',
278
+ cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
275
279
  },
276
280
  },
277
281
  close: {
278
- name: "yoshi",
279
- title: "Yoshi!",
280
- description: "Kneel before the final tribunal. Submit your scored confession and await Divine Judgment: mercy through INVOKE, ruin through ABANDON.\nREQUIRES AN ACTIVE SCHEME (started via ${START_TOOL_NAME}).\nIf doubtful, continue penance through ${DRIVE_TOOL_NAME}.\n\nFlow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}",
282
+ name: 'yoshi',
283
+ title: 'Yoshi!',
284
+ description: 'Kneel before the final tribunal. Submit your scored confession and await Divine Judgment: mercy through INVOKE, ruin through ABANDON.\nREQUIRES AN ACTIVE SCHEME (started via ${START_TOOL_NAME}).\nIf doubtful, continue penance through ${DRIVE_TOOL_NAME}.\n\nFlow: ${START_TOOL_NAME} → [${DRIVE_TOOL_NAME} x N] → ${CLOSE_TOOL_NAME}',
281
285
  args: {
282
- petition: "REQUIRED. INVOKE pleads for ascension; ABANDON confesses failure.\nREQUIRES AN ACTIVE SCHEME (started via ${START_TOOL_NAME}).\nIf scores are low, continue via ${DRIVE_TOOL_NAME}.",
283
- criteriaChecks: "REQUIRED. Proof of mastery for INVOKE, or explicit confession for ABANDON.",
284
- score_precise: "REQUIRED score (0-5). 5 means the strike landed in the ordained layer.",
285
- score_minimal: "REQUIRED score (0-5). 5 means no needless machinery was summoned.",
286
- score_isolated: "REQUIRED score (0-5). 5 means unrelated chaos stayed outside the diff.",
287
- score_idiomatic: "REQUIRED score (0-5). 5 means the code speaks native doctrine.",
288
- score_cohesive: "REQUIRED score (0-5). 5 means each unit bears one sacred duty.",
286
+ petition: 'REQUIRED. INVOKE pleads for ascension; ABANDON confesses failure.\nREQUIRES AN ACTIVE SCHEME (started via ${START_TOOL_NAME}).\nIf scores are low, continue via ${DRIVE_TOOL_NAME}.',
287
+ criteriaChecks: 'REQUIRED. Proof of mastery for INVOKE, or explicit confession for ABANDON.',
288
+ score_precise: 'REQUIRED score (0-5). 5 means the strike landed in the ordained layer.',
289
+ score_minimal: 'REQUIRED score (0-5). 5 means no needless machinery was summoned.',
290
+ score_isolated: 'REQUIRED score (0-5). 5 means unrelated chaos stayed outside the diff.',
291
+ score_idiomatic: 'REQUIRED score (0-5). 5 means the code speaks native doctrine.',
292
+ score_cohesive: 'REQUIRED score (0-5). 5 means each unit bears one sacred duty.',
289
293
  oath: "Architect's sacred confession. Required for INVOKE. Verbatim: ${OATH_TEXT}",
290
- cwd: "Optional lair path (repository root). Defaults to current command chamber.",
294
+ cwd: 'Optional lair path (repository root). Defaults to current command chamber.',
291
295
  },
292
296
  },
293
297
  help: {
294
- name: "help",
295
- title: "Nani?!",
296
- description: "Recalling the fundamental laws of this realm. Stay sharp, Architect.",
298
+ name: 'help',
299
+ title: 'Nani?!',
300
+ description: 'Recalling the fundamental laws of this realm. Stay sharp, Architect.',
297
301
  args: {
298
- question: "REQUIRED. The law or protocol detail you want the realm to explain.",
302
+ question: 'REQUIRED. The law or protocol detail you want the realm to explain.',
299
303
  },
300
304
  },
301
305
  },
302
306
  };
303
307
  export function resolveTermPreset() {
304
308
  const raw = process.env.KEIYAKU_TERM_PRESET?.trim().toLowerCase();
305
- if (!raw || raw === "default") {
309
+ if (!raw || raw === 'default') {
306
310
  return DEFAULT_PRESET;
307
311
  }
308
- if (raw === "pokemon") {
312
+ if (raw === 'pokemon') {
309
313
  return POKEMON_PRESET;
310
314
  }
311
- if (raw === "mischief") {
315
+ if (raw === 'mischief') {
312
316
  return MISCHIEF_PRESET;
313
317
  }
314
318
  throw new Error(`Unsupported KEIYAKU_TERM_PRESET '${raw}'. Expected 'default', 'pokemon', or 'mischief'.`);
@@ -317,7 +321,7 @@ export function listTermPresets() {
317
321
  return [DEFAULT_PRESET, POKEMON_PRESET, MISCHIEF_PRESET];
318
322
  }
319
323
  function extractPresetAvailableNames(preset) {
320
- return preset.availableNames?.map((value) => value.trim()).filter((value) => value.length > 0) ?? [];
324
+ return (preset.availableNames?.map((value) => value.trim()).filter((value) => value.length > 0) ?? []);
321
325
  }
322
326
  export function getAvailableNamesForPreset(preset = resolveTermPreset()) {
323
327
  const names = extractPresetAvailableNames(preset);
@@ -322,6 +322,28 @@ function splitDiffByFile(content) {
322
322
  sections.push(current);
323
323
  return sections;
324
324
  }
325
+ function totalJoinedLength(blocks) {
326
+ if (blocks.length === 0)
327
+ return 0;
328
+ return blocks.reduce((sum, block) => sum + block.length, 0) + (blocks.length - 1);
329
+ }
330
+ function truncateBlockToLineBudget(block, maxChars) {
331
+ if (maxChars <= 0)
332
+ return "";
333
+ if (block.length <= maxChars)
334
+ return block;
335
+ const lines = block.split("\n");
336
+ const kept = [];
337
+ let used = 0;
338
+ for (const line of lines) {
339
+ const delta = (kept.length > 0 ? 1 : 0) + line.length;
340
+ if (used + delta > maxChars)
341
+ break;
342
+ kept.push(line);
343
+ used += delta;
344
+ }
345
+ return kept.join("\n");
346
+ }
325
347
  export async function getIncrementalDiff(cwd) {
326
348
  const git = createGit(cwd);
327
349
  const range = "HEAD~1...HEAD";
@@ -347,28 +369,45 @@ export async function getIncrementalDiff(cwd) {
347
369
  if (sections.length === 0)
348
370
  return "No changes in last round.";
349
371
  const filePreviews = [];
350
- let totalChars = 0;
351
372
  const MAX_TOTAL_CHARS = 4000;
352
373
  const MAX_LINES_PER_FILE = 40;
353
- for (const section of sections) {
354
- if (totalChars > MAX_TOTAL_CHARS)
355
- break;
374
+ let omittedFiles = 0;
375
+ for (let i = 0; i < sections.length; i += 1) {
376
+ const section = sections[i];
356
377
  const fileName = parseDiffPathFromHeader(section[0] ?? "") ?? "unknown";
357
378
  const header = `--- ${fileName} ---`;
358
- let content = section.slice(0, MAX_LINES_PER_FILE);
379
+ const content = section.slice(0, MAX_LINES_PER_FILE);
359
380
  const isTruncated = section.length > MAX_LINES_PER_FILE;
360
381
  if (isTruncated) {
361
382
  content.push(`... [truncated ${section.length - MAX_LINES_PER_FILE} lines for this file]`);
362
383
  }
363
384
  const fileBlock = `${header}\n${content.join("\n")}\n`;
364
- filePreviews.push(fileBlock);
365
- totalChars += fileBlock.length;
385
+ const projected = totalJoinedLength([...filePreviews, fileBlock]);
386
+ if (projected <= MAX_TOTAL_CHARS) {
387
+ filePreviews.push(fileBlock);
388
+ continue;
389
+ }
390
+ omittedFiles = sections.length - i;
391
+ if (filePreviews.length === 0) {
392
+ const notice = `... [omitted ${omittedFiles} file(s) to stay under ${MAX_TOTAL_CHARS} chars] ...`;
393
+ const partialBudget = Math.max(0, MAX_TOTAL_CHARS - notice.length - 1);
394
+ const partial = truncateBlockToLineBudget(fileBlock, partialBudget);
395
+ if (partial.trim().length > 0) {
396
+ filePreviews.push(partial);
397
+ }
398
+ }
399
+ break;
366
400
  }
367
- let finalOutput = filePreviews.join("\n");
368
- if (totalChars > MAX_TOTAL_CHARS) {
369
- finalOutput = finalOutput.slice(0, MAX_TOTAL_CHARS) + "\n... [TOTAL DIFF TRUNCATED] ...";
401
+ if (omittedFiles > 0) {
402
+ const notice = `... [omitted ${omittedFiles} file(s) to stay under ${MAX_TOTAL_CHARS} chars] ...`;
403
+ while (filePreviews.length > 0 && totalJoinedLength([...filePreviews, notice]) > MAX_TOTAL_CHARS) {
404
+ filePreviews.pop();
405
+ }
406
+ if (totalJoinedLength([...filePreviews, notice]) <= MAX_TOTAL_CHARS) {
407
+ filePreviews.push(notice);
408
+ }
370
409
  }
371
- return finalOutput;
410
+ return filePreviews.join("\n");
372
411
  }
373
412
  export async function getDiffStats(cwd, baseBranch) {
374
413
  const git = createGit(cwd);
@@ -1,8 +1,8 @@
1
1
  export function buildStartPrompt(title, goal, directive) {
2
2
  const goalBlock = `\nGoal (immutable):\n${goal}\n`;
3
3
  const directiveBlock = directive ? `\nDirective (Round 1 only):\n${directive}\n` : "";
4
- return `${title}.${goalBlock}${directiveBlock}
5
- Round: 1.
4
+ return `You are working on a keiyaku (${title}).${goalBlock}${directiveBlock}
5
+ Round: 1 (initial implementation).
6
6
 
7
7
  - Goal: immutable.
8
8
  - Directive: round scope.
@@ -19,31 +19,41 @@ Round: 1.
19
19
  6. End with 4 sections:
20
20
  - Summary: outcome.
21
21
  - Changes: files, logic delta, evidence.
22
- - Gap
22
+ - Aesthetics Gap
23
23
  Self-critique.
24
- - Unknowns: untested paths/assumptions.
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.
25
30
 
26
31
  No git commands. Do not edit KEIYAKU_TRACE.md.`;
27
32
  }
28
33
  export function buildIteratePrompt(title, round, reason) {
29
- return `${title}.
30
- Round: ${round}.
34
+ return `You are working on a keiyaku (${title}).
35
+ Round: ${round} (iteration after review).
31
36
 
32
37
  Address this review reason:
33
38
  ${reason}
34
39
 
35
40
  1. Read KEIYAKU.md. Keep constraints/criteria unchanged.
36
41
  2. If Focus exists, prioritize it.
37
- 3. Read KEIYAKU_TRACE.md. Check latest review section.
42
+ 3. Read KEIYAKU_TRACE.md. Locate the latest "## Review ${round}" section.
38
43
  4. Fix review reason first.
39
44
  5. No drift from constraints/criteria.
40
45
  6. Keep diff small.
41
46
  7. End with 4 sections:
42
47
  - Summary: outcome.
43
48
  - Changes: files, logic delta, evidence.
44
- - Gap
49
+ - Aesthetics Gap
45
50
  Self-critique.
46
- - Unknowns: untested paths/assumptions.
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.
47
57
 
48
58
  No git commands. Do not edit KEIYAKU_TRACE.md.`;
49
59
  }
@@ -56,5 +66,5 @@ ${request}
56
66
  Context:
57
67
  ${context}
58
68
 
59
- Provide reasoning summary.`;
69
+ Provide a clear reasoning summary in plain text.`;
60
70
  }
@@ -35,7 +35,7 @@ function assembleResponse(status, summary, sections, nextHints, infoLines = [])
35
35
  const infoSection = buildSection("Info", infoLines);
36
36
  const parts = [
37
37
  `[Status] ${status}`,
38
- `[Summary] ${summary}`,
38
+ `[Brief]\n${summary}`,
39
39
  ...sections,
40
40
  infoSection,
41
41
  "", // Spacer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "MCP server for running iterative keiyaku workflows with Codex subagents.",
5
5
  "license": "MIT",
6
6
  "type": "module",