Skip to content

Merge dev into main#43

Merged
4entrepreneur merged 6 commits into
mainfrom
dev
May 4, 2026
Merged

Merge dev into main#43
4entrepreneur merged 6 commits into
mainfrom
dev

Conversation

@vkehfdl1

@vkehfdl1 vkehfdl1 commented May 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Merge dev into main.
  • Adds the Ink-based interactive TUI chat flow, including markdown rendering and UI components.
  • Adds single-shot chat session handling and persisted/resumable session behavior.
  • Updates CLI docs and OpenClone skill docs to match the new chat/session behavior.
  • Adds tests for Ink conversation rendering, markdown rendering, and single-shot sessions.

Diff scope

  • Commits: 4 commits ahead of main.
  • Files changed: 25 files, +3216/-45.
  • Key areas: CLI chat entrypoint, conversation/session persistence, Ink UI, markdown terminal rendering, docs, package dependencies, test coverage.

Validation

  • npm run typecheck: passed.
  • npm test: failed: test/single-shot.test.mjs subtest single-shot: --resume=<id> targets a specific session asserts two generated timestamp session IDs differ, but both were equal in the same millisecond.
  • node --test test/single-shot.test.mjs: same failure reproduced.

vkehfdl1 and others added 5 commits April 24, 2026 20:46
… Skill (#40)

* docs(clones): 토스 이승건(sglee) 빌트인 클론 추가 (#38)

PO SESSION 7편 전체, EO Korea 인터뷰 2편, 퓨처앤잡 인터뷰 1편,
Square of Toss / 창업 현실 영상 2편, Fortune Korea Visionary
기사 2편을 바탕으로 구성. 총 14개 dated knowledge 파일.

categories: founder (primary), expert (PO/Growth 프레임).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(clones): 센드버드 김동신(johnkim) 빌트인 클론 추가 (#39)

* docs(clones): 토스 이승건(sglee) 빌트인 클론 추가

PO SESSION 7편 전체, EO Korea 인터뷰 2편, 퓨처앤잡 인터뷰 1편,
Square of Toss / 창업 현실 영상 2편, Fortune Korea Visionary
기사 2편을 바탕으로 구성. 총 14개 dated knowledge 파일.

categories: founder (primary), expert (PO/Growth 프레임).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(clones): 센드버드 김동신(johnkim) 빌트인 클론 추가

@doshkim 존잡생각 채널 10편, 외부 인터뷰·강연 4편, 서울경제
CEO&STORY 기사 1편을 기반으로 구성. 총 15개 dated knowledge 파일.

프레임 중심 정리: PPTM(글로벌 진출), Art of Pivot, Capacity/People
Scaling, RAMP(내적 동기), 2x2 행복 매트릭스, 지속가능한 헌신의
구간, 긍정적 집요함 등.

categories: founder (primary), expert (B2B SaaS/피봇/글로벌).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add standalone openclone CLI without replacing Claude Code

The CLI reuses existing persona and knowledge markdown as the source
of truth while keeping the Claude Code skill, hooks, setup, and statusline
paths intact. Provider routing starts with OpenAI-compatible APIs and adds
explicit Codex OAuth and Ollama paths for local/API-based chat.

Constraint: Existing Claude Code skill behavior must remain compatible
Constraint: Markdown clone files remain the canonical data model
Rejected: Treat Codex OAuth token as a plain api.openai.com/v1 key | Codex OAuth requires the Codex backend transport
Rejected: Trigger built-in knowledge fetch during list | list must be read-only
Confidence: high
Scope-risk: moderate
Directive: Do not route Codex OAuth through api.openai.com/v1; use the Codex backend/provider path
Tested: npm run validate
Tested: markdownlint-cli2 "**/*.md"
Tested: shellcheck -S error hooks/*.sh scripts/*.sh setup uninstall
Tested: npm audit --audit-level=moderate
Tested: npm run build
Tested: live Codex OAuth smoke with gpt-5.5
Tested: live Ollama smoke with ministral-3:8b
Not-tested: Native provider adapters beyond OpenAI-compatible, Codex OAuth, and Ollama

* Bring CLI closer to Claude Code persona fidelity

The standalone CLI now uses AI SDK tool calling to let the model inspect
local markdown knowledge on demand, fetch/search external facts, and keep
an Ollama-run-like in-memory conversation when no prompt is supplied. The
package is scoped for the new npm org and release publishing is tied to
GitHub Release tags so package versions are not manually drift-prone.

Constraint: Existing Claude Code skill, hooks, setup, and statusline behavior must remain intact
Constraint: Markdown persona and knowledge files remain the canonical data model
Constraint: npm package should publish under the newly created @OpenClone scope, not the unrelated unscoped openclone name
Rejected: Always stuff every knowledge file into the prompt | too expensive and weaker than dynamic file reads
Rejected: Publish on every main merge | npm release should require an explicit GitHub Release event
Confidence: high
Scope-risk: moderate
Directive: Do not make main merges publish npm automatically unless release policy changes; publish is Release-published driven
Directive: Keep Codex OAuth explicit and routed through the Codex backend/provider path
Tested: npm run validate
Tested: npm run build
Tested: markdownlint-cli2 "**/*.md"
Tested: shellcheck -S error hooks/*.sh scripts/*.sh setup uninstall
Tested: npm audit --audit-level=moderate
Tested: npm pack --dry-run confirms @openclone/openclone and no stale codex-auth artifact
Not-tested: Actual npm publish because it requires a real GitHub Release and npm token

* Prevent runaway interactive CLI context

Long persona chats previously kept every turn verbatim until the provider
failed or degraded from an oversized request. The CLI now compacts older
turns into a continuity summary while preserving recent turns, and exposes
/compact for explicit user control.

Constraint: Keep the feature provider-agnostic and dependency-free
Constraint: Preserve one-shot chat, Claude Code support, and existing CLI tool calling
Rejected: Exact tokenizer-based budgeting | provider/model tokenizers differ and are unnecessary for a first safety guard
Rejected: Drop old turns without summary | loses user goals and decisions that persona continuity needs
Confidence: high
Scope-risk: narrow
Directive: Compaction is approximate char-based; revisit only if provider-specific token windows become first-class config
Tested: npm run validate
Tested: npm run build
Tested: markdownlint-cli2 "**/*.md"
Tested: shellcheck -S error hooks/*.sh scripts/*.sh setup uninstall
Tested: npm audit --audit-level=moderate
Tested: npm pack --dry-run
Tested: Architect review APPROVE

* Teach agents how to guide openclone CLI users

The CLI now has enough provider and runtime surface area that agents need a
separate, progressively disclosed guide instead of overloading the Claude Code
/openclone dispatcher skill. The new openclone-cli skill keeps the main skill
small and splits local Ollama, Codex OAuth, OpenAI-compatible, conversation,
knowledge, and troubleshooting details into reference files.

Constraint: Root SKILL.md remains the Claude Code dispatcher and must not absorb CLI help details
Constraint: Ollama guidance must state that openclone is only the client and does not start the Ollama server
Rejected: Put all CLI provider instructions in one SKILL.md | too much context and poor progressive disclosure
Rejected: Reuse the root openclone skill for CLI help | it is tied to Claude Code slash-command behavior
Confidence: high
Scope-risk: narrow
Directive: Keep provider-specific CLI guidance in skills/openclone-cli/references rather than bloating SKILL.md
Tested: npm run validate
Tested: npm run build
Tested: markdownlint-cli2 "**/*.md"
Tested: shellcheck -S error hooks/*.sh scripts/*.sh setup uninstall
Tested: npm audit --audit-level=moderate
Tested: npm pack --dry-run includes skills/openclone-cli files
Tested: Architect review APPROVE

* Keep Codex OAuth response references resolvable

Codex OAuth runs through the Responses API, and AI SDK tool or conversation steps can send item references back to the backend. The oauth provider defaulted to non-persistent response items, which made those references disappear between requests and produced Item not found errors.\n\nDefault Codex OAuth to stored response items while keeping an explicit opt-out for privacy-sensitive local experiments.\n\nConstraint: Codex backend rejects AI SDK item references when response items are not persisted\nRejected: Disable tools for Codex OAuth | would break dynamic knowledge and web lookup behavior\nRejected: Force users to set an env var for the working path | the default path should not fail at runtime\nConfidence: high\nScope-risk: narrow\nDirective: Do not default Codex OAuth store back to false without re-testing multi-step tool calls\nTested: npm run build\nTested: npm test\nTested: npm run validate\nTested: npx markdownlint-cli2 README.md skills/openclone-cli/references/codex-oauth.md

* Persist CLI conversations and add cross-clone history view

Conversation history persistence
- Add HistoryStore that saves each interactive session to
  ~/.openclone/conversations/<slug>/<sessionId>.json on every turn
  and on exit, with a stable sessionId that is reused across resumes
  so a single session is one JSON file.
- runConversation now accepts initialMessages, initialSummary, and an
  onPersist callback; the library itself does no I/O so callers can
  swap the store. CLI wires HistoryStore via chatCommand.
- New chat flags: --resume (latest session for that clone),
  --resume=<id> (specific session), --no-persist (ephemeral run).
- On resume the prior summary and every restored message are replayed
  to stdout in chronological order before the live prompt loop, so
  scrolling up shows the full prior dialogue.

History command
- New 'openclone history' subcommand. Bare 'openclone history' now
  prints help instead of silently falling back to the active clone.
- 'openclone history <slug>' lists sessions for one clone with a
  table header, a per-session 'resume:' hint, and a footer tip.
- 'openclone history --all' walks every directory under
  ~/.openclone/conversations/, groups sessions by clone, and tags
  any group whose slug is missing from CloneLoader.listClones() with
  '[orphan: clone not found]'.
- HistoryStore.listClonesWithSessions and HistoryStore.listAllSessions
  back the cross-clone view.
- --quiet suppresses the column header and per-session hints for piping.

Codex OAuth default
- Default codexStore to false so the ChatGPT backend
  (chatgpt.com/backend-api/codex) accepts requests; OPENCLONE_CODEX_STORE=1
  remains as an explicit opt-in for environments that allow store=true.

npm publish payload
- Trim package.json files array to dist/, clones/, README.md, LICENSE.
  references/, scripts/, hooks/, skills/, assets/, and SKILL.md are
  Claude Code skill assets and are not loaded by the npm CLI binary.
- Build script now chmods dist/cli/index.js so global installs work.
- Bump README status badge to v0.3.0.
- Ignore *.tgz so npm pack output does not get committed by accident.

Tests
- 53 tests pass (was 20). New coverage for history store round-trips,
  cross-clone listing, orphan classification, persist callbacks,
  resume replay output, history command formatting, and the codex
  store default flip.

* Restructure README install paths and refresh openclone-cli Agent Skill

README
- Split the install section into three clearly separated entry points:
  A. Claude Code skill install, B. Standalone CLI install (the Vercel
  Agent Skill is now documented inside the CLI section as the
  recommended way for any AI agent to teach users how to use the CLI),
  C. Codex CLI experimental install. The mixed 'Node.js CLI'
  + 'Agent skill for CLI help' text was reorganized into B1 install,
  B2 Vercel Agent Skill install with example agent prompts, B3 direct
  usage, B4 provider setup, B5 interactive mode, B6 dev checkout.
- Document the canonical install command for the agent skill:
  npx skills add open-clone/openclone --skill openclone-cli.

Vercel Agent Skill (skills/openclone-cli)
- Delete skills/openclone-cli/agents/openai.yaml. agents/ is not part
  of the agentskills.io / vercel-labs/skills directory layout, and the
  YAML duplicated frontmatter that already lives in SKILL.md.
- SKILL.md description now lists the v0.3.0 surface (npm install,
  history, --resume, --resume=<id>, --no-persist, conversation
  persistence path, knowledge lookup, errors) so the skill activates
  on the right user phrases. Body adds 'session persistence and
  resuming prior conversations' as an explicit dispatch reason for
  conversation-and-knowledge.md.
- references/conversation-and-knowledge.md no longer claims chat
  history is dropped on exit. New sections cover persistence path,
  --no-persist, --resume / --resume=<id>, the resumed banner, replay
  to stdout, openclone history (single + --all + --quiet), and how
  the compaction summary survives across resumes.
- references/quickstart.md gains a 'Sessions and resuming' section
  covering history listing, resume by latest or by id, --no-persist,
  and --quiet. Notes that bare 'openclone history' prints help.
- references/codex-oauth.md updated for the flipped default: the CLI
  now sends store=false because the ChatGPT backend rejects store=true
  for ChatGPT-tier OAuth tokens. OPENCLONE_CODEX_STORE=1 is documented
  as opt-in for hypothetical future backend behavior, with the
  expected 'Store must be set to false' 400 noted as a troubleshooting
  case.

* Make CI green: drop legacy openai.yaml check, sync i18n README rows

validate-skill.ts
- Drop the 'agents/openai.yaml' presence check. The agents/ directory
  is not part of the agentskills.io / vercel-labs/skills directory
  layout; the canonical layout is SKILL.md + scripts/ + references/
  + assets/. Requiring openai.yaml duplicates SKILL.md frontmatter
  and tied us to a non-spec format. SKILL.md frontmatter remains the
  single source of skill metadata.

README_en.md, README_zh.md
- Add johnkim and sglee rows so validate-readme-i18n no longer flags
  drift. johnkim and sglee landed on dev via #38 and #39 after the
  last translation regen, leaving the i18n validator failing on dev.
  English translations are direct; Chinese rows follow the existing
  REVIEW NEEDED machine-translation convention.

---------

Co-authored-by: Hayun Song <100770062+4entrepreneur@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Korean README is the single source of truth and was significantly
expanded in #40 (added Standalone CLI section B1-B6, Vercel Agent Skill
install instructions, two-path A/B/C install table, npm release section,
session persistence and resume documentation, and the johnkim / sglee
clone rows). The English and Chinese mirrors were left at the v0.2 era
structure, missing all of the above.

This commit retranslates both files from scratch against README.md at
commit a5072ed so the three READMEs match section-for-section.

README_en.md
- Sync header bumped to a5072ed.
- Status badge bumped from v0.2 to v0.3.0.
- Hero tagline updated to mention both the Claude Code skill and the
  standalone CLI.
- New install split: A (Claude Code skill), B (Standalone CLI with
  B1 install / B2 Vercel Agent Skill / B3 direct usage / B4 provider
  setup / B5 interactive mode / B6 dev checkout), C (Codex CLI
  experimental).
- B2 documents the canonical "npx skills add open-clone/openclone
  --skill openclone-cli" install with example natural-language prompts.
- B5 documents on-disk session persistence at
  ~/.openclone/conversations/<slug>/<sessionId>.json, --resume / --resume=<id>,
  the [resumed: N message(s)] banner, the in-terminal replay of prior
  dialogue, and the compaction summary surviving across resumes.
- B4 explains the Codex OAuth store=false default and why
  previous_response_id is unnecessary.
- New "npm release" section describes the publish-npm.yml workflow
  and dist-tag rules.

README_zh.md
- Same restructure mirrored into Simplified Chinese.
- Sync header bumped to a5072ed; REVIEW NEEDED marker preserved
  because no native zh-CN reviewer has signed off on the new sections.
- Status badge bumped to v0.3.0.
- Same A/B/C install split, B1-B6 substructure, and additional
  paragraphs covering Vercel Agent Skill install, conversation
  persistence, --resume / --resume=<id>, replay-on-resume, and the
  Codex OAuth store=false default.

Verification
- node .github/scripts/validate-readme-i18n.ts now reports
  '3 files, 14 clone row(s) cross-referenced, picker + sync headers
  + install fragments verified'.
- markdownlint-cli2 across all three READMEs returns 0 errors.
- npm run validate (typecheck + 53 unit tests + 5 hook smoke tests)
  passes.
…ns (#42)

* chore(deps): add Ink 7 + React 19 + marked stack for TUI chat

Wires up the runtime dependency tree and tsconfig JSX support that the
forthcoming Ink-based interactive chat path requires:

- Add ink@^7.0.1, react@^19.2.0, react-reconciler@^0.33.0 as runtime
  deps so the published CLI ships with everything needed for the TUI.
- Add marked@^14 + marked-terminal@^7 for ANSI-rendered markdown of
  assistant responses.
- Add @types/react@^19 to devDependencies.
- tsconfig: "jsx": "react" (legacy transform — emits createElement
  calls and works with tsc-only ESM output without needing the
  react/jsx-runtime resolution dance).
- tsconfig include extended to src/**/*.tsx so the new src/ui/ tree is
  picked up.

No behavior change yet; existing tests still pass.

* refactor(conversation): export compactMessages + CONVERSATION_DEFAULTS

Lifts the previously-private compactMessages helper to a public export
and surfaces a CONVERSATION_DEFAULTS object with the env-driven
compaction tunables (compactMaxChars, compactKeepTurns,
compactSummaryMaxChars).

Both will be reused by the new Ink TUI conversation driver to share the
exact same compaction behavior as the readline path. No callers change;
all existing tests still pass.

* feat(ui): Ink-based TUI chat with markdown rendering

Adds an Ink (React 19) TUI front-end for the interactive openclone
chat REPL while keeping the existing readline path intact for
non-TTY/test scenarios.

New components (src/ui/):
- App.tsx: root component owning the full conversation state machine.
  Replicates the readline runConversation behavior 1:1: boot banner,
  resume banner + prior-summary block + replayed messages +
  continuation marker, slash commands (/help, /clear, /compact,
  /bye+/exit+/quit), auto-compaction, persistence after every turn
  and on exit, persist-failure recovery.
- Markdown.tsx: marked + marked-terminal renderer for assistant
  responses. Headers, bullets, fenced code blocks, inline code, links
  all render with ANSI styling. Pure renderMarkdown(text) helper is
  exported for testing.
- MessageView.tsx: renders MessageItem (user / assistant / system).
  User shows '>>> {text}', assistant goes through Markdown, system
  renders as plain Text.
- PromptInput.tsx: single-line input via Ink's useInput. Handles
  typed input, backspace, Enter (submits), Ctrl+C (abort). Also
  handles paste-with-newline edge case so pasted commands like
  '/bye\r' submit cleanly.
- hooks/useStateAndRef.ts: vendored verbatim from
  google-gemini/gemini-cli (Apache-2.0). Provides stale-closure-safe
  state for streaming event handlers.
- runInkConversation.tsx: the entry point that mirrors runConversation
  options. Mounts App via ink.render, awaits exit, defensively
  restores stdin (raw mode off, unref, pause) on the way out.

Layout uses Ink's <Static> for committed history (no re-render on
streaming) plus a separate live-updating region for the current
streaming response. Streaming text renders raw while in flight and
gets the full markdown pass once the response is committed
post-stream — this mirrors the readline path's character-by-character
write-then-newline rhythm.

Tests (all green, 76 total):
- markdown-render.test.mjs (8 cases): pure renderMarkdown unit tests.
- ink-render.mjs: thin custom render harness for Ink under
  node --test. FakeStdin/FakeStdout that emit 'readable' events the
  way Ink expects, inline 10-line ANSI stripper, no extra deps.
- ink-render-smoke.test.mjs: harness sanity check.
- ink-conversation.test.mjs (14 cases): full port of the
  conversation.test.mjs runConversation suite to runInkConversation,
  asserting on captured frames. Original readline tests (16) still
  pass unchanged.

* feat(cli): dispatch chat to Ink TUI on TTY, readline elsewhere

Adds the integration point that decides which conversation backend
to use:
- If both stdout and stdin are TTYs, lazy-import and call
  runInkConversation. This avoids pulling Ink/React into the single-
  shot path (--prompt, piped stdin) and keeps non-interactive tests
  hitting the unchanged readline runConversation.
- After Ink runs to completion, process.exit(0) explicitly so any
  leftover stdin event listeners or raw-mode artifacts can't keep
  the process alive after /bye.
- Single-shot path (lines below the if) is unchanged.
- All existing CLI commands (list, status, history, --dry-run,
  --prompt, piped stdin) remain bit-for-bit identical.

* feat(ui): proper TUI layout with header bar, input box, role chrome

The previous Ink port was visually indistinguishable from the
ollama-run-style readline REPL because every output line was a
1:1 textual replica. This commit replaces the textual chrome with
real TUI primitives so the interactive chat experience now has
visible structure (boxes, borders, role colors, spinner) and
clearly looks different from a plain readline loop.

What changed:
- HeaderBar.tsx: cyan rounded box at the top showing
  'openclone · {clone} · {model} · ses HH:MM'.
- InputBox.tsx: magenta rounded box with '›' prompt + cursor.
  When streaming is in flight the box turns gray, swaps the
  prompt for a dot spinner + 'thinking…', and ignores keys.
  A dim 'enter to send · /help · /bye to exit · ctrl+c'
  hint sits below the box.
- MessageView.tsx: 'you ›' / '{cloneName} ›' role headers
  with colored, bold prefixes; bodies indented two columns.
  Markdown rendering still applies to assistant bodies.
  System lines split into 'system-banner' (yellow dim, like
  '[resumed: ...]', '[compacted ...]', '--- prior summary ---')
  and 'system' (plain dim text) so banners stand out from
  inline help/error text.
- App.tsx: re-renders with the new chrome and a streaming
  region that shows the speaker label + spinner + raw tokens
  while a turn is in flight.
- runInkConversation: accepts speakerLabel / modelLabel /
  sessionLabel pass-through.
- chatCommand: derives speakerLabel from clone.displayName,
  modelLabel from provider.modelId, sessionLabel from a
  short slice of sessionId.

Tests: still 76/76 green. Three Ink-conversation assertions
loosened from '>>> {text}' / 'openclone conversation: {label}'
matchers to plain-text matchers, since the new chrome no
longer mirrors the readline output byte-for-byte. The
behavioral guarantees (turn-by-turn persistence, /clear,
/compact, auto-compact, resume, persist-failure resilience,
Ctrl+C exit) are unchanged.

Deps: + ink-spinner@^5 (runtime).

* fix(ui): render markdown with marked lexer + Ink JSX (color-coded)

The previous Markdown component piped marked-terminal's string output
into a single Ink <Text> node. With default options marked-terminal
emits little to no styling — '# H1' came through as the literal
string '# H1', '**bold**' came through as 'bold' with no decoration,
'`code`' came through as plain 'code'. Users saw raw markers and a
visually flat block.

Switched to walking marked's token tree directly and emitting Ink
<Text> nodes with explicit colors and modifiers per token type:

- Headings: marker stripped, depth maps to color (H1 magenta+bold+
  underline, H2 cyan+bold+underline, H3 yellow+bold, H4 green+bold,
  H5/H6 blue/red).
- Inline emphasis: **bold** -> yellow + bold; *italic* -> cyan +
  italic; ~~del~~ -> gray + strikethrough.
- Inline code: green text on gray background, padded.
- Links: blueBright + underline; the href trails in dim gray.
- Lists: cyan bullets ('•') for unordered, cyan numbers for
  ordered. Nested block items recurse correctly.
- Code blocks: rounded gray border with the language label dim at
  the top and green body lines.
- Blockquotes: gray '│' rail on the left, content rendered as
  nested blocks.
- Horizontal rule: dim gray '─' x 40.
- HTML / unknown blocks: dim gray fallback.
- Decodes the standard HTML entities (&amp; &lt; &gt; etc.) that
  marked emits inside text tokens so they render readably.

Tests: markdown-render.test.mjs is rewritten to mount Markdown via
the ink-render harness and assert on stripped frame content. Added
explicit checks that '#', '**', '~~', backtick, and other markers
do NOT appear in the rendered output, plus per-feature presence
checks for headings, lists (both kinds), code, links. 11 cases,
all green. Suite total now 79/79.

Streaming raw text continues to render through the unchanged App
'streaming' branch (plain <Text>), so the post-stream commit pass
through Markdown is what gets the styling — same visual contract
as before, just with actual styling applied.

* feat(conversation): raise default compaction thresholds for 250K-token contexts

Old defaults were sized very conservatively (compacted at the 5–10%
mark of any modern context window):

  OPENCLONE_COMPACT_MAX_CHARS         24000
  OPENCLONE_COMPACT_KEEP_TURNS        6
  OPENCLONE_COMPACT_SUMMARY_MAX_CHARS 6000

That made openclone fire auto-compaction in dialogues most users
would still consider 'just getting started', wasted a per-trigger
summarization round-trip, and lost more raw turns than necessary
on flagship-context models.

New defaults assume a 250K-token context window at ~70%
utilization, with Korean-leaning ~2 char/token estimate to stay
on the safe side:

  OPENCLONE_COMPACT_MAX_CHARS         350000   (~70% of 250K tok @ 2 chars/tok)
  OPENCLONE_COMPACT_KEEP_TURNS        8        (more raw turns kept post-compact)
  OPENCLONE_COMPACT_SUMMARY_MAX_CHARS 20000    (proportional to new threshold)

Smaller-context users (Ollama 8B, gpt-4o-mini etc.) or anyone
optimizing for per-turn token cost can still drop the env vars
back to the old values explicitly. The README (Korean / English /
Chinese) and skills/openclone-cli/references/conversation-and-knowledge.md
are updated to document the new defaults plus the downgrade hint.

* feat(cli): agent-friendly multi-turn via --resume + --prompt single-shot

Previously the single-shot path (--prompt, piped stdin) was stateless:
it called streamChat once, wrote the response to stdout, and exited
without saving anything. --resume hard-routed to interactive mode,
ignoring any --prompt flag. That meant agents had two bad options:

  1. Drive an interactive child process over pipes (fragile, requires
     parsing TUI output).
  2. Re-send the entire conversation history themselves on every call
     (no shared state with the persistence layer).

This commit makes single-shot stateful, mirroring what runConversation
already does for interactive mode:

  - --prompt without --resume: starts a new session, persists the
    turn (user + assistant messages) to
    ~/.openclone/conversations/<slug>/<sessionId>.json, prints
    '[session: <id>]' to stderr.
  - --resume --prompt 'X': loads newest session, appends the new
    turn, persists, prints '[session: <id>]' to stderr (same id).
  - --resume=<id> --prompt 'X': loads that specific session.
  - --no-persist --prompt 'X': stateless behavior, identical to
    pre-change. No disk write, no stderr session line.

stdout receives only the streamed response (terminated with \n),
stderr receives the session marker. This split lets agents capture
either independently:

  RESPONSE=$(openclone chat douglas --prompt 'q1' 2>session.log)
  SESSION=$(grep -oE '\[session: [^]]+\]' session.log | sed 's/\[session: //;s/\]//')
  openclone chat douglas --resume=$SESSION --prompt 'q2'

Auto-compaction also applies in single-shot: if the loaded history
plus the new prompt exceeds OPENCLONE_COMPACT_MAX_CHARS, older
turns are summarized before the LLM call and
'[auto-compacted N older message(s)]' is emitted to stderr.

Implementation:

- src/lib/single-shot.ts: new runSingleShot() helper. Takes
  cloneSlug, model, system, prompt, resume flags, and stream
  injection point. Returns { sessionId, response, messages,
  conversationSummary, persisted, ... }. Stdout/stderr writers
  are injectable so unit tests can capture both streams.
- src/cli/index.ts: chatCommand single-shot block delegates to
  runSingleShot. Interactive vs single-shot decision now treats
  any explicit prompt (--prompt / positional / piped stdin) as
  single-shot, even when --resume is set. Drops dead streamChat
  import.
- test/single-shot.test.mjs: 7 cases covering new-session create,
  --resume newest, --resume=<id>, missing-session error,
  --no-persist, stdout/stderr split, and conversationSummary
  carry-over.

Tests: 79 -> 86 passing. Interactive behavior unchanged: every
existing conversation.test.mjs and ink-conversation.test.mjs case
still passes byte-for-byte.

Docs:

- README.md / README_en.md / README_zh.md: B3 'Use it directly'
  table updated with the new --resume + --prompt rows and a
  worked agent-style multi-turn example.
- skills/openclone-cli/references/conversation-and-knowledge.md:
  new 'Single-shot mode for agents' section with stream-split
  contract, capture patterns, and persistence semantics.

* docs(skill): update openclone-cli description for guru-consultation triggers

Added explicit consultation-oriented triggers (advice, strategy, analysis,
domain expertise) and listed the 10 official clones with their categories
so that agents can recognize when to invoke openclone for expert
perspectives. The installation/troubleshooting triggers remain.
@vkehfdl1

vkehfdl1 commented May 2, 2026

Copy link
Copy Markdown
Collaborator Author

dev → main 변경점 점검 체크리스트

dev 브랜치가 main 대비 포함하는 변경 범위입니다. 리뷰/머지 전에 아래 항목을 체크해 주세요.

변경 범위 요약

  • Ink TUI 채팅 도입: src/ui/*, src/ui/runInkConversation.tsx가 추가되어 터미널 기반 대화 UI를 제공합니다.
  • Markdown 렌더링 추가: src/ui/Markdown.tsx, marked, marked-terminal 기반 렌더링 및 타입 선언이 추가되었습니다.
  • CLI 채팅 흐름 변경: src/cli/index.ts가 interactive TTY 모드, prompt/stdin 처리, resume/no-persist 옵션을 새 흐름에 연결합니다.
  • single-shot 세션 처리 추가: src/lib/single-shot.ts가 단발성 프롬프트 실행, 저장/재개, stdout 출력 분리를 담당합니다.
  • 대화/세션 영속화 영향 확인: src/lib/conversation.ts 변경이 기존 /clear, /compact, resume, persist 동작과 호환되는지 확인합니다.
  • 문서 업데이트 확인: README.md, README_en.md, README_zh.md, skills/openclone-cli/*가 새 CLI 사용법과 세션 정책을 설명합니다.
  • 의존성 변경 확인: package.json, package-lock.jsonink, react, marked, marked-terminal 등 TUI/렌더링 의존성이 추가되었습니다.
  • 테스트 커버리지 추가 확인: test/ink-conversation.test.mjs, test/markdown-render.test.mjs, test/single-shot.test.mjs 등 신규 테스트가 포함됩니다.
  • 빌드/타입 설정 확인: tsconfig.json 변경이 JSX/React UI 빌드에 필요한 설정만 포함하는지 확인합니다.

검증 상태

  • npm run typecheck 통과
  • npm test 통과 필요 — 현재 1개 실패 재현됨: test/single-shot.test.mjssingle-shot: --resume=<id> targets a specific session에서 같은 millisecond에 생성된 session id가 동일해 notStrictEqual assertion이 실패합니다.
  • 위 테스트 실패가 제품 버그인지 테스트 flake인지 결정 후 수정/재실행 필요
  • TTY 환경에서 openclone chat <slug> interactive Ink UI 수동 smoke test 권장
  • openclone chat <slug> --prompt ..., --resume, --resume=<id>, --no-persist CLI 경로 수동 확인 권장

참고 수치

  • Commits ahead of main: 4
  • Files changed: 25
  • Diff stat: +3216 / -45

Single-shot sessions can start multiple persisted threads in the same millisecond, and timestamp-only IDs could overwrite or collapse distinct sessions. Generate default session IDs monotonically while preserving deterministic timestamp formatting for explicit test dates.\n\nConstraint: CI validate failed on rapid --resume=<id> single-shot test due to millisecond ID collision\nRejected: Add sleeps in tests | would hide the persistence collision and slow CI\nConfidence: high\nScope-risk: narrow\nDirective: Keep explicit Date-based newSessionId calls deterministic for fixture readability\nTested: npm run build\nTested: npm run validate\nNot-tested: Remote GitHub Actions after push
@vkehfdl1

vkehfdl1 commented May 2, 2026

Copy link
Copy Markdown
Collaborator Author

CI validate 실패 원인 수정했습니다.

  • 원인: newSessionId()가 millisecond timestamp만 사용해서 빠르게 생성된 single-shot 세션 2개가 같은 ID를 받을 수 있었습니다. 이 때문에 single-shot: --resume=<id> targets a specific session 테스트가 CI에서 실패했고, 실제로는 세션 파일 overwrite/collapse 위험도 있었습니다.
  • 수정: 기본 세션 ID 생성을 process-local monotonic timestamp로 바꿔 같은 millisecond 내 연속 생성도 고유하게 만들었습니다. 명시적 Date를 넘기는 테스트/fixture 경로는 기존 포맷을 유지합니다.
  • 커밋: 61db407 Prevent rapid CLI session id collisions
  • 로컬 검증: npm run build, npm run validate 통과
  • 원격 검증: GitHub Actions validate 통과 https://github.com/open-clone/openclone/actions/runs/25247415117/job/74033918922

@vkehfdl1
vkehfdl1 requested review from 4entrepreneur and Koomook May 2, 2026 11:56
@4entrepreneur
4entrepreneur merged commit 50debc3 into main May 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants