nfo-cli 0.0.1

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 (240) hide show
  1. package/README.md +119 -0
  2. package/assets/agent-screen.png +0 -0
  3. package/assets/main-screen.png +0 -0
  4. package/assets/orche-clawd.png +0 -0
  5. package/dist/claude-command.js +19 -0
  6. package/dist/claude-command.js.map +1 -0
  7. package/dist/claude-detect.js +28 -0
  8. package/dist/claude-detect.js.map +1 -0
  9. package/dist/cli.js +152 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/commands/attach.js +23 -0
  12. package/dist/commands/attach.js.map +1 -0
  13. package/dist/commands/dashboard-window.js +26 -0
  14. package/dist/commands/dashboard-window.js.map +1 -0
  15. package/dist/commands/kill.js +38 -0
  16. package/dist/commands/kill.js.map +1 -0
  17. package/dist/commands/launch.js +85 -0
  18. package/dist/commands/launch.js.map +1 -0
  19. package/dist/commands/list.js +35 -0
  20. package/dist/commands/list.js.map +1 -0
  21. package/dist/commands/mcp-server.js +13 -0
  22. package/dist/commands/mcp-server.js.map +1 -0
  23. package/dist/commands/notes.js +17 -0
  24. package/dist/commands/notes.js.map +1 -0
  25. package/dist/commands/restore.js +111 -0
  26. package/dist/commands/restore.js.map +1 -0
  27. package/dist/commands/tui.js +13 -0
  28. package/dist/commands/tui.js.map +1 -0
  29. package/dist/config.js +26 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/dashboard.js +2 -0
  32. package/dist/dashboard.js.map +1 -0
  33. package/dist/mcp/config.js +34 -0
  34. package/dist/mcp/config.js.map +1 -0
  35. package/dist/mcp/handlers.js +117 -0
  36. package/dist/mcp/handlers.js.map +1 -0
  37. package/dist/mcp/server.js +32 -0
  38. package/dist/mcp/server.js.map +1 -0
  39. package/dist/mcp/tool-defs.js +132 -0
  40. package/dist/mcp/tool-defs.js.map +1 -0
  41. package/dist/musicians/dismiss.js +50 -0
  42. package/dist/musicians/dismiss.js.map +1 -0
  43. package/dist/musicians/ids.js +20 -0
  44. package/dist/musicians/ids.js.map +1 -0
  45. package/dist/musicians/lookup.js +11 -0
  46. package/dist/musicians/lookup.js.map +1 -0
  47. package/dist/musicians/message-log.js +89 -0
  48. package/dist/musicians/message-log.js.map +1 -0
  49. package/dist/musicians/message.js +54 -0
  50. package/dist/musicians/message.js.map +1 -0
  51. package/dist/musicians/query.js +13 -0
  52. package/dist/musicians/query.js.map +1 -0
  53. package/dist/musicians/spawn.js +102 -0
  54. package/dist/musicians/spawn.js.map +1 -0
  55. package/dist/notes.js +32 -0
  56. package/dist/notes.js.map +1 -0
  57. package/dist/notify.js +51 -0
  58. package/dist/notify.js.map +1 -0
  59. package/dist/orchestrator/report-back.js +21 -0
  60. package/dist/orchestrator/report-back.js.map +1 -0
  61. package/dist/permission.js +26 -0
  62. package/dist/permission.js.map +1 -0
  63. package/dist/project-key.js +12 -0
  64. package/dist/project-key.js.map +1 -0
  65. package/dist/prompts/musician-role.js +22 -0
  66. package/dist/prompts/musician-role.js.map +1 -0
  67. package/dist/prompts/orchestrator-role.js +62 -0
  68. package/dist/prompts/orchestrator-role.js.map +1 -0
  69. package/dist/prompts/tool-discipline.js +33 -0
  70. package/dist/prompts/tool-discipline.js.map +1 -0
  71. package/dist/repo.js +15 -0
  72. package/dist/repo.js.map +1 -0
  73. package/dist/shell-quote.js +7 -0
  74. package/dist/shell-quote.js.map +1 -0
  75. package/dist/state-updaters.js +81 -0
  76. package/dist/state-updaters.js.map +1 -0
  77. package/dist/state.js +38 -0
  78. package/dist/state.js.map +1 -0
  79. package/dist/state.types.js +15 -0
  80. package/dist/state.types.js.map +1 -0
  81. package/dist/tmux.js +171 -0
  82. package/dist/tmux.js.map +1 -0
  83. package/dist/tui/App.js +428 -0
  84. package/dist/tui/App.js.map +1 -0
  85. package/dist/tui/AppView.js +13 -0
  86. package/dist/tui/AppView.js.map +1 -0
  87. package/dist/tui/Auditorium.js +17 -0
  88. package/dist/tui/Auditorium.js.map +1 -0
  89. package/dist/tui/ConcertHall.js +11 -0
  90. package/dist/tui/ConcertHall.js.map +1 -0
  91. package/dist/tui/Help.js +49 -0
  92. package/dist/tui/Help.js.map +1 -0
  93. package/dist/tui/OrchestratorPane.js +34 -0
  94. package/dist/tui/OrchestratorPane.js.map +1 -0
  95. package/dist/tui/SidebarHeader.js +6 -0
  96. package/dist/tui/SidebarHeader.js.map +1 -0
  97. package/dist/tui/StatusBar.js +6 -0
  98. package/dist/tui/StatusBar.js.map +1 -0
  99. package/dist/tui/activity-line.js +16 -0
  100. package/dist/tui/activity-line.js.map +1 -0
  101. package/dist/tui/detect-permission.js +81 -0
  102. package/dist/tui/detect-permission.js.map +1 -0
  103. package/dist/tui/embedded-session-lifecycle.js +29 -0
  104. package/dist/tui/embedded-session-lifecycle.js.map +1 -0
  105. package/dist/tui/embedded-terminal.js +247 -0
  106. package/dist/tui/embedded-terminal.js.map +1 -0
  107. package/dist/tui/format-time.js +26 -0
  108. package/dist/tui/format-time.js.map +1 -0
  109. package/dist/tui/keymap.js +69 -0
  110. package/dist/tui/keymap.js.map +1 -0
  111. package/dist/tui/poll-activity.js +25 -0
  112. package/dist/tui/poll-activity.js.map +1 -0
  113. package/dist/tui/poll-idle.js +108 -0
  114. package/dist/tui/poll-idle.js.map +1 -0
  115. package/dist/tui/poll-permission.js +41 -0
  116. package/dist/tui/poll-permission.js.map +1 -0
  117. package/dist/tui/status-icon.js +33 -0
  118. package/dist/tui/status-icon.js.map +1 -0
  119. package/dist/tui/terminal-input.js +106 -0
  120. package/dist/tui/terminal-input.js.map +1 -0
  121. package/dist/tui/watch-state.js +33 -0
  122. package/dist/tui/watch-state.js.map +1 -0
  123. package/dist/worktree.js +25 -0
  124. package/dist/worktree.js.map +1 -0
  125. package/docs/plans/2026-05-29-nfo-phase-1-bootstrap.md +2152 -0
  126. package/docs/plans/2026-05-29-nfo-phase-2-mcp-musicians.md +2467 -0
  127. package/docs/plans/2026-05-29-nfo-phase-3-ink-tui.md +1611 -0
  128. package/docs/plans/2026-05-29-nfo-phase-4-permission-prompts.md +460 -0
  129. package/docs/plans/2026-05-29-nfo-phase-5-help-and-notify.md +933 -0
  130. package/docs/specs/2026-05-29-nfo-design.md +468 -0
  131. package/package.json +41 -0
  132. package/src/claude-command.ts +35 -0
  133. package/src/claude-detect.ts +42 -0
  134. package/src/cli.ts +164 -0
  135. package/src/commands/attach.ts +24 -0
  136. package/src/commands/dashboard-window.ts +33 -0
  137. package/src/commands/kill.ts +50 -0
  138. package/src/commands/launch.ts +134 -0
  139. package/src/commands/list.ts +43 -0
  140. package/src/commands/mcp-server.ts +18 -0
  141. package/src/commands/notes.ts +18 -0
  142. package/src/commands/restore.ts +153 -0
  143. package/src/commands/tui.tsx +16 -0
  144. package/src/config.ts +44 -0
  145. package/src/dashboard.ts +1 -0
  146. package/src/mcp/config.ts +39 -0
  147. package/src/mcp/handlers.ts +141 -0
  148. package/src/mcp/server.ts +50 -0
  149. package/src/mcp/tool-defs.ts +151 -0
  150. package/src/musicians/dismiss.ts +60 -0
  151. package/src/musicians/ids.ts +21 -0
  152. package/src/musicians/lookup.ts +13 -0
  153. package/src/musicians/message-log.ts +152 -0
  154. package/src/musicians/message.ts +99 -0
  155. package/src/musicians/query.ts +19 -0
  156. package/src/musicians/spawn.ts +139 -0
  157. package/src/notes.ts +39 -0
  158. package/src/notify.ts +62 -0
  159. package/src/orchestrator/report-back.ts +33 -0
  160. package/src/permission.ts +30 -0
  161. package/src/project-key.ts +12 -0
  162. package/src/prompts/musician-role.ts +22 -0
  163. package/src/prompts/orchestrator-role.ts +60 -0
  164. package/src/prompts/tool-discipline.ts +35 -0
  165. package/src/repo.ts +14 -0
  166. package/src/shell-quote.ts +7 -0
  167. package/src/state-updaters.ts +132 -0
  168. package/src/state.ts +49 -0
  169. package/src/state.types.ts +67 -0
  170. package/src/tmux.ts +226 -0
  171. package/src/tui/App.tsx +532 -0
  172. package/src/tui/AppView.tsx +96 -0
  173. package/src/tui/Auditorium.tsx +56 -0
  174. package/src/tui/ConcertHall.tsx +31 -0
  175. package/src/tui/Help.tsx +72 -0
  176. package/src/tui/OrchestratorPane.tsx +98 -0
  177. package/src/tui/SidebarHeader.tsx +32 -0
  178. package/src/tui/StatusBar.tsx +44 -0
  179. package/src/tui/activity-line.ts +16 -0
  180. package/src/tui/detect-permission.ts +93 -0
  181. package/src/tui/embedded-session-lifecycle.ts +44 -0
  182. package/src/tui/embedded-terminal.ts +325 -0
  183. package/src/tui/format-time.ts +25 -0
  184. package/src/tui/keymap.ts +104 -0
  185. package/src/tui/poll-activity.ts +25 -0
  186. package/src/tui/poll-idle.ts +149 -0
  187. package/src/tui/poll-permission.ts +50 -0
  188. package/src/tui/status-icon.ts +35 -0
  189. package/src/tui/terminal-input.ts +136 -0
  190. package/src/tui/watch-state.ts +43 -0
  191. package/src/worktree.ts +41 -0
  192. package/tests/claude-command.test.ts +30 -0
  193. package/tests/claude-detect.test.ts +14 -0
  194. package/tests/commands/attach.test.ts +60 -0
  195. package/tests/commands/kill.test.ts +66 -0
  196. package/tests/commands/launch.test.ts +75 -0
  197. package/tests/commands/list.test.ts +47 -0
  198. package/tests/commands/notes.test.ts +53 -0
  199. package/tests/commands/restore.test.ts +126 -0
  200. package/tests/helpers/tmp-config.ts +16 -0
  201. package/tests/helpers/tmp-repo.ts +29 -0
  202. package/tests/integration/orchestrator-spawn.test.ts +108 -0
  203. package/tests/mcp/handlers.test.ts +163 -0
  204. package/tests/mcp/tool-defs.test.ts +35 -0
  205. package/tests/musicians/dismiss.test.ts +102 -0
  206. package/tests/musicians/message.test.ts +159 -0
  207. package/tests/musicians/query.test.ts +65 -0
  208. package/tests/musicians/spawn.test.ts +125 -0
  209. package/tests/notes.test.ts +56 -0
  210. package/tests/notify.test.ts +80 -0
  211. package/tests/orchestrator/report-back.test.ts +18 -0
  212. package/tests/permission.test.ts +29 -0
  213. package/tests/project-key.test.ts +33 -0
  214. package/tests/prompts/tool-discipline.test.ts +25 -0
  215. package/tests/repo.test.ts +38 -0
  216. package/tests/state-updaters.test.ts +126 -0
  217. package/tests/state.test.ts +85 -0
  218. package/tests/tmux.test.ts +126 -0
  219. package/tests/tui/AppView.test.tsx +92 -0
  220. package/tests/tui/Auditorium.test.tsx +67 -0
  221. package/tests/tui/ConcertHall.test.tsx +22 -0
  222. package/tests/tui/Help.test.tsx +38 -0
  223. package/tests/tui/OrchestratorPane.test.ts +30 -0
  224. package/tests/tui/SidebarHeader.test.tsx +20 -0
  225. package/tests/tui/StatusBar.test.tsx +51 -0
  226. package/tests/tui/activity-line.test.ts +21 -0
  227. package/tests/tui/detect-permission.test.ts +92 -0
  228. package/tests/tui/embedded-session-lifecycle.test.ts +55 -0
  229. package/tests/tui/embedded-terminal.test.ts +80 -0
  230. package/tests/tui/format-time.test.ts +25 -0
  231. package/tests/tui/keymap.test.ts +93 -0
  232. package/tests/tui/poll-activity.test.ts +81 -0
  233. package/tests/tui/poll-idle.test.ts +159 -0
  234. package/tests/tui/poll-permission.test.ts +222 -0
  235. package/tests/tui/status-icon.test.ts +27 -0
  236. package/tests/tui/terminal-input.test.ts +113 -0
  237. package/tests/tui/watch-state.test.ts +54 -0
  238. package/tests/worktree.test.ts +73 -0
  239. package/tsconfig.json +19 -0
  240. package/vitest.config.ts +12 -0
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # NFO — No Fluff Orchestra
2
+
3
+ > [!WARNING]
4
+ > NFO Is highly experimental, things can break, might not work as expected or the composed music might suck!
5
+
6
+ <p style="text-align: center"><img style="height:100%" src="assets/orche-clawd.png"></p>
7
+
8
+ > (Yes, I know orchestrators don't have staffs - but look how happy he is)
9
+
10
+ A simple, "no fluff" orchestrator that doesn't overcomplicate your workflow. Tell the **Orchestrator** what you want to do and he will spread the work across multiple **Musicians**!
11
+
12
+ > [!NOTE]
13
+ > I am not a musician. I do not take responsibility for any misused musical terminology!
14
+
15
+ ## Why?
16
+ Honestly? Because I wanted to see if I could do it. Apart from that, the main reason why NFO exists is because I wanted an agent orchestrator that is not too bloated and allows you to see your agents without obfuscation. This is a learning project for me to understand agents better and to learn some (weird flavor of) React.
17
+
18
+ I wrote it by hand and with Claude - MVP was built almost entirely by Claude then I overtook and refactoring work along with new features are hand-built by me.
19
+
20
+ ## How does it work?
21
+
22
+ Just start NFO in your project's folder and you are good to go!
23
+
24
+ ```bash
25
+ nfo
26
+ ```
27
+
28
+ This will create an **Orchestra** with your project's name and an **Orchestrator**. From here on out this Orchestra will be running until you kill it - this means that you can fire away your tasks and work on something else in the meantime.
29
+
30
+ Everything that you have in your regular Claude session is available in your NFO session. Just ask your Orchestrator what you want to do, and it will do it for you by spawning several **Musicians** to tackle the task.
31
+
32
+ ![main-screen](./assets/main-screen.png)
33
+
34
+ These Musicians are either `Sonnet` or `Haiku` agents used to explore your codebase or code. Using **Hub-and-Spoke** the agents communicate with the Orchestrator and the Orchestrator decides the next steps. This structure allows the smarter model to keep its context clean and save cost by using other models at what they are best. They are all assigned to your Orchestra and you can freely move between them, see what they are doing or steer them by using `/btw`. You can also ask your Orchestrator to tell them what to do!
35
+
36
+ ![agent-screen](./assets/agent-screen.png)
37
+
38
+ They can work in parallel or sequentially, depending on what the task demands. NFO leverages **worktrees** to keep your environment clean and avoid agents stepping on each other's toes.
39
+
40
+ ### Notes, notes notes!
41
+ Notes are automatically generated by the Musicians and the Orchestrator to keep track of what has been done so far in the codebase, keep track of quirks and pitfalls. This serves as a quasi memory and helps the agents work in a continuous workflow.
42
+
43
+ ### I like control
44
+ During the entirety of the process you decide how much you want to be in control. You want fully automated? You got it. You want to approve some non-whitelisted requests? Do it. Have granular control and approve everything by hand? Whatever floats your boat.
45
+
46
+ Communication happens through Tmux and an MCP server, the Ink based UI wraps Claude Code and provides it for you for interaction.
47
+
48
+ ### I am ~~no~~ Superman
49
+
50
+ `Superpowers` are supported and the skill knows that instead of regular subagents it should deploy Musicians to finish the tasks. It also recognizes that some tasks can be tackled sequentially or parallel as well.
51
+
52
+ ### I had enough, let me go.
53
+
54
+ You can dismantle your currently running Orchestras by simply running
55
+
56
+ ``` bash
57
+ nfo --kill <orchestra-id>
58
+ ```
59
+
60
+
61
+
62
+ ## Limitations
63
+ NFO is currently very experimental. There are many things that will change, improve or well, break.
64
+
65
+ Here are the currently known limitations and what should come in the future:
66
+
67
+ - [ ] No package, NFO can only be run after being built (I am planning to ship through `npm` and `brew`)
68
+ - [ ] Worktrees are not always cleaned up properly
69
+ - [ ] Currently, the tools available to the musicians are not limited
70
+ - [ ] The notes section is broken
71
+ - [ ] Provide a simpler way to manage orchestras
72
+ - [ ] Performance can degrade over time
73
+ - [ ] The TUI can flicker because of the Node-PTY embedding
74
+ - [ ] The current Tmux based communication can be flaky
75
+
76
+ ## Requirements (These are bound to change as the project matures)
77
+
78
+ - Node.js 20+
79
+ - `tmux` on PATH
80
+ - `claude` (Claude Code CLI) on PATH, version ≥ 2.1 (see `src/claude-detect.ts`)
81
+ - `bash` or `zsh`
82
+ - Linux or macOS (Windows via WSL only for now)
83
+
84
+ ## Install (development)
85
+
86
+ ```
87
+ git clone https://github.com/javierfurus/nfo.git
88
+ cd nfo
89
+ npm install
90
+ npm run build
91
+ npm link # makes the `nfo` command globally available
92
+ ```
93
+
94
+ ## Use
95
+
96
+ In a git repo: `nfo`
97
+ List orchestras: `nfo list`
98
+ Attach by id: `nfo <id>`
99
+ Tear down: `nfo kill <id>`
100
+ Open notes: `nfo notes <id>`
101
+
102
+ ## Musicians
103
+
104
+ Inside an orchestra, the Orchestrator can use these MCP tools:
105
+
106
+ - `spawn_musician({ name, task })` — create a Musician in an isolated git worktree
107
+ - `message_musician({ musician_id, message })`
108
+ - `query_musician({ musician_id, lines? })` — read recent pane output
109
+ - `list_musicians()`
110
+ - `dismiss_musician({ musician_id, archive_worktree? })` — archived = worktree preserved under `archive/`, branch kept; dropped = worktree gone, branch deleted
111
+ - `report_done({ summary })` — called by Musicians on completion
112
+ - `note_write` / `note_read` / `note_list` — Orchestrator's persistent notes
113
+
114
+ To watch a Musician work just go to the sidebar (if not already there) by `Ctrl + g` and move the cursor to the Musician you want to observe. Press `Enter/Return` and you are in that session.
115
+
116
+ ## Copyright
117
+ Claude is a trademark owned by Anthropic.
118
+
119
+ The Claw'd art was done by me. Maybe this was the _real_ reason this project exists so that I could to that.
Binary file
Binary file
Binary file
@@ -0,0 +1,19 @@
1
+ import { shellQuote } from "./shell-quote.js";
2
+ export function buildClaudeCommand(opts) {
3
+ const args = ["claude", ...opts.flags];
4
+ if (opts.resumeSessionId) {
5
+ args.push("--resume", opts.resumeSessionId);
6
+ }
7
+ args.push("--mcp-config", opts.mcpConfigPath);
8
+ if (opts.promptFile) {
9
+ args.push("--append-system-prompt-file", opts.promptFile);
10
+ }
11
+ if (opts.model) {
12
+ args.push("--model", opts.model);
13
+ }
14
+ if (opts.prompt !== undefined) {
15
+ args.push(opts.prompt);
16
+ }
17
+ return args.map(shellQuote).join(" ");
18
+ }
19
+ //# sourceMappingURL=claude-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-command.js","sourceRoot":"","sources":["../src/claude-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAY9C,MAAM,UAAU,kBAAkB,CAAC,IAA+B;IAChE,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAEvC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAE9C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,6BAA6B,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { execa } from 'execa';
2
+ const MIN_MAJOR = 2;
3
+ const MIN_MINOR = 1;
4
+ export async function detectClaude() {
5
+ let stdout;
6
+ try {
7
+ const result = await execa('claude', ['--version']);
8
+ stdout = result.stdout;
9
+ }
10
+ catch (err) {
11
+ throw new Error(`Failed to run \`claude --version\`. Is Claude Code installed and on PATH?\nDetails: ${err.message}`);
12
+ }
13
+ // Match a semver-shaped substring in the output (claude prints e.g. "2.1.128 (Claude Code)").
14
+ const match = stdout.match(/(\d+)\.(\d+)\.(\d+)/);
15
+ if (!match) {
16
+ throw new Error(`Could not parse Claude Code version from output: ${stdout}`);
17
+ }
18
+ const [, majS, minS, patS] = match;
19
+ const major = Number(majS);
20
+ const minor = Number(minS);
21
+ const patch = Number(patS);
22
+ if (major < MIN_MAJOR || (major === MIN_MAJOR && minor < MIN_MINOR)) {
23
+ throw new Error(`NFO requires Claude Code ${MIN_MAJOR}.${MIN_MINOR}.0 or newer, found ${major}.${minor}.${patch}. ` +
24
+ `Run \`npm i -g @anthropic-ai/claude-code\` (or your package manager equivalent) to upgrade.`);
25
+ }
26
+ return { version: `${major}.${minor}.${patch}`, major, minor, patch };
27
+ }
28
+ //# sourceMappingURL=claude-detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-detect.js","sourceRoot":"","sources":["../src/claude-detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAS9B,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;QACpD,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,uFAAwF,GAAa,CAAC,OAAO,EAAE,CAChH,CAAC;IACJ,CAAC;IAED,8FAA8F;IAC9F,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oDAAoD,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAE3B,IAAI,KAAK,GAAG,SAAS,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,SAAS,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CACb,4BAA4B,SAAS,IAAI,SAAS,sBAAsB,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI;YACnG,6FAA6F,CAC9F,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACxE,CAAC"}
package/dist/cli.js ADDED
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { decideAction, createOrchestra } from './commands/launch.js';
4
+ import { attachOrRestore } from './commands/attach.js';
5
+ import { listOrchestras, formatOrchestraList } from './commands/list.js';
6
+ import { isPermissionLevel, AUTO_CONFIRM_PHRASE, AUTO_WARNING } from './permission.js';
7
+ import { detectClaude } from './claude-detect.js';
8
+ import { createInterface } from 'node:readline/promises';
9
+ const program = new Command();
10
+ program
11
+ .name('nfo')
12
+ .description('NoFluffOrchestra — TUI multi-agent orchestrator')
13
+ .version('0.0.0');
14
+ program
15
+ .argument('[id]', 'Orchestra id to attach (optional)')
16
+ .option('--notify-on-permission', 'bell + desktop notify when a musician awaits permission')
17
+ .action(async (id, opts) => {
18
+ await detectClaude();
19
+ try {
20
+ if (id) {
21
+ await attachOrRestore(id);
22
+ return;
23
+ }
24
+ const decision = await decideAction(process.cwd());
25
+ switch (decision.kind) {
26
+ case 'create': {
27
+ const level = await promptPermissionLevel();
28
+ await createOrchestra({
29
+ repoRoot: decision.repoRoot,
30
+ orchestraId: decision.orchestraId,
31
+ permissionLevel: level,
32
+ notifyOnPermission: opts.notifyOnPermission,
33
+ });
34
+ return;
35
+ }
36
+ case 'attach_existing':
37
+ await attachOrRestore(decision.orchestraId);
38
+ return;
39
+ case 'pick': {
40
+ const picked = await promptOrchestraPicker(decision.summaries);
41
+ await attachOrRestore(picked);
42
+ return;
43
+ }
44
+ case 'error':
45
+ console.error(decision.message);
46
+ process.exit(1);
47
+ }
48
+ }
49
+ catch (err) {
50
+ console.error(err instanceof Error ? err.message : String(err));
51
+ process.exit(1);
52
+ }
53
+ });
54
+ program
55
+ .command('list')
56
+ .description('List all known orchestras')
57
+ .action(async () => {
58
+ const summaries = await listOrchestras();
59
+ console.log(formatOrchestraList(summaries));
60
+ });
61
+ program
62
+ .command('restore <id>')
63
+ .description('Force-restore a stopped orchestra')
64
+ .option('--notify-on-permission', 'bell + desktop notify when a musician awaits permission')
65
+ .action(async (id, opts) => {
66
+ const { restoreOrchestra } = await import('./commands/restore.js');
67
+ await restoreOrchestra(id, undefined, opts.notifyOnPermission);
68
+ });
69
+ program
70
+ .command('kill <id>')
71
+ .description('Tear down an orchestra (state archived, notes preserved)')
72
+ .option('-y, --yes', 'Skip confirmation prompt')
73
+ .action(async (id, opts) => {
74
+ const { killOrchestra } = await import('./commands/kill.js');
75
+ await killOrchestra(id, opts);
76
+ });
77
+ program
78
+ .command('notes <id>')
79
+ .description('Open the orchestra\'s notes/ directory in $EDITOR')
80
+ .action(async (id) => {
81
+ const { openNotes } = await import('./commands/notes.js');
82
+ await openNotes(id);
83
+ });
84
+ program
85
+ .command('mcp-server', { hidden: true })
86
+ .description('(internal) Run the NFO MCP server attached to an orchestra')
87
+ .requiredOption('--orchestra-id <id>', 'Orchestra id')
88
+ .option('--caller-musician-id <id>', 'When the server is hosting a Musician')
89
+ .action(async (opts) => {
90
+ const { runMcpServerCli } = await import('./commands/mcp-server.js');
91
+ await runMcpServerCli({
92
+ orchestraId: opts.orchestraId,
93
+ callerMusicianId: opts.callerMusicianId,
94
+ });
95
+ });
96
+ program
97
+ .command('tui', { hidden: true })
98
+ .description('(internal) Run the NFO Ink TUI for an orchestra')
99
+ .requiredOption('--orchestra-id <id>', 'Orchestra id')
100
+ .action(async (opts) => {
101
+ const { runTui } = await import('./commands/tui.js');
102
+ await runTui({ orchestraId: opts.orchestraId });
103
+ });
104
+ program.parseAsync(process.argv);
105
+ async function promptPermissionLevel() {
106
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
107
+ try {
108
+ const ans = (await rl.question(`Permission level for this orchestra:
109
+ 1) auto — RISKY: bypasses all permission checks
110
+ 2) autonomous — auto-accept edits, prompt on risky tools
111
+ 3) supervised — claude's default prompt-on-risky behavior
112
+ 4) strict — read-only / plan mode
113
+ Choose [1-4] (default 3): `)).trim();
114
+ const map = {
115
+ '1': 'auto', '2': 'autonomous', '3': 'supervised', '4': 'strict', '': 'supervised',
116
+ };
117
+ const level = map[ans];
118
+ if (!level || !isPermissionLevel(level)) {
119
+ throw new Error(`Invalid choice: ${ans}`);
120
+ }
121
+ if (level === 'auto') {
122
+ console.log('\n' + AUTO_WARNING + '\n');
123
+ const confirm = (await rl.question('> ')).trim();
124
+ if (confirm !== AUTO_CONFIRM_PHRASE) {
125
+ throw new Error('Auto mode not confirmed. Aborting.');
126
+ }
127
+ }
128
+ return level;
129
+ }
130
+ finally {
131
+ rl.close();
132
+ }
133
+ }
134
+ async function promptOrchestraPicker(summaries) {
135
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
136
+ try {
137
+ console.log('Multiple orchestras found:');
138
+ summaries.forEach((s, i) => {
139
+ console.log(` ${i + 1}) ${s.running ? '●' : '○'} ${s.id} (${s.project_path})`);
140
+ });
141
+ const choice = (await rl.question('Pick one [1-N]: ')).trim();
142
+ const idx = Number(choice) - 1;
143
+ if (Number.isNaN(idx) || idx < 0 || idx >= summaries.length) {
144
+ throw new Error('Invalid choice');
145
+ }
146
+ return summaries[idx].id;
147
+ }
148
+ finally {
149
+ rl.close();
150
+ }
151
+ }
152
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { sessionExists, sessionName, attachSession, ensureNfoSessionUi, selectWindow } from '../tmux.js';
2
+ import { restoreOrchestra } from './restore.js';
3
+ import { readState } from '../state.js';
4
+ import { DASHBOARD_WINDOW_NAME } from '../dashboard.js';
5
+ import { ensureDashboardWindow, migrateLegacySidebarPane } from './dashboard-window.js';
6
+ export async function attachOrRestore(orchestraId, dryRun) {
7
+ const state = await readState(orchestraId);
8
+ if (!state)
9
+ throw new Error(`Unknown orchestra: ${orchestraId}`);
10
+ const name = sessionName(orchestraId);
11
+ if (await sessionExists(name)) {
12
+ await ensureNfoSessionUi(name);
13
+ await ensureDashboardWindow(name, state.project_path, orchestraId);
14
+ await migrateLegacySidebarPane(name);
15
+ if (!dryRun) {
16
+ await selectWindow(name, DASHBOARD_WINDOW_NAME);
17
+ await attachSession(name);
18
+ }
19
+ return { action: 'attached', orchestraId };
20
+ }
21
+ return restoreOrchestra(orchestraId, dryRun);
22
+ }
23
+ //# sourceMappingURL=attach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/commands/attach.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAExF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAmB,EAAE,MAAgB;IACzE,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,WAAW,EAAE,CAAC,CAAC;IAEjE,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,MAAM,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QACnE,MAAM,wBAAwB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,YAAY,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;YAChD,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { execa } from 'execa';
2
+ import { DASHBOARD_WINDOW_NAME } from '../dashboard.js';
3
+ import { createDetachedWindow, respawnPane, setPaneOption } from '../tmux.js';
4
+ import { shellQuote } from '../shell-quote.js';
5
+ function tuiCommand(orchestraId) {
6
+ const nfoBin = process.argv[1];
7
+ return `${shellQuote(nfoBin)} tui --orchestra-id ${shellQuote(orchestraId)}`;
8
+ }
9
+ export async function ensureDashboardWindow(session, cwd, orchestraId) {
10
+ await removeDashboardWindow(session);
11
+ const paneId = await createDetachedWindow(session, DASHBOARD_WINDOW_NAME, cwd);
12
+ await setPaneOption(paneId, 'remain-on-exit', 'on');
13
+ await respawnPane(paneId, tuiCommand(orchestraId));
14
+ }
15
+ export async function migrateLegacySidebarPane(session) {
16
+ await execa('tmux', ['kill-pane', '-t', `${session}:0.1`], { reject: false });
17
+ }
18
+ async function removeDashboardWindow(session) {
19
+ const { stdout } = await execa('tmux', ['list-windows', '-t', session, '-F', '#{window_name}']);
20
+ const names = stdout.split('\n').map((line) => { return line.trim(); }).filter(Boolean);
21
+ if (!names.includes(DASHBOARD_WINDOW_NAME)) {
22
+ return;
23
+ }
24
+ await execa('tmux', ['kill-window', '-t', `${session}:${DASHBOARD_WINDOW_NAME}`], { reject: false });
25
+ }
26
+ //# sourceMappingURL=dashboard-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-window.js","sourceRoot":"","sources":["../../src/commands/dashboard-window.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,SAAS,UAAU,CAAC,WAAmB;IACrC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,uBAAuB,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAe,EACf,GAAW,EACX,WAAmB;IAEnB,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,EAAE,GAAG,CAAC,CAAC;IAC/E,MAAM,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;IACpD,MAAM,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,OAAe;IAC5D,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,OAAO,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAChF,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,OAAe;IAClD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAChG,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxF,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;QAC3C,OAAO;IACT,CAAC;IACD,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,GAAG,OAAO,IAAI,qBAAqB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AACvG,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { rename, mkdir } from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { readState } from '../state.js';
6
+ import { sessionName, sessionExists, killSession, } from '../tmux.js';
7
+ import { archiveDir, stateFile } from '../config.js';
8
+ export async function killOrchestra(orchestraId, opts = {}) {
9
+ const state = await readState(orchestraId);
10
+ if (!state)
11
+ throw new Error(`Unknown orchestra: ${orchestraId}`);
12
+ if (!opts.yes) {
13
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
14
+ try {
15
+ const ans = (await rl.question(`Kill orchestra ${orchestraId} (${state.project_path})? [y/N] `)).trim().toLowerCase();
16
+ if (ans !== 'y' && ans !== 'yes') {
17
+ console.log('Aborted.');
18
+ return;
19
+ }
20
+ }
21
+ finally {
22
+ rl.close();
23
+ }
24
+ }
25
+ // Phase 1: no musicians (and therefore no worktrees to handle).
26
+ // Phase 2 will add the worktree-archive prompt.
27
+ const name = sessionName(orchestraId);
28
+ if (await sessionExists(name)) {
29
+ await killSession(name);
30
+ }
31
+ // Archive state.json under archive/state-<timestamp>.json so notes/ stays intact.
32
+ await mkdir(archiveDir(orchestraId), { recursive: true });
33
+ const archived = join(archiveDir(orchestraId), `state-${Date.now()}.json`);
34
+ if (existsSync(stateFile(orchestraId))) {
35
+ await rename(stateFile(orchestraId), archived);
36
+ }
37
+ }
38
+ //# sourceMappingURL=kill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill.js","sourceRoot":"","sources":["../../src/commands/kill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAMrD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,WAAmB,EAAE,OAAoB,EAAE;IAC7E,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,WAAW,EAAE,CAAC,CAAC;IAEjE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACd,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAC5B,kBAAkB,WAAW,KAAK,KAAK,CAAC,YAAY,WAAW,CAChE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACxB,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;gBACjC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBACxB,OAAO;YACT,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,gDAAgD;IAEhD,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,MAAM,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,kFAAkF;IAClF,MAAM,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,SAAS,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3E,IAAI,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;QACvC,MAAM,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAC;AACH,CAAC"}
@@ -0,0 +1,85 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { resolveRepoRoot } from '../repo.js';
4
+ import { projectKeyFromPath } from '../project-key.js';
5
+ import { ensureOrchestraDir, readState, writeState } from '../state.js';
6
+ import { makeInitialState } from '../state.types.js';
7
+ import { claudeFlagsForLevel, } from '../permission.js';
8
+ import { sessionName, createDetachedSession, attachSession, selectWindow, ensureNfoSessionUi, respawnPane, setPaneOption, } from '../tmux.js';
9
+ import { ORCHESTRATOR_ROLE_PROMPT_V1 } from '../prompts/orchestrator-role.js';
10
+ import { orchestraDir } from '../config.js';
11
+ import { listOrchestras } from './list.js';
12
+ import { noteRead, noteList } from '../notes.js';
13
+ import { DASHBOARD_WINDOW_NAME } from '../dashboard.js';
14
+ import { ensureDashboardWindow } from './dashboard-window.js';
15
+ import { buildClaudeCommand } from '../claude-command.js';
16
+ import { writeOrchestratorMcpConfig } from '../mcp/config.js';
17
+ export async function decideAction(cwd) {
18
+ const repoRoot = await resolveRepoRoot(cwd);
19
+ if (repoRoot) {
20
+ const orchestraId = projectKeyFromPath(repoRoot);
21
+ const existing = await readState(orchestraId);
22
+ if (existing) {
23
+ return { kind: 'attach_existing', orchestraId };
24
+ }
25
+ return { kind: 'create', orchestraId, repoRoot };
26
+ }
27
+ // Out of repo. Inspect known orchestras.
28
+ const summaries = await listOrchestras();
29
+ if (summaries.length === 0) {
30
+ return { kind: 'error', message: 'Open NFO in a git repository to create your first orchestra.' };
31
+ }
32
+ const running = summaries.filter(s => s.running);
33
+ if (running.length === 1) {
34
+ return { kind: 'attach_existing', orchestraId: running[0].id };
35
+ }
36
+ return { kind: 'pick', summaries };
37
+ }
38
+ export async function createOrchestra(opts) {
39
+ await ensureOrchestraDir(opts.orchestraId);
40
+ const state = makeInitialState({
41
+ orchestraId: opts.orchestraId,
42
+ projectPath: opts.repoRoot,
43
+ permissionLevel: opts.permissionLevel,
44
+ notifyOnPermission: opts.notifyOnPermission,
45
+ });
46
+ await writeState(opts.orchestraId, state);
47
+ const mcpConfigPath = await writeOrchestratorMcpConfig(opts.orchestraId);
48
+ const promptFile = join(orchestraDir(opts.orchestraId), 'orchestrator-prompt.md');
49
+ const notes = await loadOrchestratorNotes(opts.orchestraId);
50
+ await writeFile(promptFile, ORCHESTRATOR_ROLE_PROMPT_V1 + notes, 'utf8');
51
+ const name = sessionName(opts.orchestraId);
52
+ await createDetachedSession(name, opts.repoRoot);
53
+ await ensureNfoSessionUi(name);
54
+ await setPaneOption(`${name}:0`, 'remain-on-exit', 'on');
55
+ await ensureDashboardWindow(name, opts.repoRoot, opts.orchestraId);
56
+ const claudeFlags = claudeFlagsForLevel(opts.permissionLevel);
57
+ const claudeCmd = buildClaudeCommand({
58
+ flags: claudeFlags,
59
+ mcpConfigPath,
60
+ promptFile,
61
+ });
62
+ await respawnPane(`${name}:0`, claudeCmd);
63
+ if (!opts.dryRun) {
64
+ await selectWindow(name, DASHBOARD_WINDOW_NAME);
65
+ await attachSession(name);
66
+ }
67
+ return { action: 'created', orchestraId: opts.orchestraId };
68
+ }
69
+ export async function loadOrchestratorNotes(orchestraId) {
70
+ const files = await noteList(orchestraId);
71
+ const ordered = ['overview.md', 'decisions.md'].filter((f) => { return files.includes(f); });
72
+ if (ordered.length === 0) {
73
+ return '';
74
+ }
75
+ const parts = ['\n\n## Curated project notes (loaded from notes/)\n'];
76
+ for (const f of ordered) {
77
+ const content = await noteRead(orchestraId, f);
78
+ if (content.trim().length === 0) {
79
+ continue;
80
+ }
81
+ parts.push(`\n### ${f}\n\n${content}\n`);
82
+ }
83
+ return parts.join('');
84
+ }
85
+ //# sourceMappingURL=launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/commands/launch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { getProjectsDir } from '../config.js';
4
+ import { readState } from '../state.js';
5
+ import { sessionExists, sessionName } from '../tmux.js';
6
+ export async function listOrchestras() {
7
+ const projectsDir = getProjectsDir();
8
+ if (!existsSync(projectsDir))
9
+ return [];
10
+ const dirs = await readdir(projectsDir, { withFileTypes: true });
11
+ const summaries = [];
12
+ for (const d of dirs) {
13
+ if (!d.isDirectory())
14
+ continue;
15
+ const state = await readState(d.name);
16
+ if (!state)
17
+ continue;
18
+ summaries.push({
19
+ id: state.orchestra_id,
20
+ project_path: state.project_path,
21
+ permission_level: state.permission_level,
22
+ created_at: state.created_at,
23
+ running: await sessionExists(sessionName(state.orchestra_id)),
24
+ musician_count: state.musicians.length,
25
+ });
26
+ }
27
+ return summaries;
28
+ }
29
+ export function formatOrchestraList(summaries) {
30
+ if (summaries.length === 0)
31
+ return 'No orchestras found.';
32
+ const rows = summaries.map(s => `${s.running ? '●' : '○'} ${s.id}\n ${s.project_path}\n level=${s.permission_level} musicians=${s.musician_count}`);
33
+ return rows.join('\n\n');
34
+ }
35
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAWxD,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,MAAM,SAAS,GAAuB,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE;YAAE,SAAS;QAC/B,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,SAAS,CAAC,IAAI,CAAC;YACb,EAAE,EAAE,KAAK,CAAC,YAAY;YACtB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;YACxC,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO,EAAE,MAAM,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC7D,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM;SACvC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,SAA6B;IAC/D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,sBAAsB,CAAC;IAC1D,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAC7B,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,YAAY,cAAc,CAAC,CAAC,gBAAgB,cAAc,CAAC,CAAC,cAAc,EAAE,CACxH,CAAC;IACF,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { runServer } from '../mcp/server.js';
2
+ import { readState } from '../state.js';
3
+ export async function runMcpServerCli(opts) {
4
+ const state = await readState(opts.orchestraId);
5
+ if (!state) {
6
+ throw new Error(`Unknown orchestra: ${opts.orchestraId}`);
7
+ }
8
+ await runServer({
9
+ orchestraId: opts.orchestraId,
10
+ callerMusicianId: opts.callerMusicianId,
11
+ });
12
+ }
13
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../src/commands/mcp-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAOxC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAyB;IAC7D,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,SAAS,CAAC;QACd,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;KACxC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { execa } from 'execa';
3
+ import { notesDir } from '../config.js';
4
+ import { readState } from '../state.js';
5
+ export async function openNotes(orchestraId) {
6
+ const state = await readState(orchestraId);
7
+ if (!state)
8
+ throw new Error(`Unknown orchestra: ${orchestraId}`);
9
+ const dir = notesDir(orchestraId);
10
+ if (!existsSync(dir)) {
11
+ throw new Error(`Notes directory missing for ${orchestraId}: ${dir}`);
12
+ }
13
+ const editor = process.env.EDITOR ?? 'vi';
14
+ // Open the dir, not a specific file — the user picks which note to edit.
15
+ await execa(editor, [dir], { stdio: 'inherit' });
16
+ }
17
+ //# sourceMappingURL=notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.js","sourceRoot":"","sources":["../../src/commands/notes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,WAAmB;IACjD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,WAAW,EAAE,CAAC,CAAC;IAEjE,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,+BAA+B,WAAW,KAAK,GAAG,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC;IAC1C,yEAAyE;IACzE,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;AACnD,CAAC"}