@paleo/alcode 0.5.3 → 0.7.0
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 +2 -0
- package/dist/cli.js +8 -0
- package/dist/run-claude.d.ts +2 -0
- package/dist/run-claude.js +28 -4
- package/dist/session-file.d.ts +1 -1
- package/package.json +1 -1
- package/templates/cli-reference.md +10 -1
- package/templates/generic-guide.md +2 -0
- package/templates/openclaw-guide.md +2 -0
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ Coding runs can be very long: the caller always runs `alcode` as a background ta
|
|
|
12
12
|
|
|
13
13
|
If `alcode` is terminated, its signal handlers seal the session file (`status: failed`, `exitReason: terminated`) so it never stays frozen at `running`, then send `SIGTERM` to the coding-agent child, giving it a short grace to tear down its own subprocesses before a `SIGKILL` backstop guarantees no orphan is left behind. Only a `SIGKILL` of `alcode` itself (uncatchable) can leave a stale `running` status.
|
|
14
14
|
|
|
15
|
+
When the coding agent's own session on the host is missing or expired, `alcode` detects the `authentication_failed` signal in its stream, seals the session file with `exitReason: auth_required`, and exits `2` (distinct from `1`) with a one-line stderr message.
|
|
16
|
+
|
|
15
17
|
## Usage
|
|
16
18
|
|
|
17
19
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,9 @@ import { renderGuide } from "./guide.js";
|
|
|
5
5
|
import { assertPlansGate, listSessionRecords, resolveSessionFilePath, writeInitialSessionFile, } from "./session-file.js";
|
|
6
6
|
import { buildPrompt, PROTOCOLS } from "./prompt.js";
|
|
7
7
|
import { runClaude } from "./run-claude.js";
|
|
8
|
+
// Distinct from 1 (ordinary run failure) so a script can branch on an auth failure that needs an
|
|
9
|
+
// operator re-login rather than a retry.
|
|
10
|
+
const EXIT_AUTH_REQUIRED = 2;
|
|
8
11
|
export async function main(options) {
|
|
9
12
|
const argv = options?.argv ?? process.argv;
|
|
10
13
|
const stdout = options?.stdout ?? process.stdout;
|
|
@@ -73,6 +76,11 @@ async function runSession(parsed, ctx) {
|
|
|
73
76
|
if (parsed.isNew && result.sessionId) {
|
|
74
77
|
stdout.write(`\nSession ID: ${result.sessionId}\n`);
|
|
75
78
|
}
|
|
79
|
+
if (result.authRequired) {
|
|
80
|
+
stderr.write("alcode: coding agent not authenticated — an administrator must re-login on the host " +
|
|
81
|
+
"(`claude`, then `/login`).\n");
|
|
82
|
+
return EXIT_AUTH_REQUIRED;
|
|
83
|
+
}
|
|
76
84
|
return result.status === "succeeded" ? 0 : 1;
|
|
77
85
|
}
|
|
78
86
|
// Fail-fast launch guards, run against the (healed) session records before anything is written.
|
package/dist/run-claude.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface RunOutput {
|
|
|
15
15
|
}
|
|
16
16
|
export interface RunResult {
|
|
17
17
|
status: "succeeded" | "failed";
|
|
18
|
+
authRequired: boolean;
|
|
18
19
|
sessionId: string | null;
|
|
19
20
|
result: string;
|
|
20
21
|
}
|
|
@@ -26,6 +27,7 @@ export interface StreamState {
|
|
|
26
27
|
sessionId?: string;
|
|
27
28
|
result?: string;
|
|
28
29
|
isError: boolean;
|
|
30
|
+
authFailed: boolean;
|
|
29
31
|
}
|
|
30
32
|
export declare function createStreamState(): StreamState;
|
|
31
33
|
export declare function parseEventLine(line: string): unknown;
|
package/dist/run-claude.js
CHANGED
|
@@ -11,15 +11,25 @@ export async function runClaude(config, out) {
|
|
|
11
11
|
const state = createStreamState();
|
|
12
12
|
const outcome = await spawnClaude(config, state, out);
|
|
13
13
|
const failed = state.isError || outcome.exitCode !== 0 || state.result === undefined;
|
|
14
|
-
|
|
14
|
+
// An auth signal only matters when the run actually failed: claude retries a 401, so a signal it
|
|
15
|
+
// recovered from (run still succeeded) is not a problem to report.
|
|
16
|
+
const authRequired = failed && state.authFailed;
|
|
17
|
+
const result = authRequired
|
|
18
|
+
? authFailureMessage(state.result)
|
|
19
|
+
: (state.result ?? failureMessage(outcome));
|
|
15
20
|
applyCompletion(config.sessionFilePath, {
|
|
16
21
|
status: failed ? "failed" : "succeeded",
|
|
17
22
|
endedAt: new Date().toISOString(),
|
|
18
|
-
exitReason: failed ? "error" : "completed",
|
|
23
|
+
exitReason: authRequired ? "auth_required" : failed ? "error" : "completed",
|
|
19
24
|
sessionId: state.sessionId ?? null,
|
|
20
25
|
result,
|
|
21
26
|
});
|
|
22
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
status: failed ? "failed" : "succeeded",
|
|
29
|
+
authRequired,
|
|
30
|
+
sessionId: state.sessionId ?? null,
|
|
31
|
+
result,
|
|
32
|
+
};
|
|
23
33
|
}
|
|
24
34
|
function spawnClaude(config, state, out) {
|
|
25
35
|
const child = spawn("claude", buildClaudeArgs(config), {
|
|
@@ -167,7 +177,7 @@ function emitEvent(sessionFilePath, line, state, out) {
|
|
|
167
177
|
out.write(`${rendered}\n`);
|
|
168
178
|
}
|
|
169
179
|
export function createStreamState() {
|
|
170
|
-
return { isError: false };
|
|
180
|
+
return { isError: false, authFailed: false };
|
|
171
181
|
}
|
|
172
182
|
export function parseEventLine(line) {
|
|
173
183
|
try {
|
|
@@ -183,6 +193,7 @@ export function renderEvent(event, state) {
|
|
|
183
193
|
if (!isRecord(event))
|
|
184
194
|
return;
|
|
185
195
|
captureSessionId(event, state);
|
|
196
|
+
captureAuthFailure(event, state);
|
|
186
197
|
switch (event.type) {
|
|
187
198
|
case "system":
|
|
188
199
|
return event.subtype === "init" ? `[init] session ${asString(event.session_id)}` : undefined;
|
|
@@ -204,6 +215,10 @@ function captureSessionId(event, state) {
|
|
|
204
215
|
if (id)
|
|
205
216
|
state.sessionId = id;
|
|
206
217
|
}
|
|
218
|
+
function captureAuthFailure(event, state) {
|
|
219
|
+
if (event.error === "authentication_failed")
|
|
220
|
+
state.authFailed = true;
|
|
221
|
+
}
|
|
207
222
|
function captureResult(event, state) {
|
|
208
223
|
state.isError = event.is_error === true;
|
|
209
224
|
const result = asString(event.result);
|
|
@@ -248,6 +263,15 @@ function failureMessage(outcome) {
|
|
|
248
263
|
const stderr = outcome.stderr.trim();
|
|
249
264
|
return stderr || `claude exited with code ${outcome.exitCode ?? "unknown"}`;
|
|
250
265
|
}
|
|
266
|
+
// The remedy is an operator action on the host, not a retry — so the result block names it and
|
|
267
|
+
// keeps claude's own reason line (e.g. "OAuth session expired…") beneath it.
|
|
268
|
+
function authFailureMessage(claudeReason) {
|
|
269
|
+
const base = "Coding agent not authenticated (authentication_failed): the host session is missing, expired, " +
|
|
270
|
+
"or rejected. An administrator must re-login on the host (run `claude`, then `/login`) before " +
|
|
271
|
+
"alcode can run again.";
|
|
272
|
+
const detail = claudeReason?.trim();
|
|
273
|
+
return detail ? `${base}\n\n${detail}` : base;
|
|
274
|
+
}
|
|
251
275
|
// --- Shared helpers ---
|
|
252
276
|
function isRecord(value) {
|
|
253
277
|
return typeof value === "object" && value !== null;
|
package/dist/session-file.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export declare function writeInitialSessionFile(sessionFilePath: string, frontma
|
|
|
19
19
|
export interface CompletionUpdate {
|
|
20
20
|
status: "succeeded" | "failed";
|
|
21
21
|
endedAt: string;
|
|
22
|
-
exitReason: "completed" | "error" | "terminated";
|
|
22
|
+
exitReason: "completed" | "error" | "terminated" | "auth_required";
|
|
23
23
|
sessionId: string | null;
|
|
24
24
|
result: string;
|
|
25
25
|
}
|
package/package.json
CHANGED
|
@@ -48,11 +48,20 @@ alcode --new --protocol aad --ticket AB-123 --message "Task description"
|
|
|
48
48
|
|
|
49
49
|
Answer questions as in the spec flow. The agent implements and writes a summary file, which carries a suggested commit message.
|
|
50
50
|
|
|
51
|
+
## Review workflow
|
|
52
|
+
|
|
53
|
+
Two fresh sessions: one reviews, one fixes.
|
|
54
|
+
|
|
55
|
+
1. **Review** — `alcode --new --protocol review --ticket AB-123`. The agent reviews the current branch against the base branch and writes a review file; its path is in the run's result. The base defaults to the repository's default branch; override it via `--message "Base branch: \`develop\`"`.
|
|
56
|
+
2. **Fix** (optional) — `alcode --new --protocol aad --ticket AB-123 --message "Here is a code review: \`.plans/AB-123/B1-review.md\`. What should we fix?"` (adapt the path). The agent proposes fixes; decide together what to fix, as in any AAD session, then it implements and writes a summary file.
|
|
57
|
+
|
|
58
|
+
Skip the fix step when the review is informational.
|
|
59
|
+
|
|
51
60
|
## Other protocols
|
|
52
61
|
|
|
53
62
|
- **description** — `alcode --new --protocol description --ticket AB-123`. Writes a PR/MR description for committed work. No discussion.
|
|
54
63
|
- **read** — `alcode --new --protocol read --ticket AB-123 [--message "..."]`. Loads the ticket's spec and summary files into context; with a message, answers it against that context.
|
|
55
|
-
- **review** —
|
|
64
|
+
- **review** — see the review workflow above.
|
|
56
65
|
- **merge** — `alcode --new --protocol merge --ticket AB-123`. Resolves conflicts and summarizes tricky resolutions. Pass the incoming branch via `--message` to start the merge.
|
|
57
66
|
|
|
58
67
|
## Answering agent questions
|
|
@@ -14,6 +14,8 @@ Running under OpenClaw? Read `alcode --openclaw-guide` instead — the same manu
|
|
|
14
14
|
|
|
15
15
|
Read the run's session file (the path `alcode` prints on its first line, under `_alcode/`): its frontmatter carries `status` (`succeeded`/`failed`) and the `sessionId`, and the `---- Result ----` block holds the outcome. Report it to the user where the work was requested. If the run failed, say so plainly and propose the next step.
|
|
16
16
|
|
|
17
|
+
An `exitReason` of `auth_required` in the frontmatter (alcode also exits `2`) means the coding agent is not authenticated on the host: an administrator must re-login there before any run can succeed. Report that and do not retry.
|
|
18
|
+
|
|
17
19
|
Do **not** re-verify the repo, re-run the agent, or inspect `git` — the session file is authoritative. Keep session files in place; they are the durable audit trail.
|
|
18
20
|
|
|
19
21
|
{{CLI_REFERENCE}}
|
|
@@ -45,4 +45,6 @@ Any heartbeat received while an `alcode` run is **still pending** (running, or f
|
|
|
45
45
|
|
|
46
46
|
If the session file says the run failed, report that plainly and propose the next step; don't silently retry. When a session turns bad, keep everything in place — session files, directories, and records are the durable audit trail; never delete them; just start a new session.
|
|
47
47
|
|
|
48
|
+
If the frontmatter's `exitReason` is `auth_required`, the coding agent itself is not authenticated on the host (its session is missing or expired). No run can succeed until an administrator re-logs it in on the host. Tell the user exactly that, and do not retry — a retry hits the same wall.
|
|
49
|
+
|
|
48
50
|
{{CLI_REFERENCE}}
|