@astrosheep/keiyaku 0.1.85 → 0.1.87
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 +3 -1
- package/build/.tsbuildinfo +1 -1
- package/build/agents/claude-agent-sdk-exec.js +104 -0
- package/build/agents/codex-cli-exec.js +3 -1
- package/build/agents/codex-sdk-exec.js +75 -0
- package/build/agents/gemini-cli-core-exec.js +1 -1
- package/build/agents/index.js +3 -2
- package/build/agents/progress-reporter.js +137 -0
- package/build/agents/round-runner.js +1 -0
- package/build/cli/index.js +58 -20
- package/build/config/architect-hints.js +15 -12
- package/build/config/env-keys.js +2 -0
- package/build/config/schema.js +3 -0
- package/build/generated/version.js +1 -1
- package/build/git/core.js +0 -1
- package/build/responses/responses.js +21 -36
- package/build/tools/schema.js +1 -0
- package/build/tools/status/read.js +10 -7
- package/build/tools/summon/run.js +1 -0
- package/package.json +2 -2
- package/skills/keiyaku/SKILL.md +4 -3
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { ACTOR_IDENTITY, ACTOR_IDENTITY_PLURAL, BASE_RULES_FILE, KEIYAKU_ARTIFACT_DIRS, KEIYAKU_BRANCH_PREFIX, KEIYAKU_DRAFT_DIR, RESPONSE_HISTORY_DIR, SETTINGS_FILE, TOOL_NAMES, formatArtifactDirectories, } from "../keiyaku.js";
|
|
3
3
|
import { renderTemplate } from "./render-template.js";
|
|
4
4
|
const STATUS_BRANCH_CANDIDATE_LIMIT = 3;
|
|
5
|
-
const STATUS_DIRTY_FILE_DISPLAY_LIMIT =
|
|
5
|
+
const STATUS_DIRTY_FILE_DISPLAY_LIMIT = 2;
|
|
6
6
|
const GUIDANCE_BRANCH_LIST_LIMIT = 3;
|
|
7
7
|
const WORKFLOW_HINT_TEMPLATES = {
|
|
8
8
|
bind: [
|
|
@@ -32,22 +32,23 @@ const WORKFLOW_HINT_TEMPLATES = {
|
|
|
32
32
|
const STATUS_TEXT = {
|
|
33
33
|
blockers: {
|
|
34
34
|
dirtyWorktree: "Working directory has uncommitted changes; commit or stash before continuing.",
|
|
35
|
+
dirtyWorktreeSummary: "Working directory has uncommitted changes.",
|
|
35
36
|
bindProtocolOutsideActive: "KEIYAKU.md exists outside an active keiyaku branch; remove or relocate before starting.",
|
|
36
37
|
missingBaseMetadata: "Active keiyaku branch is missing base metadata (branch.<keiyaku-branch>.keiyakuBase).",
|
|
37
38
|
missingProtocolFiles: "Active keiyaku branch is missing KEIYAKU.md; restore it before continuing.",
|
|
38
39
|
},
|
|
39
40
|
hints: {
|
|
40
|
-
missingKeiyaku: "
|
|
41
|
-
missingBaseMetadata: "
|
|
41
|
+
missingKeiyaku: "No Keiyaku is active here; bind starts a tracked contract when you want one.",
|
|
42
|
+
missingBaseMetadata: "This branch is missing base metadata; set branch.<keiyaku-branch>.keiyakuBase before round or petition commands.",
|
|
42
43
|
},
|
|
43
44
|
};
|
|
44
45
|
const HELP_BODY_TEMPLATE = [
|
|
45
46
|
"## Workflow",
|
|
46
|
-
"`bind -> round open -> [summon | amend | delivery work]* -> delivery commits -> round close -> [round open | amend | petition claim | petition forfeit]`",
|
|
47
|
+
"`bind -> round open -> [summon/ask | amend | delivery work]* -> delivery commits -> round close -> [round open | amend | petition claim | petition forfeit]`",
|
|
47
48
|
"",
|
|
48
49
|
`- **\${bind}**: Create the active Keiyaku branch and protocol files from Markdown stdin. The worktree must be clean; call it once per Keiyaku.`,
|
|
49
50
|
`- **\${roundOpen}**: Record the delivery round title, objective, and brief before work starts. It does not run agents or mutate delivery code.`,
|
|
50
|
-
`- **\${summon}**: Summon a ${ACTOR_IDENTITY} for bounded work. If a round is open, the round scope is injected automatically; the caller still owns review, integration, and commits.`,
|
|
51
|
+
`- **\${summon}** / **ask**: Summon a ${ACTOR_IDENTITY} for bounded work. If a round is open, the round scope is injected automatically; the caller still owns review, integration, and commits.`,
|
|
51
52
|
`- **zako list / zako show**: Inspect available ${ACTOR_IDENTITY_PLURAL} and their resolved provider/executable before choosing \`keiyaku summon NAME\`.`,
|
|
52
53
|
"- **delivery commits**: Commit the actual code or documentation changes in git before closing the round.",
|
|
53
54
|
`- **\${roundClose}**: Record the report and non-empty delivery commit list after the commits already exist. It does not create delivery commits.`,
|
|
@@ -58,7 +59,7 @@ const HELP_BODY_TEMPLATE = [
|
|
|
58
59
|
"",
|
|
59
60
|
"## Command Boundaries",
|
|
60
61
|
`- \`\${roundOpen}\` and \`\${roundClose}\` are ledger operations; they do not summon agents and do not edit delivery code.`,
|
|
61
|
-
`- \`\${summon}\` can run outside a Keiyaku, but an open round gives the ${ACTOR_IDENTITY} better context.`,
|
|
62
|
+
`- \`\${summon}\` and \`ask\` can run outside a Keiyaku, but an open round gives the ${ACTOR_IDENTITY} better context.`,
|
|
62
63
|
`- \`\${summon}\` starts a new session. Use \`keiyaku resume RESPONSE_PATH\` to continue a prior response artifact.`,
|
|
63
64
|
"- `zako list` and `zako show <name>` are read-only inspection commands; they do not start a helper.",
|
|
64
65
|
"- `keiyaku resume` restores the agent from the response artifact; `--incognito` summons cannot be resumed later.",
|
|
@@ -109,6 +110,7 @@ export const STATUS_OPERATION_NAMES = {
|
|
|
109
110
|
bind: TOOL_NAMES.bind,
|
|
110
111
|
roundOpen: TOOL_NAMES.roundOpen,
|
|
111
112
|
roundClose: TOOL_NAMES.roundClose,
|
|
113
|
+
summon: TOOL_NAMES.summon,
|
|
112
114
|
close: TOOL_NAMES.close,
|
|
113
115
|
};
|
|
114
116
|
function renderArchitectText(template) {
|
|
@@ -136,15 +138,15 @@ const STATUS_HINT_RULES = [
|
|
|
136
138
|
},
|
|
137
139
|
{
|
|
138
140
|
when: (input) => input.active && input.openRoundExists === false,
|
|
139
|
-
render: (input) => renderWithOperationNames("Open a round with ${roundOpen} before
|
|
141
|
+
render: (input) => renderWithOperationNames("You can ask/summon for investigation now. Open a round with ${roundOpen} before recording delivery work.", input.operationNames),
|
|
140
142
|
},
|
|
141
143
|
{
|
|
142
144
|
when: (input) => !input.active && input.localKeiyakuBranches.length > 0,
|
|
143
|
-
render: (input) => `
|
|
145
|
+
render: (input) => `ask and summon are available here. To resume a contract, checkout: ${formatBranchCandidates(input.localKeiyakuBranches, STATUS_BRANCH_CANDIDATE_LIMIT)}.`,
|
|
144
146
|
},
|
|
145
147
|
{
|
|
146
148
|
when: (input) => !input.active && input.localKeiyakuBranches.length === 0,
|
|
147
|
-
render: (input) =>
|
|
149
|
+
render: (input) => "ask and summon are available for investigation outside a contract.",
|
|
148
150
|
},
|
|
149
151
|
{
|
|
150
152
|
when: (input) => input.protocolFiles.draft.present,
|
|
@@ -172,7 +174,7 @@ export function buildStatusDirtyWorktreeBlocker(dirtyStatusLines) {
|
|
|
172
174
|
: [];
|
|
173
175
|
return [STATUS_TEXT.blockers.dirtyWorktree, ...shownLines, ...overflowLine].join("\n");
|
|
174
176
|
}
|
|
175
|
-
export function
|
|
177
|
+
export function evaluateStatusUnavailableByCommand(input) {
|
|
176
178
|
const bindBlockers = [];
|
|
177
179
|
const roundOpenBlockers = [];
|
|
178
180
|
const roundCloseBlockers = [];
|
|
@@ -181,10 +183,10 @@ export function evaluateStatusBlockedByOperation(input) {
|
|
|
181
183
|
const hasBlockingDirtyForRoundOps = input.blockingDirtyStatusLines.length > 0;
|
|
182
184
|
const missingProtocolFiles = !input.protocolFiles.keiyaku.present;
|
|
183
185
|
if (hasDirtyWorktree) {
|
|
184
|
-
bindBlockers.push(
|
|
186
|
+
bindBlockers.push(STATUS_TEXT.blockers.dirtyWorktreeSummary);
|
|
185
187
|
}
|
|
186
188
|
if (hasBlockingDirtyForRoundOps) {
|
|
187
|
-
const roundOpsDirtyBlocker =
|
|
189
|
+
const roundOpsDirtyBlocker = STATUS_TEXT.blockers.dirtyWorktreeSummary;
|
|
188
190
|
roundOpenBlockers.push(roundOpsDirtyBlocker);
|
|
189
191
|
roundCloseBlockers.push(roundOpsDirtyBlocker);
|
|
190
192
|
}
|
|
@@ -209,6 +211,7 @@ export function evaluateStatusBlockedByOperation(input) {
|
|
|
209
211
|
const missingActiveBranch = renderWithOperationNames("No active keiyaku branch; checkout a keiyaku/* branch or ${bind} a new keiyaku first.", input.operationNames);
|
|
210
212
|
roundOpenBlockers.push(missingActiveBranch);
|
|
211
213
|
roundCloseBlockers.push(missingActiveBranch);
|
|
214
|
+
closeBlockers.push(missingActiveBranch);
|
|
212
215
|
if (input.protocolFiles.keiyaku.present) {
|
|
213
216
|
bindBlockers.push(STATUS_TEXT.blockers.bindProtocolOutsideActive);
|
|
214
217
|
}
|
package/build/config/env-keys.js
CHANGED
|
@@ -13,6 +13,7 @@ export const ENV_KEYS = {
|
|
|
13
13
|
FAKE_SUBAGENT: "KEIYAKU_FAKE_SUBAGENT",
|
|
14
14
|
SUBAGENT_EXEC_TIMEOUT_MS: "KEIYAKU_SUBAGENT_EXEC_TIMEOUT_MS",
|
|
15
15
|
SUBAGENT_EXEC_IDLE_TIMEOUT_MS: "KEIYAKU_SUBAGENT_EXEC_IDLE_TIMEOUT_MS",
|
|
16
|
+
SUBAGENT_PROGRESS_INTERVAL_MIN: "KEIYAKU_SUBAGENT_PROGRESS_INTERVAL_MIN",
|
|
16
17
|
CODEX_EXEC_MAX_CAPTURE_CHARS: "KEIYAKU_CODEX_EXEC_MAX_CAPTURE_CHARS",
|
|
17
18
|
CLAUDE_EXEC_MAX_CAPTURE_CHARS: "KEIYAKU_CLAUDE_EXEC_MAX_CAPTURE_CHARS",
|
|
18
19
|
GEMINI_EXEC_MAX_CAPTURE_CHARS: "KEIYAKU_GEMINI_EXEC_MAX_CAPTURE_CHARS",
|
|
@@ -33,6 +34,7 @@ const ENV_KEY_ENTRIES = [
|
|
|
33
34
|
{ key: ENV_KEYS.DISABLE_SUBAGENT_SPAWN, defaultValue: "0", description: "Disable subprocess subagent spawning when set to 1", category: "subagent" },
|
|
34
35
|
{ key: ENV_KEYS.SUBAGENT_EXEC_TIMEOUT_MS, defaultValue: "2700000", description: "Subagent execution timeout in milliseconds", category: "subagent" },
|
|
35
36
|
{ key: ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS, defaultValue: "600000", description: "Subagent idle timeout in milliseconds", category: "subagent" },
|
|
37
|
+
{ key: ENV_KEYS.SUBAGENT_PROGRESS_INTERVAL_MIN, defaultValue: "0", description: "Print subagent tool/activity summaries every N minutes when greater than 0", category: "subagent" },
|
|
36
38
|
{ key: ENV_KEYS.CODEX_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "Codex runtime output/diagnostics capture cap", category: "subagent" },
|
|
37
39
|
{ key: ENV_KEYS.CLAUDE_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "Claude runtime output/diagnostics capture cap", category: "subagent" },
|
|
38
40
|
{ key: ENV_KEYS.GEMINI_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "Gemini runtime output/diagnostics capture cap", category: "subagent" },
|
package/build/config/schema.js
CHANGED
|
@@ -73,6 +73,7 @@ const subagentSchema = z.object({
|
|
|
73
73
|
disableSpawn: booleanFlagEnv(ENV_KEYS.DISABLE_SUBAGENT_SPAWN),
|
|
74
74
|
execTimeoutMs: positiveIntEnv(ENV_KEYS.SUBAGENT_EXEC_TIMEOUT_MS),
|
|
75
75
|
execIdleTimeoutMs: positiveIntEnv(ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS),
|
|
76
|
+
progressIntervalMin: nonNegativeIntEnv(ENV_KEYS.SUBAGENT_PROGRESS_INTERVAL_MIN),
|
|
76
77
|
fakeMode: z
|
|
77
78
|
.string()
|
|
78
79
|
.default(getEnvDefaultValue(ENV_KEYS.FAKE_SUBAGENT))
|
|
@@ -120,6 +121,7 @@ export const envSchema = z
|
|
|
120
121
|
[ENV_KEYS.FAKE_SUBAGENT]: z.string().optional(),
|
|
121
122
|
[ENV_KEYS.SUBAGENT_EXEC_TIMEOUT_MS]: z.string().optional(),
|
|
122
123
|
[ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS]: z.string().optional(),
|
|
124
|
+
[ENV_KEYS.SUBAGENT_PROGRESS_INTERVAL_MIN]: z.string().optional(),
|
|
123
125
|
[ENV_KEYS.CODEX_EXEC_MAX_CAPTURE_CHARS]: z.string().optional(),
|
|
124
126
|
[ENV_KEYS.CLAUDE_EXEC_MAX_CAPTURE_CHARS]: z.string().optional(),
|
|
125
127
|
[ENV_KEYS.GEMINI_EXEC_MAX_CAPTURE_CHARS]: z.string().optional(),
|
|
@@ -143,6 +145,7 @@ export const envSchema = z
|
|
|
143
145
|
disableSpawn: rawEnv[ENV_KEYS.DISABLE_SUBAGENT_SPAWN],
|
|
144
146
|
execTimeoutMs: rawEnv[ENV_KEYS.SUBAGENT_EXEC_TIMEOUT_MS],
|
|
145
147
|
execIdleTimeoutMs: rawEnv[ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS],
|
|
148
|
+
progressIntervalMin: rawEnv[ENV_KEYS.SUBAGENT_PROGRESS_INTERVAL_MIN],
|
|
146
149
|
fakeMode: rawEnv[ENV_KEYS.FAKE_SUBAGENT],
|
|
147
150
|
}),
|
|
148
151
|
codex: codexSchema.parse({
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/generate-version.mjs
|
|
2
|
-
export const VERSION = "0.1.
|
|
2
|
+
export const VERSION = "0.1.87";
|
package/build/git/core.js
CHANGED
|
@@ -11,7 +11,8 @@ const INFO_SECTION_TITLE = "Info";
|
|
|
11
11
|
const HINTS_SECTION_TITLE = "Hints";
|
|
12
12
|
const REVIEW_SECTION_TITLE = "Review";
|
|
13
13
|
const STATUS_KEIYAKU_SECTION_TITLE = "Keiyaku";
|
|
14
|
-
const
|
|
14
|
+
const STATUS_WORKTREE_SECTION_TITLE = "Worktree";
|
|
15
|
+
const STATUS_COMMANDS_SECTION_TITLE = "Commands";
|
|
15
16
|
const CONTENT_SECTION_TITLE = "Content";
|
|
16
17
|
const STATUS_ACTIVE_SUMMARY_PREFIX = "● Active";
|
|
17
18
|
const STATUS_INACTIVE_SUMMARY = "○ Inactive";
|
|
@@ -26,8 +27,6 @@ const INFO_COMMIT_MAX_CHARS = 100;
|
|
|
26
27
|
const INFO_PLEA_MAX_CHARS = 220;
|
|
27
28
|
const DROPPED_CHANGES_MAX_ITEMS = FORMAT_LIST_MAX_ITEMS;
|
|
28
29
|
const HINTS_LIST_MAX_ITEMS = 12;
|
|
29
|
-
const BLOCKER_LIST_MAX_ITEMS = FORMAT_LIST_MAX_ITEMS;
|
|
30
|
-
const BLOCKER_LIST_MAX_ITEM_CHARS = 300;
|
|
31
30
|
const STATUS_PREVIEW_MAX_CHARS = 400;
|
|
32
31
|
const BIND_PROTOCOL_FILES_CREATED = [KEIYAKU_FILE];
|
|
33
32
|
const BIND_PROTOCOL_FILES_LABEL = "Protocol Files";
|
|
@@ -36,7 +35,6 @@ export const STRUCTURED_SUMMARY_MAX_CHARS = 20_000;
|
|
|
36
35
|
export const STRUCTURED_DIFF_MAX_CHARS = 8_000;
|
|
37
36
|
export const STRUCTURED_RULES_MAX_CHARS = 5_000;
|
|
38
37
|
export const STRUCTURED_TRUNCATED_SUFFIX = "...[truncated]";
|
|
39
|
-
const BLOCKED_ITEMS_FIELD = "blockedItems";
|
|
40
38
|
const HEADER_MAX_WIDTH = 40;
|
|
41
39
|
const HEADER_DASH = "┈";
|
|
42
40
|
function supportsHeaderColor() {
|
|
@@ -168,16 +166,8 @@ function buildSection(title, content) {
|
|
|
168
166
|
return null;
|
|
169
167
|
return { title, body };
|
|
170
168
|
}
|
|
171
|
-
function
|
|
172
|
-
|
|
173
|
-
return [];
|
|
174
|
-
return [
|
|
175
|
-
`${label}:`,
|
|
176
|
-
...formatList(`${label} Blockers`, blockers, {
|
|
177
|
-
maxItems: BLOCKER_LIST_MAX_ITEMS,
|
|
178
|
-
maxItemChars: BLOCKER_LIST_MAX_ITEM_CHARS,
|
|
179
|
-
}),
|
|
180
|
-
];
|
|
169
|
+
function formatUnavailableReasons(reasons) {
|
|
170
|
+
return reasons.map((reason) => reason.trim().replace(/[.。]+$/, "")).join("; ");
|
|
181
171
|
}
|
|
182
172
|
function resolveStructuredStringLimit(key, parentKey) {
|
|
183
173
|
if (key === "summary")
|
|
@@ -200,8 +190,6 @@ function resolveStructuredStringLimit(key, parentKey) {
|
|
|
200
190
|
if (key === "keiyakuPreview" || key === "openRoundPreview" || key === "lastClosedRoundPreview") {
|
|
201
191
|
return STATUS_PREVIEW_MAX_CHARS;
|
|
202
192
|
}
|
|
203
|
-
if (parentKey === BLOCKED_ITEMS_FIELD)
|
|
204
|
-
return BLOCKER_LIST_MAX_ITEM_CHARS;
|
|
205
193
|
if (key === "hints" || key === "existingBranches" || key === "droppedChanges") {
|
|
206
194
|
return FORMAT_LIST_MAX_ITEM_CHARS;
|
|
207
195
|
}
|
|
@@ -480,19 +468,14 @@ export function buildHelpResponse(input) {
|
|
|
480
468
|
};
|
|
481
469
|
}
|
|
482
470
|
export function buildStatusResponse(result, labels) {
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
blockedItems[labels.roundClose] = result.blockedByOperation.roundClose;
|
|
492
|
-
}
|
|
493
|
-
if (result.blockedByOperation.close.length > 0) {
|
|
494
|
-
blockedItems[labels.close] = result.blockedByOperation.close;
|
|
495
|
-
}
|
|
471
|
+
const commandReasons = {
|
|
472
|
+
[labels.bind]: result.unavailableByCommand.bind,
|
|
473
|
+
[labels.roundOpen]: result.unavailableByCommand.roundOpen,
|
|
474
|
+
[labels.roundClose]: result.unavailableByCommand.roundClose,
|
|
475
|
+
[labels.close]: result.unavailableByCommand.close,
|
|
476
|
+
};
|
|
477
|
+
const availableCommands = ["ask", labels.summon, ...Object.entries(commandReasons).filter(([, reasons]) => reasons.length === 0).map(([label]) => label)];
|
|
478
|
+
const unavailableCommands = Object.fromEntries(Object.entries(commandReasons).filter(([, reasons]) => reasons.length > 0));
|
|
496
479
|
const summaryLine = result.branch
|
|
497
480
|
? `${STATUS_ACTIVE_SUMMARY_PREFIX} — ${result.branch} (round ${result.round})`
|
|
498
481
|
: STATUS_INACTIVE_SUMMARY;
|
|
@@ -501,16 +484,17 @@ export function buildStatusResponse(result, labels) {
|
|
|
501
484
|
...formatMaybe("Open Round Preview", result.openRoundPreview, STATUS_PREVIEW_MAX_CHARS),
|
|
502
485
|
...formatMaybe("Last Closed Round Preview", result.lastClosedRoundPreview, STATUS_PREVIEW_MAX_CHARS),
|
|
503
486
|
];
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
...
|
|
508
|
-
...
|
|
487
|
+
const unavailableCommandLines = Object.entries(unavailableCommands).map(([label, reasons]) => `• ${label}: ${formatUnavailableReasons(reasons)}`);
|
|
488
|
+
const commandLines = [
|
|
489
|
+
"Available Commands:",
|
|
490
|
+
...availableCommands.map((label) => `• ${label}`),
|
|
491
|
+
...(unavailableCommandLines.length > 0 ? ["", "Unavailable Commands:", ...unavailableCommandLines] : []),
|
|
509
492
|
];
|
|
510
493
|
const hintLines = formatHints(result.hints, { maxItems: HINTS_LIST_MAX_ITEMS, maxItemChars: FORMAT_LIST_MAX_ITEM_CHARS });
|
|
511
494
|
const text = assembleResponse(`${RESPONSE_MARKERS.round} Status`, summaryLine, [
|
|
512
495
|
buildSection(STATUS_KEIYAKU_SECTION_TITLE, stateLines),
|
|
513
|
-
buildSection(
|
|
496
|
+
buildSection(STATUS_WORKTREE_SECTION_TITLE, result.worktreeStatus ?? "Clean"),
|
|
497
|
+
buildSection(STATUS_COMMANDS_SECTION_TITLE, commandLines),
|
|
514
498
|
buildSection(HINTS_SECTION_TITLE, hintLines),
|
|
515
499
|
].filter((section) => section !== null));
|
|
516
500
|
return {
|
|
@@ -524,7 +508,8 @@ export function buildStatusResponse(result, labels) {
|
|
|
524
508
|
keiyakuPreview: result.keiyakuPreview ?? null,
|
|
525
509
|
openRoundPreview: result.openRoundPreview ?? null,
|
|
526
510
|
lastClosedRoundPreview: result.lastClosedRoundPreview ?? null,
|
|
527
|
-
|
|
511
|
+
availableCommands,
|
|
512
|
+
unavailableCommands,
|
|
528
513
|
}),
|
|
529
514
|
};
|
|
530
515
|
}
|
package/build/tools/schema.js
CHANGED
|
@@ -26,6 +26,7 @@ export const summonToolSchemaBase = z.object({
|
|
|
26
26
|
task: z.string(),
|
|
27
27
|
context: z.string().optional(),
|
|
28
28
|
agent: z.string().optional(),
|
|
29
|
+
model: z.string().optional(),
|
|
29
30
|
resumePath: z.string().optional(),
|
|
30
31
|
incognito: z.boolean().optional(),
|
|
31
32
|
cwd: z.string().optional(),
|
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { KEIYAKU_ARTIFACT_DIRS, KEIYAKU_BRANCH_PREFIX, KEIYAKU_DRAFT_DIR, KEIYAKU_FILE, } from "../../keiyaku.js";
|
|
4
4
|
import { isErrnoException } from "../../errno.js";
|
|
5
5
|
import { FlowError } from "../../flow-error.js";
|
|
6
|
-
import {
|
|
6
|
+
import { evaluateStatusUnavailableByCommand, evaluateStatusHints, buildStatusDirtyWorktreeBlocker, STATUS_OPERATION_NAMES, } from "../../config/architect-hints.js";
|
|
7
7
|
import { getCurrentBranch, isGitRepo, listLocalKeiyakuBranches } from "../../git/branches.js";
|
|
8
8
|
import { getKeiyakuBase } from "../../git/keiyaku-state.js";
|
|
9
9
|
import { getDirtyFiles, getUntrackedFiles, isPathTracked, renderDirtyFileStatusLine } from "../../git/worktree.js";
|
|
@@ -45,8 +45,8 @@ function isPathInDirectory(filePath, directoryPath) {
|
|
|
45
45
|
const normalizedDirectory = normalizeStatusPath(directoryPath).replace(/\/+$/, "");
|
|
46
46
|
return normalizedPath === normalizedDirectory || normalizedPath.startsWith(`${normalizedDirectory}/`);
|
|
47
47
|
}
|
|
48
|
-
function
|
|
49
|
-
return
|
|
48
|
+
function buildUnavailableByCommand(input) {
|
|
49
|
+
return evaluateStatusUnavailableByCommand(input);
|
|
50
50
|
}
|
|
51
51
|
function buildStatusHints(input) {
|
|
52
52
|
return evaluateStatusHints(input);
|
|
@@ -71,6 +71,7 @@ export async function readKeiyakuStatus(input) {
|
|
|
71
71
|
const roundBlockingDirtyFiles = filterDirtyFilesByAllowlist(cwd, bindBlockingDirtyFiles, []).dirtyFiles;
|
|
72
72
|
const dirtyStatusLines = bindBlockingDirtyFiles.map((file) => renderDirtyFileStatusLine(file));
|
|
73
73
|
const blockingDirtyStatusLines = roundBlockingDirtyFiles.map((file) => renderDirtyFileStatusLine(file));
|
|
74
|
+
const worktreeStatus = dirtyStatusLines.length > 0 ? buildStatusDirtyWorktreeBlocker(dirtyStatusLines) : undefined;
|
|
74
75
|
const draftPath = latestDraft?.path ?? KEIYAKU_DRAFT_DIR;
|
|
75
76
|
const draftDirty = draftPresent ? rawDirtyPaths.includes(draftPath) : false;
|
|
76
77
|
const draftTracked = draftPresent ? await isPathTracked(cwd, draftPath) : undefined;
|
|
@@ -90,7 +91,7 @@ export async function readKeiyakuStatus(input) {
|
|
|
90
91
|
if (!currentBranch.startsWith(KEIYAKU_BRANCH_PREFIX)) {
|
|
91
92
|
const keiyakuContent = await readOptionalFile(cwd, KEIYAKU_FILE);
|
|
92
93
|
const localKeiyakuBranches = await listLocalKeiyakuBranches(cwd);
|
|
93
|
-
const
|
|
94
|
+
const unavailableByCommand = buildUnavailableByCommand({
|
|
94
95
|
active: false,
|
|
95
96
|
dirtyStatusLines,
|
|
96
97
|
blockingDirtyStatusLines,
|
|
@@ -100,8 +101,9 @@ export async function readKeiyakuStatus(input) {
|
|
|
100
101
|
return {
|
|
101
102
|
active: false,
|
|
102
103
|
round: 0,
|
|
104
|
+
worktreeStatus,
|
|
103
105
|
keiyakuPreview: toStatusPreview(keiyakuContent ? extractDocumentPreviewBySections(keiyakuContent, false) : undefined),
|
|
104
|
-
|
|
106
|
+
unavailableByCommand,
|
|
105
107
|
hints: buildStatusHints({
|
|
106
108
|
active: false,
|
|
107
109
|
protocolFiles,
|
|
@@ -121,7 +123,7 @@ export async function readKeiyakuStatus(input) {
|
|
|
121
123
|
const keiyakuPreview = toStatusPreview(keiyakuContent ? extractDocumentPreviewBySections(keiyakuContent, false) : undefined);
|
|
122
124
|
const openRoundPreview = renderRoundLedgerPreview(openRound);
|
|
123
125
|
const lastClosedRoundPreview = renderRoundLedgerPreview(latestClosedRound);
|
|
124
|
-
const
|
|
126
|
+
const unavailableByCommand = buildUnavailableByCommand({
|
|
125
127
|
active: true,
|
|
126
128
|
dirtyStatusLines,
|
|
127
129
|
blockingDirtyStatusLines,
|
|
@@ -135,10 +137,11 @@ export async function readKeiyakuStatus(input) {
|
|
|
135
137
|
branch: currentBranch,
|
|
136
138
|
baseBranch: baseBranch ?? undefined,
|
|
137
139
|
round: getMaxRound(ledgerEntries),
|
|
140
|
+
worktreeStatus,
|
|
138
141
|
keiyakuPreview,
|
|
139
142
|
openRoundPreview,
|
|
140
143
|
lastClosedRoundPreview,
|
|
141
|
-
|
|
144
|
+
unavailableByCommand,
|
|
142
145
|
hints: buildStatusHints({
|
|
143
146
|
active: true,
|
|
144
147
|
baseBranch: baseBranch ?? undefined,
|
|
@@ -70,6 +70,7 @@ export async function runSummon(input) {
|
|
|
70
70
|
const subagentResult = await runSubagentWithResult(selectedSubagent, prompt, cwd, keiyakuContext.openRound?.round ?? 0, {
|
|
71
71
|
signal,
|
|
72
72
|
resume,
|
|
73
|
+
model: input.model,
|
|
73
74
|
});
|
|
74
75
|
return {
|
|
75
76
|
summary: subagentResult.summary,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrosheep/keiyaku",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.87",
|
|
4
4
|
"description": "CLI for running iterative keiyaku workflows with Codex subagents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@google/gemini-cli-core": "^0.42.0",
|
|
45
45
|
"@modelcontextprotocol/sdk": "1.26.0",
|
|
46
46
|
"@openai/codex-sdk": "^0.113.0",
|
|
47
|
-
"simple-git": "^3.
|
|
47
|
+
"simple-git": "^3.36.0",
|
|
48
48
|
"zod": "4.3.6"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
package/skills/keiyaku/SKILL.md
CHANGED
|
@@ -21,12 +21,12 @@ keiyaku <command> --help
|
|
|
21
21
|
Use this shape for committed delivery work:
|
|
22
22
|
|
|
23
23
|
```text
|
|
24
|
-
bind -> round open -> [summon | amend | delivery work]* -> delivery commits -> round close -> [round open | amend | petition claim | petition forfeit]
|
|
24
|
+
bind -> round open -> [summon/ask | amend | delivery work]* -> delivery commits -> round close -> [round open | amend | petition claim | petition forfeit]
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
- `bind`: create the active Keiyaku branch and `KEIYAKU.md` from Markdown stdin.
|
|
28
28
|
- `round open`: record the scope/objective/brief before delivery work starts.
|
|
29
|
-
- `summon`: summon a Zako helper for bounded work with `keiyaku summon NAME [prompt|-]`.
|
|
29
|
+
- `summon` / `ask`: summon a Zako helper for bounded work with `keiyaku summon NAME [prompt|-]` or `keiyaku ask NAME [prompt|-]`.
|
|
30
30
|
- `resume`: continue a prior summon session with `keiyaku resume RESPONSE_PATH [prompt|-]`.
|
|
31
31
|
- `zako list` / `zako show <name>`: inspect available Zako profiles before choosing `keiyaku summon NAME`.
|
|
32
32
|
- `round close`: after delivery commits exist, record the report and non-empty commit list.
|
|
@@ -37,7 +37,7 @@ bind -> round open -> [summon | amend | delivery work]* -> delivery commits -> r
|
|
|
37
37
|
|
|
38
38
|
## Temporary Work
|
|
39
39
|
|
|
40
|
-
For temporary tasks, quick inspection, or bounded helper work, use `summon` directly. It does not require an active Keiyaku. If a round is open, the Zako receives the round scope automatically; otherwise it receives only the task, context, and reference files.
|
|
40
|
+
For temporary tasks, quick inspection, or bounded helper work, use `summon` or `ask` directly. It does not require an active Keiyaku. If a round is open, the Zako receives the round scope automatically; otherwise it receives only the task, context, and reference files.
|
|
41
41
|
|
|
42
42
|
Recommended summon prompts use this shape, but the CLI accepts plain text and does not require Markdown sections:
|
|
43
43
|
|
|
@@ -60,6 +60,7 @@ keiyaku guide
|
|
|
60
60
|
keiyaku zako list
|
|
61
61
|
keiyaku round --help
|
|
62
62
|
keiyaku summon --help
|
|
63
|
+
keiyaku ask --help
|
|
63
64
|
keiyaku resume --help
|
|
64
65
|
keiyaku petition claim --help
|
|
65
66
|
```
|