otacon 0.1.1 → 0.1.2

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 (122) hide show
  1. package/README.md +101 -64
  2. package/dist/cli/browser.js +55 -0
  3. package/dist/cli/browser.js.map +1 -0
  4. package/dist/cli/commands/clean.js +3 -2
  5. package/dist/cli/commands/clean.js.map +1 -1
  6. package/dist/cli/commands/config.js +62 -0
  7. package/dist/cli/commands/config.js.map +1 -0
  8. package/dist/cli/commands/doctor.js +41 -21
  9. package/dist/cli/commands/doctor.js.map +1 -1
  10. package/dist/cli/commands/install.js +53 -30
  11. package/dist/cli/commands/install.js.map +1 -1
  12. package/dist/cli/commands/open.js +14 -14
  13. package/dist/cli/commands/open.js.map +1 -1
  14. package/dist/cli/commands/start.js +1 -22
  15. package/dist/cli/commands/start.js.map +1 -1
  16. package/dist/cli/install/assets.js +35 -43
  17. package/dist/cli/install/assets.js.map +1 -1
  18. package/dist/cli/install/locations.js +15 -25
  19. package/dist/cli/install/locations.js.map +1 -1
  20. package/dist/cli/main.js +6 -1
  21. package/dist/cli/main.js.map +1 -1
  22. package/dist/daemon/app.js +281 -54
  23. package/dist/daemon/app.js.map +1 -1
  24. package/dist/daemon/approve.js +49 -11
  25. package/dist/daemon/approve.js.map +1 -1
  26. package/dist/daemon/store.js +39 -2
  27. package/dist/daemon/store.js.map +1 -1
  28. package/dist/daemon/threads.js +11 -0
  29. package/dist/daemon/threads.js.map +1 -1
  30. package/dist/daemon/ui.js +3 -0
  31. package/dist/daemon/ui.js.map +1 -1
  32. package/dist/shared/config.js +290 -45
  33. package/dist/shared/config.js.map +1 -1
  34. package/dist/shared/paths.js +33 -5
  35. package/dist/shared/paths.js.map +1 -1
  36. package/dist/shared/types.js +6 -2
  37. package/dist/shared/types.js.map +1 -1
  38. package/dist/shared/version.js +1 -1
  39. package/dist/ui/assets/{arc-HhPfdCPZ.js → arc-KT3ZnaMp.js} +1 -1
  40. package/dist/ui/assets/architecture-7EHR7CIX-Cw3I1lil.js +1 -0
  41. package/dist/ui/assets/{architectureDiagram-3BPJPVTR-D2PIxGOb.js → architectureDiagram-3BPJPVTR-DLu0UM7N.js} +1 -1
  42. package/dist/ui/assets/{blockDiagram-GPEHLZMM-DQ3Dn17h.js → blockDiagram-GPEHLZMM-B8wApEWC.js} +1 -1
  43. package/dist/ui/assets/{c4Diagram-AAUBKEIU-DxITrQgS.js → c4Diagram-AAUBKEIU-BNS5gmQS.js} +1 -1
  44. package/dist/ui/assets/channel-CkOta24Z.js +1 -0
  45. package/dist/ui/assets/{chunk-2J33WTMH-Du1JoPx5.js → chunk-2J33WTMH-CTY2etwY.js} +1 -1
  46. package/dist/ui/assets/{chunk-3OPIFGDE-Dn7x2Yqf.js → chunk-3OPIFGDE-DZM4Sz84.js} +1 -1
  47. package/dist/ui/assets/{chunk-4BX2VUAB-DVnrE-4n.js → chunk-4BX2VUAB-sGwrrXIO.js} +1 -1
  48. package/dist/ui/assets/{chunk-55IACEB6-BAhFAimA.js → chunk-55IACEB6-CGlNp76o.js} +1 -1
  49. package/dist/ui/assets/{chunk-5ZQYHXKU-0hEZptem.js → chunk-5ZQYHXKU-5zebJ3jW.js} +1 -1
  50. package/dist/ui/assets/{chunk-727SXJPM-C1FN_cI3.js → chunk-727SXJPM-DelmUpvy.js} +1 -1
  51. package/dist/ui/assets/{chunk-AQP2D5EJ-A656OBd4.js → chunk-AQP2D5EJ-DMVzBf3M.js} +1 -1
  52. package/dist/ui/assets/{chunk-BSJP7CBP-D8oMbjm8.js → chunk-BSJP7CBP-CZHrcpSu.js} +1 -1
  53. package/dist/ui/assets/{chunk-CSCIHK7Q-DjIL8GLi.js → chunk-CSCIHK7Q-C1efTp0O.js} +1 -1
  54. package/dist/ui/assets/{chunk-FMBD7UC4-Otblfqvz.js → chunk-FMBD7UC4-B6axGwgn.js} +1 -1
  55. package/dist/ui/assets/{chunk-KSCS5N6A-BOjTvm3H.js → chunk-KSCS5N6A-DSaxbrm0.js} +1 -1
  56. package/dist/ui/assets/{chunk-L5ZTLDWV-CaTLaw6L.js → chunk-L5ZTLDWV-D9ZKdVGx.js} +1 -1
  57. package/dist/ui/assets/{chunk-LZXEDZCA-Dq5p7qrD.js → chunk-LZXEDZCA-DI5_1s00.js} +2 -2
  58. package/dist/ui/assets/{chunk-ND2GUHAM-jZ_NNnWi.js → chunk-ND2GUHAM-DQQ4RVLE.js} +1 -1
  59. package/dist/ui/assets/{chunk-NZK2D7GU-U_7l_sCh.js → chunk-NZK2D7GU-9zJJaDpb.js} +1 -1
  60. package/dist/ui/assets/{chunk-O5CBEL6O-MewqqNB7.js → chunk-O5CBEL6O-DUOshAt2.js} +1 -1
  61. package/dist/ui/assets/chunk-QZHKN3VN-DqRxzBM_.js +1 -0
  62. package/dist/ui/assets/chunk-WU5MYG2G-B1Mk3aBp.js +1 -0
  63. package/dist/ui/assets/{chunk-XPW4576I-D5ArxNEF.js → chunk-XPW4576I-DQVL_GAl.js} +1 -1
  64. package/dist/ui/assets/classDiagram-4FO5ZUOK-BH-5P4jH.js +1 -0
  65. package/dist/ui/assets/classDiagram-v2-Q7XG4LA2-BH-5P4jH.js +1 -0
  66. package/dist/ui/assets/{cose-bilkent-S5V4N54A-PFXzf7WV.js → cose-bilkent-S5V4N54A-C7m3VlSW.js} +1 -1
  67. package/dist/ui/assets/{dagre-BM42HDAG-xrCfjZuZ.js → dagre-BM42HDAG-CoU_T6EY.js} +1 -1
  68. package/dist/ui/assets/{diagram-2AECGRRQ-BFf-cyKY.js → diagram-2AECGRRQ-C_R7oNKE.js} +1 -1
  69. package/dist/ui/assets/{diagram-5GNKFQAL-kNPV4NfV.js → diagram-5GNKFQAL-UhE2W3Yi.js} +1 -1
  70. package/dist/ui/assets/{diagram-KO2AKTUF-ByC1IUwG.js → diagram-KO2AKTUF-D4KZCRld.js} +1 -1
  71. package/dist/ui/assets/{diagram-LMA3HP47-DZIJMPK0.js → diagram-LMA3HP47-CsOe8-S6.js} +1 -1
  72. package/dist/ui/assets/{diagram-OG6HWLK6-CSDED9A-.js → diagram-OG6HWLK6-Bkbs2V54.js} +1 -1
  73. package/dist/ui/assets/{dist-YwjsDswi.js → dist-BcowSnUl.js} +1 -1
  74. package/dist/ui/assets/{erDiagram-TEJ5UH35-yuzvjE6J.js → erDiagram-TEJ5UH35-DJXsykZz.js} +1 -1
  75. package/dist/ui/assets/eventmodeling-FCH6USID-DSDI1f5T.js +1 -0
  76. package/dist/ui/assets/{flowDiagram-I6XJVG4X-ApPtVyYM.js → flowDiagram-I6XJVG4X-Chguj9Xz.js} +1 -1
  77. package/dist/ui/assets/{ganttDiagram-6RSMTGT7-BeMLXtAr.js → ganttDiagram-6RSMTGT7-DXdpFesp.js} +1 -1
  78. package/dist/ui/assets/{gitGraph-WXDBUCRP-JmTTBa7j.js → gitGraph-WXDBUCRP-Doq1CcxB.js} +1 -1
  79. package/dist/ui/assets/{gitGraphDiagram-PVQCEYII-Cjjnjs71.js → gitGraphDiagram-PVQCEYII-uxGwHEPv.js} +1 -1
  80. package/dist/ui/assets/index-BsTJ9Ul_.css +1 -0
  81. package/dist/ui/assets/index-D-TSanrw.js +11 -0
  82. package/dist/ui/assets/{info-J43DQDTF-8vZ3gome.js → info-J43DQDTF-CgWT_d3M.js} +1 -1
  83. package/dist/ui/assets/{infoDiagram-5YYISTIA-CnMk1cA-.js → infoDiagram-5YYISTIA-B3oDA2Ct.js} +1 -1
  84. package/dist/ui/assets/{ishikawaDiagram-YF4QCWOH-Bl8z6huD.js → ishikawaDiagram-YF4QCWOH-C6l0lvC3.js} +1 -1
  85. package/dist/ui/assets/{journeyDiagram-JHISSGLW-DYIVfMpS.js → journeyDiagram-JHISSGLW-COf53NwC.js} +1 -1
  86. package/dist/ui/assets/{kanban-definition-UN3LZRKU-BnR0ZzOz.js → kanban-definition-UN3LZRKU-DaOPRBld.js} +1 -1
  87. package/dist/ui/assets/{line-DcBdQit6.js → line-new3jLu3.js} +1 -1
  88. package/dist/ui/assets/{linear-HKjRHFAO.js → linear-yA22knFB.js} +1 -1
  89. package/dist/ui/assets/{mermaid-parser.core-DkYXrPlA.js → mermaid-parser.core-D8JVCOKU.js} +2 -2
  90. package/dist/ui/assets/{mermaid.core-BmkfCI3b.js → mermaid.core-BCrPyVBK.js} +3 -3
  91. package/dist/ui/assets/{mindmap-definition-RKZ34NQL-sIAd4nDi.js → mindmap-definition-RKZ34NQL-CVcJTWsW.js} +1 -1
  92. package/dist/ui/assets/{packet-YPE3B663-BxbxcfXN.js → packet-YPE3B663-DlSwpK-G.js} +1 -1
  93. package/dist/ui/assets/{pie-LRSECV5Y-BJxazjNs.js → pie-LRSECV5Y-CyMpbo4C.js} +1 -1
  94. package/dist/ui/assets/{pieDiagram-4H26LBE5-BiOhc9GR.js → pieDiagram-4H26LBE5-Dy7day5R.js} +1 -1
  95. package/dist/ui/assets/{plan-view-CH6NzUDb.js → plan-view-CE2ha0qY.js} +3 -3
  96. package/dist/ui/assets/{quadrantDiagram-W4KKPZXB-CVyHbWgo.js → quadrantDiagram-W4KKPZXB-CVjVgbaQ.js} +1 -1
  97. package/dist/ui/assets/{radar-GUYGQ44K-D9ohbnbV.js → radar-GUYGQ44K-DLPfv0jT.js} +1 -1
  98. package/dist/ui/assets/{requirementDiagram-4Y6WPE33-Ba24_hqc.js → requirementDiagram-4Y6WPE33-y_0KOdN8.js} +1 -1
  99. package/dist/ui/assets/{sankeyDiagram-5OEKKPKP-CxD4wiPL.js → sankeyDiagram-5OEKKPKP-8T3b7meL.js} +1 -1
  100. package/dist/ui/assets/{sequenceDiagram-3UESZ5HK-7qA7lD61.js → sequenceDiagram-3UESZ5HK-Cn8DZiEr.js} +1 -1
  101. package/dist/ui/assets/{src-IM8AE8MK.js → src-BieOuieI.js} +1 -1
  102. package/dist/ui/assets/{stateDiagram-AJRCARHV-DNElRCuH.js → stateDiagram-AJRCARHV-nPEVGd3E.js} +1 -1
  103. package/dist/ui/assets/stateDiagram-v2-BHNVJYJU-CKrff-KJ.js +1 -0
  104. package/dist/ui/assets/{timeline-definition-PNZ67QCA-ChYC4Grd.js → timeline-definition-PNZ67QCA-Dj_qK0VJ.js} +1 -1
  105. package/dist/ui/assets/{treeView-BLDUP644-Il0KnMi_.js → treeView-BLDUP644-G5Bsebqu.js} +1 -1
  106. package/dist/ui/assets/{treemap-LRROVOQU-CIiKcdRo.js → treemap-LRROVOQU-anUEJzTb.js} +1 -1
  107. package/dist/ui/assets/{vennDiagram-CIIHVFJN-Ulhkum9i.js → vennDiagram-CIIHVFJN-D71ne3dS.js} +1 -1
  108. package/dist/ui/assets/{wardley-L42UT6IY-BNd4ljz7.js → wardley-L42UT6IY-DrJilmE_.js} +1 -1
  109. package/dist/ui/assets/{wardleyDiagram-YWT4CUSO-BicXxh84.js → wardleyDiagram-YWT4CUSO-B66G4ayp.js} +1 -1
  110. package/dist/ui/assets/{xychartDiagram-2RQKCTM6-Duf-m_th.js → xychartDiagram-2RQKCTM6-CReSRxNG.js} +1 -1
  111. package/dist/ui/index.html +2 -2
  112. package/package.json +1 -1
  113. package/dist/ui/assets/architecture-7EHR7CIX-BPLblcyi.js +0 -1
  114. package/dist/ui/assets/channel-ipcU8ZNI.js +0 -1
  115. package/dist/ui/assets/chunk-QZHKN3VN-DzGPH44B.js +0 -1
  116. package/dist/ui/assets/chunk-WU5MYG2G-DyEIVjoo.js +0 -1
  117. package/dist/ui/assets/classDiagram-4FO5ZUOK-Byg2Hl9D.js +0 -1
  118. package/dist/ui/assets/classDiagram-v2-Q7XG4LA2-Byg2Hl9D.js +0 -1
  119. package/dist/ui/assets/eventmodeling-FCH6USID-CZR4eNG-.js +0 -1
  120. package/dist/ui/assets/index-BFQVRcSI.js +0 -11
  121. package/dist/ui/assets/index-Bj_kTrwP.css +0 -1
  122. package/dist/ui/assets/stateDiagram-v2-BHNVJYJU-D6qTYpY3.js +0 -1
package/README.md CHANGED
@@ -1,88 +1,125 @@
1
- <img width="9034" height="1857" alt="github-otacon" src="https://github.com/user-attachments/assets/18796111-63d7-4df6-a2ba-b95f132eabd3" />
1
+ <p align="center">
2
+ <img src="https://github.com/user-attachments/assets/18796111-63d7-4df6-a2ba-b95f132eabd3" alt="otacon" width="720">
3
+ </p>
2
4
 
3
- Plan review surface for coding agents (Claude Code, Codex, OpenCode). Replaces native
4
- plan modes with one CLI protocol: schema'd concise plans, anchored comments and
5
- questions from any device (phone included, over Tailscale), revision diffs against
6
- what you last reviewed, and a mandatory grill-me interview phase before any plan
7
- reaches review. Approval produces a committed plan artifact for a future implementer
8
- skill (`snake`) to execute.
5
+ <h3 align="center">Stop rubber-stamping your agent's plans</h3>
9
6
 
10
- Behavior spec: [DESIGN.md](DESIGN.md) · tradeoff rationale: [DECISIONS.md](DECISIONS.md)
11
- · agent conventions: [AGENTS.md](AGENTS.md)
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/otacon"><img src="https://img.shields.io/npm/v/otacon.svg" alt="npm version"></a>
9
+ <a href="https://www.npmjs.com/package/otacon"><img src="https://img.shields.io/node/v/otacon.svg" alt="node"></a>
10
+ <a href="LICENSE"><img src="https://img.shields.io/npm/l/otacon.svg" alt="license"></a>
11
+ </p>
12
12
 
13
- Personal tool by/for Zero. Zero API spend by construction — all model work happens in
14
- your interactive subscription-backed agent session; the daemon, CLI, and UI never call
15
- an LLM.
13
+ <p align="center">
14
+ <a href="#installation"><b>Installation</b></a>&nbsp; ·&nbsp;
15
+ <a href="#get-started"><b>Get started</b></a>&nbsp; ·&nbsp;
16
+ <a href="#why-otacon"><b>Why Otacon</b></a>&nbsp; ·&nbsp;
17
+ <a href="docs/PHONE-ACCESS.md"><b>Phone access</b></a>
18
+ </p>
16
19
 
17
- ## Install
20
+ <br/>
18
21
 
19
- One-time machine setup (DESIGN.md §16):
22
+ Otacon replaces your coding agent's native plan mode with a review surface you'll actually use. Your agent (Claude Code, Codex, or OpenCode) interviews you until it understands the goal, drafts a concise plan with the discipline of an engineering design doc, and hands you a real place to comment, ask, and diff revisions, then sign off before a line of code is written.
23
+
24
+ ## Installation
20
25
 
21
26
  ```sh
22
- npm install -g otacon # one package: CLI + daemon (Node ≥ 20)
23
- otacon install --all # agent wrappers; or --agent claude|codex|opencode
24
- otacon install --agent claude --hooks # also register the Claude Code Stop hook
25
- otacon doctor # verify: node, daemon boots, wrappers, Tailscale
27
+ npm install -g otacon
26
28
  ```
27
29
 
28
- `otacon install` writes the protocol wrapper into each agent's skill location —
29
- Claude Code: `~/.claude/skills/otacon/SKILL.md` (+ the Stop hook script at
30
- `~/.claude/hooks/otacon-stop.sh`); Codex: a marked block in `~/.codex/AGENTS.md`;
31
- OpenCode: `~/.config/opencode/skills/otacon/SKILL.md`. Wrappers are managed files:
32
- reinstalls overwrite them (outside-the-markers content in Codex's shared file
33
- survives). `--hooks` merges the Stop hook into `~/.claude/settings.json` additively
34
- and idempotently, backing the file up first. The daemon is never started by hand —
35
- any `otacon` command auto-spawns it.
36
-
37
- Per-repo setup: **none.** The first `otacon start` in a repo creates `.otacon/` and
38
- gitignores it. Approved plans land committed in `docs/plans/`. `otacon clean` archives
39
- ended sessions' working state to `.otacon/archive/`.
30
+ ## Get started
40
31
 
41
- ### Updating
32
+ **Install the skill into your agent.** This drops the Otacon skill into the agent's skill
33
+ folder so it knows how to run a review:
42
34
 
43
35
  ```sh
44
- npm update -g otacon # the version handshake restarts the daemon on next use
36
+ otacon install --all # all three agents
37
+ otacon install --agent claude # or just one: claude, codex, or opencode
45
38
  ```
46
39
 
47
- ### Build from source (contributors)
40
+ **Plan a feature.** In your agent, run `/otacon` (or just ask it to plan something with
41
+ Otacon). It interviews you to lock down intent, drafts the plan, and hands you a local
42
+ review URL.
48
43
 
49
- For contributors or the bleeding edge only the published npm package is the
50
- supported user path. Clone the repo and run from source:
44
+ **Review and sign off.** Open the URL: answer the interview, leave inline comments, ask
45
+ questions, and diff revisions in the browser, or [from your phone](docs/PHONE-ACCESS.md).
51
46
 
52
- ```sh
53
- git clone https://github.com/zeroliu/otacon && cd otacon
54
- bun install
55
- ./bin/otacon doctor # run straight from source
56
- # — or build a Node artifact and link it onto PATH —
57
- bun run build && npm link # `otacon` now points at this checkout
58
- ```
47
+ When the plan is ready, choose how it ships:
48
+
49
+ - **Save**: the approved plan lands in your home archive (`~/.otacon/sessions/`) plus a copy in the project (`.otacon/plans/` by default; set `plans.dir` to `docs/plans` to group it with your tracked plans). Commit the project copy if you want it in git.
50
+ - **Implement**: the same agent builds it in an isolated git worktree, phase by phase, and opens a PR.
51
+
52
+ ## Why Otacon
53
+
54
+ Native plan mode hands you a wall of terminal text without fully clarifying your intent, and asks you to bless it. When you share your feedback, the agent sends back the full plan again. Re-reviewing the next revision costs as much as the first read.
55
+
56
+ The native plan also mixes low-level implementation detail with high-level design. Serious engineering orgs don't work that way. They sign off on the interface, the blast radius, and the behavior, dig into the implementation only where it matters, and treat review as a back-and-forth rather than a one-shot blessing. Otacon brings that discipline to coding agents.
57
+
58
+ <table>
59
+ <tr>
60
+ <td width="50%" valign="top">
61
+
62
+ #### 📄 Plans you actually read
63
+
64
+ A wall of prose is easy to skim and impossible to vet. Otacon plans are modeled on the
65
+ design docs strong eng orgs actually review. They lead with the high-level interface and
66
+ the impact, fold the code-level detail into collapsible sections, and put visuals where
67
+ they carry weight.
68
+
69
+ </td>
70
+ <td width="50%" valign="top">
71
+
72
+ #### 💬 Comment, don't rubber-stamp
73
+
74
+ Select any passage and leave an inline comment. Your note is
75
+ captured and routed to the agent without breaking your reading flow. Comments batch into
76
+ **one** clean revision with a changelog. Need a
77
+ clarification, not a change? **Quick Ask** gets you an instant answer without touching the
78
+ plan. And each revision lands as a diff that shows only what changed since you last looked,
79
+ so you never dig through a wall of terminal text to find it.
80
+
81
+ </td>
82
+ </tr>
83
+ <tr>
84
+ <td width="50%" valign="top">
85
+
86
+ #### 🎙️ It interviews you first
87
+
88
+ Otacon grills
89
+ you before it writes a word of the plan. It interviews until you're aligned, then drafts. Every
90
+ decision traces back to the answer that produced it, and the interview ships
91
+ with the approved plan, so the _why_ is never lost.
92
+
93
+ </td>
94
+ <td width="50%" valign="top">
95
+
96
+ #### 📱 Review from your phone
59
97
 
60
- (`npm i -g github:zeroliu/otacon` is **not** a supported install the published
61
- package ships a prebuilt `dist/`, and a GitHub install would need a build-on-install
62
- step that is intentionally not wired.)
98
+ The five minutes before you hit the road shouldn't block your agent. Reviews run over
99
+ Tailscale, so you can continue the review, comment, and sign off from your phone. Because it's your own tailnet, your plans never leave your own devices.
100
+ [Phone setup →](docs/PHONE-ACCESS.md)
63
101
 
64
- ## Phone access
102
+ </td>
103
+ </tr>
104
+ <tr>
105
+ <td width="50%" valign="top">
65
106
 
66
- Reviews work from a phone over Tailscale (DESIGN.md §11) — plans never leave your
67
- devices, and the tailnet is the auth:
107
+ #### 🚀 From approved plan to shipped PR
68
108
 
69
- 1. Install Tailscale on the Mac and the phone; log in (`tailscale up`).
70
- 2. Enable **HTTPS Certificates** for the tailnet: Tailscale admin console DNS
71
- Enable HTTPS (MagicDNS must be on). This is the one step otacon cannot do for you.
72
- 3. `otacon expose` — configures `tailscale serve` for the daemon port, verifies the
73
- tailnet URL actually serves, and prints the HTTPS URL with `verified: true`.
74
- Bookmark it on the phone.
75
- 4. Keep the Mac awake while a plan is in review: `caffeinate -i`.
109
+ Approval shouldn't be where the rigor ends. **Approve & Implement** carries the plan
110
+ straight into the build inside an isolated git worktree: the same agent walks the phases
111
+ with a fresh subagent per phase,
112
+ then opens a PR.
76
113
 
77
- If you skip step 2, `tailscale serve` still succeeds but the URL resets every TLS
78
- handshake so `otacon expose` reports `verified: false` and links the admin DNS page
79
- instead of handing you a dead URL. (Just enabled HTTPS? The cert can take a minute to
80
- provision; re-run `expose`.)
114
+ </td>
115
+ <td width="50%" valign="top">
81
116
 
82
- On the Mac App Store Tailscale, putting `tailscale` on your `PATH` needs a manual
83
- launcher — a wrapper script that runs the app-bundle binary (a bare symlink crashes).
84
- otacon finds the app-bundle binary on its own either way.
117
+ #### 🔒 Private & free by construction
85
118
 
86
- ---
119
+ The daemon, CLI, and UI never call an LLM. All the intelligence runs in your existing
120
+ subscription-backed agent session, so Otacon adds **zero API spend** of its own. It's
121
+ local-first, so your plans stay on your machines.
87
122
 
88
- Maintainers cutting a release: see [RELEASING.md](RELEASING.md).
123
+ </td>
124
+ </tr>
125
+ </table>
@@ -0,0 +1,55 @@
1
+ // Launch a URL in the user's default browser for `otacon open` / `otacon config`
2
+ // (DESIGN.md §6, DECISIONS.md "open and config launch the browser"). Both run on
3
+ // the human's machine and the whole point of these two verbs is "show me the
4
+ // page", so they spawn the browser instead of printing the URL.
5
+ //
6
+ // The launch is best-effort and detached: a missing opener (ENOENT), a non-GUI
7
+ // host, or a slow browser must never fail the command, throw out of the CLI's
8
+ // JSON-on-stdout contract, or stall an agent that ran it. OTACON_NO_BROWSER
9
+ // suppresses the launch and prints the URL as one JSON line instead. That is the
10
+ // seam headless hosts (CI, the e2e scripts) and any agent that wants to parse the
11
+ // URL use to get the old print-only behavior back.
12
+ import { spawn } from "node:child_process";
13
+ import { notice, printJson } from "./output.js";
14
+ /** The platform's "open this URL with the default app" command + args. */
15
+ function opener(url) {
16
+ switch (process.platform) {
17
+ case "darwin":
18
+ return { command: "open", args: [url] };
19
+ case "win32":
20
+ // `start` is a cmd builtin; the empty "" is its window-title argument, so a
21
+ // URL carrying spaces/`&` isn't swallowed as the title instead of the URL.
22
+ return { command: "cmd", args: ["/c", "start", "", url] };
23
+ default:
24
+ return { command: "xdg-open", args: [url] };
25
+ }
26
+ }
27
+ /** Fire the default browser at `url`, detached; never throws (see header). */
28
+ export function openInBrowser(url) {
29
+ const { command, args } = opener(url);
30
+ try {
31
+ const child = spawn(command, args, { detached: true, stdio: "ignore" });
32
+ // Without a listener an ENOENT (no opener on PATH) would surface as an
33
+ // uncaught error event; swallow it. The stderr notice still carries the URL.
34
+ child.on("error", () => { });
35
+ child.unref();
36
+ }
37
+ catch {
38
+ // spawn can also throw synchronously; the URL is still in the notice.
39
+ }
40
+ }
41
+ /**
42
+ * Deliver a URL the way `open`/`config` do: launch the browser and print a stderr
43
+ * notice carrying the link (a clickable fallback when the launch is a no-op).
44
+ * Under OTACON_NO_BROWSER it falls back to the print-only contract (one JSON line
45
+ * on stdout) for headless hosts, the e2e scripts, and URL-parsing agents.
46
+ */
47
+ export function openOrPrint(url, payload) {
48
+ if (process.env.OTACON_NO_BROWSER) {
49
+ printJson(payload);
50
+ return;
51
+ }
52
+ notice(`opening ${url}`);
53
+ openInBrowser(url);
54
+ }
55
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/cli/browser.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,iFAAiF;AACjF,6EAA6E;AAC7E,gEAAgE;AAChE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,iFAAiF;AACjF,kFAAkF;AAClF,mDAAmD;AAEnD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEhD,0EAA0E;AAC1E,SAAS,MAAM,CAAC,GAAW;IACzB,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,KAAK,OAAO;YACV,4EAA4E;YAC5E,2EAA2E;YAC3E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;QAC5D;YACE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACxE,uEAAuE;QACvE,6EAA6E;QAC7E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;IACxE,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,OAAgC;IACvE,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;QAClC,SAAS,CAAC,OAAO,CAAC,CAAC;QACnB,OAAO;IACT,CAAC;IACD,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC;IACzB,aAAa,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC"}
@@ -3,8 +3,9 @@
3
3
  // calls DELETE /api/sessions/:id; the daemon deregisters the session and
4
4
  // archives its .otacon/<id>/ dir to .otacon/archive/<id>/, reporting the
5
5
  // destination as `archivedTo` (the review UI drives the same route — terminal
6
- // archives, non-terminal hard-deletes). Committed artifacts under docs/plans/
7
- // are never touched (DECISIONS.md "clean: daemon deregisters and archives").
6
+ // archives, non-terminal hard-deletes). The home archive (~/.otacon/sessions/)
7
+ // and any Save-time project copy are never touched (DECISIONS.md "clean: daemon
8
+ // deregisters and archives").
8
9
  import { parseArgs } from "node:util";
9
10
  import { TERMINAL_STATUSES } from "../../shared/types.js";
10
11
  import { api, ensureDaemon } from "../client.js";
@@ -1 +1 @@
1
- {"version":3,"file":"clean.js","sourceRoot":"","sources":["../../../src/cli/commands/clean.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,yEAAyE;AACzE,yEAAyE;AACzE,8EAA8E;AAC9E,8EAA8E;AAC9E,6EAA6E;AAE7E,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAc;IAC/C,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC3B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;KACtD,CAAC,CAAC;IACH,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;IACtC,wEAAwE;IACxE,6EAA6E;IAC7E,8EAA8E;IAC9E,yEAAyE;IACzE,mDAAmD;IACnD,MAAM,OAAO,GAAG,CAAC,MAAM,YAAY,EAAE,CAAC,CAAC,MAAM,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAC3F,CAAC;IAEF,MAAM,OAAO,GAAkF,EAAE,CAAC;IAClG,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,EAAE,iBAAiB,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QACpE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,CAAC,YAAY,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnE,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;QAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,OAAO,CAAC,EAAE,KAAK,OAAO,wCAAwC,CAAC,CAAC;QAC5E,CAAC;QACD,6EAA6E;QAC7E,MAAM,UAAU,GAAI,QAAQ,CAAC,IAAI,CAAC,UAAwC,IAAI,IAAI,CAAC;QACnF,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,yBAAyB,IAAI,cAAc,CAAC,CAAC;IAClG,CAAC;IACD,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC;AACX,CAAC"}
1
+ {"version":3,"file":"clean.js","sourceRoot":"","sources":["../../../src/cli/commands/clean.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,yEAAyE;AACzE,yEAAyE;AACzE,8EAA8E;AAC9E,+EAA+E;AAC/E,gFAAgF;AAChF,8BAA8B;AAE9B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAc;IAC/C,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC3B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;KACtD,CAAC,CAAC;IACH,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;IACtC,wEAAwE;IACxE,6EAA6E;IAC7E,8EAA8E;IAC9E,yEAAyE;IACzE,mDAAmD;IACnD,MAAM,OAAO,GAAG,CAAC,MAAM,YAAY,EAAE,CAAC,CAAC,MAAM,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAC3F,CAAC;IAEF,MAAM,OAAO,GAAkF,EAAE,CAAC;IAClG,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,EAAE,iBAAiB,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QACpE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,CAAC,YAAY,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnE,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;QAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,OAAO,CAAC,EAAE,KAAK,OAAO,wCAAwC,CAAC,CAAC;QAC5E,CAAC;QACD,6EAA6E;QAC7E,MAAM,UAAU,GAAI,QAAQ,CAAC,IAAI,CAAC,UAAwC,IAAI,IAAI,CAAC;QACnF,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,yBAAyB,IAAI,cAAc,CAAC,CAAC;IAClG,CAAC;IACD,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,62 @@
1
+ // otacon config [open] | config get <key> (DESIGN.md §6, §16).
2
+ //
3
+ // otacon config open the Settings web UI in the browser. Inside a
4
+ // git repo the URL carries `?repo=<repo root>` so the screen defaults to
5
+ // Project scope for this repo; outside any repo it is the bare `/settings`
6
+ // (User scope). Like `otacon open`, it launches the browser; OTACON_NO_BROWSER
7
+ // prints the URL instead (DECISIONS.md "open and config launch the browser").
8
+ // otacon config get <key> read-only: resolve the merged effective value of
9
+ // one dotted key (`worktree.dir`, `budgets.summaryLines`, …) from the config
10
+ // files via loadConfig and print it. No daemon: it reads config directly so
11
+ // the agent implement loop can consume `worktree.dir` (DECISIONS.md
12
+ // "read-only `config get`"). Editing config stays UI-only.
13
+ import { CONFIG_SCHEMA, loadConfig } from "../../shared/config.js";
14
+ import { openOrPrint } from "../browser.js";
15
+ import { baseUrl, ensureDaemon } from "../client.js";
16
+ import { fail, printJson, usageError } from "../output.js";
17
+ import { findRepoRoot, realpathOr } from "../session.js";
18
+ /** Every valid dotted key, derived from the one schema source of truth. */
19
+ const KNOWN_KEYS = new Set(CONFIG_SCHEMA.map((field) => `${field.section}.${field.key}`));
20
+ export async function configCommand(argv) {
21
+ const sub = argv[0];
22
+ if (sub === "get")
23
+ return configGet(argv.slice(1));
24
+ // No sub-form, or the explicit `open` verb: open the Settings web UI.
25
+ if (sub === undefined || sub === "open")
26
+ return configOpen();
27
+ usageError("usage: otacon config [open] | otacon config get <key>");
28
+ }
29
+ /** Launch the Settings web UI in the browser (mirrors `otacon open`). */
30
+ async function configOpen() {
31
+ await ensureDaemon();
32
+ // Inside a git repo, default the Settings screen to this repo's Project scope
33
+ // via ?repo=; outside any repo there is no project target, so open User scope.
34
+ const repoRoot = findRepoRoot(realpathOr(process.cwd()));
35
+ const url = repoRoot === undefined
36
+ ? `${baseUrl()}/settings`
37
+ : `${baseUrl()}/settings?repo=${encodeURIComponent(repoRoot)}`;
38
+ openOrPrint(url, { ok: true, url, ...(repoRoot === undefined ? {} : { repo: repoRoot }) });
39
+ return 0;
40
+ }
41
+ /** Read-only merged lookup of one dotted config key. */
42
+ function configGet(args) {
43
+ const key = args[0];
44
+ if (key === undefined || args.length > 1) {
45
+ usageError("otacon config get takes exactly one <key> (e.g. worktree.dir)");
46
+ }
47
+ if (!KNOWN_KEYS.has(key)) {
48
+ fail("E_UNKNOWN_KEY", `unknown config key ${key}; valid keys: ${[...KNOWN_KEYS].join(", ")}`);
49
+ }
50
+ // Resolve against the cwd repo's merged config when inside a repo, else the
51
+ // defaults←user layers alone — exactly what the runtime would see here.
52
+ const repoRoot = findRepoRoot(realpathOr(process.cwd()));
53
+ const config = loadConfig(repoRoot);
54
+ const [section, leaf] = key.split(".");
55
+ // `key` passed the CONFIG_SCHEMA check above, so section/leaf name a real
56
+ // config field; the unknown hop is the schema-driven section→leaf indexing
57
+ // (same shape overlayConfig uses) rather than a per-section union.
58
+ const value = config[section][leaf];
59
+ printJson({ ok: true, key, value });
60
+ return 0;
61
+ }
62
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"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"}
@@ -7,31 +7,41 @@ import { existsSync, readFileSync } from "node:fs";
7
7
  import { parseArgs } from "node:util";
8
8
  import { otaconPort } from "../../shared/paths.js";
9
9
  import { ensureDaemon } from "../client.js";
10
- import { CODEX_BEGIN, MANAGED_MARKER } from "../install/assets.js";
11
- import { claudeHookScriptPath, claudeSkillPath, codexAgentsPath, opencodeSkillPath, settingsRegisterStopHook, } from "../install/locations.js";
10
+ import { MANAGED_MARKER } from "../install/assets.js";
11
+ import { claudeHookScriptPath, claudeSkillPath, codexSkillPath, opencodeSkillPath, settingsRegisterStopHook, } from "../install/locations.js";
12
12
  import { findTailscale, tailscaleStatus } from "../install/tailscale.js";
13
13
  import { CliError, printJson } from "../output.js";
14
- function wrapperCheck(name, path, marker) {
15
- const present = existsSync(path) && readFileSync(path, "utf8").includes(marker);
16
- return present
17
- ? { name, status: "ok", detail: path }
18
- : {
19
- name,
20
- status: "warn",
21
- detail: `wrapper not installed at ${path}; run otacon install --agent ${name.replace("wrapper-", "")}`,
22
- };
14
+ import { findRepoRoot } from "../session.js";
15
+ function wrapperPresent(path, marker) {
16
+ return existsSync(path) && readFileSync(path, "utf8").includes(marker);
23
17
  }
24
- function stopHookCheck() {
25
- const name = "stop-hook";
26
- if (settingsRegisterStopHook()) {
27
- return { name, status: "ok", detail: claudeHookScriptPath() };
28
- }
18
+ // A wrapper is the otacon protocol skill (the SKILL.md `otacon install` writes); it's
19
+ // optional, so absence is a warn, never a failure. When run inside a repo, accept the
20
+ // wrapper at EITHER the user path or the project path so a `--project` install doesn't
21
+ // trip a spurious "not installed" warning report whichever scope satisfied it.
22
+ export function wrapperCheck(name, candidates, marker) {
23
+ const hit = candidates.find((c) => wrapperPresent(c.path, marker));
24
+ if (hit)
25
+ return { name, status: "ok", detail: `${hit.path} (${hit.scope})` };
26
+ const agent = name.replace("wrapper-", "");
27
+ const looked = candidates.map((c) => c.path).join(" and ");
28
+ const projectHint = candidates.some((c) => c.scope === "project")
29
+ ? " or add --project to install it into this repo"
30
+ : "";
29
31
  return {
30
32
  name,
31
33
  status: "warn",
32
- detail: "Stop hook not registered; run otacon install --agent claude --hooks",
34
+ detail: `otacon protocol skill not found for ${agent} (looked in ${looked}); run \`otacon install --agent ${agent}\`${projectHint}`,
33
35
  };
34
36
  }
37
+ // The Stop hook is optional — a belt-and-suspenders guard on top of the skill's
38
+ // never-end-your-turn rule, not a required piece. Confirm it when wired up, but
39
+ // never flag its absence (return undefined → omitted from the report, no warning).
40
+ function stopHookCheck() {
41
+ if (!settingsRegisterStopHook())
42
+ return undefined;
43
+ return { name: "stop-hook", status: "ok", detail: claudeHookScriptPath() };
44
+ }
35
45
  function tailscaleCheck() {
36
46
  const name = "tailscale";
37
47
  const bin = findTailscale();
@@ -74,10 +84,20 @@ export async function doctorCommand(argv) {
74
84
  detail: error instanceof CliError ? `${error.code}: ${error.message}` : String(error),
75
85
  });
76
86
  }
77
- checks.push(wrapperCheck("wrapper-claude", claudeSkillPath(), MANAGED_MARKER));
78
- checks.push(wrapperCheck("wrapper-codex", codexAgentsPath(), CODEX_BEGIN));
79
- checks.push(wrapperCheck("wrapper-opencode", opencodeSkillPath(), MANAGED_MARKER));
80
- checks.push(stopHookCheck());
87
+ // When in a git repo, also accept a project-scope wrapper (otacon install --project)
88
+ // so a committed `.claude`/`.codex`/`.opencode` doesn't read as "not installed".
89
+ const projectRoot = findRepoRoot(process.cwd());
90
+ const project = projectRoot === undefined ? undefined : { kind: "project", root: projectRoot };
91
+ const candidates = (skillPath) => [
92
+ { path: skillPath(), scope: "user" },
93
+ ...(project ? [{ path: skillPath(project), scope: "project" }] : []),
94
+ ];
95
+ checks.push(wrapperCheck("wrapper-claude", candidates(claudeSkillPath), MANAGED_MARKER));
96
+ checks.push(wrapperCheck("wrapper-codex", candidates(codexSkillPath), MANAGED_MARKER));
97
+ checks.push(wrapperCheck("wrapper-opencode", candidates(opencodeSkillPath), MANAGED_MARKER));
98
+ const stopHook = stopHookCheck();
99
+ if (stopHook)
100
+ checks.push(stopHook);
81
101
  checks.push(tailscaleCheck());
82
102
  const ok = checks.every((c) => c.status !== "fail");
83
103
  printJson({ ok, checks });
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -1,38 +1,44 @@
1
1
  // otacon install --agent claude|codex|opencode [--agent …] | --all [--hooks] —
2
2
  // write the protocol wrapper into each agent's skill location (DESIGN.md §16).
3
3
  // Pure file writes — no daemon needed. Wrappers are managed files: reinstall
4
- // overwrites them (Codex: only the marked block inside its shared AGENTS.md).
5
- // --hooks additionally registers the Claude Code Stop hook in
6
- // ~/.claude/settings.json merged additively and idempotently, with a backup
7
- // before the first change, never clobbering what cannot be parsed.
4
+ // overwrites them wholesale. --hooks additionally registers the Claude Code Stop
5
+ // hook in ~/.claude/settings.json merged additively and idempotently, with a
6
+ // backup before the first change, never clobbering what cannot be parsed.
8
7
  import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
8
  import { dirname } from "node:path";
10
9
  import { parseArgs } from "node:util";
11
- import { CODEX_BEGIN, CODEX_END, codexBlock, skillMd, STOP_HOOK_SCRIPT } from "../install/assets.js";
12
- import { claudeHookScriptPath, claudeSettingsPath, claudeSkillPath, codexAgentsPath, mergeStopHook, opencodeSkillPath, settingsRegisterStopHook, upsertMarkedBlock, } from "../install/locations.js";
10
+ import { skillMd, STOP_HOOK_SCRIPT } from "../install/assets.js";
11
+ import { claudeHookScriptPath, claudeSettingsPath, claudeSkillPath, codexSkillPath, mergeStopHook, opencodeSkillPath, settingsRegisterStopHook, } from "../install/locations.js";
13
12
  import { fail, notice, printJson, usageError } from "../output.js";
13
+ import { findRepoRoot } from "../session.js";
14
14
  const AGENTS = ["claude", "codex", "opencode"];
15
15
  function writeManaged(path, content) {
16
16
  mkdirSync(dirname(path), { recursive: true });
17
17
  writeFileSync(path, content);
18
18
  }
19
- function installAgent(agent) {
19
+ function installAgent(agent, scope) {
20
20
  switch (agent) {
21
21
  case "claude": {
22
- writeManaged(claudeSkillPath(), skillMd());
23
- writeManaged(claudeHookScriptPath(), STOP_HOOK_SCRIPT);
24
- chmodSync(claudeHookScriptPath(), 0o755);
25
- return { agent, files: [claudeSkillPath(), claudeHookScriptPath()] };
22
+ const skill = claudeSkillPath(scope);
23
+ writeManaged(skill, skillMd());
24
+ // The Stop hook script lives in the user home only — it is never written at
25
+ // project scope (DECISIONS.md "Stop hook deferred at project scope"), so a
26
+ // committed `.claude/` ships an inert skill wrapper, never a hook pointing at
27
+ // a script teammates may not have. `--hooks --project` is rejected upstream.
28
+ if (scope.kind === "user") {
29
+ writeManaged(claudeHookScriptPath(), STOP_HOOK_SCRIPT);
30
+ chmodSync(claudeHookScriptPath(), 0o755);
31
+ return { agent, files: [skill, claudeHookScriptPath()] };
32
+ }
33
+ return { agent, files: [skill] };
26
34
  }
27
35
  case "codex": {
28
- const path = codexAgentsPath();
29
- const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
30
- writeManaged(path, upsertMarkedBlock(existing, codexBlock(), CODEX_BEGIN, CODEX_END));
31
- return { agent, files: [path] };
36
+ writeManaged(codexSkillPath(scope), skillMd());
37
+ return { agent, files: [codexSkillPath(scope)] };
32
38
  }
33
39
  case "opencode": {
34
- writeManaged(opencodeSkillPath(), skillMd());
35
- return { agent, files: [opencodeSkillPath()] };
40
+ writeManaged(opencodeSkillPath(scope), skillMd());
41
+ return { agent, files: [opencodeSkillPath(scope)] };
36
42
  }
37
43
  }
38
44
  }
@@ -65,19 +71,14 @@ function applyStopHook() {
65
71
  notice(`registered the otacon Stop hook in ${path}`);
66
72
  return { registered: true, command, settings: path, ...(backup ? { backup } : {}) };
67
73
  }
68
- /** Without --hooks: report the current state and offer the flag (DESIGN.md §16). */
74
+ // Without --hooks: report registration state without nagging (DESIGN.md §16). The
75
+ // Stop hook is optional, so its absence is neither warned about nor "offered" — the
76
+ // JSON still factually carries `registered` for anyone who wants to wire it up.
69
77
  function offerStopHook() {
70
- const path = claudeSettingsPath();
71
- const registered = settingsRegisterStopHook();
72
- if (!registered) {
73
- notice("Stop hook not registered — run `otacon install --agent claude --hooks` to add it to " +
74
- `${path} (merged additively, existing settings preserved, backup written first)`);
75
- }
76
78
  return {
77
- registered,
79
+ registered: settingsRegisterStopHook(),
78
80
  command: claudeHookScriptPath(),
79
- settings: path,
80
- ...(registered ? {} : { hint: "re-run with --hooks to register the Stop hook" }),
81
+ settings: claudeSettingsPath(),
81
82
  };
82
83
  }
83
84
  export async function installCommand(argv) {
@@ -87,6 +88,7 @@ export async function installCommand(argv) {
87
88
  agent: { type: "string", multiple: true },
88
89
  all: { type: "boolean", default: false },
89
90
  hooks: { type: "boolean", default: false },
91
+ project: { type: "boolean", default: false },
90
92
  },
91
93
  });
92
94
  const picked = values.all ? [...AGENTS] : (values.agent ?? []);
@@ -101,13 +103,34 @@ export async function installCommand(argv) {
101
103
  if (values.hooks && !agents.includes("claude")) {
102
104
  usageError("--hooks registers the Claude Code Stop hook; include --agent claude (or --all)");
103
105
  }
104
- const installed = agents.map(installAgent);
105
- const hooks = agents.includes("claude")
106
+ // The Stop hook is a user-level Claude Code registration; it is never installed at
107
+ // project scope (DECISIONS.md "Stop hook deferred at project scope"), so the two
108
+ // flags are mutually exclusive rather than silently picking one.
109
+ if (values.hooks && values.project) {
110
+ usageError("--hooks installs a user-level Stop hook and cannot be combined with --project; run --hooks without --project");
111
+ }
112
+ // --project resolves the install base to the current git repo root so the
113
+ // wrappers can be committed and shared; outside any repo it is a hard error
114
+ // (DECISIONS.md "`--project` resolves to the git repo root").
115
+ let scope = { kind: "user" };
116
+ if (values.project) {
117
+ const cwd = process.cwd();
118
+ const root = findRepoRoot(cwd);
119
+ if (root === undefined) {
120
+ usageError(`otacon install --project must run inside a git repo; none found at ${cwd}`);
121
+ }
122
+ scope = { kind: "project", root };
123
+ }
124
+ const installed = agents.map((agent) => installAgent(agent, scope));
125
+ // The Stop hook report is user-only: at project scope --hooks is rejected, and
126
+ // offerStopHook() would read the user ~/.claude/settings.json — misleading for a
127
+ // project install — so the entire hooks branch is gated on user scope.
128
+ const hooks = scope.kind === "user" && agents.includes("claude")
106
129
  ? values.hooks
107
130
  ? applyStopHook()
108
131
  : offerStopHook()
109
132
  : undefined;
110
- printJson({ ok: true, installed, ...(hooks ? { hooks } : {}) });
133
+ printJson({ ok: true, scope: scope.kind, installed, ...(hooks ? { hooks } : {}) });
111
134
  return 0;
112
135
  }
113
136
  //# sourceMappingURL=install.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/cli/commands/install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/cli/commands/install.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,+EAA+E;AAC/E,6EAA6E;AAC7E,iFAAiF;AACjF,+EAA+E;AAC/E,0EAA0E;AAE1E,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,cAAc,EAEd,aAAa,EACb,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAU,CAAC;AAGxD,SAAS,YAAY,CAAC,IAAY,EAAE,OAAe;IACjD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,YAAY,CAAC,KAAY,EAAE,KAAmB;IACrD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YACrC,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/B,4EAA4E;YAC5E,2EAA2E;YAC3E,8EAA8E;YAC9E,6EAA6E;YAC7E,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1B,YAAY,CAAC,oBAAoB,EAAE,EAAE,gBAAgB,CAAC,CAAC;gBACvD,SAAS,CAAC,oBAAoB,EAAE,EAAE,KAAK,CAAC,CAAC;gBACzC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC;YAC3D,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,YAAY,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACnD,CAAC;QACD,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,YAAY,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAClD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACtD,CAAC;IACH,CAAC;AACH,CAAC;AASD,2FAA2F;AAC3F,SAAS,aAAa;IACpB,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAC;IACvC,IAAI,GAAG,GAAY,EAAE,CAAC;IACtB,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CACF,uBAAuB,EACvB,GAAG,IAAI,uFAAuF,CAC/F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,IAAI,CACF,kBAAkB,EAClB,GAAG,IAAI,+FAA+F,OAAO,GAAG,CACjH,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1E,IAAI,MAA0B,CAAC;IAC/B,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,IAAI,kBAAkB,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAC/C,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3B,MAAM,CAAC,aAAa,IAAI,OAAO,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,YAAY,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;IACrD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACtF,CAAC;AAED,kFAAkF;AAClF,oFAAoF;AACpF,gFAAgF;AAChF,SAAS,aAAa;IACpB,OAAO;QACL,UAAU,EAAE,wBAAwB,EAAE;QACtC,OAAO,EAAE,oBAAoB,EAAE;QAC/B,QAAQ,EAAE,kBAAkB,EAAE;KAC/B,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAc;IACjD,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC3B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACP,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;YACzC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YACxC,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YAC1C,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;SAC7C;KACF,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAc,CAAC;IAC7E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,UAAU,CAAC,6EAA6E,CAAC,CAAC;IAC5F,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAE,MAA4B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,UAAU,CAAC,kBAAkB,OAAO,yCAAyC,CAAC,CAAC;IACjF,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAY,CAAC;IAC/C,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/C,UAAU,CAAC,gFAAgF,CAAC,CAAC;IAC/F,CAAC;IACD,mFAAmF;IACnF,iFAAiF;IACjF,iEAAiE;IACjE,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnC,UAAU,CACR,8GAA8G,CAC/G,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,4EAA4E;IAC5E,8DAA8D;IAC9D,IAAI,KAAK,GAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC3C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,UAAU,CAAC,sEAAsE,GAAG,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,KAAK,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACpC,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACpE,+EAA+E;IAC/E,iFAAiF;IACjF,uEAAuE;IACvE,MAAM,KAAK,GACT,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAChD,CAAC,CAAC,MAAM,CAAC,KAAK;YACZ,CAAC,CAAC,aAAa,EAAE;YACjB,CAAC,CAAC,aAAa,EAAE;QACnB,CAAC,CAAC,SAAS,CAAC;IAChB,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACnF,OAAO,CAAC,CAAC;AACX,CAAC"}