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.
- package/README.md +119 -0
- package/assets/agent-screen.png +0 -0
- package/assets/main-screen.png +0 -0
- package/assets/orche-clawd.png +0 -0
- package/dist/claude-command.js +19 -0
- package/dist/claude-command.js.map +1 -0
- package/dist/claude-detect.js +28 -0
- package/dist/claude-detect.js.map +1 -0
- package/dist/cli.js +152 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/attach.js +23 -0
- package/dist/commands/attach.js.map +1 -0
- package/dist/commands/dashboard-window.js +26 -0
- package/dist/commands/dashboard-window.js.map +1 -0
- package/dist/commands/kill.js +38 -0
- package/dist/commands/kill.js.map +1 -0
- package/dist/commands/launch.js +85 -0
- package/dist/commands/launch.js.map +1 -0
- package/dist/commands/list.js +35 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp-server.js +13 -0
- package/dist/commands/mcp-server.js.map +1 -0
- package/dist/commands/notes.js +17 -0
- package/dist/commands/notes.js.map +1 -0
- package/dist/commands/restore.js +111 -0
- package/dist/commands/restore.js.map +1 -0
- package/dist/commands/tui.js +13 -0
- package/dist/commands/tui.js.map +1 -0
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard.js +2 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/mcp/config.js +34 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/handlers.js +117 -0
- package/dist/mcp/handlers.js.map +1 -0
- package/dist/mcp/server.js +32 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool-defs.js +132 -0
- package/dist/mcp/tool-defs.js.map +1 -0
- package/dist/musicians/dismiss.js +50 -0
- package/dist/musicians/dismiss.js.map +1 -0
- package/dist/musicians/ids.js +20 -0
- package/dist/musicians/ids.js.map +1 -0
- package/dist/musicians/lookup.js +11 -0
- package/dist/musicians/lookup.js.map +1 -0
- package/dist/musicians/message-log.js +89 -0
- package/dist/musicians/message-log.js.map +1 -0
- package/dist/musicians/message.js +54 -0
- package/dist/musicians/message.js.map +1 -0
- package/dist/musicians/query.js +13 -0
- package/dist/musicians/query.js.map +1 -0
- package/dist/musicians/spawn.js +102 -0
- package/dist/musicians/spawn.js.map +1 -0
- package/dist/notes.js +32 -0
- package/dist/notes.js.map +1 -0
- package/dist/notify.js +51 -0
- package/dist/notify.js.map +1 -0
- package/dist/orchestrator/report-back.js +21 -0
- package/dist/orchestrator/report-back.js.map +1 -0
- package/dist/permission.js +26 -0
- package/dist/permission.js.map +1 -0
- package/dist/project-key.js +12 -0
- package/dist/project-key.js.map +1 -0
- package/dist/prompts/musician-role.js +22 -0
- package/dist/prompts/musician-role.js.map +1 -0
- package/dist/prompts/orchestrator-role.js +62 -0
- package/dist/prompts/orchestrator-role.js.map +1 -0
- package/dist/prompts/tool-discipline.js +33 -0
- package/dist/prompts/tool-discipline.js.map +1 -0
- package/dist/repo.js +15 -0
- package/dist/repo.js.map +1 -0
- package/dist/shell-quote.js +7 -0
- package/dist/shell-quote.js.map +1 -0
- package/dist/state-updaters.js +81 -0
- package/dist/state-updaters.js.map +1 -0
- package/dist/state.js +38 -0
- package/dist/state.js.map +1 -0
- package/dist/state.types.js +15 -0
- package/dist/state.types.js.map +1 -0
- package/dist/tmux.js +171 -0
- package/dist/tmux.js.map +1 -0
- package/dist/tui/App.js +428 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/AppView.js +13 -0
- package/dist/tui/AppView.js.map +1 -0
- package/dist/tui/Auditorium.js +17 -0
- package/dist/tui/Auditorium.js.map +1 -0
- package/dist/tui/ConcertHall.js +11 -0
- package/dist/tui/ConcertHall.js.map +1 -0
- package/dist/tui/Help.js +49 -0
- package/dist/tui/Help.js.map +1 -0
- package/dist/tui/OrchestratorPane.js +34 -0
- package/dist/tui/OrchestratorPane.js.map +1 -0
- package/dist/tui/SidebarHeader.js +6 -0
- package/dist/tui/SidebarHeader.js.map +1 -0
- package/dist/tui/StatusBar.js +6 -0
- package/dist/tui/StatusBar.js.map +1 -0
- package/dist/tui/activity-line.js +16 -0
- package/dist/tui/activity-line.js.map +1 -0
- package/dist/tui/detect-permission.js +81 -0
- package/dist/tui/detect-permission.js.map +1 -0
- package/dist/tui/embedded-session-lifecycle.js +29 -0
- package/dist/tui/embedded-session-lifecycle.js.map +1 -0
- package/dist/tui/embedded-terminal.js +247 -0
- package/dist/tui/embedded-terminal.js.map +1 -0
- package/dist/tui/format-time.js +26 -0
- package/dist/tui/format-time.js.map +1 -0
- package/dist/tui/keymap.js +69 -0
- package/dist/tui/keymap.js.map +1 -0
- package/dist/tui/poll-activity.js +25 -0
- package/dist/tui/poll-activity.js.map +1 -0
- package/dist/tui/poll-idle.js +108 -0
- package/dist/tui/poll-idle.js.map +1 -0
- package/dist/tui/poll-permission.js +41 -0
- package/dist/tui/poll-permission.js.map +1 -0
- package/dist/tui/status-icon.js +33 -0
- package/dist/tui/status-icon.js.map +1 -0
- package/dist/tui/terminal-input.js +106 -0
- package/dist/tui/terminal-input.js.map +1 -0
- package/dist/tui/watch-state.js +33 -0
- package/dist/tui/watch-state.js.map +1 -0
- package/dist/worktree.js +25 -0
- package/dist/worktree.js.map +1 -0
- package/docs/plans/2026-05-29-nfo-phase-1-bootstrap.md +2152 -0
- package/docs/plans/2026-05-29-nfo-phase-2-mcp-musicians.md +2467 -0
- package/docs/plans/2026-05-29-nfo-phase-3-ink-tui.md +1611 -0
- package/docs/plans/2026-05-29-nfo-phase-4-permission-prompts.md +460 -0
- package/docs/plans/2026-05-29-nfo-phase-5-help-and-notify.md +933 -0
- package/docs/specs/2026-05-29-nfo-design.md +468 -0
- package/package.json +41 -0
- package/src/claude-command.ts +35 -0
- package/src/claude-detect.ts +42 -0
- package/src/cli.ts +164 -0
- package/src/commands/attach.ts +24 -0
- package/src/commands/dashboard-window.ts +33 -0
- package/src/commands/kill.ts +50 -0
- package/src/commands/launch.ts +134 -0
- package/src/commands/list.ts +43 -0
- package/src/commands/mcp-server.ts +18 -0
- package/src/commands/notes.ts +18 -0
- package/src/commands/restore.ts +153 -0
- package/src/commands/tui.tsx +16 -0
- package/src/config.ts +44 -0
- package/src/dashboard.ts +1 -0
- package/src/mcp/config.ts +39 -0
- package/src/mcp/handlers.ts +141 -0
- package/src/mcp/server.ts +50 -0
- package/src/mcp/tool-defs.ts +151 -0
- package/src/musicians/dismiss.ts +60 -0
- package/src/musicians/ids.ts +21 -0
- package/src/musicians/lookup.ts +13 -0
- package/src/musicians/message-log.ts +152 -0
- package/src/musicians/message.ts +99 -0
- package/src/musicians/query.ts +19 -0
- package/src/musicians/spawn.ts +139 -0
- package/src/notes.ts +39 -0
- package/src/notify.ts +62 -0
- package/src/orchestrator/report-back.ts +33 -0
- package/src/permission.ts +30 -0
- package/src/project-key.ts +12 -0
- package/src/prompts/musician-role.ts +22 -0
- package/src/prompts/orchestrator-role.ts +60 -0
- package/src/prompts/tool-discipline.ts +35 -0
- package/src/repo.ts +14 -0
- package/src/shell-quote.ts +7 -0
- package/src/state-updaters.ts +132 -0
- package/src/state.ts +49 -0
- package/src/state.types.ts +67 -0
- package/src/tmux.ts +226 -0
- package/src/tui/App.tsx +532 -0
- package/src/tui/AppView.tsx +96 -0
- package/src/tui/Auditorium.tsx +56 -0
- package/src/tui/ConcertHall.tsx +31 -0
- package/src/tui/Help.tsx +72 -0
- package/src/tui/OrchestratorPane.tsx +98 -0
- package/src/tui/SidebarHeader.tsx +32 -0
- package/src/tui/StatusBar.tsx +44 -0
- package/src/tui/activity-line.ts +16 -0
- package/src/tui/detect-permission.ts +93 -0
- package/src/tui/embedded-session-lifecycle.ts +44 -0
- package/src/tui/embedded-terminal.ts +325 -0
- package/src/tui/format-time.ts +25 -0
- package/src/tui/keymap.ts +104 -0
- package/src/tui/poll-activity.ts +25 -0
- package/src/tui/poll-idle.ts +149 -0
- package/src/tui/poll-permission.ts +50 -0
- package/src/tui/status-icon.ts +35 -0
- package/src/tui/terminal-input.ts +136 -0
- package/src/tui/watch-state.ts +43 -0
- package/src/worktree.ts +41 -0
- package/tests/claude-command.test.ts +30 -0
- package/tests/claude-detect.test.ts +14 -0
- package/tests/commands/attach.test.ts +60 -0
- package/tests/commands/kill.test.ts +66 -0
- package/tests/commands/launch.test.ts +75 -0
- package/tests/commands/list.test.ts +47 -0
- package/tests/commands/notes.test.ts +53 -0
- package/tests/commands/restore.test.ts +126 -0
- package/tests/helpers/tmp-config.ts +16 -0
- package/tests/helpers/tmp-repo.ts +29 -0
- package/tests/integration/orchestrator-spawn.test.ts +108 -0
- package/tests/mcp/handlers.test.ts +163 -0
- package/tests/mcp/tool-defs.test.ts +35 -0
- package/tests/musicians/dismiss.test.ts +102 -0
- package/tests/musicians/message.test.ts +159 -0
- package/tests/musicians/query.test.ts +65 -0
- package/tests/musicians/spawn.test.ts +125 -0
- package/tests/notes.test.ts +56 -0
- package/tests/notify.test.ts +80 -0
- package/tests/orchestrator/report-back.test.ts +18 -0
- package/tests/permission.test.ts +29 -0
- package/tests/project-key.test.ts +33 -0
- package/tests/prompts/tool-discipline.test.ts +25 -0
- package/tests/repo.test.ts +38 -0
- package/tests/state-updaters.test.ts +126 -0
- package/tests/state.test.ts +85 -0
- package/tests/tmux.test.ts +126 -0
- package/tests/tui/AppView.test.tsx +92 -0
- package/tests/tui/Auditorium.test.tsx +67 -0
- package/tests/tui/ConcertHall.test.tsx +22 -0
- package/tests/tui/Help.test.tsx +38 -0
- package/tests/tui/OrchestratorPane.test.ts +30 -0
- package/tests/tui/SidebarHeader.test.tsx +20 -0
- package/tests/tui/StatusBar.test.tsx +51 -0
- package/tests/tui/activity-line.test.ts +21 -0
- package/tests/tui/detect-permission.test.ts +92 -0
- package/tests/tui/embedded-session-lifecycle.test.ts +55 -0
- package/tests/tui/embedded-terminal.test.ts +80 -0
- package/tests/tui/format-time.test.ts +25 -0
- package/tests/tui/keymap.test.ts +93 -0
- package/tests/tui/poll-activity.test.ts +81 -0
- package/tests/tui/poll-idle.test.ts +159 -0
- package/tests/tui/poll-permission.test.ts +222 -0
- package/tests/tui/status-icon.test.ts +27 -0
- package/tests/tui/terminal-input.test.ts +113 -0
- package/tests/tui/watch-state.test.ts +54 -0
- package/tests/worktree.test.ts +73 -0
- package/tsconfig.json +19 -0
- 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
|
+

|
|
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
|
+

|
|
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
|
package/dist/cli.js.map
ADDED
|
@@ -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"}
|