jeo-code 0.7.16 → 0.7.18
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 +34 -0
- package/README.ja.md +7 -7
- package/README.ko.md +7 -7
- package/README.md +32 -7
- package/README.zh.md +7 -7
- package/package.json +4 -2
- package/src/agent/AGENTS.md +2 -2
- package/src/agent/engine.ts +36 -13
- package/src/agent/goal-verifier.ts +6 -0
- package/src/agent/loop-guards.ts +24 -5
- package/src/agent/memory.ts +137 -63
- package/src/commands/deep-interview.ts +3 -1
- package/src/commands/launch.ts +163 -77
- package/src/commands/skills.ts +3 -12
- package/src/prompts/agents/architect.md +2 -0
- package/src/prompts/agents/critic.md +1 -0
- package/src/skills/catalog.ts +24 -1
- package/src/tui/components/autocomplete.ts +79 -3
- package/src/tui/components/live-model-picker.ts +40 -0
- package/src/tui/components/slash.ts +15 -3
- package/src/util/file-attachment.ts +58 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
## [0.7.18] - 2026-06-26
|
|
11
|
+
_Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget._
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **Slash completion gains gjc §2.1 fuzzy name matching with a description fallback.** `src/tui/components/autocomplete.ts` adds pure `fuzzyMatch`/`fuzzyScore` (exact 100 > startsWith 80 > includes 60 > subsequence `40 − gaps×5`, min 1) and a `fuzzyCommandHits` ranker, so `/mdl` → `/model` now completes while prefix-exact behaviour (`/mod` → `/model`) is preserved. When nothing matches a command name, both the autocomplete dropdown and `matchSlash` (`slash.ts`) fall back to a description substring match — `/oauth` → `/login`, `/clipboard` → `/dump` — gated to ≥2-char queries with a real substring hit to avoid flooding. New `SLASH_COMMAND_DESCRIPTIONS` map backs the fallback.
|
|
15
|
+
- **Resolved skills can contribute `/` slash commands, not just `$<name>`.** Every slash alias a resolved skill declares (frontmatter `aliases:`/`slash:`) becomes a real, dispatchable palette command grouped under "skills" — extending the slash menu, autocomplete, and previews. `runLaunchCommand` (`src/commands/launch.ts`) wires `skillSlashAliases`/`parseSkillSlashInvocation` so both the interactive REPL (`/obsidian-capture note`) and one-shot mode (`jeo "/obsidian-capture note"`) dispatch the owning skill, with built-in slash handlers always winning and first-declarer-wins alias scoping.
|
|
16
|
+
- **`buildThinkingLevelChoices` extracts the reasoning menu (gjc `#renderThinkingMenu`).** `src/tui/components/live-model-picker.ts` adds `THINKING_LEVEL_ORDER` and a pure `buildThinkingLevelChoices(current, { inheritLabel, tokenHint })` that renders `<level> — <description> (<tokens>)` with the current level flagged and an optional leading `inherit` row for role targets — fully unit-testable.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **`/model` runs gjc's two-menu target → reasoning flow.** After a model is picked, `applyPickedModelWithTarget` now shows `applyTargetChoices` ("Set as DEFAULT / EXECUTOR / …", which target uses the model) followed by the per-target reasoning menu. DEFAULT updates the active session model and default thinking; a subagent role writes `~/.jeo/config.json` (model + thinking, or inherit) without switching the chat model. Replaces the prior default-only assignment.
|
|
20
|
+
|
|
21
|
+
### Verified
|
|
22
|
+
- `bun run typecheck` clean; full suite green — 2009 pass / 0 fail across 234 files — including new `test/autocomplete.test.ts` cases (`fuzzyMatch` subsequence, `fuzzyScore` ranking, non-prefix subsequence completion, prefix-exact preservation, description-only fallback), `test/slash.test.ts` (`matchSlash` description fallback for intent queries), and `test/live-model-picker.test.ts` (`buildThinkingLevelChoices` ordering, token hints skipping minimal, `inherit` row only with a label, explicit role level outranking inherit).
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## [0.7.17] - 2026-06-25
|
|
26
|
+
_Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact._
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- **`bun run dev:link` / `dev:doctor` run the global `jeo` from this checkout's source.** New `scripts/dev-link.ts`: `link` symlinks `jeo` → `<repo>/src/cli.ts` into `~/.local/bin` (override `JEO_DEV_LINK_DIR`), refuses to proceed when another `jeo` shadows the managed link earlier on `PATH`, ensures the `#!/usr/bin/env bun` entry stays executable, and runs a `--version` smoke test; `doctor` classifies whatever `jeo` the current `PATH` resolves to as `linked` (this hot source), `drift` (a compiled binary or npm-installed copy — exit 1), or `missing`. Unlike gjc there is no `build:native` prerequisite — jeo has zero native deps, so a bare symlink to the shebang entry is fully functional. Pure helpers (`findShadowingJeo`, `classifyDevDoctor`, `splitPathEntries`, `defaultLinkDir`, `resolveJeoOnPath`) back the `dev:link`/`dev:doctor` `package.json` scripts.
|
|
30
|
+
- **OKF concept memory gains in-memory search, scoring, and budget-aware injection.** `src/agent/memory.ts` adds `loadConcepts`/`scoreConcept`/`searchConcepts` over the OKF concept bundle (`.jeo/memory`), `index.md` progressive disclosure, and a relevance-selected `memoryPromptSection` that fills the `MEMORY_INJECT_MAX_CHARS` budget by picking whole high-scoring concepts instead of mid-string truncation, plus `recordFailedAttempt` for failure-aware recall (docs/okf_mem sprint-03 Search & Reference).
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- **README documents skill migration/inspection and the dev workflow.** New "Skill migration and bundled skill inspection" section walks `jeo skills list` / `read <name>` / `sync --check` / `sync --force`, and a new "Development" section covers `dev:link`/`dev:doctor` and running from source — bringing the gajae-code README's targeted sections to jeo.
|
|
34
|
+
- **Workflow prompts (engine, goal-verifier, deep-interview, critic, architect) tightened against punting and false blocks.** The turn protocol now asks a multi-edit batch to name each change in its `reasoning` and a multi-file task to sketch a todo plan before the first edit, the `web_search` reflex re-searches when sources conflict, and OUTPUT_DISCIPLINE forbids punting with only a disclaimer/cutoff caveat/offer-to-search. The Goal Verifier judges the goal as written with positive per-requirement evidence (MET when satisfied, IMPOSSIBLE only when genuinely unsatisfiable); deep-interview stops filling ambiguity with assumptions yet still lowers its score when dimensions truly resolve; the critic treats softening as signal but never manufactures blocks; the architect notes a clean verdict ≠ no inspection and adds an `Inspected:` line. All escape hatches (single-use done latches, `MAX_RE_BLOCKS`, step-budget hard cap) are unchanged, so no new loop risk.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- **Attaching an image no longer pushes the input caret several columns past the `[image #N]` tag.** Terminals pad a dragged-and-dropped image path with their own surrounding spaces, and the Ctrl+V clipboard insert added its own separator, so the swapped-in tag ended up flanked by multiple spaces and the caret parked well past it (the "spacebar ×3" feel). The two attach paths now share one tested helper in `src/util/file-attachment.ts`: `normalizeImageTags` collapses the whitespace around every `[image #N]` to exactly one space (trimming line edges, idempotent), `insertImageTag` (Ctrl+V) inserts the tag single-spaced with the caret right after it, and `attachImagePaths` (drag-drop / submit-time) normalizes its output and returns the precise post-tag `cursor`. The live REPL parks the box caret at that offset, so it sits immediately after the tag's single trailing space — ready for the prompt.
|
|
38
|
+
|
|
39
|
+
### Verified
|
|
40
|
+
- **"Works beside your existing agent or bot" now reflects jeo's real interop surface (all four READMEs).** The table documents `--worktree <name>` (run in an isolated sibling git worktree — `src/commands/launch/tmux.ts` `resolveWorktree`) for the Codex/Claude/Claw rows and `jeo mcp serve` / `jeo mcp tools` (the MCP stdio tool contract — `src/commands/mcp.ts`) for the external-controller/bot row, instead of the prior generic "standard terminal/CLI interfaces" wording.
|
|
41
|
+
- `bun run typecheck` clean; full suite green — 1991 pass / 0 fail across 234 files — including the new `test/dev-link.test.ts` (target/link-dir resolution incl. `JEO_DEV_LINK_DIR` override, PATH splitting, earlier-on-PATH shadow detection, and doctor `linked`/`drift`/`missing` classification), the new `test/memory-search-okf.test.ts` (concept load/score/search, `index.md` disclosure, budget-aware relevance injection, failed-attempt recall), expanded `test/cycle-and-turn-budget.test.ts`, and expanded `test/file-attachment.test.ts` + `test/input-box.test.ts` (whitespace normalization around `[image #N]`, idempotence, `insertImageTag`/`caretAfterTag`/`attachImagePaths` caret offsets, and the end-to-end render proving the caret column lands right after the rendered tag). Real CLI runs verified `dev:link`/`dev:doctor` end-to-end against a temp link dir (symlink + `--version` smoke test, shadow-guard refusal, drift detection, `linked` after linking) and the macOS clipboard image reader end-to-end (`readClipboardImage` decoded a clipboard PNG via the `osascript` fallback).
|
|
42
|
+
|
|
43
|
+
|
|
10
44
|
## [0.7.16] - 2026-06-25
|
|
11
45
|
_Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite._
|
|
12
46
|
|
package/README.ja.md
CHANGED
|
@@ -153,13 +153,13 @@ jeo ultragoal
|
|
|
153
153
|
|
|
154
154
|
| ツールまたはボット | 推奨される jeo コマンド | 境界 |
|
|
155
155
|
| ----------- | ----------------------- | -------- |
|
|
156
|
-
| Codex CLI | `jeo --tmux
|
|
157
|
-
| Claude Code | `jeo --tmux` または `jeo
|
|
156
|
+
| Codex CLI | `jeo --tmux --worktree <name>` または `jeo` | `--worktree` は jeo が管理する兄弟 git worktree を指定します(basename → 新しいブランチ)。既存のパスは先に `cd` してください。 |
|
|
157
|
+
| Claude Code | `jeo --tmux` または `jeo --tmux --worktree <name>` | jeo は Claude Code の拡張機能にはなりません。 |
|
|
158
158
|
| OpenCode | `jeo` または `jeo --tmux` | 外部ランナーのワークフローのみ。 |
|
|
159
|
-
| Claw Code | `jeo --tmux
|
|
160
|
-
| 外部コントローラー / ボット | `jeo` (
|
|
159
|
+
| Claw Code | `jeo --tmux --worktree <name>` | jeo は Claw Code にインストールされたり、置き換えたりしません。 |
|
|
160
|
+
| 外部コントローラー / ボット | `jeo mcp serve` (MCP stdio サーバー) | 外部コントローラーはスクロールバックのスクレイピングではなく、MCP ツール契約を介して jeo を駆動します。 |
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
`--worktree <name>` は隔離された兄弟 git worktree で jeo を実行するため(パスがあれば再利用、なければ basename ブランチで作成)、リスクのある作業やレビュー対象の作業がメインのチェックアウトに触れることはありません。`jeo mcp serve` は stdio を介して MCP 対応のあらゆるコントローラーに jeo のツールを公開します(`jeo mcp tools` で一覧表示)。`-q`/`--quiet` (または `JEO_QUIET=1`) を追加すると、起動バナー・ウェルカムアニメーション・リリースノート・再開ヒントが抑制され、jeo を別のエージェントと並べて実行したりボットから駆動したりできます。`-p`/`--print` は quiet を含みます。
|
|
163
163
|
|
|
164
164
|
|
|
165
165
|
## ローカルモデル
|
|
@@ -205,11 +205,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
|
|
|
205
205
|
## 変更履歴 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.18]** (2026-06-26) — Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget.
|
|
209
|
+
- **[0.7.17]** (2026-06-25) — Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact.
|
|
208
210
|
- **[0.7.16]** (2026-06-25) — Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite.
|
|
209
211
|
- **[0.7.15]** (2026-06-25) — Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9.
|
|
210
212
|
- **[0.7.14]** (2026-06-25) — Quieter, cleaner output: a new `-q`/`--quiet` flag strips launch banners and courtesy logs, `/resume` no longer dumps walls of raw escaped JSON into scrollback, and a turn that edits files now reliably surfaces those paths to orchestrators.
|
|
211
|
-
- **[0.7.9]** (2026-06-24) — Local model providers actually run, and OAuth/login hygiene: Ollama requests now carry an explicit `num_ctx` so jeo's large system prompt no longer overflows Ollama's small default window (the "ollama 모델이 안 돌아간다" blocker), LM Studio reasoning models that emit the whole reply on the reasoning channel are recovered instead of dying empty, GGUF chat templates that can't render the `tools` array are retried with native tools stripped, the OAuth success tab auto-closes with a clear hint, the effective-credential picker prefers a verified OAuth login over a stray API key, and abandoned idle `jeo` tmux REPL sessions are reaped on launch.
|
|
212
|
-
- **[0.7.8]** (2026-06-24) — Long-session performance & resource hygiene: the detached memory-distill worker now always terminates itself (closing the `jeo memory-distill` orphan pileup that pinned each session's transcript in RSS), the live reasoning/output block re-wraps only its trailing window (O(tail) per frame instead of O(len²) over a long stream), the team loop stops reprinting the todo guide every iteration, and the readline caret stays aligned after an image attach.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/README.ko.md
CHANGED
|
@@ -153,13 +153,13 @@ jeo ultragoal
|
|
|
153
153
|
|
|
154
154
|
| 도구 또는 봇 | 권장 jeo 명령 | 경계 |
|
|
155
155
|
| ----------- | ----------------------- | -------- |
|
|
156
|
-
| Codex CLI | `jeo --tmux
|
|
157
|
-
| Claude Code | `jeo --tmux` 또는 `jeo
|
|
156
|
+
| Codex CLI | `jeo --tmux --worktree <name>` 또는 `jeo` | `--worktree`는 jeo가 관리하는 형제 git worktree를 지정합니다(basename → 새 브랜치). 기존 경로는 먼저 `cd`로 이동하세요. |
|
|
157
|
+
| Claude Code | `jeo --tmux` 또는 `jeo --tmux --worktree <name>` | jeo는 Claude Code 확장 프로그램이 되지 않습니다. |
|
|
158
158
|
| OpenCode | `jeo` 또는 `jeo --tmux` | 현재는 외부 러너 워크플로만 지원합니다. |
|
|
159
|
-
| Claw Code | `jeo --tmux
|
|
160
|
-
| 외부 컨트롤러 / 봇 | `jeo` (
|
|
159
|
+
| Claw Code | `jeo --tmux --worktree <name>` | jeo는 Claw Code에 설치되거나 대체되지 않습니다. |
|
|
160
|
+
| 외부 컨트롤러 / 봇 | `jeo mcp serve` (MCP stdio 서버) | 외부 컨트롤러는 스크롤백 스크래핑이 아니라 MCP 도구 계약을 통해 jeo를 구동합니다. |
|
|
161
161
|
|
|
162
|
-
`-q`/`--quiet` (또는 `JEO_QUIET=1`)를 추가하면 시작 배너, 환영 애니메이션, 릴리스 노트, 재개 힌트가 억제되어 jeo를 다른 에이전트와 나란히 실행하거나 봇으로 구동할 수 있습니다. `-p`/`--print`는 quiet를 함의합니다.
|
|
162
|
+
`--worktree <name>`는 격리된 형제 git worktree에서 jeo를 실행하므로(경로가 있으면 재사용, 없으면 basename 브랜치로 생성) 위험하거나 검토가 필요한 작업이 메인 체크아웃을 건드리지 않습니다. `jeo mcp serve`는 stdio를 통해 MCP를 지원하는 모든 컨트롤러에 jeo의 도구를 노출합니다(`jeo mcp tools`로 목록 확인). `-q`/`--quiet` (또는 `JEO_QUIET=1`)를 추가하면 시작 배너, 환영 애니메이션, 릴리스 노트, 재개 힌트가 억제되어 jeo를 다른 에이전트와 나란히 실행하거나 봇으로 구동할 수 있습니다. `-p`/`--print`는 quiet를 함의합니다.
|
|
163
163
|
|
|
164
164
|
|
|
165
165
|
## 로컬 모델
|
|
@@ -205,11 +205,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
|
|
|
205
205
|
## 변경 이력 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.18]** (2026-06-26) — Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget.
|
|
209
|
+
- **[0.7.17]** (2026-06-25) — Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact.
|
|
208
210
|
- **[0.7.16]** (2026-06-25) — Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite.
|
|
209
211
|
- **[0.7.15]** (2026-06-25) — Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9.
|
|
210
212
|
- **[0.7.14]** (2026-06-25) — Quieter, cleaner output: a new `-q`/`--quiet` flag strips launch banners and courtesy logs, `/resume` no longer dumps walls of raw escaped JSON into scrollback, and a turn that edits files now reliably surfaces those paths to orchestrators.
|
|
211
|
-
- **[0.7.9]** (2026-06-24) — Local model providers actually run, and OAuth/login hygiene: Ollama requests now carry an explicit `num_ctx` so jeo's large system prompt no longer overflows Ollama's small default window (the "ollama 모델이 안 돌아간다" blocker), LM Studio reasoning models that emit the whole reply on the reasoning channel are recovered instead of dying empty, GGUF chat templates that can't render the `tools` array are retried with native tools stripped, the OAuth success tab auto-closes with a clear hint, the effective-credential picker prefers a verified OAuth login over a stray API key, and abandoned idle `jeo` tmux REPL sessions are reaped on launch.
|
|
212
|
-
- **[0.7.8]** (2026-06-24) — Long-session performance & resource hygiene: the detached memory-distill worker now always terminates itself (closing the `jeo memory-distill` orphan pileup that pinned each session's transcript in RSS), the live reasoning/output block re-wraps only its trailing window (O(tail) per frame instead of O(len²) over a long stream), the team loop stops reprinting the todo guide every iteration, and the readline caret stays aligned after an image attach.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/README.md
CHANGED
|
@@ -154,13 +154,13 @@ Non-zero hook output is appended to the tool result the model reads (deduped per
|
|
|
154
154
|
|
|
155
155
|
| Tool or bot | Recommended jeo command | Boundary |
|
|
156
156
|
| ----------- | ----------------------- | -------- |
|
|
157
|
-
| Codex CLI | `jeo --tmux
|
|
158
|
-
| Claude Code | `jeo --tmux` or `jeo
|
|
157
|
+
| Codex CLI | `jeo --tmux --worktree <name>` or `jeo` | `--worktree` names a jeo-managed sibling git worktree (basename → new branch); for an existing path, `cd` there first. |
|
|
158
|
+
| Claude Code | `jeo --tmux` or `jeo --tmux --worktree <name>` | jeo does not become a Claude Code extension. |
|
|
159
159
|
| OpenCode | `jeo` or `jeo --tmux` | External-runner workflow only. |
|
|
160
|
-
| Claw Code | `jeo --tmux
|
|
161
|
-
| External controller / bot | `jeo` (
|
|
160
|
+
| Claw Code | `jeo --tmux --worktree <name>` | jeo does not install into or replace Claw Code. |
|
|
161
|
+
| External controller / bot | `jeo mcp serve` (MCP stdio server) | External controllers drive jeo over the MCP tool contract, not scrollback scraping. |
|
|
162
162
|
|
|
163
|
-
Add `-q`/`--quiet` (or `JEO_QUIET=1`) to suppress startup banners, welcome animation, release notes, and resume hints so jeo
|
|
163
|
+
`--worktree <name>` runs jeo in an isolated sibling git worktree (reused if the path exists, else created on a branch named after the basename) so risky or reviewable work never touches your main checkout. `jeo mcp serve` exposes jeo's tools to any MCP-capable controller over stdio (`jeo mcp tools` lists them). Add `-q`/`--quiet` (or `JEO_QUIET=1`) to suppress startup banners, the welcome animation, release notes, and resume hints so jeo runs cleanly beside another agent or is driven by a bot — `-p`/`--print` implies quiet.
|
|
164
164
|
|
|
165
165
|
|
|
166
166
|
## Local models
|
|
@@ -191,8 +191,33 @@ JEO_TOOL_OUTPUT_MAX=4000 # model-visible tool output cap (full output spi
|
|
|
191
191
|
|
|
192
192
|
Retry behavior is tunable via `retry` in `~/.jeo/config.json` (`requestMaxRetries`, `streamMaxRetries`, `rateLimitRetries`, `failFastStatuses`, …). The step budget is dynamic by default — it extends while recent tool calls show novel progress and consolidates with a wrap-up when stalled; `--max-steps N` restores a bounded flow.
|
|
193
193
|
|
|
194
|
+
## Skill migration and bundled skill inspection
|
|
195
|
+
|
|
196
|
+
When moving a workflow into jeo, inspect the bundled defaults before installing or overwriting anything:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
jeo skills list # bundled + user + project skills, with discovery dirs
|
|
200
|
+
jeo skills read ralplan # print one skill's full SKILL.md
|
|
201
|
+
jeo skills sync --check # report drift vs ~/.jeo/skills (non-zero exit on drift)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`jeo skills sync` installs the bundled workflow skills (deep-interview, deep-dive, ralplan, team, ultragoal) into `~/.jeo/skills` and **preserves existing local files by default** — a differing local copy is reported as `preserved`, never clobbered. If `--check` flags a missing or different file, compare it with `jeo skills read <name>` first; use `jeo skills sync --force` only when you intentionally want to replace local default workflow skill files. Target a different dir with a trailing path argument (or `JEO_CONFIG_DIR`), and add `--json` for the structured `SkillSyncResult`.
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
jeo is pure TypeScript on Bun with **zero native dependencies**, so the global `jeo` command can run this checkout's source directly — no build step, hot to every edit.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
bun install
|
|
212
|
+
bun run dev:link # symlink `jeo` -> <repo>/src/cli.ts into ~/.local/bin
|
|
213
|
+
bun run dev:doctor # report whether global `jeo` runs this source (linked/drift/missing)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`dev:link` refuses to proceed if another `jeo` shadows the managed link earlier on `PATH` (override the destination with `JEO_DEV_LINK_DIR`) and runs a `--version` smoke test. `dev:doctor` exits non-zero when the resolved `jeo` is a compiled binary or an installed copy rather than this source. Run from source without linking via `bun src/cli.ts --help`. Bundled workflow skills live in source at `src/prompts/skills/<name>/SKILL.md`; verify with `bun run typecheck` and `bun test`.
|
|
217
|
+
|
|
194
218
|
## Publishing
|
|
195
219
|
|
|
220
|
+
|
|
196
221
|
CI publishes via `.github/workflows/npm-publish.yml` — triggered by a published GitHub release, or manually with `workflow_dispatch` (optional dry-run). The workflow typechecks, tests, verifies the token (`npm whoami`), then runs `npm publish --provenance`.
|
|
197
222
|
|
|
198
223
|
Required npm token permissions (repository secret `NPM_TOKEN`):
|
|
@@ -207,11 +232,11 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
|
|
|
207
232
|
## Changelog
|
|
208
233
|
|
|
209
234
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
235
|
+
- **[0.7.18]** (2026-06-26) — Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget.
|
|
236
|
+
- **[0.7.17]** (2026-06-25) — Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact.
|
|
210
237
|
- **[0.7.16]** (2026-06-25) — Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite.
|
|
211
238
|
- **[0.7.15]** (2026-06-25) — Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9.
|
|
212
239
|
- **[0.7.14]** (2026-06-25) — Quieter, cleaner output: a new `-q`/`--quiet` flag strips launch banners and courtesy logs, `/resume` no longer dumps walls of raw escaped JSON into scrollback, and a turn that edits files now reliably surfaces those paths to orchestrators.
|
|
213
|
-
- **[0.7.9]** (2026-06-24) — Local model providers actually run, and OAuth/login hygiene: Ollama requests now carry an explicit `num_ctx` so jeo's large system prompt no longer overflows Ollama's small default window (the "ollama 모델이 안 돌아간다" blocker), LM Studio reasoning models that emit the whole reply on the reasoning channel are recovered instead of dying empty, GGUF chat templates that can't render the `tools` array are retried with native tools stripped, the OAuth success tab auto-closes with a clear hint, the effective-credential picker prefers a verified OAuth login over a stray API key, and abandoned idle `jeo` tmux REPL sessions are reaped on launch.
|
|
214
|
-
- **[0.7.8]** (2026-06-24) — Long-session performance & resource hygiene: the detached memory-distill worker now always terminates itself (closing the `jeo memory-distill` orphan pileup that pinned each session's transcript in RSS), the live reasoning/output block re-wraps only its trailing window (O(tail) per frame instead of O(len²) over a long stream), the team loop stops reprinting the todo guide every iteration, and the readline caret stays aligned after an image attach.
|
|
215
240
|
|
|
216
241
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
217
242
|
<!-- CHANGELOG:END -->
|
package/README.zh.md
CHANGED
|
@@ -153,13 +153,13 @@ jeo ultragoal
|
|
|
153
153
|
|
|
154
154
|
| 工具或机器人 | 推荐的 jeo 命令 | 边界 |
|
|
155
155
|
| ----------- | ----------------------- | -------- |
|
|
156
|
-
| Codex CLI | `jeo --tmux
|
|
157
|
-
| Claude Code | `jeo --tmux` 或 `jeo
|
|
156
|
+
| Codex CLI | `jeo --tmux --worktree <name>` 或 `jeo` | `--worktree` 指定一个由 jeo 管理的同级 git worktree(basename → 新分支);对于已有路径,请先 `cd` 进入。 |
|
|
157
|
+
| Claude Code | `jeo --tmux` 或 `jeo --tmux --worktree <name>` | jeo 不会成为 Claude Code 的扩展。 |
|
|
158
158
|
| OpenCode | `jeo` 或 `jeo --tmux` | 仅限外部运行器工作流。 |
|
|
159
|
-
| Claw Code | `jeo --tmux
|
|
160
|
-
| 外部控制器 / 机器人 | `jeo` (
|
|
159
|
+
| Claw Code | `jeo --tmux --worktree <name>` | jeo 不会安装到或替换 Claw Code。 |
|
|
160
|
+
| 外部控制器 / 机器人 | `jeo mcp serve` (MCP stdio 服务器) | 外部控制器通过 MCP 工具契约驱动 jeo,而非抓取滚动回显。 |
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
`--worktree <name>` 在隔离的同级 git worktree 中运行 jeo(路径存在则复用,否则以 basename 分支创建),因此有风险或需审查的工作绝不会触及您的主检出。`jeo mcp serve` 通过 stdio 向任何支持 MCP 的控制器公开 jeo 的工具(用 `jeo mcp tools` 列出)。添加 `-q`/`--quiet`(或 `JEO_QUIET=1`)可抑制启动横幅、欢迎动画、发布说明和恢复提示,使 jeo 能够与其他代理并行运行或由机器人驱动。`-p`/`--print` 隐含 quiet。
|
|
163
163
|
|
|
164
164
|
|
|
165
165
|
## 本地模型
|
|
@@ -205,11 +205,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
|
|
|
205
205
|
## 更新日志 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.18]** (2026-06-26) — Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget.
|
|
209
|
+
- **[0.7.17]** (2026-06-25) — Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact.
|
|
208
210
|
- **[0.7.16]** (2026-06-25) — Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite.
|
|
209
211
|
- **[0.7.15]** (2026-06-25) — Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9.
|
|
210
212
|
- **[0.7.14]** (2026-06-25) — Quieter, cleaner output: a new `-q`/`--quiet` flag strips launch banners and courtesy logs, `/resume` no longer dumps walls of raw escaped JSON into scrollback, and a turn that edits files now reliably surfaces those paths to orchestrators.
|
|
211
|
-
- **[0.7.9]** (2026-06-24) — Local model providers actually run, and OAuth/login hygiene: Ollama requests now carry an explicit `num_ctx` so jeo's large system prompt no longer overflows Ollama's small default window (the "ollama 모델이 안 돌아간다" blocker), LM Studio reasoning models that emit the whole reply on the reasoning channel are recovered instead of dying empty, GGUF chat templates that can't render the `tools` array are retried with native tools stripped, the OAuth success tab auto-closes with a clear hint, the effective-credential picker prefers a verified OAuth login over a stray API key, and abandoned idle `jeo` tmux REPL sessions are reaped on launch.
|
|
212
|
-
- **[0.7.8]** (2026-06-24) — Long-session performance & resource hygiene: the detached memory-distill worker now always terminates itself (closing the `jeo memory-distill` orphan pileup that pinned each session's transcript in RSS), the live reasoning/output block re-wraps only its trailing window (O(tail) per frame instead of O(len²) over a long stream), the team loop stops reprinting the todo guide every iteration, and the readline caret stays aligned after an image attach.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jeo-code",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.18",
|
|
4
4
|
"description": "Clean, highly optimized AI coding agent using spec-first loop",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/cli.ts",
|
|
@@ -52,7 +52,9 @@
|
|
|
52
52
|
"publish:npm": "npm publish --access public --registry https://registry.npmjs.org/",
|
|
53
53
|
"changelog:sync": "bun scripts/sync-changelog.ts",
|
|
54
54
|
"test": "bun test",
|
|
55
|
-
"verify:tmux": "bash scripts/tmux-verify.sh"
|
|
55
|
+
"verify:tmux": "bash scripts/tmux-verify.sh",
|
|
56
|
+
"dev:link": "bun scripts/dev-link.ts link",
|
|
57
|
+
"dev:doctor": "bun scripts/dev-link.ts doctor"
|
|
56
58
|
},
|
|
57
59
|
"dependencies": {
|
|
58
60
|
"zod": "^3.24.1",
|
package/src/agent/AGENTS.md
CHANGED
|
@@ -17,8 +17,8 @@ The core runtime loop, tool registry, session management, and state persistence
|
|
|
17
17
|
| `hooks.ts` | Brief description of purpose |
|
|
18
18
|
| `json.ts` | Brief description of purpose |
|
|
19
19
|
| `loop.ts` | The primary execution loop orchestrating model calls and tool execution |
|
|
20
|
-
| `loop-guards.ts` | Intermediate-judgment classification (gjc ultragoal-guard parity): named `GuardState` taxonomy, `GUARD_LIMITS` thresholds, and pure classifiers (`isVerificationSignal`, `repeatHint`, `classifyDoneGate`) consumed by `engine.ts` |
|
|
21
|
-
| `memory.ts` | OKF concept-bundle memory: session distill, query-aware budget injection, legacy MEMORY.md migration (`migrateLegacyMemory`) + `JEO_MEMORY_LEGACY` rollback toggle |
|
|
20
|
+
| `loop-guards.ts` | Intermediate-judgment classification (gjc ultragoal-guard parity): named `GuardState` taxonomy, `GUARD_LIMITS` thresholds, and pure classifiers (`isVerificationSignal`, `repeatHint`, `classifyDoneGate` — incl. `done_stale_verification` for a passing test/build that predates the last edit) consumed by `engine.ts` |
|
|
21
|
+
| `memory.ts` | OKF concept-bundle memory: session distill, query-aware budget injection (failure-first `priorityOrder` + pinned-invariant reserved budget), mid-loop `recordFailedAttempt` capture, shared `upsertConceptFile` writer, legacy MEMORY.md migration (`migrateLegacyMemory`) + `JEO_MEMORY_LEGACY` rollback toggle |
|
|
22
22
|
| `memory-okf.ts` | OKF v0.1 format layer: frontmatter parse/serialize, concept IDs, conformance validation |
|
|
23
23
|
| `memory-graph.ts` | Concept cross-link graph: build/expand (1-hop search), broken-link-tolerant lint, optional graphify detection |
|
|
24
24
|
| `model-recency.ts` | Brief description of purpose |
|
package/src/agent/engine.ts
CHANGED
|
@@ -105,9 +105,9 @@ export const TOOL_PROTOCOL = [
|
|
|
105
105
|
"",
|
|
106
106
|
"Alternatively, you may batch up to 6 independent calls in a single turn using the following format:",
|
|
107
107
|
'{ "reasoning": "<one short sentence>", "tools": [{ "tool": "<name>", "arguments": { ... } }, ...] }',
|
|
108
|
-
"Batch only independent calls; NEVER batch 'done', and NEVER put a mutating tool (write/edit/bash) after another mutating tool in one batch whose inputs depend on the earlier one.",
|
|
109
|
-
"Tool calibration: scale calls to difficulty — one for a known fact, a few for a normal task, more only when evidence is genuinely missing. Locate before you open: search/find first, then read the hit, instead of guessing paths.",
|
|
110
|
-
"web_search reflex: if the request hinges on a name, version, library, or event you do not actually recognize, search before answering instead of guessing; never claim a result's absence proves nonexistence.",
|
|
108
|
+
"Batch only independent calls; NEVER batch 'done', and NEVER put a mutating tool (write/edit/bash) after another mutating tool in one batch whose inputs depend on the earlier one. In a multi-edit batch, let the turn `reasoning` name each change — not a vague one-liner.",
|
|
109
|
+
"Tool calibration: scale calls to difficulty — one for a known fact, a few for a normal task, more only when evidence is genuinely missing. For multi-file or multi-step work, sketch a short plan (todo) before the first edit; a one-line change goes straight to the edit. Locate before you open: search/find first, then read the hit, instead of guessing paths.",
|
|
110
|
+
"web_search reflex: if the request hinges on a name, version, library, or event you do not actually recognize, search before answering instead of guessing; never claim a result's absence proves nonexistence. When sources conflict or look incomplete, run another targeted search to disambiguate rather than picking one at random.",
|
|
111
111
|
"Quoting fetched/searched text: paraphrase by default — quote at most one short phrase per source, cite it, and never paste long passages.",
|
|
112
112
|
].join("\n");
|
|
113
113
|
|
|
@@ -165,6 +165,7 @@ export const OUTPUT_DISCIPLINE = [
|
|
|
165
165
|
"- When using lists, ensure each bullet carries a complete thought; avoid fragmented or shredded reports.",
|
|
166
166
|
"- Don't stall on ambiguity: make reasonable assumptions and ask at most one clarifying question if absolutely necessary.",
|
|
167
167
|
"- Report only what is done or in progress; never announce future work instead of doing it.",
|
|
168
|
+
"- Give a substantive answer first; never punt with only a disclaimer, a knowledge-cutoff caveat, or an offer to search — answer with what you have, then note the caveat.",
|
|
168
169
|
"- Match reply length to the task: a one-line change gets a one-line report.",
|
|
169
170
|
].join("\n");
|
|
170
171
|
|
|
@@ -272,6 +273,10 @@ export interface AgentLoopResult {
|
|
|
272
273
|
doneReason?: string;
|
|
273
274
|
/** Summed provider token usage across the turn's steps, when reported. */
|
|
274
275
|
usage?: { inputTokens: number; outputTokens: number };
|
|
276
|
+
/** Set ONLY when the turn ended in a guard-detected dead end the model could not
|
|
277
|
+
* recover from (repeated/cycled/consecutively-failing calls). Lets the caller
|
|
278
|
+
* capture the stall into failure memory; `undefined` for done/budget/cancelled. */
|
|
279
|
+
stopClass?: "consecutive_failure" | "cycle" | "repeat";
|
|
275
280
|
}
|
|
276
281
|
|
|
277
282
|
|
|
@@ -369,7 +374,7 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
369
374
|
// "Stopped: …" — throwing away everything found this turn — do ONE final no-tools
|
|
370
375
|
// call asking the model to answer with what it already has. Mirrors the
|
|
371
376
|
// budget-exhaustion wrap-up below. Best-effort: falls back to the plain stop.
|
|
372
|
-
const consolidateStop = async (stopReason: string): Promise<AgentLoopResult> => {
|
|
377
|
+
const consolidateStop = async (stopReason: string, stopClass: AgentLoopResult["stopClass"]): Promise<AgentLoopResult> => {
|
|
373
378
|
try {
|
|
374
379
|
if (!opts.signal?.aborted) {
|
|
375
380
|
const wrapUp = await invokeCallLlm(
|
|
@@ -391,12 +396,13 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
391
396
|
return finish({
|
|
392
397
|
done: false,
|
|
393
398
|
steps: step,
|
|
399
|
+
stopClass,
|
|
394
400
|
doneReason: `${consolidated}\n\n(Stopped: ${stopReason} — consolidated answer above from what was found; continue with a follow-up request)`,
|
|
395
401
|
});
|
|
396
402
|
}
|
|
397
403
|
}
|
|
398
404
|
} catch { /* best-effort; fall through to the plain stop message */ }
|
|
399
|
-
return finish({ done: false, steps: step, doneReason: `Stopped: ${stopReason}` });
|
|
405
|
+
return finish({ done: false, steps: step, stopClass, doneReason: `Stopped: ${stopReason}` });
|
|
400
406
|
};
|
|
401
407
|
// No-progress guard: weak/local models often repeat the same tool call without
|
|
402
408
|
// ever emitting `done`. Two escalating corrections (B), then a consolidated stop.
|
|
@@ -414,6 +420,12 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
414
420
|
// doc/config changes where verification is genuinely not applicable).
|
|
415
421
|
let sawMutation = false;
|
|
416
422
|
let sawVerification = false;
|
|
423
|
+
// Monotonic ordering of mutation vs. verification events so the done gate can
|
|
424
|
+
// tell a STALE verification (passing test/build that predates the last edit)
|
|
425
|
+
// from a fresh one. A single counter captures intra-batch order too.
|
|
426
|
+
let mutVerSeq = 0;
|
|
427
|
+
let lastMutationSeq = 0;
|
|
428
|
+
let lastVerificationSeq = 0;
|
|
417
429
|
let donePushbackUsed = false;
|
|
418
430
|
// Caller-owned done gate (onBeforeDone) — also strictly once per turn.
|
|
419
431
|
let beforeDoneNudgeUsed = false;
|
|
@@ -717,7 +729,12 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
717
729
|
// done-verification gate — jeo's descendant of gjc's ultragoal-guard completion
|
|
718
730
|
// state machine (plan/gjc-inheritance.md B4). The classifier owns the JUDGMENT
|
|
719
731
|
// (which named state, which message); the loop owns the once-pushback latch.
|
|
720
|
-
const doneGate = classifyDoneGate({
|
|
732
|
+
const doneGate = classifyDoneGate({
|
|
733
|
+
sawMutation,
|
|
734
|
+
sawVerification,
|
|
735
|
+
verificationStale: sawMutation && lastMutationSeq > lastVerificationSeq,
|
|
736
|
+
pendingHookFailure,
|
|
737
|
+
});
|
|
721
738
|
if (doneGate.block && !donePushbackUsed) {
|
|
722
739
|
donePushbackUsed = true; // second done always passes — escape hatch
|
|
723
740
|
pushAssistantTurn(history, responseText, reasonBuf, artifactBuf);
|
|
@@ -805,7 +822,7 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
805
822
|
}
|
|
806
823
|
if (repeatCount >= MAX_REPEAT) {
|
|
807
824
|
const what = toolCalls.length === 1 ? `the same '${toolCalls[0].tool}' call` : "the same tool calls";
|
|
808
|
-
return await consolidateStop(`repeated ${what} ${MAX_REPEAT}× even after explicit corrections (the model never signaled done)
|
|
825
|
+
return await consolidateStop(`repeated ${what} ${MAX_REPEAT}× even after explicit corrections (the model never signaled done)`, "repeat");
|
|
809
826
|
}
|
|
810
827
|
|
|
811
828
|
// Cycle guard: an A↔B (or A↔B↔C-minus-one) alternation never trips the
|
|
@@ -833,7 +850,7 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
833
850
|
step++;
|
|
834
851
|
continue;
|
|
835
852
|
}
|
|
836
|
-
return await consolidateStop(`the model cycled through the same tool calls for ${CYCLE_WINDOW} consecutive steps even after an explicit correction (it never signaled done)
|
|
853
|
+
return await consolidateStop(`the model cycled through the same tool calls for ${CYCLE_WINDOW} consecutive steps even after an explicit correction (it never signaled done)`, "cycle");
|
|
837
854
|
}
|
|
838
855
|
|
|
839
856
|
// Helper to execute a single tool call
|
|
@@ -1037,15 +1054,20 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
1037
1054
|
}
|
|
1038
1055
|
// done-verification guard bookkeeping: write/edit successes mark the turn as
|
|
1039
1056
|
// mutating; a successful bash whose command/output looks like a test/build run
|
|
1040
|
-
// counts as verification. A
|
|
1041
|
-
//
|
|
1057
|
+
// counts as verification. A monotonic seq records ORDER so the done gate can
|
|
1058
|
+
// reject a verification that predates the last mutation (stale evidence).
|
|
1042
1059
|
for (let i = 0; i < toolCalls.length; i++) {
|
|
1043
1060
|
if (!results[i].executed || !results[i].success) continue;
|
|
1044
1061
|
const t = toolCalls[i].tool;
|
|
1045
|
-
if (t === "write" || t === "edit")
|
|
1046
|
-
|
|
1062
|
+
if (t === "write" || t === "edit") {
|
|
1063
|
+
sawMutation = true;
|
|
1064
|
+
lastMutationSeq = ++mutVerSeq;
|
|
1065
|
+
} else if (t === "bash") {
|
|
1047
1066
|
const cmd = String(toolCalls[i].arguments?.command ?? "");
|
|
1048
|
-
if (isVerificationSignal(cmd, results[i].output))
|
|
1067
|
+
if (isVerificationSignal(cmd, results[i].output)) {
|
|
1068
|
+
sawVerification = true;
|
|
1069
|
+
lastVerificationSeq = ++mutVerSeq;
|
|
1070
|
+
}
|
|
1049
1071
|
}
|
|
1050
1072
|
}
|
|
1051
1073
|
// F6 (round 4 architect, Low): judge the step by its NON-TRIVIAL calls — a
|
|
@@ -1069,6 +1091,7 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
1069
1091
|
return finish({
|
|
1070
1092
|
done: false,
|
|
1071
1093
|
steps: step,
|
|
1094
|
+
stopClass: "consecutive_failure",
|
|
1072
1095
|
doneReason: stopMsg,
|
|
1073
1096
|
});
|
|
1074
1097
|
}
|
|
@@ -33,6 +33,12 @@ Analyze the transcript carefully. Pay attention to:
|
|
|
33
33
|
2. What actions the agent took (tool calls, file modifications, tests run).
|
|
34
34
|
3. The final outcome and verification results.
|
|
35
35
|
|
|
36
|
+
Verdict discipline:
|
|
37
|
+
- Judge the goal exactly as written. Do NOT reframe, narrow, or reinterpret it to fit what the agent happened to build — if the goal as stated is not satisfied, that is NOT_MET, and name precisely what is missing.
|
|
38
|
+
- Require positive evidence per requirement: cite the concrete command, test, or file change that shows it was satisfied. The mere absence of an observed failure is NOT proof of success.
|
|
39
|
+
- Do not invent requirements beyond the stated goal. When every part of the goal as written has positive evidence, return MET — withholding MET to demand unrequested extras is itself an error.
|
|
40
|
+
- Reserve IMPOSSIBLE for goals that cannot be satisfied as specified (self-contradictory, or blocked by missing credentials/services), not for goals that are merely incomplete.
|
|
41
|
+
|
|
36
42
|
You must respond with a JSON object containing:
|
|
37
43
|
{
|
|
38
44
|
"verdict": "MET" | "NOT_MET" | "IMPOSSIBLE",
|
package/src/agent/loop-guards.ts
CHANGED
|
@@ -27,6 +27,7 @@ export type GuardState =
|
|
|
27
27
|
| "context_overflow_retry" // provider reported context overflow → ONE trim + retry
|
|
28
28
|
| "refusal_retry" // transient safety refusal → bounded resend ladder
|
|
29
29
|
| "done_unverified" // mutated files, no verification signal → pushback on done
|
|
30
|
+
| "done_stale_verification" // verified, then mutated again → re-verify before done
|
|
30
31
|
| "done_hook_failing" // post-turn hook still failing → pushback on done
|
|
31
32
|
| "done_ok"; // done accepted — the turn is finished
|
|
32
33
|
|
|
@@ -100,13 +101,19 @@ export interface DoneGateInput {
|
|
|
100
101
|
sawMutation: boolean;
|
|
101
102
|
/** A test/build/typecheck/lint command succeeded this turn. */
|
|
102
103
|
sawVerification: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* A mutation landed AFTER the most recent successful verification — so the
|
|
106
|
+
* passing test/build no longer reflects the current tree. Optional; defaults
|
|
107
|
+
* to false, in which case the gate uses the order-insensitive `sawVerification`.
|
|
108
|
+
*/
|
|
109
|
+
verificationStale?: boolean;
|
|
103
110
|
/** The run-command of the most recent still-failing post-turn hook, or null. */
|
|
104
111
|
pendingHookFailure: string | null;
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
/** Verdict from {@link classifyDoneGate}: whether to bounce `done`, and the message. */
|
|
108
115
|
export interface DoneGateVerdict {
|
|
109
|
-
state: Extract<GuardState, "done_ok" | "done_unverified" | "done_hook_failing">;
|
|
116
|
+
state: Extract<GuardState, "done_ok" | "done_unverified" | "done_stale_verification" | "done_hook_failing">;
|
|
110
117
|
/** When true, `done` should be bounced ONCE with `message` (the caller owns the once-gate). */
|
|
111
118
|
block: boolean;
|
|
112
119
|
/** Corrective message to push back on `done`; empty when `state === "done_ok"`. */
|
|
@@ -117,13 +124,15 @@ export interface DoneGateVerdict {
|
|
|
117
124
|
* Classify whether a `done` should be accepted or bounced — the direct descendant of
|
|
118
125
|
* gjc's `ultragoal-guard` completion gate (plan/gjc-inheritance.md B4).
|
|
119
126
|
*
|
|
120
|
-
* A turn that MUTATED files
|
|
121
|
-
*
|
|
122
|
-
*
|
|
127
|
+
* A turn that MUTATED files is blocked ONCE when its verification is missing
|
|
128
|
+
* (no test/build signal), STALE (a passing run that predates the last edit), or
|
|
129
|
+
* a post-turn hook is still failing. The caller owns the single-pushback latch; a
|
|
130
|
+
* second `done` always passes (the escape hatch for unverifiable docs/config changes).
|
|
123
131
|
*/
|
|
124
132
|
export function classifyDoneGate(input: DoneGateInput): DoneGateVerdict {
|
|
125
133
|
const hookFailing = input.pendingHookFailure !== null;
|
|
126
|
-
const
|
|
134
|
+
const stale = input.sawVerification && input.verificationStale === true;
|
|
135
|
+
const block = input.sawMutation && (!input.sawVerification || stale || hookFailing);
|
|
127
136
|
if (!block) return { state: "done_ok", block: false, message: "" };
|
|
128
137
|
if (hookFailing) {
|
|
129
138
|
return {
|
|
@@ -135,6 +144,16 @@ export function classifyDoneGate(input: DoneGateInput): DoneGateVerdict {
|
|
|
135
144
|
"If the hook failure is a false positive, call done again and say why in the reason.",
|
|
136
145
|
};
|
|
137
146
|
}
|
|
147
|
+
if (stale) {
|
|
148
|
+
return {
|
|
149
|
+
state: "done_stale_verification",
|
|
150
|
+
block: true,
|
|
151
|
+
message:
|
|
152
|
+
"You verified earlier, but then modified files again — your last passing test/build no longer reflects the current tree. " +
|
|
153
|
+
"Re-run the narrowest verification command against the latest changes, then call done. " +
|
|
154
|
+
"If the later edits are verification-irrelevant (docs/config-only), call done again and say why in the reason.",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
138
157
|
return {
|
|
139
158
|
state: "done_unverified",
|
|
140
159
|
block: true,
|