Conversation
Centered hero + differentiator tagline + centered badges, a two-column feature table (the signature Hermes layout), a curl one-liner install leading, a command-reference table, a documentation map, and a concise contributing section reflecting the dev->PR->main workflow. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Validated against Claude Code 2.1.204 --help: - Remove --max-turns: not a valid flag in this CLI; loop iteration bounding is already owned by the scheduler's MAX_ITERATIONS. - Pass --allowedTools/--disallowedTools as comma-separated single args so tool specs containing spaces (e.g. 'Bash(git *)') are not split into broken tokens; the space-joined form silently granted no usable tools. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ee deadlock
Validated against live Claude Code 2.1.204 and Codex CLI 0.143.0:
- claude: drop invalid --max-turns; pass --allowedTools/--disallowedTools as a
single comma-separated arg so specs like 'Bash(git *)' are not split.
- codex: drop nonexistent --ask-for-approval (codex exec is non-interactive;
--sandbox governs writes).
- workspace: exclude Curator's own .curator/ state from the clean-tree guard,
and write .curator/.gitignore ('*') at init, so a fresh repo no longer blocks
the writer on the state directory init just created.
Verified by a real end-to-end run: the Codex writer streams events and creates
files; implementation + validation evidence are captured.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rify guidance) Surfaced by real end-to-end runs against the live Codex CLI: - driver: give subprocesses stdin=DEVNULL so CLIs that opportunistically read stdin (codex exec) get EOF instead of hanging forever — this was deadlocking the writer on every run depending on the parent's stdin. - clean-tree guard: enforce only on the loop's first writer dispatch; retries and resumes legitimately build on the writer's own prior uncommitted output, so the guard no longer deadlocks resume. Guard moved from the adapters into the scheduler (it is loop policy, not provider policy). - verifier: clearer, actionable pause message when no verification commands are discovered (kept the no-synthetic-success policy). Tests: engine-level clean-tree guard test; adapter tests updated. 209 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(claude): correct real Claude Code CLI flags (--allowedTools, drop --max-turns)
docs: Hermes-style README landing page
The setup banner used to teach the OS-terminal command form inside the REPL, where any non-slash input under 41 words auto-started an agent loop with zero confirmation — typing the banner's own suggestion spawned a doomed goal loop that ended in a human_handoff pause trap. - shell/intent.py: classify command-shaped input (curator-prefixed or known subcommand shapes) and answer with the slash equivalent or terminal guidance instead of goal intake - setup mode now refuses to start natural-language goals and writes nothing to disk; guidance points at /init and /provider add - gate mode defaults on (--gate/--no-gate); /gate off remains the explicit opt-in back into the fast path - bare yes/no are no longer global reserved words; restart recovery moves to the explicit /goal start with a hint when a draft exists - unknown slash commands get a closest-match suggestion - hint strings are context-correct per surface: shell text uses slash commands, terminal output uses curator commands (provider_setup_hint gains a surface parameter; registry/engine pause texts name both) - tests: intent classifier unit tests; CLI acceptance tests rewritten around the new contract with a live-mode fixture and a scripted fake claude binary for real dispatch paths Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.claude/, .codegraph/, and .mcp.json are machine-local session tooling, not project source. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shell/banner.py: ASCII CURATOR banner with a version / project path / git branch identity line (reads .git/HEAD, handles worktree pointers) - diagnostics/preflight.py: real environment checks before the first prompt — Python floor, git repo + dirty-tree warning, provider CLIs probed in parallel with version and login-state heuristics (credential files / env keys), verification-command discovery, and open-pause detection; rendered as ✓/!/✗ lines with per-check fixes - startup order is banner → preflight → first-run offer → welcome; probes run on interactive terminals (CURATOR_PREFLIGHT=force/skip overrides), and the welcome text now warns about a persisted pause instead of pretending the shell is idle - /doctor joins the shell (health + preflight, shell-dialect next step); curator doctor appends the same preflight report - welcome text slims down: the banner owns identity, detection moved into preflight, so the old per-launch 2×10s --version probes in build_welcome_text are gone Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shell/wizard.py: three-step wizard (team roles -> per-seat provider selection with login-state warnings -> summary + one consent write). IO is injected (ask/say) so the state machine is testable and can be re-skinned by the full-screen TUI without rewrites; nothing touches disk before the final confirmation - /setup joins the shell and the terminal; the fresh- project first-run offer now leads into the wizard instead of a bare y/N init, and the setup-mode refusal names /setup first - provider add records the login-state heuristic in profile metadata and warns on unverified logins (degrade, not block); it now requires initialized state instead of silently running init — the last write_init_state side effects outside explicit init/setup are gone - preflight exposes probe_provider/provider_auth_state for reuse Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- tui/shell_app.py: Textual app hosting the shared shell contract — scrollback log, bottom input, slash-completion suggestion bar, and a persistent status line (mode, writer/reviewer bindings, gate, cwd, open-pause notice). Engine dispatch runs in a worker thread with provider events streamed into the log via ShellState.emit_event; /setup suspends the app and reuses the line wizard - bare curator on an interactive terminal now takes over the screen; --no-tui (finally functional) and non-tty sessions keep the plain line shell, so scripts and tests are unchanged; the dead --yes root flag is gone - startup preflight runs off the UI thread inside the app; first-run wizard offer happens before the screen takeover - repl: ShellState.emit_event injection point, public KNOWN_SLASH_ROOTS and KNOWN_SLASH_COMMANDS; onboarding: public open_pause_exists - deleted the dead snapshot-only WorkflowApp (tui/app.py) and its test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude Code stores its OAuth token in the macOS keychain, not a plaintext ~/.claude/.credentials.json, so the file+env heuristic reported a logged-in CLI as 'login state unknown' on macOS. - _macos_keychain_has(service): read-only `security find-generic-password` existence check (no -w, so no decrypt and no access prompt); no-op off darwin and degrades to False on any security error - _auth_state consults it for claude-code after the env/file checks, reporting 'logged in (keychain)' on a hit; codex is unaffected - tests: keychain hit/miss, codex never consults it, non-darwin skip, security exit-code mapping and failure degradation; the existing unknown-login test now pins the keychain off for hermeticity Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(preflight): detect Claude Code login from the macOS keychain
feat(shell): input guard, setup-mode gate, and context-correct hints (UX PR 1/4)
feat(shell): product banner and startup environment preflight (UX PR 2/4)
feat(shell): guided setup wizard with single-consent writes (UX PR 3/4)
feat(tui): full-screen Curator shell MVP (UX PR 4/4)
…ce ownership - Persist OUTPUT_CHUNK provider stdout through a new redact_secrets helper (redaction without the error-tail truncation) via _ledger_event_payload, so credentials echoed in provider output can no longer land in the SQLite ledger in cleartext (only provider *errors* were redacted before). - Derive recovery's durable workspace_owned from persisted IMPLEMENTATION evidence (matching the engine's _has_implementation_evidence) instead of the mere existence of an IMPLEMENT iteration; a writer that crashed before producing evidence no longer claims the workspace, so resume re-runs the clean-tree guard instead of misattributing a dirty tree. - Document that an untyped provider exception classified as provider_unavailable intentionally STOP_FAILEDs rather than pausing (test_provider_failure_matrix_stops_on_untyped_runtime_error). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t Esc, and bound growth - CancellationToken.reset() re-arms the shared token at each new run; previously one Esc/Ctrl+C set the flag permanently, so every later goal/resume aborted before its first step and the full-screen shell was dead until restart. - Typing a reply over an open proposal menu now submits the text instead of discarding it to run the highlighted option (Enter no longer runs 'yes' when the user typed 'no'); Enter routes through on_input_submitted, which accepts the highlighted option only on empty input. - Esc on the first-run trust modal now exits: the app's priority Esc binding was shadowing the modal's own handler, leaving its advertised 'Esc to exit' dead. - Bound the reflow-log cache (and RichLog max_lines), the plain-text transcript, and the on-disk shell_history file so a long streamed run cannot grow memory or disk without limit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…setup - Compact line setup (curator setup and /setup in the line REPL) assigned the provider chosen at the seat labelled 'Engineer' to the reviewer seat and forced the engineer/writer seat to the PM's provider, so the engineer provider could never be chosen. The pick now binds the engineer seat it is labelled for, with the read-only reviewer mirroring it (Enter = same as PM keeps all three equal). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pre-existing work-in-progress on the onboarding branch, committed as a checkpoint alongside the review fixes: refreshed ASCII banner, simplified welcome/mode copy, /exit plus bare quit/exit exit commands, the user_echo TUI formatting helper, setup/trust screen tweaks, a textual>=8.2.8 pin, and accompanying test updates. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(redact): scrub secrets split across streamed provider output chunks
docs(readme): lead the header with the Curator logo
Give every doc except the README a shared visual identity drawn from the Curator logo — near-black console, chrome wordmark, and an electric blue -> violet -> magenta signal gradient. One rule carries meaning everywhere: LIVE / Phase 0 is solid glowing blue, TARGET / V1 is a dashed violet-magenta outline, so the palette itself encodes the Phase 0 vs V1 boundary that every doc turns on. - New shared design system: docs/assets/curator-docs.css (masthead, W/H/Y/F section markers, tables, callouts, chips, figure/legend, footer doc index, responsive + reduced-motion + focus states). - All diagrams authored as Mermaid (docs/assets/curator-mermaid.js loads Mermaid from a pinned CDN and themes it to the brand palette) rather than hand-drawn — 13 diagrams across 9 docs, incl. the orchestration control loop, the V1 identity boundary, the shell-adapter boundary, the runtime kernel loop, the V1.1–V1.6 migration ladder, and the Orca fan-out / target artifact / ontology-migration figures. - Content preserved verbatim (EN + ZH); ASCII <pre> diagrams promoted to Mermaid; the previously-untracked Orca design doc is brought into the repo. Verified: all 13 diagrams render to SVG with zero syntax errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… offline The diagrams loaded over http:// but not when a doc was opened directly from disk. Cause: the shared init was an ES module (<script type="module">), and browsers block module scripts over file:// (null origin / CORS) — so Mermaid never initialized even though the CSS loaded. Vendor Mermaid locally (docs/assets/mermaid.min.js, self-contained UMD build, zero runtime dynamic imports) and load it plus the init as CLASSIC scripts. Classic local scripts run over file://, and nothing is fetched at render time, so diagrams now render both when double-clicked and offline — no CDN needed. curator-mermaid.js keeps the brand theme and calls mermaid.run() on DOM ready. Verified: all 13 diagrams across the 9 docs render to SVG with the global set and zero syntax errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous logo image spelled the wordmark "CARUTOR" — the earlier fix only corrected the file name, not the pixels. Swap in the corrected neon wordmark (2124x740) that reads CURATOR, keeping the same path so the README reference is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: branded, diagram-first redesign of all HTML docs (Mermaid)
fix(brand): replace logo artwork with correctly-spelled CURATOR wordmark
…son) These are local tooling artifacts, not part of the project. Ignoring them keeps the working tree clean so Curator's clean-baseline guard does not pause a real run on unrelated local files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…redactor buffer reset Two audit-fidelity regressions plus a housekeeping ignore. streamed transcript: run_harness_async ran inside `with transaction(...)`, so the raised error discarded every OUTPUT_CHUNK event — erasing exactly the output needed to debug the failing run. Catch the harness error inside the block so the transaction commits the streamed events, then re-raise into the existing failure handling. asyncio.CancelledError is deliberately left uncaught, preserving the rollback-and-reconcile path. counts redacted chars, but the reset sliced the raw buffer and zeroed `_emitted`, losing the length difference wherever redaction had shortened the text. Discount the dropped prefix in redacted coordinates (`_emitted -= len(redact_secrets(dropped))`) and retain the trailing half of the window. No secret ever leaked; this restores byte-for-byte transcript fidelity. Both fixes are covered by regression tests written test-first (RED: 22 chars dropped / 0 chunks persisted; GREEN after the fix). Also ignore the generated .codegraph/ index. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… boundary Close the release-readiness gaps between the README's promises and what the code actually delivers, per the three v0.1.0 gating decisions (all resolved in favor of documenting the real contract, not new features): - Reviewer is advisory, not a gate. README now states the reviewer surfaces findings for the human confirm gate and does not automatically block delivery. - OUTPUT_CHUNK is a redacted continuous delta, not a per-source-chunk record. StreamRedactor's emission contract is spelled out in its docstring and in a new README "Known limitations" section. - README "replayable" reconciled with the release notes: the auditable system-of-record is the decision/evidence/pause rows (resumable via /resume); the provider transcript is not replayed. Also add two characterization tests locking the cancellation boundary: ProviderCancelledError (the bundled CLI-adapter path) preserves its streamed transcript and pauses; a bare asyncio.CancelledError rolls back the in-flight transcript batch by design. Closes the previously-uncovered protocol edge. No behavior change. 331 tests pass, ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-reset fix(scheduler,redact): preserve failed-run transcript and fix stream …
The local .curator/ ledger can accumulate hand-seeded demo rows (mock sessions,
provider='mock' runs). Those never belong in the repo, and shipping code must
never gain a synthetic provider — Curator's contract is "no fallback theater".
Add guards so a regression fails before merge.
Guards (run in the existing PR checks):
- tests/test_no_mock_or_seed.py:
- ProviderName stays exactly {codex, claude-code}.
- src/ defines no Mock/Fake/Stub/Synthetic/Dummy provider or driver class.
- no .curator/ state or *.sqlite* ledger is git-tracked.
- a fresh `curator init` seeds zero sessions/goals/provider_runs/loop rows.
- A dedicated fast "Guard" CI job repeats the two repo-hygiene checks with no
Python install, so the most important rule fails in seconds with a clear name.
Verified: 335 tests pass, ruff clean, and both guards were checked to catch a
real MockProvider class / tracked .sqlite while ignoring ProviderDriver etc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci: guard against synthetic providers and committed seed/mock state
The completion palette showed a generic "Run this Curator command" for most commands (only ~13 of ~35 had a description), and there was no way to fill a command by mouse or a dimmed inline hint. Pre-v0 UX polish: - repl.py: give every command in KNOWN_SLASH_COMMANDS a specific description; arg-taking commands spell their usage (e.g. "/agent bind <slot> <provider>", "/approve <id> [note]"). A test asserts none fall back to the generic text. - palette.py: render the command in an accent color and the description dimmed, so the description reads as a hint, not equal-weight noise. - shell_app.py: attach SuggestFromList to the input for dimmed ghost-text completion (Right/End accepts; Tab still cycles the palette), and add a palette click handler that fills the input with the chosen command so the user can type its arguments next. Verified: ghost text completes slash prefixes and stays silent for normal goals; clicking a palette row fills the input; 340 tests pass, ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(tui): explain every slash command + ghost-text & click-to-fill
…ken use Make an active run readable and expressive above the input. 1. Tool-call detail. Both adapters only kept the tool *name*, so the transcript showed opaque "tool_call command_execution". Now: - Codex: extract the command (or changed paths, + non-zero exit) from the item. - Claude Code: extract the tool_use input (command / file_path / …). Details are redacted (redact_secrets) and length-bounded, and the TUI renders them dimmed next to the tool name. 2. Music-production phase word. The flat "Working…" becomes the loop phase read live from the ledger's running iteration: plan→Composing, implement→Arranging, validate→Mixing, review→Auditioning, confirm→Mastering (default: Cueing up). This covers the deterministic verify step too, which emits no provider event. 3. Provider token usage. Adapters now report the provider name (on STARTED) and a token total (on the usage/result event), parsed defensively. The working line shows e.g. "codex: 1.5k". Counts reset per run. Note: exact Codex/Claude usage + tool field names vary by CLI version; parsing is defensive and simply omits a field it cannot find, so it degrades gracefully and should be smoke-checked against the installed CLIs. Verified: 351 tests pass, ruff clean; a captured TUI render shows tool commands, "Auditioning…", and "codex: 1.5k". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(tui): tool-call detail + music-themed phase + provider token usage
…es don't pause runs
asyncio's StreamReader defaults to a 64 KiB line limit. A single provider JSONL
event that inlines a diff, tool output, or a review of the change routinely
exceeds that, so readline() raised ValueError("Separator is not found, and chunk
exceeds the limit"). That was classified as invalid_output and paused the loop
with a misleading "Provider invalid output" — even though the model output was
fine and only Curator's own reader crashed.
Pass a generous limit (16 MiB) to create_subprocess_exec so a big-but-ordinary
line streams through. Regression test emits a 128 KiB line and asserts the run
succeeds; it fails with the exact original error when the limit is removed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(providers): raise subprocess stream-reader limit so big JSONL lines don't pause runs
…learly
The Claude Code and Codex CLIs don't expose remaining quota programmatically
(no stream-json field, no quota command, no config file), so Curator can't
compute "95% of your limit". But when a run actually hits the limit, the CLI
says so in its error output — often with a reset time. Relay that instead of
inventing a threshold.
- providers/limits.py: tolerant detection of usage/rate-limit phrasing and a
reset-time hint (wording varies by CLI version; unmatched errors fall through
to the ordinary path).
- ProviderErrorKind.USAGE_LIMIT; both adapters classify a rate-limited exit as
USAGE_LIMIT with a provider-attributed message ("Codex usage limit reached —
resets 15:00").
- decide_runtime pauses on it with a clear, actionable reason ("… Wait for the
reset, then /resume to retry.") instead of a generic "provider unavailable".
Scope note: this fires when the provider itself reports the limit (at/near the
cap). A proactive running percentage is not available from the CLIs' headless
output, so it is intentionally not attempted.
359 tests pass, ruff clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(providers): surface the provider's own usage/rate-limit signal clearly
… summary
Before: every provider TOOL_CALL rendered as its own transcript line with the
full command, so a big shell command wrapped over 3 lines and repeated — noisy
and hard to read.
Now, following the Claude Code "⏺ tool(arg)" pattern (and grok-build's compact
scrollback / Orca's status-oriented display):
- Consecutive same-type tool calls stream into ONE live block above the input
("⏺ command_execution ×3 <latest cmd, one line>"), updated in place.
- When the group closes (a non-tool event, a new tool type, or the run ending),
a single concise summary lands in the scrollback ("⏺ command_execution ·
4 calls"; a lone call keeps its truncated command).
- The command is collapsed to one line and truncated — no more wrap-spam. Glyph
and tool name in accent, args/count dimmed.
5 tests cover coalescing, the summary flush (non-tool event / new type / run
end), single-call detail, and one-line truncation. 364 tests pass, ruff clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The writer clean-tree guard pauses on a dirty tree so the recorded diff stays attributable to the writer alone. Add an opt-in convenience: `/resume stash` tucks the user's uncommitted work into a git stash (excluding `.curator/`, so Curator's own ledger is never swept) and re-runs the writer on a clean baseline; the user restores later with `git stash pop`. Pair it with a clearer dirty-pause card that names shell-valid commands (`/resume stash` | `/resume continue`) instead of a bare `/resume`. Deliberate ceilings: no auto-pop (avoids mid-loop merge conflicts), and the stash return is advisory — the clean-tree guard remains the fail-safe, so a failed or empty stash simply re-pauses. Both documented in code. Also document the future multi-worktree parallel model in docs/future-optimizations.html — the V1 direction that supersedes this guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
README: lead with the product vision and the multi-provider pain points Curator solves; Getting Started is now global-install-then-run only (drop the from-source/dev path); add a Versions list (v0.1.0 + the V1.1–V1.6 roadmap) and an expanded Commands list; remove the developer docs/*.html table. The dev/local setup moves into CONTRIBUTING.md. Governance (all greenfield): CONTRIBUTING.md documents the gated model — anyone files issues, invited contributors open PRs into dev, admin merges dev→main as a version bump. Adds CODEOWNERS, SECURITY.md, CODE_OF_CONDUCT.md (Contributor Covenant 2.1), structured issue forms (blank issues disabled) + a PR template, and CHANGELOG.md as the version-history source. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… as default) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(tui): coalesce streamed tool calls into one live block + concise summary
feat(workspace): opt-in /resume stash for the clean-tree guard
chore(release): v0.1.0 README overhaul + open-source governance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First public release — brings
devtomain(72 commits) and cuts v0.1.0. See CHANGELOG.md. Includes the auto-stash clean-tree guard, TUI tool-call coalescing, README overhaul, and open-source governance.