jeo-code 0.6.40 → 0.6.41
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/CHANGELOG.md +11 -0
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/package.json +1 -1
- package/src/agent/config-schema.ts +1 -0
- package/src/agent/state.ts +1 -0
- package/src/ai/model-manager.ts +23 -5
- package/src/commands/launch.ts +48 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
|
|
8
8
|
|
|
9
|
+
## [0.6.41] - 2026-06-21
|
|
10
|
+
_The non-streaming model path now inherits the v0.6.40 300s window: an unattended long-reasoning turn (compaction, ralplan, deep-interview, memory distill, goal-verify, subagent/autopilot steps) is no longer false-aborted at the old 120s wall cap. Re-verified leak-free (`mem-probe`, 2000 turns, −610 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6)._
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **The non-streaming `call()` path no longer false-aborts a long completion at 120s — it now matches the 300s streaming idle window.** v0.6.40's wire heartbeat only re-armed the streaming watchdog (`manager.stream()`); the non-streaming twin `manager.call()` — taken by every `callLlm` WITHOUT an `onToken` consumer (`compaction`, `ralplan`, `deep-interview`, `memory` distill, `goal-verifier`, and subagent/autopilot engine steps) — kept a hard 120s wall cap, so the exact unattended long-reasoning turns the release meant to protect still aborted, exhausted the attempt budget, and stopped the turn. The cap is now 300s, matching `STREAM_IDLE_TIMEOUT_MS`, via a new env-overridable `callTimeoutMs()` helper (`JEO_CALL_TIMEOUT_MS`) mirroring the existing `streamIdleMs()`. A wire heartbeat can't help here — the path collects an opaque buffered body with no per-chunk signal — so a wall clock is the only correct lever; non-streaming `readSse` sites and `call()`'s buffered reads are deliberately left without an idle watchdog (it would false-abort a healthy opaque read).
|
|
14
|
+
|
|
15
|
+
### Verified
|
|
16
|
+
- `bun run typecheck` clean; full suite **1756 pass / 0 fail** (216 files), including the new `callTimeoutMs` default + `JEO_CALL_TIMEOUT_MS` override parsing tests in `test/round-b.test.ts`.
|
|
17
|
+
- `scripts/mem-probe.ts` (2000 turns × 40 tools): no long-term leak (per-turn slope **−610 bytes/turn**, exit-listeners flat at 1, heap returns to baseline).
|
|
18
|
+
- `scripts/tmux-verify.sh battery`: **6/6 PASSED** on a fresh `jeo --tmux` boot (boot, `/help`, unknown `$skill`, `/agents`, `$ultragoal`, unresolved `/command`).
|
|
19
|
+
|
|
9
20
|
## [0.6.40] - 2026-06-21
|
|
10
21
|
_Wire-level stream heartbeat: ANY bytes from the provider (SSE keepalive/ping, filtered events) now re-arm the idle watchdog — a connected-but-quiet stream can no longer trip a false `stream idle` retry. Default idle window raised to 300s so Ollama/llama.cpp model-load silence no longer exhausts retries and stops the turn._
|
|
11
22
|
|
package/README.ja.md
CHANGED
|
@@ -200,11 +200,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
|
|
|
200
200
|
## 変更履歴 (Changelog)
|
|
201
201
|
|
|
202
202
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
203
|
+
- **[0.6.41]** (2026-06-21) — The non-streaming model path now inherits the v0.6.40 300s window: an unattended long-reasoning turn (compaction, ralplan, deep-interview, memory distill, goal-verify, subagent/autopilot steps) is no longer false-aborted at the old 120s wall cap. Re-verified leak-free (`mem-probe`, 2000 turns, −610 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
203
204
|
- **[0.6.40]** (2026-06-21) — Wire-level stream heartbeat: ANY bytes from the provider (SSE keepalive/ping, filtered events) now re-arm the idle watchdog — a connected-but-quiet stream can no longer trip a false `stream idle` retry. Default idle window raised to 300s so Ollama/llama.cpp model-load silence no longer exhausts retries and stops the turn.
|
|
204
205
|
- **[0.6.39]** (2026-06-21) — A long "thinking" phase no longer trips a false stream-idle retry: reasoning/thinking deltas now act as a stream heartbeat, so a model that streams thought tokens past the idle window before any visible text is no longer mistaken for a stalled stream and retried (which discarded the in-progress reasoning). Re-verified leak-free (`mem-probe`, 2000 turns, −525 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
205
206
|
- **[0.6.38]** (2026-06-21) — The OKF concept-bundle memory no longer silently drops what it learns: a legacy single-doc `MEMORY.md` (or a text-only distill fallback) can no longer shadow the concept bundle, break OKF conformance, or lose a turn's learnings — its content is folded into the concept merge and the stale blob is archived. Re-verified leak-free (`mem-probe`, 2000 turns, −570 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
206
207
|
- **[0.6.37]** (2026-06-20) — Two dead-end fixes: the boxed prompt's ↑/↓ now recalls input history on a soft-wrapped one-liner (only a genuine multi-line draft gets in-box caret nav), and every terminating Spec-first stage (deep-interview, ralplan, team) now surfaces a user-visible answer instead of silently stalling — re-verified leak-free (`mem-probe`) with a fresh `jeo --tmux` boot.
|
|
207
|
-
- **[0.6.36]** (2026-06-20) — When `jeo --tmux` flips the mouse on so you can drag-select, the drag now actually lands on the system clipboard — the in-session tmux profile sets `set-clipboard on` + a local `copy-command` on the CURRENT session only — plus `/help` documents the drag-to-copy and the Shift/Option-drag escape hatch, re-verified leak-free (`mem-probe`) with a fresh `jeo --tmux` boot.
|
|
208
208
|
|
|
209
209
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
210
210
|
<!-- CHANGELOG:END -->
|
package/README.ko.md
CHANGED
|
@@ -200,11 +200,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
|
|
|
200
200
|
## 변경 이력 (Changelog)
|
|
201
201
|
|
|
202
202
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
203
|
+
- **[0.6.41]** (2026-06-21) — The non-streaming model path now inherits the v0.6.40 300s window: an unattended long-reasoning turn (compaction, ralplan, deep-interview, memory distill, goal-verify, subagent/autopilot steps) is no longer false-aborted at the old 120s wall cap. Re-verified leak-free (`mem-probe`, 2000 turns, −610 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
203
204
|
- **[0.6.40]** (2026-06-21) — Wire-level stream heartbeat: ANY bytes from the provider (SSE keepalive/ping, filtered events) now re-arm the idle watchdog — a connected-but-quiet stream can no longer trip a false `stream idle` retry. Default idle window raised to 300s so Ollama/llama.cpp model-load silence no longer exhausts retries and stops the turn.
|
|
204
205
|
- **[0.6.39]** (2026-06-21) — A long "thinking" phase no longer trips a false stream-idle retry: reasoning/thinking deltas now act as a stream heartbeat, so a model that streams thought tokens past the idle window before any visible text is no longer mistaken for a stalled stream and retried (which discarded the in-progress reasoning). Re-verified leak-free (`mem-probe`, 2000 turns, −525 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
205
206
|
- **[0.6.38]** (2026-06-21) — The OKF concept-bundle memory no longer silently drops what it learns: a legacy single-doc `MEMORY.md` (or a text-only distill fallback) can no longer shadow the concept bundle, break OKF conformance, or lose a turn's learnings — its content is folded into the concept merge and the stale blob is archived. Re-verified leak-free (`mem-probe`, 2000 turns, −570 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
206
207
|
- **[0.6.37]** (2026-06-20) — Two dead-end fixes: the boxed prompt's ↑/↓ now recalls input history on a soft-wrapped one-liner (only a genuine multi-line draft gets in-box caret nav), and every terminating Spec-first stage (deep-interview, ralplan, team) now surfaces a user-visible answer instead of silently stalling — re-verified leak-free (`mem-probe`) with a fresh `jeo --tmux` boot.
|
|
207
|
-
- **[0.6.36]** (2026-06-20) — When `jeo --tmux` flips the mouse on so you can drag-select, the drag now actually lands on the system clipboard — the in-session tmux profile sets `set-clipboard on` + a local `copy-command` on the CURRENT session only — plus `/help` documents the drag-to-copy and the Shift/Option-drag escape hatch, re-verified leak-free (`mem-probe`) with a fresh `jeo --tmux` boot.
|
|
208
208
|
|
|
209
209
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
210
210
|
<!-- CHANGELOG:END -->
|
package/README.md
CHANGED
|
@@ -201,11 +201,11 @@ Required npm token permissions (repository secret `NPM_TOKEN`):
|
|
|
201
201
|
## Changelog
|
|
202
202
|
|
|
203
203
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
204
|
+
- **[0.6.41]** (2026-06-21) — The non-streaming model path now inherits the v0.6.40 300s window: an unattended long-reasoning turn (compaction, ralplan, deep-interview, memory distill, goal-verify, subagent/autopilot steps) is no longer false-aborted at the old 120s wall cap. Re-verified leak-free (`mem-probe`, 2000 turns, −610 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
204
205
|
- **[0.6.40]** (2026-06-21) — Wire-level stream heartbeat: ANY bytes from the provider (SSE keepalive/ping, filtered events) now re-arm the idle watchdog — a connected-but-quiet stream can no longer trip a false `stream idle` retry. Default idle window raised to 300s so Ollama/llama.cpp model-load silence no longer exhausts retries and stops the turn.
|
|
205
206
|
- **[0.6.39]** (2026-06-21) — A long "thinking" phase no longer trips a false stream-idle retry: reasoning/thinking deltas now act as a stream heartbeat, so a model that streams thought tokens past the idle window before any visible text is no longer mistaken for a stalled stream and retried (which discarded the in-progress reasoning). Re-verified leak-free (`mem-probe`, 2000 turns, −525 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
206
207
|
- **[0.6.38]** (2026-06-21) — The OKF concept-bundle memory no longer silently drops what it learns: a legacy single-doc `MEMORY.md` (or a text-only distill fallback) can no longer shadow the concept bundle, break OKF conformance, or lose a turn's learnings — its content is folded into the concept merge and the stale blob is archived. Re-verified leak-free (`mem-probe`, 2000 turns, −570 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
207
208
|
- **[0.6.37]** (2026-06-20) — Two dead-end fixes: the boxed prompt's ↑/↓ now recalls input history on a soft-wrapped one-liner (only a genuine multi-line draft gets in-box caret nav), and every terminating Spec-first stage (deep-interview, ralplan, team) now surfaces a user-visible answer instead of silently stalling — re-verified leak-free (`mem-probe`) with a fresh `jeo --tmux` boot.
|
|
208
|
-
- **[0.6.36]** (2026-06-20) — When `jeo --tmux` flips the mouse on so you can drag-select, the drag now actually lands on the system clipboard — the in-session tmux profile sets `set-clipboard on` + a local `copy-command` on the CURRENT session only — plus `/help` documents the drag-to-copy and the Shift/Option-drag escape hatch, re-verified leak-free (`mem-probe`) with a fresh `jeo --tmux` boot.
|
|
209
209
|
|
|
210
210
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
211
211
|
<!-- CHANGELOG:END -->
|
package/README.zh.md
CHANGED
|
@@ -200,11 +200,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
|
|
|
200
200
|
## 更新日志 (Changelog)
|
|
201
201
|
|
|
202
202
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
203
|
+
- **[0.6.41]** (2026-06-21) — The non-streaming model path now inherits the v0.6.40 300s window: an unattended long-reasoning turn (compaction, ralplan, deep-interview, memory distill, goal-verify, subagent/autopilot steps) is no longer false-aborted at the old 120s wall cap. Re-verified leak-free (`mem-probe`, 2000 turns, −610 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
203
204
|
- **[0.6.40]** (2026-06-21) — Wire-level stream heartbeat: ANY bytes from the provider (SSE keepalive/ping, filtered events) now re-arm the idle watchdog — a connected-but-quiet stream can no longer trip a false `stream idle` retry. Default idle window raised to 300s so Ollama/llama.cpp model-load silence no longer exhausts retries and stops the turn.
|
|
204
205
|
- **[0.6.39]** (2026-06-21) — A long "thinking" phase no longer trips a false stream-idle retry: reasoning/thinking deltas now act as a stream heartbeat, so a model that streams thought tokens past the idle window before any visible text is no longer mistaken for a stalled stream and retried (which discarded the in-progress reasoning). Re-verified leak-free (`mem-probe`, 2000 turns, −525 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
205
206
|
- **[0.6.38]** (2026-06-21) — The OKF concept-bundle memory no longer silently drops what it learns: a legacy single-doc `MEMORY.md` (or a text-only distill fallback) can no longer shadow the concept bundle, break OKF conformance, or lose a turn's learnings — its content is folded into the concept merge and the stale blob is archived. Re-verified leak-free (`mem-probe`, 2000 turns, −570 bytes/turn slope) with a fresh `jeo --tmux` boot battery (6/6).
|
|
206
207
|
- **[0.6.37]** (2026-06-20) — Two dead-end fixes: the boxed prompt's ↑/↓ now recalls input history on a soft-wrapped one-liner (only a genuine multi-line draft gets in-box caret nav), and every terminating Spec-first stage (deep-interview, ralplan, team) now surfaces a user-visible answer instead of silently stalling — re-verified leak-free (`mem-probe`) with a fresh `jeo --tmux` boot.
|
|
207
|
-
- **[0.6.36]** (2026-06-20) — When `jeo --tmux` flips the mouse on so you can drag-select, the drag now actually lands on the system clipboard — the in-session tmux profile sets `set-clipboard on` + a local `copy-command` on the CURRENT session only — plus `/help` documents the drag-to-copy and the Shift/Option-drag escape hatch, re-verified leak-free (`mem-probe`) with a fresh `jeo --tmux` boot.
|
|
208
208
|
|
|
209
209
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
210
210
|
<!-- CHANGELOG:END -->
|
package/package.json
CHANGED
package/src/agent/state.ts
CHANGED
package/src/ai/model-manager.ts
CHANGED
|
@@ -383,9 +383,18 @@ async function resolveCall(options: Partial<CallOptions>, kind: "request" | "str
|
|
|
383
383
|
return { adapter, callOptions, credential: credentialForCall(provider, effective, config, baseUrl), retry };
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
/** Hard cap for a single
|
|
387
|
-
* blackholed/unreachable provider must not hang the agent or `jeo team`).
|
|
388
|
-
|
|
386
|
+
/** Hard wall-clock cap for a single NON-streaming provider request (service-readiness: a
|
|
387
|
+
* blackholed/unreachable provider must not hang the agent or `jeo team`). Unlike the
|
|
388
|
+
* streaming idle watchdog, this path collects an opaque buffered body (`response.json()`,
|
|
389
|
+
* or an internally-streamed collect in codexResponsesCall / antigravity.call) and exposes
|
|
390
|
+
* no per-chunk signal — so a wall clock is the only lever and a wire heartbeat cannot help.
|
|
391
|
+
* Raised to 300s to match STREAM_IDLE_TIMEOUT_MS: non-interactive turns (callLlm WITHOUT
|
|
392
|
+
* onToken — compaction, ralplan, deep-interview, memory distill, goal-verify, and subagent/
|
|
393
|
+
* autopilot engine steps) route here, and a long reasoning completion legitimately exceeds
|
|
394
|
+
* 120s; too-tight, it aborts an alive call, retries re-incur the same slow request, the
|
|
395
|
+
* attempt budget exhausts, and the turn STOPS — the same false-failure the streaming
|
|
396
|
+
* watchdog guards, on the path the wire heartbeat never reaches. */
|
|
397
|
+
const DEFAULT_CALL_TIMEOUT_MS = 300_000;
|
|
389
398
|
|
|
390
399
|
/** Per-chunk idle cap for streaming: a stream that emits NOTHING for this long is
|
|
391
400
|
* aborted, but a healthy long generation (chunks keep arriving) runs unbounded —
|
|
@@ -505,6 +514,15 @@ export function streamIdleMs(env?: Record<string, string | undefined>): number {
|
|
|
505
514
|
return Number.isFinite(n) && n > 0 ? n : STREAM_IDLE_TIMEOUT_MS;
|
|
506
515
|
}
|
|
507
516
|
|
|
517
|
+
/** Hard wall-clock cap (ms) for a NON-streaming provider call, falling back to the
|
|
518
|
+
* built-in default. Mirrors streamIdleMs so a slow non-interactive turn (callLlm without
|
|
519
|
+
* onToken) can relax the cap via JEO_CALL_TIMEOUT_MS without a code change. */
|
|
520
|
+
export function callTimeoutMs(env?: Record<string, string | undefined>): number {
|
|
521
|
+
const raw = jeoEnv("CALL_TIMEOUT_MS", env);
|
|
522
|
+
const n = raw !== undefined ? parseInt(raw, 10) : NaN;
|
|
523
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_CALL_TIMEOUT_MS;
|
|
524
|
+
}
|
|
525
|
+
|
|
508
526
|
export async function* retryableStream(
|
|
509
527
|
makeIter: () => AsyncIterator<string>,
|
|
510
528
|
retry: RetryOptions,
|
|
@@ -526,7 +544,7 @@ export function createModelManager(): ModelManager {
|
|
|
526
544
|
resolveProvider,
|
|
527
545
|
async call(messages, options = {}) {
|
|
528
546
|
const { adapter, callOptions, credential, retry } = await resolveCall(options);
|
|
529
|
-
return withRetry(() => adapter.call(messages, { ...callOptions, signal: withTimeout(callOptions.signal,
|
|
547
|
+
return withRetry(() => adapter.call(messages, { ...callOptions, signal: withTimeout(callOptions.signal, callTimeoutMs()) }, credential), retry);
|
|
530
548
|
},
|
|
531
549
|
async *stream(messages, options = {}) {
|
|
532
550
|
const { adapter, callOptions, credential, retry } = await resolveCall(options, "stream");
|
|
@@ -572,7 +590,7 @@ export function createModelManager(): ModelManager {
|
|
|
572
590
|
});
|
|
573
591
|
} else {
|
|
574
592
|
// Fallback: providers without streaming yield the full response as one chunk.
|
|
575
|
-
yield await withRetry(() => adapter.call(messages, { ...callOptions, signal: withTimeout(callOptions.signal,
|
|
593
|
+
yield await withRetry(() => adapter.call(messages, { ...callOptions, signal: withTimeout(callOptions.signal, callTimeoutMs()) }, credential), retry);
|
|
576
594
|
}
|
|
577
595
|
},
|
|
578
596
|
};
|
package/src/commands/launch.ts
CHANGED
|
@@ -1025,6 +1025,31 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
1025
1025
|
}
|
|
1026
1026
|
sessionUsage.turns++;
|
|
1027
1027
|
const usage = result.usage ? ` (${result.usage.inputTokens} in / ${result.usage.outputTokens} out tokens)` : "";
|
|
1028
|
+
|
|
1029
|
+
if (turnConfig.gitAutoCommit) {
|
|
1030
|
+
try {
|
|
1031
|
+
const statusRes = Bun.spawnSync(["git", "status", "--porcelain"], { cwd, stdout: "pipe", stderr: "ignore" });
|
|
1032
|
+
if (statusRes.exitCode === 0 && statusRes.stdout.toString().trim().length > 0) {
|
|
1033
|
+
Bun.spawnSync(["git", "add", "."], { cwd });
|
|
1034
|
+
const diffRes = Bun.spawnSync(["git", "diff", "--cached", "--name-status"], { cwd, stdout: "pipe", stderr: "ignore" });
|
|
1035
|
+
const changedFiles = diffRes.stdout.toString().trim().split("\n").map(line => line.split("\t")[1]).filter(Boolean);
|
|
1036
|
+
const fileSummary = changedFiles.length > 0 ? ` (modified: ${changedFiles.slice(0, 3).join(", ")}${changedFiles.length > 3 ? ", ..." : ""})` : "";
|
|
1037
|
+
const commitMsg = `[jeo] auto-commit${fileSummary}\n\nPrompt: ${userInput.slice(0, 100)}${userInput.length > 100 ? "..." : ""}`;
|
|
1038
|
+
const commitRes = Bun.spawnSync(["git", "commit", "-m", commitMsg], { cwd });
|
|
1039
|
+
if (commitRes.exitCode === 0) {
|
|
1040
|
+
if (tui) tui.events().onNotice?.(`(auto-committed changes to git)`);
|
|
1041
|
+
else console.log(`(auto-committed changes to git)`);
|
|
1042
|
+
} else {
|
|
1043
|
+
if (tui) tui.events().onNotice?.(`(auto-commit failed: git commit returned non-zero)`);
|
|
1044
|
+
else console.error(`(auto-commit failed: git commit returned non-zero)`);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
} catch (e) {
|
|
1048
|
+
if (tui) tui.events().onNotice?.(`(auto-commit failed: ${e instanceof Error ? e.message : String(e)})`);
|
|
1049
|
+
else console.error(`(auto-commit failed: ${e instanceof Error ? e.message : String(e)})`);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1028
1053
|
return { done: result.done, steps: result.steps, reply, rendered: !!tui, usage };
|
|
1029
1054
|
};
|
|
1030
1055
|
|
|
@@ -3349,6 +3374,29 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
3349
3374
|
}
|
|
3350
3375
|
continue;
|
|
3351
3376
|
}
|
|
3377
|
+
if (input === "/undo") {
|
|
3378
|
+
try {
|
|
3379
|
+
const logRes = Bun.spawnSync(["git", "log", "-1", "--pretty=%B"], { cwd, stdout: "pipe", stderr: "ignore" });
|
|
3380
|
+
if (logRes.exitCode === 0) {
|
|
3381
|
+
const msg = logRes.stdout.toString().trim();
|
|
3382
|
+
if (msg.startsWith("[jeo] auto-commit:")) {
|
|
3383
|
+
const resetRes = Bun.spawnSync(["git", "reset", "--hard", "HEAD~1"], { cwd, stdout: "pipe", stderr: "pipe" });
|
|
3384
|
+
if (resetRes.exitCode === 0) {
|
|
3385
|
+
console.log("(undid the last jeo auto-commit and restored the working tree)");
|
|
3386
|
+
} else {
|
|
3387
|
+
console.log(`! undo failed: ${resetRes.stderr.toString().trim()}`);
|
|
3388
|
+
}
|
|
3389
|
+
} else {
|
|
3390
|
+
console.log("! the last commit was not made by jeo (no '[jeo] auto-commit:' prefix). Use git manually to revert.");
|
|
3391
|
+
}
|
|
3392
|
+
} else {
|
|
3393
|
+
console.log("! undo failed: could not read git log (not a git repo?)");
|
|
3394
|
+
}
|
|
3395
|
+
} catch (err) {
|
|
3396
|
+
console.log(`! undo failed: ${(err as Error).message}`);
|
|
3397
|
+
}
|
|
3398
|
+
continue;
|
|
3399
|
+
}
|
|
3352
3400
|
if (input === "/goal" || input.startsWith("/goal ")) {
|
|
3353
3401
|
const arg = input.substring(5).trim();
|
|
3354
3402
|
if (!arg) {
|