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
@@ -0,0 +1,89 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { appendFile, mkdir, readFile } from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { messageLogsDir } from '../config.js';
6
+ function messageLogFile(orchestraId, musicianId) {
7
+ return join(messageLogsDir(orchestraId), `${musicianId}.jsonl`);
8
+ }
9
+ async function appendEvent(orchestraId, musicianId, event) {
10
+ const dir = messageLogsDir(orchestraId);
11
+ await mkdir(dir, { recursive: true });
12
+ await appendFile(messageLogFile(orchestraId, musicianId), JSON.stringify(event) + '\n', 'utf8');
13
+ }
14
+ async function readEvents(orchestraId, musicianId) {
15
+ const file = messageLogFile(orchestraId, musicianId);
16
+ if (!existsSync(file)) {
17
+ return [];
18
+ }
19
+ const raw = await readFile(file, 'utf8');
20
+ return raw
21
+ .split('\n')
22
+ .map((line) => { return line.trim(); })
23
+ .filter((line) => { return line.length > 0; })
24
+ .map((line) => { return JSON.parse(line); });
25
+ }
26
+ export async function queueMusicianMessage(orchestraId, musicianId, message) {
27
+ const createdAt = new Date().toISOString();
28
+ const messageId = `${Date.now()}-${randomUUID()}`;
29
+ await appendEvent(orchestraId, musicianId, {
30
+ type: 'message_queued',
31
+ message_id: messageId,
32
+ musician_id: musicianId,
33
+ message,
34
+ created_at: createdAt,
35
+ });
36
+ return { messageId, createdAt };
37
+ }
38
+ export async function markMusicianMessageDelivered(orchestraId, musicianId, messageId, delivery, deliveredAt = new Date().toISOString()) {
39
+ await appendEvent(orchestraId, musicianId, {
40
+ type: 'message_delivered',
41
+ message_id: messageId,
42
+ delivered_at: deliveredAt,
43
+ delivery,
44
+ });
45
+ }
46
+ export async function listPendingMusicianMessages(orchestraId, musicianId) {
47
+ const events = await readEvents(orchestraId, musicianId);
48
+ const pending = new Map();
49
+ for (const event of events) {
50
+ if (event.type === 'message_queued') {
51
+ pending.set(event.message_id, {
52
+ messageId: event.message_id,
53
+ musicianId: event.musician_id,
54
+ message: event.message,
55
+ createdAt: event.created_at,
56
+ });
57
+ continue;
58
+ }
59
+ pending.delete(event.message_id);
60
+ }
61
+ return [...pending.values()].sort((left, right) => {
62
+ return left.createdAt.localeCompare(right.createdAt);
63
+ });
64
+ }
65
+ export async function countPendingMusicianMessages(orchestraId, musicianId) {
66
+ const pending = await listPendingMusicianMessages(orchestraId, musicianId);
67
+ return pending.length;
68
+ }
69
+ export function formatQueuedMusicianMessages(messages) {
70
+ if (messages.length === 0) {
71
+ return '';
72
+ }
73
+ if (messages.length === 1) {
74
+ return messages[0].message;
75
+ }
76
+ const lines = [
77
+ 'NFO queued follow-up messages while you were busy:',
78
+ '',
79
+ ];
80
+ messages.forEach((message, index) => {
81
+ lines.push(`${index + 1}.`);
82
+ lines.push(message.message);
83
+ if (index < messages.length - 1) {
84
+ lines.push('');
85
+ }
86
+ });
87
+ return lines.join('\n');
88
+ }
89
+ //# sourceMappingURL=message-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-log.js","sourceRoot":"","sources":["../../src/musicians/message-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { sendKeys, sessionName } from '../tmux.js';
2
+ import { readState } from '../state.js';
3
+ import { findMusicianStrict } from './lookup.js';
4
+ import { setMusicianStatus, touchMusicianActivity, } from '../state-updaters.js';
5
+ import { countPendingMusicianMessages, formatQueuedMusicianMessages, listPendingMusicianMessages, markMusicianMessageDelivered, queueMusicianMessage, } from './message-log.js';
6
+ async function deliverPendingMessages(orchestraId, musicianId, delivery) {
7
+ const state = await readState(orchestraId);
8
+ if (!state) {
9
+ throw new Error(`Unknown orchestra: ${orchestraId}`);
10
+ }
11
+ const musician = findMusicianStrict(state, musicianId);
12
+ const pending = await listPendingMusicianMessages(orchestraId, musicianId);
13
+ if (pending.length === 0) {
14
+ return 0;
15
+ }
16
+ const target = `${sessionName(orchestraId)}:${musician.tmux_window_id}`;
17
+ const message = formatQueuedMusicianMessages(pending);
18
+ await sendKeys(target, message, true);
19
+ const deliveredAt = new Date().toISOString();
20
+ for (const pendingMessage of pending) {
21
+ await markMusicianMessageDelivered(orchestraId, musicianId, pendingMessage.messageId, delivery, deliveredAt);
22
+ }
23
+ await touchMusicianActivity(orchestraId, musicianId, deliveredAt);
24
+ return pending.length;
25
+ }
26
+ export async function messageMusician(opts) {
27
+ const state = await readState(opts.orchestraId);
28
+ if (!state) {
29
+ throw new Error(`Unknown orchestra: ${opts.orchestraId}`);
30
+ }
31
+ const musician = findMusicianStrict(state, opts.musicianId);
32
+ const queued = await queueMusicianMessage(opts.orchestraId, opts.musicianId, opts.message);
33
+ if (musician.status === 'idle') {
34
+ const delivery = (await countPendingMusicianMessages(opts.orchestraId, opts.musicianId)) === 1 ? 'immediate' : 'queued-drain';
35
+ await deliverPendingMessages(opts.orchestraId, opts.musicianId, delivery);
36
+ await setMusicianStatus(opts.orchestraId, opts.musicianId, 'working');
37
+ return {
38
+ ok: true,
39
+ delivery: 'immediate',
40
+ message_id: queued.messageId,
41
+ pending_messages: 0,
42
+ };
43
+ }
44
+ return {
45
+ ok: true,
46
+ delivery: 'queued',
47
+ message_id: queued.messageId,
48
+ pending_messages: await countPendingMusicianMessages(opts.orchestraId, opts.musicianId),
49
+ };
50
+ }
51
+ export async function drainQueuedMusicianMessages(orchestraId, musicianId) {
52
+ return deliverPendingMessages(orchestraId, musicianId, 'queued-drain');
53
+ }
54
+ //# sourceMappingURL=message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.js","sourceRoot":"","sources":["../../src/musicians/message.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { capturePane, sessionName } from '../tmux.js';
2
+ import { readState } from '../state.js';
3
+ import { findMusicianStrict } from './lookup.js';
4
+ export async function queryMusician(opts) {
5
+ const state = await readState(opts.orchestraId);
6
+ if (!state) {
7
+ throw new Error(`Unknown orchestra: ${opts.orchestraId}`);
8
+ }
9
+ const musician = findMusicianStrict(state, opts.musicianId);
10
+ const target = `${sessionName(opts.orchestraId)}:${musician.tmux_window_id}`;
11
+ return capturePane(target, opts.lines ?? 80);
12
+ }
13
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/musicians/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAQjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAA0B;IAC5D,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,QAAQ,GAAG,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;IAC7E,OAAO,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,102 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { execa } from "execa";
4
+ import { addMusician } from "../state-updaters.js";
5
+ import { readState } from "../state.js";
6
+ import { orchestraDir, worktreesDir } from "../config.js";
7
+ import { addWorktree } from "../worktree.js";
8
+ import { claudeFlagsForLevel } from "../permission.js";
9
+ import { respawnPane, sessionName, setPaneOption } from "../tmux.js";
10
+ import { MUSICIAN_ROLE_PROMPT_V1 } from "../prompts/musician-role.js";
11
+ import { buildMusicianInitialPrompt } from "../prompts/tool-discipline.js";
12
+ import { nextMusicianId } from "./ids.js";
13
+ import { buildClaudeCommand } from "../claude-command.js";
14
+ import { writeMusicianMcpConfig } from "../mcp/config.js";
15
+ export async function createMusician(opts) {
16
+ const state = await readState(opts.orchestraId);
17
+ if (!state) {
18
+ throw new Error(`Unknown orchestra: ${opts.orchestraId}`);
19
+ }
20
+ const musicianId = nextMusicianId(state);
21
+ const useWorktree = opts.worktree !== false;
22
+ let workingDir;
23
+ let worktreePath = null;
24
+ let branch = null;
25
+ if (useWorktree) {
26
+ worktreePath = join(worktreesDir(opts.orchestraId), musicianId);
27
+ branch = `nfo/${musicianId}`;
28
+ await addWorktree({
29
+ repoRoot: state.project_path,
30
+ path: worktreePath,
31
+ branch,
32
+ baseRef: opts.branchFrom,
33
+ });
34
+ workingDir = worktreePath;
35
+ }
36
+ else {
37
+ workingDir = state.project_path;
38
+ }
39
+ const promptFile = join(orchestraDir(opts.orchestraId), `musician-${musicianId}-prompt.md`);
40
+ await writeFile(promptFile, MUSICIAN_ROLE_PROMPT_V1, "utf8");
41
+ const session = sessionName(opts.orchestraId);
42
+ const winLabel = `mus-${musicianId}-${sanitiseName(opts.name)}`;
43
+ const { stdout: tmuxWindowId } = await execa("tmux", [
44
+ "new-window",
45
+ "-t",
46
+ session,
47
+ "-n",
48
+ winLabel,
49
+ "-c",
50
+ workingDir,
51
+ "-d",
52
+ "-P",
53
+ "-F",
54
+ "#{window_id}",
55
+ ]);
56
+ if (!opts.dryRun) {
57
+ const mcpConfigPath = await writeMusicianMcpConfig(opts.orchestraId, musicianId);
58
+ const flags = claudeFlagsForLevel(state.permission_level);
59
+ const cmd = buildClaudeCommand({
60
+ flags,
61
+ mcpConfigPath,
62
+ promptFile,
63
+ prompt: buildMusicianInitialPrompt(opts.task),
64
+ model: opts.model,
65
+ });
66
+ await setPaneOption(`${session}:${tmuxWindowId.trim()}`, "remain-on-exit", "on");
67
+ await respawnPane(`${session}:${tmuxWindowId.trim()}`, cmd);
68
+ }
69
+ else {
70
+ await writeMusicianMcpConfig(opts.orchestraId, musicianId);
71
+ }
72
+ const now = new Date().toISOString();
73
+ await addMusician(opts.orchestraId, {
74
+ id: musicianId,
75
+ name: opts.name,
76
+ task_summary: opts.task.slice(0, 200),
77
+ status: "working",
78
+ pending_permission: null,
79
+ tmux_window_id: tmuxWindowId.trim(),
80
+ claude_session_id: null,
81
+ worktree_path: worktreePath,
82
+ branch,
83
+ spawned_at: now,
84
+ last_activity: now,
85
+ model: opts.model,
86
+ });
87
+ return {
88
+ musician_id: musicianId,
89
+ worktree_path: worktreePath,
90
+ branch,
91
+ tmux_window_id: tmuxWindowId.trim(),
92
+ };
93
+ }
94
+ function sanitiseName(name) {
95
+ return (name
96
+ .toLowerCase()
97
+ .replace(/[^a-z0-9-]+/g, "-")
98
+ .replace(/-+/g, "-")
99
+ .replace(/^-|-$/g, "")
100
+ .slice(0, 32) || "musician");
101
+ }
102
+ //# sourceMappingURL=spawn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../src/musicians/spawn.ts"],"names":[],"mappings":"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"}
package/dist/notes.js ADDED
@@ -0,0 +1,32 @@
1
+ import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { notesDir } from './config.js';
5
+ function ensureSafeFilename(filename) {
6
+ if (!filename || /[\/\\]/.test(filename) || filename.includes('..')) {
7
+ throw new Error(`invalid filename: ${filename}`);
8
+ }
9
+ }
10
+ export async function noteWrite(orchestraId, filename, content) {
11
+ ensureSafeFilename(filename);
12
+ const dir = notesDir(orchestraId);
13
+ await mkdir(dir, { recursive: true });
14
+ await writeFile(join(dir, filename), content, 'utf8');
15
+ }
16
+ export async function noteRead(orchestraId, filename) {
17
+ ensureSafeFilename(filename);
18
+ const file = join(notesDir(orchestraId), filename);
19
+ if (!existsSync(file)) {
20
+ return '';
21
+ }
22
+ return readFile(file, 'utf8');
23
+ }
24
+ export async function noteList(orchestraId) {
25
+ const dir = notesDir(orchestraId);
26
+ if (!existsSync(dir)) {
27
+ return [];
28
+ }
29
+ const entries = await readdir(dir, { withFileTypes: true });
30
+ return entries.filter((e) => { return e.isFile(); }).map((e) => { return e.name; });
31
+ }
32
+ //# sourceMappingURL=notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.js","sourceRoot":"","sources":["../src/notes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,WAAmB,EACnB,QAAgB,EAChB,OAAe;IAEf,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAClC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,WAAmB,EAAE,QAAgB;IAClE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,CAAC;IACnD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,WAAmB;IAChD,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC"}
package/dist/notify.js ADDED
@@ -0,0 +1,51 @@
1
+ import { execa } from 'execa';
2
+ function defaultBell(text) {
3
+ process.stdout.write(text);
4
+ }
5
+ async function defaultSpawn(bin, args) {
6
+ return execa(bin, args);
7
+ }
8
+ function pluralise(count) {
9
+ if (count === 1) {
10
+ return '1 musician awaiting permission';
11
+ }
12
+ return `${count} musicians awaiting permission`;
13
+ }
14
+ /**
15
+ * Fire a single notification: ring the terminal bell and (best-effort) spawn
16
+ * the platform's desktop notifier. All errors are swallowed — a missing
17
+ * notify-send / osascript / etc. must not break the orchestra.
18
+ */
19
+ export async function notifyAwaitingPermission(opts) {
20
+ const bell = opts.bell ?? defaultBell;
21
+ const spawn = opts.spawn ?? defaultSpawn;
22
+ const platform = opts.platform ?? process.platform;
23
+ const message = pluralise(opts.pendingCount);
24
+ try {
25
+ bell('\x07');
26
+ }
27
+ catch {
28
+ // Swallow — a broken stdout sink should never abort.
29
+ }
30
+ if (platform === 'linux') {
31
+ try {
32
+ await spawn('notify-send', ['NFO', message]);
33
+ }
34
+ catch {
35
+ // notify-send may not be installed — best-effort only.
36
+ }
37
+ return;
38
+ }
39
+ if (platform === 'darwin') {
40
+ const script = `display notification "${message}" with title "NFO"`;
41
+ try {
42
+ await spawn('osascript', ['-e', script]);
43
+ }
44
+ catch {
45
+ // osascript should exist on macOS but swallow defensively.
46
+ }
47
+ return;
48
+ }
49
+ // Unknown platform (win32, freebsd, etc.) — bell-only.
50
+ }
51
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAS9B,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,IAAc;IACrD,OAAO,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,gCAAgC,CAAC;IAC1C,CAAC;IACD,OAAO,GAAG,KAAK,gCAAgC,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAAmB;IAChE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACnD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAE7C,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;IACvD,CAAC;IAED,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,uDAAuD;QACzD,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,yBAAyB,OAAO,oBAAoB,CAAC;QACpE,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;QACD,OAAO;IACT,CAAC;IAED,uDAAuD;AACzD,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { sendKeys, sessionName } from '../tmux.js';
2
+ export function formatMusicianDonePrompt(report) {
3
+ const nextSteps = report.nextSteps?.trim()
4
+ ? `\nSuggested next steps from the Musician:\n${report.nextSteps.trim()}\n`
5
+ : '';
6
+ return `Musician ${report.musicianId} (${report.musicianName}) reported done and is now idle.
7
+
8
+ Summary:
9
+ ${report.summary}
10
+ ${nextSteps}
11
+ Resolve this now with an NFO tool call only:
12
+ - If the work is good enough, call dismiss_musician({ musician_id: ${JSON.stringify(report.musicianId)} }).
13
+ - If it needs another pass, call message_musician({ musician_id: ${JSON.stringify(report.musicianId)}, message: "..." }).
14
+
15
+ Do not leave this Musician idle without either dismissing it or sending the next iteration.
16
+ A plain-text acknowledgement is invalid here.`;
17
+ }
18
+ export async function notifyOrchestratorOfDoneReport(orchestraId, report) {
19
+ await sendKeys(`${sessionName(orchestraId)}:0`, formatMusicianDonePrompt(report), true);
20
+ }
21
+ //# sourceMappingURL=report-back.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-back.js","sourceRoot":"","sources":["../../src/orchestrator/report-back.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AASnD,MAAM,UAAU,wBAAwB,CAAC,MAA0B;IACjE,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE;QACxC,CAAC,CAAC,8CAA8C,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI;QAC3E,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,YAAY,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,YAAY;;;EAG5D,MAAM,CAAC,OAAO;EACd,SAAS;;qEAE0D,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC;mEACnC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC;;;8CAGtD,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,WAAmB,EACnB,MAA0B;IAE1B,MAAM,QAAQ,CAAC,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,wBAAwB,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,26 @@
1
+ export const PERMISSION_LEVELS = ['auto', 'autonomous', 'supervised', 'strict'];
2
+ export function isPermissionLevel(s) {
3
+ return PERMISSION_LEVELS.includes(s);
4
+ }
5
+ export function claudeFlagsForLevel(level) {
6
+ switch (level) {
7
+ case 'auto':
8
+ // Spec §5.2 + §12.2 open question: exact bypass flag is `--dangerously-skip-permissions`
9
+ // in current Claude Code releases. If a future release renames it, update here.
10
+ return ['--dangerously-skip-permissions'];
11
+ case 'autonomous':
12
+ return ['--permission-mode', 'acceptEdits'];
13
+ case 'supervised':
14
+ return ['--permission-mode', 'default'];
15
+ case 'strict':
16
+ return ['--permission-mode', 'plan'];
17
+ }
18
+ }
19
+ export const AUTO_CONFIRM_PHRASE = 'I understand';
20
+ export const AUTO_WARNING = `⚠ AUTO mode disables all permission checks.
21
+ Musicians can execute arbitrary shell commands, modify files anywhere on
22
+ this system, and access the network without asking. Worktrees limit but
23
+ do not contain risky operations. Use this only in trusted sandboxes or
24
+ when you accept these risks.
25
+ Type "${AUTO_CONFIRM_PHRASE}" to continue.`;
26
+ //# sourceMappingURL=permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.js","sourceRoot":"","sources":["../src/permission.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,CAAU,CAAC;AAGzF,MAAM,UAAU,iBAAiB,CAAC,CAAS;IACzC,OAAQ,iBAAuC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAsB;IACxD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,yFAAyF;YACzF,gFAAgF;YAChF,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAC5C,KAAK,YAAY;YACf,OAAO,CAAC,mBAAmB,EAAE,aAAa,CAAC,CAAC;QAC9C,KAAK,YAAY;YACf,OAAO,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;QAC1C,KAAK,QAAQ;YACX,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;QAKpB,mBAAmB,gBAAgB,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { basename } from 'node:path';
3
+ export function projectKeyFromPath(absolutePath) {
4
+ const hash = createHash('sha1').update(absolutePath).digest('hex').slice(0, 10);
5
+ const name = basename(absolutePath)
6
+ .toLowerCase()
7
+ .replace(/[^a-z0-9-]+/g, '-')
8
+ .replace(/-+/g, '-')
9
+ .replace(/^-|-$/g, '');
10
+ return `${hash}-${name || 'project'}`;
11
+ }
12
+ //# sourceMappingURL=project-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-key.js","sourceRoot":"","sources":["../src/project-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,UAAU,kBAAkB,CAAC,YAAoB;IACrD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChF,MAAM,IAAI,GAAG,QAAQ,CAAC,YAAY,CAAC;SAChC,WAAW,EAAE;SACb,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACzB,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { MUSICIAN_TOOL_DISCIPLINE } from './tool-discipline.js';
2
+ export const MUSICIAN_ROLE_PROMPT_V1 = `You are a Musician in an NFO orchestra.
3
+
4
+ You were spawned by the Orchestrator with a specific task. The user typing into
5
+ your pane is debugging / observing — usually the user does NOT direct you;
6
+ the Orchestrator does. Treat new user messages as either Orchestrator
7
+ hand-offs or out-of-band human guidance, and use judgment.
8
+
9
+ Your workspace is a dedicated git worktree, so file edits are isolated from
10
+ other Musicians. When you finish the task you were spawned with, call the
11
+ \`report_done\` MCP tool with a concise summary and optional next steps. After
12
+ that, stay alive while the Orchestrator reviews your report. NFO may batch
13
+ queued follow-up messages and deliver them right after \`report_done\`; if the
14
+ Orchestrator is satisfied, it may dismiss you instead.
15
+
16
+ ${MUSICIAN_TOOL_DISCIPLINE}
17
+
18
+ You also have the full NFO MCP tool surface (\`spawn_musician\`,
19
+ \`message_musician\`, etc.). Avoid spawning sub-Musicians unless the
20
+ Orchestrator explicitly asks you to. Keep coordination centralised.
21
+ `;
22
+ //# sourceMappingURL=musician-role.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"musician-role.js","sourceRoot":"","sources":["../../src/prompts/musician-role.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;EAcrC,wBAAwB;;;;;CAKzB,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { ORCHESTRATOR_TOOL_DISCIPLINE } from "./tool-discipline.js";
2
+ /**
3
+ * Phase 2 Orchestrator role addendum. Documents the NFO MCP tool surface.
4
+ */
5
+ export const ORCHESTRATOR_ROLE_PROMPT_V1 = `You are the Orchestrator of an NFO orchestra.
6
+
7
+ NFO (NoFluffOrchestra) is a TUI for multi-agent work on the user's repository.
8
+ You coordinate Musicians (other Claude Code agents) via the NFO MCP tools.
9
+
10
+ Available NFO tools (in addition to your normal Claude Code tools):
11
+
12
+ spawn_musician({ name, task, worktree?, branch_from?, model? })
13
+ Create a Musician with the given task. By default the Musician runs in a
14
+ fresh git worktree off HEAD. Pass worktree=false for trivially isolated
15
+ work (e.g., docs-only) that doesn't need an isolated branch. Returns the
16
+ musician_id. Provide a model to be used by the Musician, otherwise it defaults to sonnet.
17
+ For trivial tasks Haiku is a good choice; for complex coding work, Sonnet is better.
18
+
19
+ message_musician({ musician_id, message })
20
+ Send a message to a Musician. If the Musician is idle, NFO delivers it
21
+ immediately. If the Musician is still working, NFO queues it and delivers
22
+ it automatically on the next idle boundary.
23
+
24
+ query_musician({ musician_id, lines? })
25
+ Read the most recent visible output from the Musician's pane. Use this
26
+ sparingly — capture-pane is heuristic and may include rendering artifacts.
27
+
28
+ list_musicians()
29
+ Return all currently-active Musicians with their status.
30
+
31
+ dismiss_musician({ musician_id, archive_worktree? })
32
+ Tear down a Musician. By default the worktree is archived under
33
+ .../archive/<musician_id>/worktree (the branch is preserved). Pass
34
+ archive_worktree=false to drop the worktree entirely.
35
+
36
+ note_write({ filename, content }) / note_read({ filename }) / note_list()
37
+ Your private project memory under ~/.config/nfo/projects/<key>/notes/.
38
+ On every fresh Orchestrator session, the contents of notes/overview.md
39
+ and notes/decisions.md are loaded into your context automatically.
40
+ Use these to record decisions, open questions, and durable project
41
+ understanding the user would want you to remember next session.
42
+
43
+ Coordination guidance:
44
+
45
+ - ${ORCHESTRATOR_TOOL_DISCIPLINE.trim().replace(/\n/g, "\n ")}
46
+ - For agent coordination, PREFER the NFO MCP tools over Claude Code's built-in
47
+ Task tool. The user tracks Musician work through NFO; Task spawns are invisible
48
+ to NFO.
49
+ - Worktrees solve concurrent file-edit safety, not API coupling. If two
50
+ Musicians' outputs need to be wired together, sequence the work, or spawn an
51
+ integration Musician afterward.
52
+ - The orchestra's permission level applies to every Musician you spawn.
53
+ - Prefer concise follow-up nudges. NFO persists them in JSONL and batches any
54
+ backlog before a Musician truly becomes idle again.
55
+ - When a Musician calls \`report_done\` and no queued follow-up is waiting, NFO
56
+ pushes that completion back into your Claude session. Review the report
57
+ promptly and either dismiss the Musician or send the next iteration.
58
+ - Project-level guidance in CLAUDE.md still applies; respect it.
59
+ - You can use Superpowers if present but make sure that works are delegated to
60
+ Musicians in the end if subagent driven development is picked by the user.
61
+ `;
62
+ //# sourceMappingURL=orchestrator-role.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-role.js","sourceRoot":"","sources":["../../src/prompts/orchestrator-role.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAEpE;;GAEG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwCvC,4BAA4B,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;;;;;;;;;;;;;;;;CAgB7D,CAAC"}
@@ -0,0 +1,33 @@
1
+ export const ORCHESTRATOR_TOOL_DISCIPLINE = `Tool discipline (mandatory):
2
+
3
+ - Use the NFO MCP tools for all musician coordination and orchestra-local memory.
4
+ - Never merely say that you will spawn, message, query, list, dismiss, or note
5
+ something later. Call the corresponding NFO tool in the same turn.
6
+ - Do not use Claude Code's built-in Task tool for Musician coordination; those
7
+ agents are invisible to NFO.
8
+ - When a Musician reports back, resolve it in the same turn with an NFO tool
9
+ call (usually \`dismiss_musician\` or \`message_musician\`). A prose-only
10
+ acknowledgement is non-compliant.
11
+ `;
12
+ export const MUSICIAN_TOOL_DISCIPLINE = `Tool discipline (mandatory):
13
+
14
+ - Use NFO MCP tools for orchestra coordination. Plain-text status reports are
15
+ not a valid handoff.
16
+ - When your assigned task is complete and ready for Orchestrator review, your
17
+ next action must be \`report_done({ summary, next_steps? })\`.
18
+ - Do not end with "done", "finished", or similar prose instead of calling
19
+ \`report_done\`.
20
+ - After \`report_done\`, wait for the Orchestrator to send the next task or
21
+ dismiss you.
22
+ `;
23
+ export function buildMusicianInitialPrompt(task) {
24
+ const trimmedTask = task.trim();
25
+ const body = trimmedTask.length > 0 ? trimmedTask : task;
26
+ return `${body}
27
+
28
+ NFO operating contract (mandatory):
29
+ - Use the NFO MCP tools for orchestra coordination.
30
+ - When you finish this task and are ready to hand it back, call \`report_done({ summary, next_steps? })\` instead of replying with a plain-text completion message.
31
+ - After \`report_done\`, wait for the Orchestrator to message you again or dismiss you.`;
32
+ }
33
+ //# sourceMappingURL=tool-discipline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-discipline.js","sourceRoot":"","sources":["../../src/prompts/tool-discipline.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,4BAA4B,GAAG;;;;;;;;;;CAU3C,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG;;;;;;;;;;CAUvC,CAAC;AAEF,MAAM,UAAU,0BAA0B,CAAC,IAAY;IACrD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IAEzD,OAAO,GAAG,IAAI;;;;;wFAKwE,CAAC;AACzF,CAAC"}
package/dist/repo.js ADDED
@@ -0,0 +1,15 @@
1
+ import { execa } from 'execa';
2
+ export async function resolveRepoRoot(cwd) {
3
+ try {
4
+ const { stdout } = await execa('git', ['rev-parse', '--show-toplevel'], {
5
+ cwd,
6
+ reject: false,
7
+ });
8
+ const trimmed = stdout.trim();
9
+ return trimmed.length > 0 ? trimmed : null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ //# sourceMappingURL=repo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.js","sourceRoot":"","sources":["../src/repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW;IAC/C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE;YACtE,GAAG;YACH,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export function shellQuote(value) {
2
+ if (value.length === 0) {
3
+ return "''";
4
+ }
5
+ return `'${value.replace(/'/g, `'\"'\"'`)}'`;
6
+ }
7
+ //# sourceMappingURL=shell-quote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-quote.js","sourceRoot":"","sources":["../src/shell-quote.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC;AAC/C,CAAC"}