sanook-cli 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.env.example +19 -0
  2. package/CHANGELOG.md +144 -0
  3. package/README.md +153 -20
  4. package/README.th.md +136 -0
  5. package/dist/agentContext.js +4 -0
  6. package/dist/approval.js +6 -0
  7. package/dist/bin.js +394 -51
  8. package/dist/brain.js +92 -59
  9. package/dist/brand.js +47 -0
  10. package/dist/checkpoint.js +37 -0
  11. package/dist/commands.js +86 -6
  12. package/dist/compaction.js +76 -5
  13. package/dist/config.js +100 -12
  14. package/dist/cost.js +60 -3
  15. package/dist/doctor.js +92 -0
  16. package/dist/gateway/auth.js +2 -2
  17. package/dist/gateway/ledger.js +2 -2
  18. package/dist/gateway/scheduler.js +1 -0
  19. package/dist/gateway/serve.js +6 -4
  20. package/dist/gateway/server.js +10 -2
  21. package/dist/git.js +11 -2
  22. package/dist/hooks.js +43 -17
  23. package/dist/knowledge.js +48 -49
  24. package/dist/loop.js +182 -66
  25. package/dist/lsp/client.js +173 -0
  26. package/dist/lsp/framing.js +56 -0
  27. package/dist/lsp/index.js +138 -0
  28. package/dist/lsp/servers.js +82 -0
  29. package/dist/mcp-server.js +244 -0
  30. package/dist/mcp.js +184 -29
  31. package/dist/memory-store.js +559 -0
  32. package/dist/memory.js +143 -29
  33. package/dist/orchestrate.js +150 -0
  34. package/dist/providers/codex.js +2 -2
  35. package/dist/providers/keys.js +3 -2
  36. package/dist/providers/registry.js +133 -1
  37. package/dist/repomap.js +93 -0
  38. package/dist/search/chunk.js +158 -0
  39. package/dist/search/embed-store.js +187 -0
  40. package/dist/search/engine.js +203 -0
  41. package/dist/search/fuse.js +35 -0
  42. package/dist/search/index-core.js +187 -0
  43. package/dist/search/indexer.js +241 -0
  44. package/dist/search/store.js +77 -0
  45. package/dist/session.js +42 -8
  46. package/dist/skill-install.js +10 -10
  47. package/dist/skills.js +12 -9
  48. package/dist/summarize.js +31 -0
  49. package/dist/tools/bash.js +21 -2
  50. package/dist/tools/diagnostics.js +41 -0
  51. package/dist/tools/edit.js +29 -7
  52. package/dist/tools/index.js +8 -1
  53. package/dist/tools/list.js +7 -2
  54. package/dist/tools/permission.js +90 -9
  55. package/dist/tools/read.js +23 -4
  56. package/dist/tools/remember.js +1 -1
  57. package/dist/tools/sandbox.js +61 -0
  58. package/dist/tools/search.js +105 -4
  59. package/dist/tools/task.js +195 -29
  60. package/dist/tools/timeout.js +35 -0
  61. package/dist/tools/util.js +10 -0
  62. package/dist/tools/write.js +6 -4
  63. package/dist/trust.js +89 -0
  64. package/dist/ui/app.js +218 -27
  65. package/dist/ui/banner.js +4 -9
  66. package/dist/ui/history.js +30 -0
  67. package/dist/ui/mentions.js +44 -0
  68. package/dist/ui/setup.js +6 -5
  69. package/dist/ui/useEditor.js +83 -0
  70. package/dist/update.js +114 -0
  71. package/dist/worktree.js +173 -0
  72. package/package.json +11 -5
  73. package/scripts/postinstall.mjs +33 -0
  74. package/second-brain/.agents/_Index.md +30 -0
  75. package/second-brain/.agents/skills/_Index.md +30 -0
  76. package/second-brain/.agents/workflows/_Index.md +30 -0
  77. package/second-brain/AGENTS.md +4 -4
  78. package/second-brain/Acceptance/_Index.md +30 -0
  79. package/second-brain/Acceptance/golden-case-template.md +39 -0
  80. package/second-brain/Areas/_Index.md +30 -0
  81. package/second-brain/Bugs/System-OS/_Index.md +30 -0
  82. package/second-brain/Bugs/_Index.md +30 -0
  83. package/second-brain/CLAUDE.md +4 -1
  84. package/second-brain/Checklists/_Index.md +30 -0
  85. package/second-brain/Checklists/preflight-postflight-template.md +29 -0
  86. package/second-brain/Distillations/_Index.md +30 -0
  87. package/second-brain/Entities/_Index.md +30 -0
  88. package/second-brain/Entities/entity-template.md +33 -0
  89. package/second-brain/Evals/_Index.md +30 -0
  90. package/second-brain/Evals/correction-pairs.md +24 -0
  91. package/second-brain/Evals/failure-taxonomy.md +24 -0
  92. package/second-brain/Evals/golden-set.md +25 -0
  93. package/second-brain/Evals/quality-ledger.md +23 -0
  94. package/second-brain/Evals/self-eval-rubric.md +23 -0
  95. package/second-brain/GEMINI.md +4 -4
  96. package/second-brain/Goals/_Index.md +30 -0
  97. package/second-brain/Handoffs/_Index.md +30 -0
  98. package/second-brain/Home.md +7 -0
  99. package/second-brain/Intake/Raw Sources/_Index.md +30 -0
  100. package/second-brain/Intake/_Index.md +30 -0
  101. package/second-brain/Intake/_Quarantine/_Index.md +30 -0
  102. package/second-brain/Learning/_Index.md +30 -0
  103. package/second-brain/Playbooks/_Index.md +30 -0
  104. package/second-brain/Playbooks/playbook-template.md +23 -0
  105. package/second-brain/Projects/_Index.md +30 -0
  106. package/second-brain/Prompts/_Index.md +30 -0
  107. package/second-brain/README.md +2 -1
  108. package/second-brain/Research/_Index.md +30 -0
  109. package/second-brain/Retrospectives/_Index.md +30 -0
  110. package/second-brain/Reviews/_Index.md +30 -0
  111. package/second-brain/Runbooks/_Index.md +30 -0
  112. package/second-brain/Runbooks/eval-loop.md +24 -0
  113. package/second-brain/Sessions/_Index.md +30 -0
  114. package/second-brain/Shared/AI-Context-Index.md +20 -0
  115. package/second-brain/Shared/AI-Threads/_Index.md +30 -0
  116. package/second-brain/Shared/Archive/_Index.md +30 -0
  117. package/second-brain/Shared/Assets/_Index.md +30 -0
  118. package/second-brain/Shared/Context-Packs/_Index.md +30 -0
  119. package/second-brain/Shared/Context7-Docs/_Index.md +30 -0
  120. package/second-brain/Shared/Coordination/NOW.md +28 -0
  121. package/second-brain/Shared/Coordination/_Index.md +30 -0
  122. package/second-brain/Shared/Coordination/agent-registry.md +24 -0
  123. package/second-brain/Shared/Coordination/task-board/_Index.md +30 -0
  124. package/second-brain/Shared/Coordination/task-board/task-template.md +43 -0
  125. package/second-brain/Shared/Coordination/task-board.md +32 -0
  126. package/second-brain/Shared/Core-Facts/_Index.md +30 -0
  127. package/second-brain/Shared/Decision-Memory/_Index.md +30 -0
  128. package/second-brain/Shared/Glossary/_Index.md +30 -0
  129. package/second-brain/Shared/Memory-Inbox/_Index.md +30 -0
  130. package/second-brain/Shared/Operating-State/_Index.md +30 -0
  131. package/second-brain/Shared/Prompting/_Index.md +30 -0
  132. package/second-brain/Shared/Provenance/_Index.md +30 -0
  133. package/second-brain/Shared/Rules/_Index.md +30 -0
  134. package/second-brain/Shared/Rules/contextual-note-rule.md +30 -0
  135. package/second-brain/Shared/Rules/frontmatter-standard.md +10 -0
  136. package/second-brain/Shared/Rules/memory-write-protocol.md +28 -0
  137. package/second-brain/Shared/Rules/procedural-runbook-header.md +40 -0
  138. package/second-brain/Shared/Rules/review-and-staleness-policy.md +22 -0
  139. package/second-brain/Shared/Rules/rules-formatting.md +34 -0
  140. package/second-brain/Shared/Scripts/_Index.md +30 -0
  141. package/second-brain/Shared/Scripts-Archive/_Index.md +30 -0
  142. package/second-brain/Shared/Tech-Standards/_Index.md +30 -0
  143. package/second-brain/Shared/Tech-Standards/verification-standard.md +40 -0
  144. package/second-brain/Shared/User-Memory/_Index.md +30 -0
  145. package/second-brain/Shared/User-Persona/_Index.md +30 -0
  146. package/second-brain/Shared/User-Persona/owner-profile.md +25 -0
  147. package/second-brain/Shared/Working-Memory/_Index.md +30 -0
  148. package/second-brain/Shared/_Index.md +30 -0
  149. package/second-brain/Shared/mcp-servers/_Index.md +30 -0
  150. package/second-brain/Skills/_Index.md +30 -0
  151. package/second-brain/Templates/_Index.md +30 -0
  152. package/second-brain/Templates/bug.md +2 -0
  153. package/second-brain/Templates/handoff.md +2 -0
  154. package/second-brain/Templates/session.md +2 -0
  155. package/second-brain/Tools/_Index.md +30 -0
  156. package/second-brain/Traces/_Index.md +30 -0
  157. package/second-brain/Vault Structure Map.md +33 -1
  158. package/second-brain/copilot/_Index.md +30 -0
  159. package/skills/audit-license-compliance/SKILL.md +117 -0
  160. package/skills/author-codemod/SKILL.md +110 -0
  161. package/skills/build-audit-logging/SKILL.md +112 -0
  162. package/skills/build-cdc-streaming-pipeline/SKILL.md +123 -0
  163. package/skills/build-cli-tool/SKILL.md +108 -0
  164. package/skills/build-data-table/SKILL.md +141 -0
  165. package/skills/build-native-mobile-ui/SKILL.md +154 -0
  166. package/skills/build-offline-first-sync/SKILL.md +118 -0
  167. package/skills/build-realtime-channel/SKILL.md +122 -0
  168. package/skills/build-vector-search/SKILL.md +131 -0
  169. package/skills/compose-local-dev-stack/SKILL.md +149 -0
  170. package/skills/configure-bundler-build/SKILL.md +166 -0
  171. package/skills/configure-dns-tls/SKILL.md +142 -0
  172. package/skills/configure-reverse-proxy-lb/SKILL.md +129 -0
  173. package/skills/configure-security-headers-csp/SKILL.md +122 -0
  174. package/skills/contract-testing/SKILL.md +140 -0
  175. package/skills/datetime-timezone-correctness/SKILL.md +125 -0
  176. package/skills/debug-ci-pipeline-failure/SKILL.md +134 -0
  177. package/skills/debug-flaky-tests/SKILL.md +128 -0
  178. package/skills/defend-llm-prompt-injection/SKILL.md +110 -0
  179. package/skills/deliver-webhooks/SKILL.md +116 -0
  180. package/skills/design-api-pagination/SKILL.md +144 -0
  181. package/skills/design-authorization-model/SKILL.md +119 -0
  182. package/skills/design-backup-dr-recovery/SKILL.md +113 -0
  183. package/skills/design-event-sourcing-cqrs/SKILL.md +143 -0
  184. package/skills/design-multi-tenancy/SKILL.md +100 -0
  185. package/skills/design-protobuf-grpc-service/SKILL.md +146 -0
  186. package/skills/design-relational-schema/SKILL.md +129 -0
  187. package/skills/design-search-index-infra/SKILL.md +151 -0
  188. package/skills/design-state-machine/SKILL.md +108 -0
  189. package/skills/design-token-system/SKILL.md +109 -0
  190. package/skills/distributed-locks-leases/SKILL.md +120 -0
  191. package/skills/encrypt-sensitive-data/SKILL.md +148 -0
  192. package/skills/feature-flags-rollout/SKILL.md +130 -0
  193. package/skills/file-upload-object-storage/SKILL.md +107 -0
  194. package/skills/fuzz-dynamic-security-test/SKILL.md +111 -0
  195. package/skills/harden-llm-app-reliability/SKILL.md +126 -0
  196. package/skills/i18n-localization-setup/SKILL.md +113 -0
  197. package/skills/idempotency-keys/SKILL.md +107 -0
  198. package/skills/implement-push-notifications/SKILL.md +142 -0
  199. package/skills/ingest-webhook-secure/SKILL.md +120 -0
  200. package/skills/integrate-oauth-oidc/SKILL.md +126 -0
  201. package/skills/load-stress-test/SKILL.md +129 -0
  202. package/skills/map-privacy-data-gdpr/SKILL.md +146 -0
  203. package/skills/model-nosql-data/SKILL.md +118 -0
  204. package/skills/money-decimal-arithmetic/SKILL.md +123 -0
  205. package/skills/monitor-ml-drift/SKILL.md +109 -0
  206. package/skills/numeric-precision-units/SKILL.md +144 -0
  207. package/skills/optimize-llm-cost-latency/SKILL.md +103 -0
  208. package/skills/optimize-react-rerenders/SKILL.md +124 -0
  209. package/skills/orchestrate-agent-workflow/SKILL.md +100 -0
  210. package/skills/payments-billing-integration/SKILL.md +114 -0
  211. package/skills/pin-toolchain-versions/SKILL.md +116 -0
  212. package/skills/plan-strangler-migration/SKILL.md +95 -0
  213. package/skills/property-based-testing/SKILL.md +108 -0
  214. package/skills/publish-package-registry/SKILL.md +130 -0
  215. package/skills/recover-git-state/SKILL.md +119 -0
  216. package/skills/remediate-web-vulnerabilities/SKILL.md +125 -0
  217. package/skills/resilience-timeouts-retries/SKILL.md +104 -0
  218. package/skills/resolve-merge-rebase-conflict/SKILL.md +97 -0
  219. package/skills/rewrite-git-history/SKILL.md +109 -0
  220. package/skills/scaffold-cross-platform-app/SKILL.md +137 -0
  221. package/skills/schema-evolution-compatibility/SKILL.md +121 -0
  222. package/skills/send-transactional-email/SKILL.md +126 -0
  223. package/skills/serve-deploy-ml-model/SKILL.md +107 -0
  224. package/skills/setup-cdn-edge-waf/SKILL.md +107 -0
  225. package/skills/setup-devcontainer-env/SKILL.md +131 -0
  226. package/skills/setup-lint-format-precommit/SKILL.md +140 -0
  227. package/skills/setup-monorepo-tooling/SKILL.md +125 -0
  228. package/skills/ship-mobile-app-store-release/SKILL.md +137 -0
  229. package/skills/structured-output-llm/SKILL.md +86 -0
  230. package/skills/supply-chain-sbom-provenance/SKILL.md +120 -0
  231. package/skills/test-data-factories/SKILL.md +158 -0
  232. package/skills/threat-model-stride/SKILL.md +123 -0
  233. package/skills/train-evaluate-ml-model/SKILL.md +109 -0
  234. package/skills/unicode-text-correctness/SKILL.md +109 -0
  235. package/skills/visual-regression-testing/SKILL.md +120 -0
package/.env.example CHANGED
@@ -21,3 +21,22 @@ ZHIPU_API_KEY=xxx # z.ai / open.bigmodel.cn (GLM — GLM
21
21
 
22
22
  # default model (alias หรือ provider:model) — เช่น sonnet | opus | gpt | grok | groq:fast
23
23
  SANOOK_MODEL=sonnet
24
+
25
+ # ── Token / cost tuning (optional) ──
26
+ # SANOOK_CACHE_TTL=1h # Anthropic prompt cache: 5m (default) หรือ 1h
27
+ # SANOOK_COMPACTION=summarize # summarize context แทน truncate เมื่อบทสนทนายาว
28
+ # SANOOK_SUMMARY_MODEL=haiku # model สำหรับ summarize-compaction (default = fast sibling)
29
+ # SANOOK_SUBAGENT_MODEL=haiku # ให้ sub-agent ใช้ model ถูกกว่า main agent
30
+ # SANOOK_THINKING=4000 # เปิด Anthropic extended thinking พร้อม budgetTokens cap
31
+ # SANOOK_EMBEDDING_MODEL=openai:text-embedding-3-small # semantic/hybrid search
32
+ # SANOOK_PRICING='{"openai:gpt-5.5":{"input":1.25,"output":10}}' # override price table
33
+
34
+ # ── Safety / privacy opt-ins ──
35
+ # SANOOK_ALLOW_OUTSIDE_WORKSPACE=1 # ให้ file tools แตะ path นอก cwd/second-brain
36
+ # SANOOK_NO_SANDBOX=1 # ปิด OS sandbox ของ run_bash (Seatbelt/bubblewrap)
37
+ # SANOOK_GATEWAY_ALLOW_WRITE=1 # ให้ sanook serve รัน write/bash แบบ unattended
38
+ # SANOOK_HOOKS_INHERIT_ENV=1 # ส่ง env เต็มให้ hooks (default ส่งเฉพาะ safe env)
39
+ # SANOOK_DISABLE_UPDATE_CHECK=1 # ปิด prompt เช็กอัปเดตใน TUI
40
+ # SANOOK_DISABLE_PERSISTENCE=1 # ไม่บันทึก sessions/memory
41
+ # SANOOK_DISABLE_WORKLOG=1 # ไม่เขียน worklog เข้า second-brain
42
+ # SANOOK_TRUST_PROJECT=1 # trust project .sanook mcp/hooks/skills/commands ชั่วคราว
package/CHANGELOG.md CHANGED
@@ -1,5 +1,149 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ### Install UX — `sanook doctor` + post-install guidance (the "`sanook` is not recognized" fix)
6
+
7
+ The #1 first-run snag is `npm i sanook-cli` **without `-g`** → a local install that never lands on PATH, so typing `sanook` fails. Two root-cause fixes:
8
+
9
+ - **`sanook doctor`** — a new diagnose-and-fix command. Checks Node version, the npm global-bin dir, whether the `sanook` shim is installed there, whether that dir is on PATH, and whether a local install exists in the cwd — then prints the **exact, OS-safe remedy**. On Windows it emits the *safe* user-PATH PowerShell one-liner (`[Environment]::SetEnvironmentVariable(... 'User')`), deliberately **not** `setx %PATH%` (which truncates PATH at 1024 chars and duplicates the system PATH — a known corruption footgun). Runnable as `npx sanook doctor` even before a global install.
10
+ - **post-install hint** — right after `npm i`, the installer now prints the working command: local install → `npx sanook` or `npm i -g sanook-cli`; global install → "ready, type `sanook`". Never fails the install (always exits 0); stays quiet during repo-dev self-installs and CI.
11
+
12
+ ### Cross-platform hardening — Windows / macOS / Linux
13
+
14
+ Audited the whole codebase (process spawning, paths, external deps, terminal) and fixed the real Windows breakers:
15
+
16
+ - **Child-process spawning on Windows**: `npx`/`npm`/`codex` are `.cmd` shims, so `spawn('npx', …)` failed with ENOENT — breaking **MCP servers** (incl. the second-brain filesystem MCP), **`sanook update`**, and the **Codex provider**. Now spawned with `shell` on `win32`. (`hooks` already used a shell; git/LSP already resolve correctly.)
17
+ - **`grep` works without ripgrep**: if `rg` isn't installed (common on a fresh Windows box) the grep tool now falls back to a pure-Node search (recursive walk, default-ignore set, binary/large-file skip, CRLF-aware) instead of a cryptic `spawn rg ENOENT`. Verified end-to-end with `rg` removed from PATH.
18
+ - **The agent knows its OS**: the system prompt now states the platform + shell, so on Windows it generates `dir`/`type`/`findstr` (or prefers the cross-platform read/list/glob/grep tools) instead of `ls`/`cat`/`grep` into cmd.exe.
19
+ - **CRLF-safe vault indexing**: the markdown chunker normalizes `\r\n` so frontmatter/sections parse identically on Windows.
20
+ - **Terminal**: respects `NO_COLOR` and auto-disables ANSI when output is piped/redirected (no `[2m` garbage on legacy cmd); `FORCE_COLOR` overrides.
21
+ - **Clear "not installed" errors** for `git` (and ripgrep) instead of raw `spawn … ENOENT`.
22
+ - (Prior turn) Node-version guard (≥ 22) with a clear message; install docs clarified for `-g` / `npx` / Windows `setx`.
23
+
24
+ ### Skills — 16 more real-work skills, author+verify reviewed (130 → 146)
25
+
26
+ A second, larger batch filling concrete gaps in distributed-systems correctness, testing rigor, frontend, security, and AI — each one **independently verified** (a separate reviewer agent read the written file and judged frontmatter validity, structure, cross-reference resolution, technical accuracy, and *actionability* — would an agent actually be able to follow it; all 16 passed):
27
+
28
+ - **Backend/distributed**: `design-api-pagination` (cursor/keyset, stable ordering, Relay connections), `distributed-locks-leases` (Redlock caveats + fencing tokens, leader election), `design-state-machine` (explicit FSM/statecharts vs boolean-flag soup), `schema-evolution-compatibility` (backward/forward compat, reserve-don't-reuse, expand-then-contract).
29
+ - **Testing**: `debug-flaky-tests` (taxonomy + root-cause fixes, not retry-masking), `test-data-factories` (factories over fixtures, faker, deterministic seeds), `property-based-testing` (invariants + shrinking), `contract-testing` (consumer-driven Pact + can-i-deploy), `visual-regression-testing` (deterministic screenshots).
30
+ - **Frontend**: `build-data-table` (virtualized sortable/filterable grids), `optimize-react-rerenders` (profiler-driven memoization).
31
+ - **Security**: `configure-security-headers-csp` (strict CSP with nonces, HSTS, CORS done right), `encrypt-sensitive-data` (KMS envelope encryption, AEAD, key rotation).
32
+ - **AI/LLM**: `build-vector-search` (ANN indexes, hybrid + RRF, eval), `structured-output-llm` (json_schema/tool-calling + validate-and-repair).
33
+ - **DevEx**: `debug-ci-pipeline-failure` (reproduce locally, classify, fix root cause).
34
+
35
+ All load cleanly (catalog now 146), cross-reference only real sibling skills, and follow the When-to-Use + NOT-this-skill + dense Steps structure.
36
+
37
+ ### Skills — 7 high-value additions, closing reliability/integration gaps (123 → 130)
38
+
39
+ New bundled skills filling conspicuous holes in the catalog, authored to the existing dense, cross-referenced bar:
40
+
41
+ - **resilience-timeouts-retries** — timeouts on everything, deadline propagation, retry-only-if-idempotent, exponential backoff + full jitter, circuit breakers, bulkheads, load-shedding (the general reliability primitive the catalog was missing alongside `rate-limiting`).
42
+ - **idempotency-keys** — make writes safe to repeat: idempotency by design (PUT/upsert/conditional) vs by key (the `Idempotency-Key` header pattern, dedup table, 409/422, outbox), consumer-side dedup, "effectively-once".
43
+ - **integrate-oauth-oidc** — "Log in with Google/GitHub/…": Authorization Code + PKCE, state/nonce, server-side token exchange, full ID-token validation, safe `email_verified` account linking, refresh rotation, native system-browser-only. Complements `auth-jwt-session` (which owns *your* session).
44
+ - **send-transactional-email** — the deliverability half: SPF/DKIM/DMARC alignment, provider-not-cold-MTA, transactional/marketing stream isolation, bounce/complaint → suppression, idempotent sends, sandbox testing.
45
+ - **design-multi-tenancy** — tenant isolation models (shared+RLS / schema / DB-per-tenant), the #1 cross-tenant-leak bug + defense-in-depth (app scoping + Postgres RLS), tenant context propagation, per-tenant migrations/export/delete.
46
+ - **build-cli-tool** — argument parsing, exit codes, stdout=data/stderr=logs, TTY/NO_COLOR, config precedence (flags>env>file), no-secrets-in-flags, dry-run — the CLI/UX design counterpart to `shell-script-robust`.
47
+ - **deliver-webhooks** — the *producer* side: HMAC signing + rotation, replay tolerance, at-least-once retries + dead-letter + replay, stable event ids for consumer dedup, SSRF defenses — complements `ingest-webhook-secure` (the receiver).
48
+
49
+ Each loads cleanly (frontmatter `name`/`description`/`when_to_use`), cross-references only real sibling skills, and follows the When-to-Use + NOT-this-skill + Steps structure.
50
+
51
+ ### Onboarding & ease-of-use — no more dead-ends, clearer guidance
52
+
53
+ The first-run / no-key experience now guides instead of dumping a raw error, and the REPL surfaces more of what's available:
54
+
55
+ - **Headless with no key no longer dead-ends**: `sanook "task"` before any key is set now prints an actionable hint — run `sanook` for the wizard, or `export <ENV>=…` with the **provider console URL** to get a key — and, if a *different* provider's key is already in the environment, it suggests `sanook -m <that-provider> "…"`.
56
+ - **Smart first-run**: if an API key is already in the environment (e.g. you `export ANTHROPIC_API_KEY=…` before installing), sanook skips the setup wizard, picks that provider, and confirms `✅ ready` instead of asking you to re-enter everything.
57
+ - **Setup wizard** now shows the **console URL** for the chosen provider at the key step, and the run ends with a clear `✅ ตั้งค่าเสร็จ — พิมพ์งานได้เลย`.
58
+ - **Actionable key errors**: a missing key names the provider + console URL + how to set it; a wrong-format key shows a readable example (`sk-ant-…`, `AIza…`) instead of a raw regex (and the example is kept short so redaction doesn't mangle it).
59
+ - **Discoverability**: `/tools` now lists the orchestration tools (`task_parallel`/`task_spawn`/`task_collect`/`task_cancel`/`task_status`) and `diagnostics`; `/help` points to the shell-side commands (`search`/`index`/`brain`/`serve`/`mcp serve`/`config set`); the REPL empty-state hints `/help` and `/tools`.
60
+
61
+ ### Token/cost tuning knobs — 1h cache, summarize-compaction, sub-agent routing, thinking budget
62
+
63
+ New `config.json` fields (and `SANOOK_*` env overrides) that trade tokens/cost without hurting quality — read once per turn via `agentTuning()`:
64
+
65
+ - **`cacheTtl: "5m" | "1h"`** (env `SANOOK_CACHE_TTL`): the Anthropic prompt-cache lifetime. `"1h"` keeps the cached preamble alive across pauses (lunch, a meeting) so resuming doesn't re-pay full price for it. Default `"5m"` (unchanged).
66
+ - **`compaction: "truncate" | "summarize"`** (env `SANOOK_COMPACTION`): when the conversation gets long, `"summarize"` condenses the dropped middle with a **cheap model** (the fast sibling of your main model, same key — `fastSibling()`) instead of truncating it — better recall at the same token budget. Used by `/compact` and proactively before very long turns; falls back to truncation if the summarizer fails (never blocks a turn). Default `"truncate"` (zero-LLM, unchanged). `src/compaction.ts` `summarizeCompact()` is pure (injected summarizer) and unit-tested with no network.
67
+ - **`SANOOK_SUBAGENT_MODEL`**: route all sub-agent work to a cheaper model (e.g. `haiku`) while the main agent keeps the strong one — big savings for exploration-heavy runs. Default: inherit the parent model.
68
+ - **`thinking: true | <budget>`** (env `SANOOK_THINKING`): opt-in Anthropic extended thinking on the **main** agent only (never sub-agents), with a hard `budgetTokens` cap so reasoning can't run away. Default off (no change).
69
+
70
+ ### Token efficiency — line-range reads + targeted edits
71
+
72
+ Two quality-neutral cuts to per-turn token cost (the model asks for exactly what it needs):
73
+
74
+ - **`read_file` line ranges**: `read_file({ path, offset?, limit? })` returns just lines `offset..offset+limit` (with a `[lines A–B of N]` header) instead of the whole file. Paired with `grep` (which already returns line numbers), the model reads a small window around a symbol rather than an entire large file — large input-token savings, identical result. Default (no offset/limit) is unchanged. The system prompt now nudges grep→read-range over whole-file reads.
75
+ - **`edit_file` `replace_all`**: rename/repeated edits use a short `old_string` with `replace_all: true` instead of padding it with surrounding context for uniqueness (which the model otherwise sends twice, old + new). The ambiguous-match error now suggests `replace_all` rather than "add more context".
76
+ - **Terser replies**: the system prompt tells the agent not to paste back file contents / large code blocks it just read or edited (the user already sees the diff) — cuts output tokens with no loss.
77
+ - **Sub-agent model routing** (`SANOOK_SUBAGENT_MODEL`): opt-in env that routes all sub-agent work to a cheaper model (e.g. `haiku`) while the main agent keeps the strong model — big cost savings for exploration-heavy runs, default unchanged (inherit the parent model).
78
+
79
+ ### LSP diagnostics — `diagnostics` tool (type errors without a full build)
80
+
81
+ A Node-native, zero-dependency Language Server Protocol client (`src/lsp/`) gives the agent a tight edit→check feedback loop: after editing a file it can pull the language server's diagnostics (type errors / lint) for that one file, no project-wide compile.
82
+
83
+ - **`diagnostics` tool**: `diagnostics({ path, content? })` → ranked errors/warnings as `✗ path:line:col message [code]`. Pass `content` to check an unsaved buffer. Respects worktree scoping (`agentCwd`) and the read-path guard.
84
+ - **Real LSP client** over Content-Length framing (distinct from MCP's newline framing): initialize handshake, `didOpen`/`didChange`, diagnostics that **settle** (a quiet period after the last publish so we return the final set, not an early empty one), and it answers server→client requests so a server can't stall. Positions convert from LSP 0-based to human 1-based.
85
+ - **Server pool**: servers are spawned once per (binary, workspace) and reused — re-checking a file becomes a `didChange`, so the slow project-load cost is paid once, not per call.
86
+ - **Zero-config floor**: sanook bundles no language servers (like ripgrep). It maps extension → conventional server (typescript-language-server, pyright, gopls, rust-analyzer, vscode-json-language-server, bash-language-server), detects whether it's installed (node_modules/.bin or PATH), and when it isn't, returns a clear install hint instead of crashing.
87
+ - Tests: the framing codec (split frames, multibyte, malformed-recovery), the client handshake + diagnostics-settle + server-request handling against a **fake server** (no real LSP needed), the server registry + binary detection, and the tool's graceful degradation. Verified end-to-end against a real `typescript-language-server` (caught TS2322 at the right position; pooled `didChange` cleared it after a fix).
88
+
89
+ ### Real-time steering — interrupt a running turn + queue follow-ups (REPL)
90
+
91
+ The interactive REPL was input-locked while the agent worked, and Ctrl+C quit the whole app. Now a turn is steerable:
92
+
93
+ - **Interrupt mid-turn**: the running turn gets a real `AbortController` (wired into `runAgent`'s `signal`). Press **Esc** (or Ctrl+C) to stop the stream/tool loop right away and return to the prompt — without exiting the app. Partial output is kept for reference, the turn is dropped from the model history, and any files a tool already changed are recoverable via `/rewind`.
94
+ - **Type-ahead queue**: you can type while the agent is busy; pressing Enter **queues** the message (shown as `⏳ คิว …`) and it runs automatically as the next turn when the current one finishes. Interrupting clears the queue.
95
+ - Covered by an Ink integration test (mocked agent) asserting the signal is passed, input queues while busy, and Esc both aborts and clears the queue.
96
+
97
+ ### Worktree isolation — safe parallel WRITE subagents (`task_parallel isolate:true`)
98
+
99
+ Parallel subagents that edit files would clobber a shared tree. Now each write subagent can run in its own throwaway **git worktree** (`src/worktree.ts`), branched from the current HEAD, and its changes are merged back afterward:
100
+
101
+ - **Per-agent working directory**: `AgentContext` gains a `cwd`, and every file tool (read/write/edit/list/glob/grep/bash) plus the write-confinement guard (`permission.allowedRoots`) now resolve through `agentCwd()`. A subagent's relative path (`src/foo.ts`) lands in ITS worktree, not the main tree — so isolation can't leak. The main agent is unchanged (no `cwd` ⇒ `process.cwd()`); the OS sandbox already confines writes to the active cwd, so a worktree is automatically sandboxed.
102
+ - **`task_parallel isolate:true`**: creates a worktree per write subagent, runs them concurrently in isolation, captures each worktree's diff, and applies them back to the main tree **sequentially** with `git apply --3way` — conflicts are reported (with the touched files), never silently overwritten. Worktrees are always cleaned up. Requires a git repo (falls back with a clear message otherwise).
103
+ - **`src/worktree.ts`**: `createWorktree` / `captureDiff` (incl. untracked) / `applyDiff` / `removeWorktree` / `runInWorktrees`, all over the existing `runGit` helper. The create→isolate→merge→cleanup lifecycle is unit-tested against a real throwaway git repo with an injected work callback (no agent/network), covering the round-trip, the parallel different-files case, empty diffs, non-git fallback, and conflict reporting.
104
+
105
+ ### Subagent orchestration — parallel fan-out + background + nested (`task_parallel` / `task_spawn` / `task_collect` / `task_cancel` / `task_status`)
106
+
107
+ The single one-shot `task` subagent grows into a real orchestration layer (`src/orchestrate.ts`):
108
+
109
+ - **Parallel fan-out** (`task_parallel`): run up to 16 subagents concurrently with a real concurrency cap and **per-item error isolation** (one failure never sinks the batch); results returned in order. For independent work — explore N modules, review N angles — instead of serial subagents.
110
+ - **Background / async** (`task_spawn` → `task_collect` / `task_cancel` / `task_status`): spawn a detached subagent, get an id immediately, keep working, gather the result later in the same session, or cancel it. `task_collect` supports a timeout so the agent can poll instead of block; failures are captured as `error` state (never an unhandled rejection); `task_cancel` aborts via the subagent's `AbortSignal`.
111
+ - **Nested**: subagents may themselves `task` / `task_parallel` (bounded by the depth cap), so the main agent can decompose, then each branch can decompose again.
112
+ - **Parallel-safe context**: each subagent runs inside its own `AsyncLocalStorage.run()` scope, so concurrent/nested agents never bleed model/budget/depth into one another.
113
+ - Orchestration core is pure with an **injected runner** + injectable clock/id-gen, unit-tested with a fake runner (concurrency cap, error isolation, spawn/collect/cancel/timeout) — zero model calls in CI.
114
+
115
+ ### Brain search — hybrid semantic search over the second brain (`sanook index` / `sanook search` / `sanook mcp serve`)
116
+
117
+ A Node-native search subsystem (`src/search/`) over the second-brain vault **plus** bi-temporal memory, past sessions, and skills — one ranked surface, zero new heavy deps.
118
+
119
+ - **Real BM25, zero-dependency floor**: a pure-TS inverted index (k1=1.2, b=0.75, genuine corpus-stat IDF, title field-boost) — no SQLite, no Bun, no native binary, works with no key and no network on any OS Node 22 runs on. Tokenization builds on the canonical `normalize()` and segments with `Intl.Segmenter` so **Thai** splits into real words.
120
+ - **Incremental indexer** (`sanook index`): mtime+size+sha256 manifest diff over the existing vault via `brainPath` — O(delta), unchanged files cost one `stat()`, deleted files are evicted precisely. No `ψ/`-style directory convention required. Folds in active memory facts (with an importance prior; quarantined/inbox facts excluded), recent sessions, and skills.
121
+ - **Optional BYOK semantic** (`--mode hybrid|semantic`): embeddings through your *existing* provider key (`@ai-sdk/{openai,mistral,google,…}` `embedMany`), stored as a compact Float32 sidecar, cosine in-process over a candidate set; fused with BM25 via **Reciprocal Rank Fusion** (scale-free, k=60). Lazy — absent without a key; a model change self-invalidates the cache. Degrades silently to BM25 on any embedding error.
122
+ - **MCP SERVER** (`sanook mcp serve`): sanook was MCP client-only; it now also *exposes* a stdio JSON-RPC server (`sanook_search` / `sanook_recall` / `sanook_remember` / `sanook_index` / `sanook_stats`), so Claude Desktop / Cursor / any MCP host can mount sanook's brain. Same zero-dep framing as the client; stdout stays protocol-clean.
123
+ - **`recall` upgraded**: the agent's recall tool now ranks by BM25 across all four corpora with snippets (was substring term-counting), and folds freshly-`remember`ed facts in on every call.
124
+ - Tests: BM25 ranking/IDF/no-posting-creep, heading chunker + frontmatter + wikilinks, RRF, incremental indexer (in-memory fs), cosine/serialize (fake vectors), engine modes + degradation, MCP server dispatch + a real piped-child round-trip (≈70 new).
125
+
126
+ ### Competitive parity pass (table-stakes the OSS field now expects)
127
+
128
+ - **Remote MCP**: connect MCP servers over Streamable-HTTP (`{ "url": "https://…", "headers": {…} }`), not just stdio — half the hosted MCP ecosystem (GitHub/Slack/Postgres/…) is HTTP-only. `sanook mcp add <name> <url>` auto-detects remote.
129
+ - **Prompt caching**: the static system preamble (instructions + skills + brain + repo map) is sent as a cached Anthropic block, cutting cost/latency on multi-step turns; safely ignored by other providers.
130
+ - **OS sandbox for `run_bash`**: Seatbelt (macOS) / bubblewrap (Linux) confine shell **writes** to the workspace/brain/tmp — defense-in-depth over the regex blocklist. Reads + network unchanged; `SANOOK_NO_SANDBOX=1` to disable.
131
+ - **Checkpoint + `/rewind`**: a shadow-git snapshot before each turn; `/rewind` restores files **and** truncates the conversation (recoverable — it stashes the current state first).
132
+ - **Input ergonomics**: multiline (trailing `\` / Alt+Enter), `↑`/`↓` persisted prompt history, readline keys (Ctrl-A/E/U/K/W), and `@file` mentions that inline file contents.
133
+ - **Image / vision input**: `@image.png` attaches the image to vision-capable models (history keeps a lightweight placeholder, not the bytes).
134
+ - **Custom slash commands**: `.sanook/commands/<name>.md` prompt templates invoked as `/<name>` (project commands gated by trust).
135
+ - **Repo map**: a zero-dep, git-aware symbol map injected at session start so the agent selects files without blind grepping.
136
+ - **Reliability**: rate-limit/overload retry with exponential backoff, kept distinct from auth/billing failures (which fail fast); per-tool execution timeouts so a runaway read/grep can't hang the loop.
137
+ - **Minimal terminal-first TUI**: compact gradient banner, a real cursor + placeholder, and cleaner turn rendering.
138
+
139
+ ### Fixes
140
+
141
+ - **Multi-turn history loss**: REPL/`--continue` now retain the full conversation (the user's earlier turns were being dropped — only assistant/tool messages were kept).
142
+ - **Budget cap was silent for ~all non-Anthropic models**: added approximate list prices for the other providers, a `pricing` override (`sanook config set pricing …` / `SANOOK_PRICING`), and a warning when `-b` is set for a model (or fallback model) with no pricing. Cost now carries over when the model falls back instead of resetting.
143
+ - MCP SSE parsing no longer aborts on a malformed earlier event; checkpoint restore pins the snapshot commit (correct even after an intervening commit) and removes files added after the snapshot.
144
+
145
+ - Tests: remote-MCP, sandbox, repo map, prompt-caching/system-preservation, rate-limit classification, tool timeout, checkpoint restore, cost merge, custom commands (≈200 total).
146
+
3
147
  ## 0.4.0 — second brain, GLM Coding Plan, Telegram, hardening
4
148
 
5
149
  - **Second brain**: `sanook brain init` scaffolds a portable Obsidian "second-brain" workspace — full folder taxonomy (each with `_Index.md`), a central `Vault Structure Map.md`, seed memory files, and a portable AI operating constitution (`CLAUDE/GEMINI/AGENTS.md`). Research-backed rules: context-assembly (anti context-rot), intake quarantine + injection-scan, bi-temporal fact validity, provenance tracking, a verification-gated `Skills/` library, sleep-time consolidation. The agent now **loads the vault** into context, and `brain init` **auto-wires a filesystem MCP** to it. First-run wizard offers to create one (personalized).
package/README.md CHANGED
@@ -2,19 +2,25 @@
2
2
 
3
3
  # Sanook CLI
4
4
 
5
- **A terminal AI coding agent built from scratch in TypeScript.**
5
+ **The open-source terminal AI coding agent that remembers across sessions.**
6
6
 
7
- Bring your own key, run with any of 12 model providers, and let it remember what it did across sessions.
7
+ Bring your own key · 12 providers · MCP · a built-in **"second brain"** that gives the AI durable memory across sessions — the thing Claude Code, Codex, and Gemini CLI lose at the session boundary.
8
8
 
9
- [![Version](https://img.shields.io/badge/version-0.3.0-2563eb.svg)](https://github.com/Sir-chawakorn/sanook-cli/releases)
9
+ 🇹🇭 [อ่านภาษาไทย](README.th.md)
10
+
11
+ [![npm](https://img.shields.io/npm/v/sanook-cli.svg?color=2563eb)](https://www.npmjs.com/package/sanook-cli)
12
+ [![downloads](https://img.shields.io/npm/dm/sanook-cli.svg?color=2563eb)](https://www.npmjs.com/package/sanook-cli)
10
13
  [![License](https://img.shields.io/badge/license-Apache--2.0-22c55e.svg)](LICENSE)
11
14
  [![Node](https://img.shields.io/badge/node-%E2%89%A5%2022-339933.svg?logo=node.js&logoColor=white)](https://nodejs.org)
12
15
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6.svg?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
13
- [![Tests](https://img.shields.io/badge/tests-143%20passing-22c55e.svg)](#development)
16
+ [![Tests](https://img.shields.io/badge/tests-381%20passing-22c55e.svg)](#development)
14
17
  [![CI](https://github.com/Sir-chawakorn/sanook-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/Sir-chawakorn/sanook-cli/actions/workflows/ci.yml)
15
18
 
16
19
  [Quickstart](#quickstart) · [Providers](#providers) · [Usage](#usage) · [Gateway](#gateway--scheduling) · [Skills](#skills) · [MCP](#mcp) · [Security](#security)
17
20
 
21
+ <!-- 📹 DEMO GIF — record the close-session → reopen → "it remembered" loop (asciinema + agg), save to docs/demo.gif, then uncomment: -->
22
+ <!-- ![sanook-cli demo](docs/demo.gif) -->
23
+
18
24
  </div>
19
25
 
20
26
  ---
@@ -31,12 +37,43 @@ prompt → LLM → tool call → result → loop → answer
31
37
 
32
38
  It is **BYOK (bring your own key)** by design. Every provider connects with a **direct API key from that provider's own console** — Sanook never reuses OAuth or subscription credentials, because that violates provider terms and gets accounts banned.
33
39
 
40
+ ## How it compares
41
+
42
+ The agent loop, BYOK, and MCP are table stakes now. What Sanook has that the big vendor CLIs don't is **memory that survives the session** — a structured Obsidian "second brain" the agent reads at the start of every run.
43
+
44
+ | | **Sanook** | Claude Code | Codex CLI | Gemini CLI |
45
+ |---|:---:|:---:|:---:|:---:|
46
+ | Open-source | ✅ | ❌ | ✅ | ✅ |
47
+ | Bring your own key | ✅ | — | ✅ | ✅ |
48
+ | Providers | **12** | 1 | 1 | 1 |
49
+ | Local models (Ollama / LM Studio) | ✅ | ❌ | ❌ | ❌ |
50
+ | MCP (stdio **+ remote HTTP**) | ✅ | ✅ | ✅ | ✅ |
51
+ | OS sandbox (Seatbelt / bubblewrap) | ✅ | ✅ | ✅ | ✅ |
52
+ | Checkpoint / rewind | ✅ | ✅ | ✅ | ✅ |
53
+ | Image / vision input | ✅ | ✅ | ✅ | ✅ |
54
+ | Prompt caching | ✅ | ✅ | ✅ | ✅ |
55
+ | **Durable cross-session memory** | ✅ | ❌ | ❌ | ❌ |
56
+ | **Local gateway + cron + Telegram** | ✅ | ❌ | ❌ | ❌ |
57
+
58
+ On raw benchmark scores the frontier vendors win — Sanook's bet is **portability + persistent memory**, not beating Opus on SWE-bench. Use whatever fits; this one remembers.
59
+
34
60
  ## Quickstart
35
61
 
62
+ Install **globally** — the `-g` is required (needs **Node ≥ 22**; check with `node -v`):
63
+
36
64
  ```bash
37
65
  npm install -g sanook-cli
66
+ ```
67
+
68
+ > ⚠️ **`'sanook' is not recognized` / command not found?** You installed it locally — `npm i sanook-cli` (without `-g`) drops it into the current folder, **not on your PATH**, so the `sanook` command isn't found. Fix: reinstall with `npm install -g sanook-cli`, or just run it via **`npx sanook`** (uses the local copy you already installed).
69
+ > Run **`npx sanook doctor`** to auto-diagnose Node version / PATH / install state and print the exact fix for your OS (incl. a safe Windows PATH one-liner).
70
+
71
+ Set an API key (or run `sanook` with no task for the interactive setup wizard):
72
+
73
+ ```bash
74
+ export ANTHROPIC_API_KEY=sk-ant-... # macOS / Linux
75
+ setx ANTHROPIC_API_KEY "sk-ant-..." # Windows (export won't work in cmd) — then open a NEW terminal
38
76
 
39
- export ANTHROPIC_API_KEY=sk-ant-... # or run `sanook` and use the setup wizard
40
77
  sanook "read package.json and list the dependencies"
41
78
  ```
42
79
 
@@ -52,15 +89,20 @@ sanook --json "..." # JSONL output for CI / scripts
52
89
 
53
90
  | Area | What you get |
54
91
  |---|---|
55
- | **Agent loop** | Built on the Vercel AI SDK 6 (`streamText` + `stopWhen` + `fullStream`), with streamed output, a cost meter, and a budget cap. |
56
- | **Tools** | `read_file` · `write_file` · `edit_file` (multi-tier matcher) · `list_dir` · `glob` · `grep` · `run_bash`, plus git tools — gated by a permission layer that denies destructive commands and protected paths. |
57
- | **Approval** | Interactive `ask` mode prompts `y/n` before any file write or shell command. `--yes` for auto-approve; headless defaults to safe-deny. |
58
- | **Memory** | The agent writes its own notes (`remember`), recalls them across past sessions (`recall`), and `--continue` resumes the last run where it left off. |
59
- | **Skills** | 69 built-in skills + install your own from a GitHub repo, URL, or local path. The agent can also author new skills after a repeatable task. |
92
+ | **Agent loop** | Built on the Vercel AI SDK 6 (`streamText` + `stopWhen` + `fullStream`), with streamed output, a cost meter, a budget cap, Anthropic **prompt caching** on the static preamble, and rate-limit-aware retry/backoff (distinct from auth failures) with model fallback. |
93
+ | **Tools** | `read_file` · `write_file` · `edit_file` (multi-tier matcher) · `list_dir` · `glob` · `grep` · `run_bash`, plus git tools — gated by a permission layer that denies destructive commands, protected paths, and paths outside the workspace/brain by default. Non-bash tools are timeout-guarded so a runaway read/grep can't hang the loop. |
94
+ | **Sandbox** | `run_bash` is confined by an OS sandbox **Seatbelt** on macOS, **bubblewrap** on Linux — so shell writes stay inside the workspace/brain/tmp (reads + network unaffected). Opt out with `SANOOK_NO_SANDBOX=1`. |
95
+ | **Approval** | `ask` mode is the default and prompts `y/n` before any file write or shell command. `--yes` for auto-approve; headless ask-mode safely denies mutations when no approval UI exists. |
96
+ | **Input** | Multiline editing, `↑`/`↓` persisted prompt history, readline keys (Ctrl-A/E/U/K/W), and `@file` mentions that inline a file's contents (or attach an **image** for vision-capable models). |
97
+ | **Checkpoint** | A shadow-git snapshot is taken before each turn; `/rewind` restores the files **and** truncates the conversation — recoverable (it stashes the current state first). |
98
+ | **Memory** | The agent writes its own notes (`remember`), recalls them across past sessions (`recall`), and `--continue` resumes the latest run for the current project. |
99
+ | **Repo map** | A lightweight symbol map of the repo (zero-dep, git-aware) is injected at session start so the agent picks the right files without blind grepping. |
100
+ | **Skills** | Built-in skills + install your own from a GitHub repo, URL, or local path. The agent can also author new skills after a repeatable task. |
101
+ | **Custom commands** | Drop a `.sanook/commands/<name>.md` prompt template and call it as `/<name>` (project commands require trust). |
60
102
  | **Subagents** | A `task` tool spawns a fresh-context sub-agent for scoped exploration without bloating the main context — read-only by default, depth-guarded. |
61
103
  | **Gateway + cron** | `sanook serve` runs a long-lived daemon: a loopback OpenAI-compatible HTTP endpoint plus a cron scheduler. Ask it in plain language and it schedules itself. |
62
104
  | **Channels** | A Telegram adapter (long-polling, no public URL) lets you drive the agent from your phone — locked down with a required allowlist and private-chat-only policy. |
63
- | **MCP** | Connect any Model Context Protocol server (filesystem, GitHub, Postgres, …) via `~/.sanook/mcp.json`. |
105
+ | **MCP** | Connect any Model Context Protocol server over **stdio or remote Streamable-HTTP** (filesystem, GitHub, Postgres, hosted servers, …) via `~/.sanook/mcp.json`. |
64
106
  | **Git** | Branch, uncommitted changes, and recent commits are injected automatically, with `git_status` / `git_diff` / `git_log` / `git_commit` tools. |
65
107
  | **Hooks** | Run your own command before/after any tool. A non-zero `PreToolUse` exit blocks the tool — enforce lint, format, or policy. |
66
108
  | **Plan mode** | `--plan` restricts the agent to read-only tools and asks it to produce a plan before touching anything. |
@@ -98,9 +140,11 @@ sanook models anthropic # curated ids (+ live verification if a key is set
98
140
  ```
99
141
  sanook "<task>" run one task (headless)
100
142
  sanook interactive REPL
101
- sanook -c "<task>" resume the latest session
143
+ sanook -c "<task>" resume the latest session for this project
144
+ sanook --continue-any resume the newest session across all projects
102
145
  sanook --plan "<task>" plan mode (read-only)
103
146
  sanook --json "<task>" JSONL output for scripts / CI
147
+ sanook update update the CLI to the latest npm release
104
148
 
105
149
  -m, --model <spec> model or provider:model-id
106
150
  -b, --budget <usd> stop when estimated cost exceeds this
@@ -109,7 +153,20 @@ sanook --json "<task>" JSONL output for scripts / CI
109
153
  -h, --help show help
110
154
  ```
111
155
 
112
- **REPL slash commands:** `/model` · `/tools` · `/skills` · `/cost` · `/clear` · `/compact` · `/help` · `/quit`
156
+ **REPL slash commands:** `/model` · `/tools` · `/skills` · `/cost` · `/diff` · `/undo` · `/rewind` · `/clear` · `/compact` · `/help` · `/quit` — plus your own `.sanook/commands/*.md`. Input supports `↑`/`↓` history, `@file` mentions (text or image), and multiline (trailing `\` or Alt+Enter).
157
+
158
+ ## Updating
159
+
160
+ Use the built-in updater whenever a new CLI version is available:
161
+
162
+ ```bash
163
+ sanook update
164
+ sanook update --check # check only
165
+ ```
166
+
167
+ It checks the npm `latest` release for `sanook-cli` and, when newer than your installed version, runs `npm install -g sanook-cli@latest`.
168
+
169
+ When you launch the interactive TUI with plain `sanook`, the CLI checks for updates at most once per day. If a newer version exists, it asks `Yes/No` before running the same updater. Set `SANOOK_DISABLE_UPDATE_CHECK=1` to silence the prompt.
113
170
 
114
171
  ## Gateway & scheduling
115
172
 
@@ -122,7 +179,7 @@ sanook cron list
122
179
  sanook cron rm <id>
123
180
  ```
124
181
 
125
- The HTTP server binds to **loopback only** and authenticates every endpoint (except `/health`) with a bearer token stored at `~/.sanook/gateway/token` (chmod 600). It speaks the OpenAI chat-completions shape, so existing clients work unchanged:
182
+ The HTTP server binds to **loopback only** and authenticates every endpoint (except `/health`) with a bearer token stored at `~/.sanook/gateway/token` (chmod 600). It runs mutating tools in `ask` mode by default; opt into unattended writes with `sanook config set permissionMode auto` or `SANOOK_GATEWAY_ALLOW_WRITE=1`. It speaks the OpenAI chat-completions shape, so existing clients work unchanged:
126
183
 
127
184
  ```bash
128
185
  curl http://127.0.0.1:8787/v1/chat/completions \
@@ -151,7 +208,7 @@ The channel is **fail-closed**: with no allowlist it refuses to start, it accept
151
208
 
152
209
  ## Skills
153
210
 
154
- A skill is a `SKILL.md` file (front-matter + instructions) the agent loads on demand. Sanook ships with 69 built-in skills and can install more.
211
+ A skill is a `SKILL.md` file (front-matter + instructions) the agent loads on demand. Sanook ships with built-in skills and can install more.
155
212
 
156
213
  ```bash
157
214
  sanook skill list # browse all skills
@@ -176,21 +233,55 @@ It creates a full folder taxonomy (`Projects/`, `Sessions/`, `Shared/` memory la
176
233
 
177
234
  Everything is **create-if-missing** — re-running never overwrites your notes. Point an Obsidian or filesystem MCP server at the workspace to let the agent read and write it.
178
235
 
236
+ ### Brain search
237
+
238
+ Ranked search over the vault **and** the agent's memory, past sessions, and skills — one surface, no native binaries:
239
+
240
+ ```bash
241
+ sanook index # incremental index of vault + memory + sessions + skills (O(delta))
242
+ sanook search "vercel edge deploy" # ranked hits with snippets
243
+ sanook search "race condition" --mode hybrid --source vault,memory --limit 5
244
+ ```
245
+
246
+ - **Zero-config floor** — a pure-TypeScript BM25 inverted index (genuine corpus-stat IDF, title boost, `Intl.Segmenter` word breaks for Thai). No SQLite, no Bun, no native binary, no API key, no network.
247
+ - **Optional semantic** — `--mode hybrid|semantic` embeds through your *existing* provider key (OpenAI / Mistral / Google / …), stores compact Float32 vectors locally, and fuses with BM25 via Reciprocal Rank Fusion. Activates only when a key resolves; degrades silently to BM25 otherwise. Configure with `sanook config set embeddingModel openai:text-embedding-3-small` (or `SANOOK_EMBEDDING_MODEL`).
248
+ - **Incremental** — only changed files are re-read (mtime+sha manifest); deleted files are evicted. Run after editing the vault, or wire it into a hook/cron.
249
+
250
+ The agent's `recall` tool uses the same engine, so remembered facts and vault notes are searchable the moment they exist.
251
+
179
252
  ## MCP
180
253
 
181
- Connect Model Context Protocol servers (stdio) with the same config shape you already use elsewhere:
254
+ Connect Model Context Protocol servers over **stdio or remote Streamable-HTTP** with the same config shape you already use elsewhere:
182
255
 
183
256
  ```jsonc
184
257
  // ~/.sanook/mcp.json
185
258
  {
186
259
  "mcpServers": {
187
- "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"] }
260
+ "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"] },
261
+ "remote": { "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer <token>" } }
188
262
  }
189
263
  }
190
264
  ```
191
265
 
266
+ Add servers from the CLI too: `sanook mcp add fs npx -y @modelcontextprotocol/server-filesystem /path` (stdio) or `sanook mcp add remote https://example.com/mcp` (a URL is detected as remote HTTP).
267
+
192
268
  Their tools are merged into the agent's toolset automatically. `/tools` in the REPL lists everything currently available.
193
269
 
270
+ sanook is also an MCP **server**: `sanook mcp serve` exposes your brain (`sanook_search` / `sanook_recall` / `sanook_remember` / `sanook_index` / `sanook_stats`) over stdio, so Claude Desktop, Cursor, or any MCP host can query it:
271
+
272
+ ```jsonc
273
+ // in another host's MCP config
274
+ { "mcpServers": { "sanook-brain": { "command": "sanook", "args": ["mcp", "serve"] } } }
275
+ ```
276
+
277
+ Project-local `.sanook/mcp.json`, `.sanook/hooks.json`, `.sanook/skills/`, and `.sanook/commands/` are ignored until the project is trusted:
278
+
279
+ ```bash
280
+ sanook trust status
281
+ sanook trust add # allow this project's .sanook mcp/hooks/skills/commands
282
+ sanook trust remove
283
+ ```
284
+
194
285
  ## Configuration
195
286
 
196
287
  Everything lives under `~/.sanook/` (with per-project `.sanook/` overrides where relevant):
@@ -198,21 +289,55 @@ Everything lives under `~/.sanook/` (with per-project `.sanook/` overrides where
198
289
  ```
199
290
  ~/.sanook/auth.json API keys (chmod 600)
200
291
  ~/.sanook/memory/ auto-memory the agent writes
292
+ ~/.sanook/search/ brain-search index + optional embedding vectors (regenerable via `sanook index`)
201
293
  ~/.sanook/sessions/ saved conversations (for --continue)
202
294
  ~/.sanook/skills/<name>/ installed SKILL.md files
203
295
  ~/.sanook/mcp.json MCP servers { "mcpServers": { … } }
204
296
  ~/.sanook/hooks.json PreToolUse / PostToolUse hooks
205
297
  ~/.sanook/gateway/ gateway token + task ledger
298
+ ~/.sanook/trusted-projects.json project roots allowed to load project .sanook extensions
206
299
  SANOOK.md project memory (hierarchical, like a system prompt)
207
300
  ```
208
301
 
302
+ Untrusted project config can set ordinary project defaults, but it cannot lower `permissionMode` to `auto`; trust the project first if you want project-local config to control mutation approval.
303
+
304
+ ### Token / cost tuning
305
+
306
+ Quality-neutral knobs in `~/.sanook/config.json` (or the matching `SANOOK_*` env var) to cut tokens/cost:
307
+
308
+ | `config set …` | env | effect |
309
+ |---|---|---|
310
+ | `cacheTtl 1h` | `SANOOK_CACHE_TTL=1h` | keep the cached system preamble alive for 1h (default `5m`) — cheaper to resume after a pause |
311
+ | `compaction summarize` | `SANOOK_COMPACTION=summarize` | when context gets long, condense it with a **cheap model** instead of truncating — better recall at the same budget (default `truncate`, zero-LLM) |
312
+ | — | `SANOOK_SUBAGENT_MODEL=haiku` | run all sub-agent work (exploration/search) on a cheaper model while the main agent keeps the strong one |
313
+ | `summaryModel <spec>` | `SANOOK_SUMMARY_MODEL=<spec>` | model used for summarize-compaction (default: the fast sibling of your main model) |
314
+ | `thinking 4000` | `SANOOK_THINKING=4000` | opt-in Anthropic extended thinking on the main agent with a `budgetTokens` cap (default off) |
315
+
316
+ Read-side savings are automatic: the agent reads file ranges (`read_file` with `offset`/`limit`) and edits with minimal `old_string` / `replace_all` rather than rewriting whole files.
317
+
318
+ Useful environment flags:
319
+
320
+ ```bash
321
+ SANOOK_MODEL=sonnet # default model alias or provider:model
322
+ SANOOK_ALLOW_OUTSIDE_WORKSPACE=1 # allow file tools outside cwd/brain
323
+ SANOOK_GATEWAY_ALLOW_WRITE=1 # let sanook serve run mutating tools unattended
324
+ SANOOK_HOOKS_INHERIT_ENV=1 # pass full env to hooks instead of a minimal safe env
325
+ SANOOK_DISABLE_PERSISTENCE=1 # do not save sessions or memory
326
+ SANOOK_DISABLE_UPDATE_CHECK=1 # do not show interactive update prompts
327
+ SANOOK_DISABLE_WORKLOG=1 # do not append second-brain worklogs
328
+ SANOOK_TRUST_PROJECT=1 # temporary trust override for project .sanook extensions
329
+ ```
330
+
209
331
  ## Security
210
332
 
211
333
  Sanook runs shell commands and edits files, so safety is built into the core rather than bolted on:
212
334
 
213
335
  - **BYOK, direct keys only** — OAuth and subscription tokens are rejected by an explicit guard (`ya29.`, `Bearer`, `sk-ant-oat…`). This keeps you within every provider's terms of service.
214
- - **Permission gate** — destructive commands (`rm -rf`, `git reset --hard`, `push --force`, fork bombs, …) and writes to protected paths are denied; interactive `ask` mode confirms mutations.
215
- - **Secret redaction** — API keys are stripped from error messages and logs.
336
+ - **Permission gate** — destructive commands (`rm -rf`, `git reset --hard`, `push --force`, fork bombs, …), protected paths (`.env`, `.git`, `node_modules`, credential folders), and paths outside the workspace/brain are denied unless explicitly opted in.
337
+ - **OS sandbox** — `run_bash` runs under Seatbelt (macOS) / bubblewrap (Linux) when available, confining shell writes to the workspace/brain/tmp — defense in depth beyond the regex blocklist (`SANOOK_NO_SANDBOX=1` to disable).
338
+ - **Project trust gate** — project `.sanook/mcp.json`, `.sanook/hooks.json`, `.sanook/skills/`, and `.sanook/commands/` can execute or steer the agent, so they are ignored until `sanook trust add`.
339
+ - **Secret redaction** — API keys are stripped from error messages, saved sessions, memory, and worklogs.
340
+ - **Safe fallback** — provider fallback does not retry after a mutating tool call has already happened, avoiding duplicate side effects.
216
341
  - **Gateway** — HTTP binds to `127.0.0.1` only and requires a bearer token on every non-health endpoint.
217
342
  - **Telegram** — fail-closed: a required allowlist, private-chat-only, per-chat rate-limiting, and generic error replies that never reveal internal paths.
218
343
 
@@ -223,7 +348,7 @@ Hardened across several adversarial security reviews covering command injection,
223
348
  ```bash
224
349
  npm install
225
350
  npm run build # → dist/
226
- npm test # vitest — 143 tests
351
+ npm test # vitest
227
352
  npm run typecheck # tsc --noEmit (strict)
228
353
  npm run dev -- "…" # run from source without building
229
354
  ```
@@ -234,6 +359,14 @@ CI runs the suite across macOS / Linux / Windows on Node 22 and 24. Requires **N
234
359
 
235
360
  [Apache-2.0](LICENSE)
236
361
 
362
+ ---
363
+
237
364
  <div align="center">
365
+
366
+ **Built by [Sanook AI](https://www.facebook.com/sanookai)** — AI tools & education 🇹🇭
367
+
368
+ [Facebook](https://www.facebook.com/sanookai) · [X / Twitter](https://x.com/sanook_ai)
369
+
238
370
  <sub>Built from scratch in TypeScript on the Vercel AI SDK — no framework, no magic.</sub>
371
+
239
372
  </div>
package/README.th.md ADDED
@@ -0,0 +1,136 @@
1
+ <div align="center">
2
+
3
+ # Sanook CLI
4
+
5
+ **AI coding agent ใน terminal ที่ "จำงานข้ามวันได้" — open-source**
6
+
7
+ ใส่ API key ของคุณเอง (BYOK) · 12 providers · MCP · มี **"สมองที่สอง" (second brain)** ที่ทำให้ AI จำ context ข้าม session ได้ — สิ่งที่ Claude Code / Codex / Gemini CLI ลืมทุกครั้งที่ปิด terminal
8
+
9
+ [![npm](https://img.shields.io/npm/v/sanook-cli.svg?color=2563eb)](https://www.npmjs.com/package/sanook-cli)
10
+ [![downloads](https://img.shields.io/npm/dm/sanook-cli.svg?color=2563eb)](https://www.npmjs.com/package/sanook-cli)
11
+ [![License](https://img.shields.io/badge/license-Apache--2.0-22c55e.svg)](LICENSE)
12
+
13
+ 🇬🇧 [Read in English](README.md)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## มันคืออะไร
20
+
21
+ Sanook คือ **AI coding agent** ที่รันใน terminal — สั่งงานเป็นภาษาคน แล้วมันอ่านไฟล์ / แก้โค้ด / รันคำสั่ง / commit ให้ หัวใจคือ loop เดียว:
22
+
23
+ ```text
24
+ prompt → LLM → เรียก tool → ผลลัพธ์ → loop → ตอบ
25
+ ```
26
+
27
+ จุดต่างจากเจ้าใหญ่คือ **second brain** (โครงสร้างโน้ต Obsidian) ที่ agent อ่านก่อนทำงานทุกครั้ง จึงจำได้ว่าเคยทำอะไร ชอบอะไร และตัดสินใจอะไรไปแล้วข้าม session
28
+
29
+ ## เริ่มใช้
30
+
31
+ ติดตั้งแบบ **global** (ต้องมี `-g`) — ต้องมี **Node ≥ 22** (เช็กด้วย `node -v`):
32
+
33
+ ```bash
34
+ npm install -g sanook-cli
35
+ ```
36
+
37
+ > ⚠️ **`'sanook' is not recognized` / command not found?**
38
+ > แปลว่าลงแบบ local — `npm i sanook-cli` (ไม่มี `-g`) มันลงในโฟลเดอร์ปัจจุบัน **ไม่เข้า PATH** คำสั่ง `sanook` เลยหาไม่เจอ
39
+ > แก้: ลงใหม่ด้วย `npm install -g sanook-cli` · หรือเรียกผ่าน **`npx sanook`** (ใช้ตัวที่ลง local ไปแล้วได้เลย)
40
+ > หรือรัน **`npx sanook doctor`** — ตรวจ Node/PATH/สถานะการติดตั้งให้ แล้วบอกคำสั่งแก้ที่ตรงกับ OS (มีบรรทัดแก้ PATH บน Windows แบบปลอดภัยให้ก็อปด้วย)
41
+
42
+ ตั้ง API key (หรือรัน `sanook` เฉย ๆ ครั้งแรกจะมี setup wizard ให้เลือก provider + วาง key):
43
+
44
+ ```bash
45
+ # macOS / Linux
46
+ export ANTHROPIC_API_KEY=sk-ant-...
47
+
48
+ # Windows (Command Prompt) — export ใช้ไม่ได้ ต้อง setx แล้วเปิด terminal ใหม่
49
+ setx ANTHROPIC_API_KEY "sk-ant-..."
50
+ ```
51
+
52
+ แล้วสั่งงานได้เลย:
53
+
54
+ ```bash
55
+ sanook # REPL (ครั้งแรก = setup wizard)
56
+ sanook "อ่าน package.json แล้วบอกว่ามี dependencies อะไรบ้าง"
57
+ sanook -c "ทำต่อจาก session ล่าสุดของ project นี้"
58
+ sanook --continue-any "ทำต่อจาก session ล่าสุดข้าม project"
59
+ ```
60
+
61
+ ## ทำอะไรได้บ้าง
62
+
63
+ - **BYOK + 12 providers** — Anthropic, Google, OpenAI, DeepSeek, xAI, Mistral, Groq, MiniMax, GLM, Ollama, LM Studio, Codex
64
+ - **Second brain** — `sanook brain init` สร้าง workspace Obsidian ให้ AI จำงานข้ามวัน
65
+ - **Tools** — อ่าน/เขียน/แก้ไฟล์ · รัน bash · git · grep/glob พร้อม permission gate
66
+ - **Gateway + cron** — `sanook serve` รันเป็น service 24/7 + ตั้งงานล่วงหน้า + ต่อ Telegram
67
+ - **MCP + Skills** — ต่อ MCP server ได้ + มี built-in skills และติดตั้งเพิ่มได้
68
+ - **Update ง่าย** — ใช้ `sanook update` เพื่ออัปเดต CLI เป็นเวอร์ชันล่าสุดจาก npm
69
+
70
+ ## ใช้ provider ไหนก็ได้
71
+
72
+ ```bash
73
+ sanook -m sonnet "..." # Claude
74
+ sanook -m gemini "..." # Gemini
75
+ sanook -m glm:smart "..." # GLM (z.ai Coding Plan)
76
+ sanook -m ollama "..." # local ไม่ต้องมี key
77
+ ```
78
+
79
+ ตั้งค่า default model ได้ด้วย:
80
+
81
+ ```bash
82
+ sanook config set model sonnet
83
+ # หรือใช้ env
84
+ SANOOK_MODEL=sonnet sanook "..."
85
+ ```
86
+
87
+ ## อัปเดต CLI
88
+
89
+ เวลามีเวอร์ชันใหม่ ใช้คำสั่งเดียว:
90
+
91
+ ```bash
92
+ sanook update
93
+ sanook update --check # เช็กอย่างเดียว
94
+ ```
95
+
96
+ คำสั่งนี้จะเช็ก npm `latest` ของ `sanook-cli` แล้วอัปเดตด้วย `npm install -g sanook-cli@latest` เมื่อมีเวอร์ชันใหม่กว่า
97
+
98
+ ถ้าเปิด TUI ด้วย `sanook` เฉย ๆ CLI จะเช็กอัปเดตอย่างมากวันละครั้ง ถ้ามีเวอร์ชันใหม่จะถาม `Yes/No` ก่อนอัปเดต ปิด prompt ได้ด้วย `SANOOK_DISABLE_UPDATE_CHECK=1`
99
+
100
+ ## ความปลอดภัย
101
+
102
+ Sanook ตั้งค่าเริ่มต้นให้ระวังไว้ก่อน:
103
+
104
+ - `ask` mode เป็นค่าเริ่มต้น ก่อนเขียนไฟล์หรือรัน shell จะขออนุมัติ ถ้าเป็น headless แล้วไม่มี UI จะปฏิเสธ mutation
105
+ - file tools แตะได้เฉพาะ workspace ปัจจุบันและ second brain ที่ตั้งไว้ ถ้าจะออกนอก scope ต้อง opt-in ด้วย `SANOOK_ALLOW_OUTSIDE_WORKSPACE=1`
106
+ - path เสี่ยงอย่าง `.env`, `.git`, `node_modules`, credential folders และ `~/.sanook` ถูกบล็อก
107
+ - project `.sanook/config.json` ที่ยังไม่ trusted ตั้งค่าทั่วไปได้ แต่ลด `permissionMode` เป็น `auto` ไม่ได้
108
+ - project `.sanook/mcp.json`, `.sanook/hooks.json`, `.sanook/skills/`, และ `.sanook/commands/` ถูก ignore จนกว่าจะ trust project:
109
+
110
+ ```bash
111
+ sanook trust status
112
+ sanook trust add
113
+ sanook trust remove
114
+ ```
115
+
116
+ - gateway bind ที่ `127.0.0.1` และต้องใช้ bearer token ยกเว้น `/health`; mutating tools ใน `sanook serve` เป็น `ask` โดย default และ opt-in unattended write ได้ด้วย `sanook config set permissionMode auto` หรือ `SANOOK_GATEWAY_ALLOW_WRITE=1`
117
+ - session/memory/worklog redact API keys ก่อนบันทึก และปิด persistence ได้ด้วย `SANOOK_DISABLE_PERSISTENCE=1`
118
+
119
+ ## พัฒนา
120
+
121
+ ```bash
122
+ npm install
123
+ npm run build
124
+ npm test # vitest
125
+ npm run typecheck
126
+ ```
127
+
128
+ ---
129
+
130
+ <div align="center">
131
+
132
+ **สร้างโดย [Sanook AI](https://www.facebook.com/sanookai)** — เครื่องมือ + ความรู้ AI สำหรับคนไทย
133
+
134
+ [Facebook](https://www.facebook.com/sanookai) · [X / Twitter](https://x.com/sanook_ai)
135
+
136
+ </div>