nfo-cli 0.0.4-improve-prompting → 0.0.6-a89844d-dev

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 (178) hide show
  1. package/dist/claude-command.js +6 -1
  2. package/dist/claude-command.js.map +1 -1
  3. package/dist/claude-trust.js +46 -0
  4. package/dist/claude-trust.js.map +1 -0
  5. package/dist/cli.js +5 -4
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/attach.js +8 -8
  8. package/dist/commands/attach.js.map +1 -1
  9. package/dist/commands/launch.js +3 -6
  10. package/dist/commands/launch.js.map +1 -1
  11. package/dist/commands/restore.js +6 -10
  12. package/dist/commands/restore.js.map +1 -1
  13. package/dist/commands/tui.js +17 -1
  14. package/dist/commands/tui.js.map +1 -1
  15. package/dist/mcp/handlers.js +5 -0
  16. package/dist/mcp/handlers.js.map +1 -1
  17. package/dist/mcp/tool-defs.js +10 -0
  18. package/dist/mcp/tool-defs.js.map +1 -1
  19. package/dist/musicians/dismiss.js +1 -1
  20. package/dist/musicians/dismiss.js.map +1 -1
  21. package/dist/musicians/reconcile.js +27 -0
  22. package/dist/musicians/reconcile.js.map +1 -0
  23. package/dist/musicians/roles.js +15 -0
  24. package/dist/musicians/roles.js.map +1 -0
  25. package/dist/musicians/spawn.js +53 -18
  26. package/dist/musicians/spawn.js.map +1 -1
  27. package/dist/permission.js +6 -0
  28. package/dist/permission.js.map +1 -1
  29. package/dist/prompts/musician-role.js +2 -1
  30. package/dist/prompts/musician-role.js.map +1 -1
  31. package/dist/prompts/orchestrator-role.js +18 -6
  32. package/dist/prompts/orchestrator-role.js.map +1 -1
  33. package/dist/prompts/tool-discipline.js +7 -3
  34. package/dist/prompts/tool-discipline.js.map +1 -1
  35. package/dist/tmux.js +23 -0
  36. package/dist/tmux.js.map +1 -1
  37. package/dist/tui/components/App.js +8 -12
  38. package/dist/tui/components/App.js.map +1 -1
  39. package/dist/tui/components/Help.js +1 -1
  40. package/dist/tui/components/Help.js.map +1 -1
  41. package/dist/tui/keymap.js +1 -1
  42. package/dist/tui/keymap.js.map +1 -1
  43. package/package.json +18 -8
  44. package/assets/agent-screen.png +0 -0
  45. package/assets/main-screen.png +0 -0
  46. package/assets/orche-clawd.png +0 -0
  47. package/dist/tui/App.js +0 -428
  48. package/dist/tui/App.js.map +0 -1
  49. package/dist/tui/AppView.js +0 -13
  50. package/dist/tui/AppView.js.map +0 -1
  51. package/dist/tui/Auditorium.js +0 -17
  52. package/dist/tui/Auditorium.js.map +0 -1
  53. package/dist/tui/ConcertHall.js +0 -11
  54. package/dist/tui/ConcertHall.js.map +0 -1
  55. package/dist/tui/Help.js +0 -49
  56. package/dist/tui/Help.js.map +0 -1
  57. package/dist/tui/OrchestratorPane.js +0 -34
  58. package/dist/tui/OrchestratorPane.js.map +0 -1
  59. package/dist/tui/SidebarHeader.js +0 -6
  60. package/dist/tui/SidebarHeader.js.map +0 -1
  61. package/dist/tui/StatusBar.js +0 -6
  62. package/dist/tui/StatusBar.js.map +0 -1
  63. package/docs/plans/2026-05-29-nfo-phase-1-bootstrap.md +0 -2152
  64. package/docs/plans/2026-05-29-nfo-phase-2-mcp-musicians.md +0 -2467
  65. package/docs/plans/2026-05-29-nfo-phase-3-ink-tui.md +0 -1611
  66. package/docs/plans/2026-05-29-nfo-phase-4-permission-prompts.md +0 -460
  67. package/docs/plans/2026-05-29-nfo-phase-5-help-and-notify.md +0 -933
  68. package/docs/specs/2026-05-29-nfo-design.md +0 -468
  69. package/plan-explorer-musician-hardening.md +0 -56
  70. package/src/claude-command.ts +0 -35
  71. package/src/claude-detect.ts +0 -42
  72. package/src/cli.ts +0 -197
  73. package/src/commands/attach.ts +0 -24
  74. package/src/commands/dashboard-window.ts +0 -33
  75. package/src/commands/kill.ts +0 -50
  76. package/src/commands/launch.ts +0 -134
  77. package/src/commands/list.ts +0 -43
  78. package/src/commands/mcp-server.ts +0 -18
  79. package/src/commands/notes.ts +0 -18
  80. package/src/commands/restore.ts +0 -153
  81. package/src/commands/tui.tsx +0 -22
  82. package/src/config.ts +0 -44
  83. package/src/dashboard.ts +0 -1
  84. package/src/mcp/config.ts +0 -39
  85. package/src/mcp/handlers.ts +0 -141
  86. package/src/mcp/server.ts +0 -50
  87. package/src/mcp/tool-defs.ts +0 -151
  88. package/src/musicians/dismiss.ts +0 -60
  89. package/src/musicians/ids.ts +0 -21
  90. package/src/musicians/lookup.ts +0 -13
  91. package/src/musicians/message-log.ts +0 -152
  92. package/src/musicians/message.ts +0 -99
  93. package/src/musicians/query.ts +0 -19
  94. package/src/musicians/spawn.ts +0 -139
  95. package/src/notes.ts +0 -39
  96. package/src/notify.ts +0 -62
  97. package/src/orchestrator/report-back.ts +0 -33
  98. package/src/permission.ts +0 -30
  99. package/src/project-key.ts +0 -12
  100. package/src/prompts/musician-role.ts +0 -22
  101. package/src/prompts/orchestrator-role.ts +0 -84
  102. package/src/prompts/tool-discipline.ts +0 -41
  103. package/src/repo.ts +0 -14
  104. package/src/shell-quote.ts +0 -7
  105. package/src/state-updaters.ts +0 -132
  106. package/src/state.ts +0 -49
  107. package/src/state.types.ts +0 -67
  108. package/src/tmux.ts +0 -226
  109. package/src/tui/activity-line.ts +0 -16
  110. package/src/tui/components/App.tsx +0 -534
  111. package/src/tui/components/AppView.tsx +0 -98
  112. package/src/tui/components/Auditorium.tsx +0 -56
  113. package/src/tui/components/ConcertHall.tsx +0 -31
  114. package/src/tui/components/Help.tsx +0 -63
  115. package/src/tui/components/OrchestratorPane.tsx +0 -98
  116. package/src/tui/components/SidebarHeader.tsx +0 -34
  117. package/src/tui/components/StatusBar.tsx +0 -42
  118. package/src/tui/detect-permission.ts +0 -93
  119. package/src/tui/embedded-session-lifecycle.ts +0 -44
  120. package/src/tui/embedded-terminal.ts +0 -325
  121. package/src/tui/format-time.ts +0 -25
  122. package/src/tui/keymap.ts +0 -104
  123. package/src/tui/poll-activity.ts +0 -25
  124. package/src/tui/poll-idle.ts +0 -149
  125. package/src/tui/poll-permission.ts +0 -50
  126. package/src/tui/status-icon.ts +0 -35
  127. package/src/tui/terminal-input.ts +0 -136
  128. package/src/tui/watch-state.ts +0 -43
  129. package/src/worktree.ts +0 -41
  130. package/tests/claude-command.test.ts +0 -30
  131. package/tests/claude-detect.test.ts +0 -14
  132. package/tests/commands/attach.test.ts +0 -60
  133. package/tests/commands/kill.test.ts +0 -66
  134. package/tests/commands/launch.test.ts +0 -75
  135. package/tests/commands/list.test.ts +0 -47
  136. package/tests/commands/notes.test.ts +0 -53
  137. package/tests/commands/restore.test.ts +0 -126
  138. package/tests/helpers/tmp-config.ts +0 -16
  139. package/tests/helpers/tmp-repo.ts +0 -29
  140. package/tests/integration/orchestrator-spawn.test.ts +0 -108
  141. package/tests/mcp/handlers.test.ts +0 -163
  142. package/tests/mcp/tool-defs.test.ts +0 -35
  143. package/tests/musicians/dismiss.test.ts +0 -102
  144. package/tests/musicians/message.test.ts +0 -159
  145. package/tests/musicians/query.test.ts +0 -65
  146. package/tests/musicians/spawn.test.ts +0 -125
  147. package/tests/notes.test.ts +0 -56
  148. package/tests/notify.test.ts +0 -80
  149. package/tests/orchestrator/report-back.test.ts +0 -18
  150. package/tests/permission.test.ts +0 -39
  151. package/tests/project-key.test.ts +0 -33
  152. package/tests/prompts/tool-discipline.test.ts +0 -25
  153. package/tests/repo.test.ts +0 -38
  154. package/tests/state-updaters.test.ts +0 -126
  155. package/tests/state.test.ts +0 -85
  156. package/tests/tmux.test.ts +0 -126
  157. package/tests/tui/AppView.test.tsx +0 -92
  158. package/tests/tui/Auditorium.test.tsx +0 -67
  159. package/tests/tui/ConcertHall.test.tsx +0 -22
  160. package/tests/tui/Help.test.tsx +0 -38
  161. package/tests/tui/OrchestratorPane.test.ts +0 -30
  162. package/tests/tui/SidebarHeader.test.tsx +0 -20
  163. package/tests/tui/StatusBar.test.tsx +0 -51
  164. package/tests/tui/activity-line.test.ts +0 -21
  165. package/tests/tui/detect-permission.test.ts +0 -92
  166. package/tests/tui/embedded-session-lifecycle.test.ts +0 -55
  167. package/tests/tui/embedded-terminal.test.ts +0 -80
  168. package/tests/tui/format-time.test.ts +0 -25
  169. package/tests/tui/keymap.test.ts +0 -93
  170. package/tests/tui/poll-activity.test.ts +0 -81
  171. package/tests/tui/poll-idle.test.ts +0 -159
  172. package/tests/tui/poll-permission.test.ts +0 -222
  173. package/tests/tui/status-icon.test.ts +0 -27
  174. package/tests/tui/terminal-input.test.ts +0 -113
  175. package/tests/tui/watch-state.test.ts +0 -54
  176. package/tests/worktree.test.ts +0 -73
  177. package/tsconfig.json +0 -19
  178. package/vitest.config.ts +0 -12
@@ -1,31 +0,0 @@
1
- import type { ReactElement } from "react";
2
- import { Box, Text } from "ink";
3
- import type { OrchestraSummary } from "../../commands/list.js";
4
-
5
- export interface ConcertHallProps {
6
- orchestras: OrchestraSummary[];
7
- currentId: string;
8
- }
9
-
10
- export function ConcertHall(props: ConcertHallProps): ReactElement {
11
- return (
12
- <Box
13
- flexDirection="column"
14
- borderStyle="single"
15
- borderBottom={true}
16
- paddingX={1}
17
- >
18
- <Text bold={true}>Concert Hall</Text>
19
- {props.orchestras.map((o) => {
20
- const current = o.id === props.currentId;
21
- const marker = current ? "▸" : " ";
22
- const dot = o.running ? "●" : "○";
23
- return (
24
- <Text key={o.id} bold={current}>
25
- {marker} {dot} {o.id} ({o.musician_count})
26
- </Text>
27
- );
28
- })}
29
- </Box>
30
- );
31
- }
@@ -1,63 +0,0 @@
1
- import type { ReactElement } from "react";
2
- import { Box, Text } from "ink";
3
-
4
- interface Row {
5
- key: string;
6
- label: string;
7
- }
8
-
9
- const ROWS: Row[] = [
10
- { key: "↑ / k", label: "move selection up" },
11
- { key: "↓ / j", label: "move selection down" },
12
- { key: "Enter", label: "open the selected target in the left pane" },
13
- { key: "n", label: "open notes for this orchestra" },
14
- {
15
- key: "d",
16
- label:
17
- "arm dismiss for selected Musician (press d again / y / Enter to confirm)",
18
- },
19
- { key: "n / Esc", label: "cancel pending dismiss confirmation" },
20
- { key: "p", label: "jump to next Musician awaiting permission" },
21
- {
22
- key: "q",
23
- label:
24
- "detach this tmux client from NFO without killing the orchestra session",
25
- },
26
- {
27
- key: "Ctrl+g",
28
- label: "switch focus between the sidebar and the embedded Claude terminal",
29
- },
30
- {
31
- key: "typed keys",
32
- label:
33
- "go directly to the currently open tmux terminal while the left pane is focused",
34
- },
35
- {
36
- key: "Alt+Enter / Shift+Enter / Ctrl+J",
37
- label:
38
- "insert a newline in the focused terminal without treating it like Enter",
39
- },
40
- {
41
- key: "Mouse wheel",
42
- label:
43
- "scroll the left terminal through local scrollback when the pointer is over that pane",
44
- },
45
- { key: "?", label: "toggle this help / close" },
46
- ];
47
-
48
- export function Help(): ReactElement {
49
- return (
50
- <Box flexDirection="column" paddingX={1}>
51
- <Text bold={true}>Keybindings</Text>
52
- {ROWS.map((row) => {
53
- return (
54
- <Text key={row.key}>
55
- <Text color="cyan">{row.key.padEnd(8)}</Text>
56
- <Text> {row.label}</Text>
57
- </Text>
58
- );
59
- })}
60
- <Text dimColor={true}>Press ? to close.</Text>
61
- </Box>
62
- );
63
- }
@@ -1,98 +0,0 @@
1
- import type { ReactElement } from "react";
2
- import { Box, Text } from "ink";
3
- import type {
4
- EmbeddedTerminalLine,
5
- EmbeddedTerminalSpan,
6
- } from "../embedded-terminal.js";
7
-
8
- export interface OrchestratorPaneProps {
9
- title: string;
10
- lines: EmbeddedTerminalLine[];
11
- focused: boolean;
12
- connected: boolean;
13
- }
14
-
15
- export function resolveSpanStyle(
16
- span: EmbeddedTerminalSpan,
17
- focused: boolean,
18
- ): Omit<EmbeddedTerminalSpan, "text" | "cursor"> {
19
- const style: Omit<EmbeddedTerminalSpan, "text" | "cursor"> = {
20
- color: span.color,
21
- backgroundColor: span.backgroundColor,
22
- dimColor: span.dimColor,
23
- bold: span.bold,
24
- italic: span.italic,
25
- underline: span.underline,
26
- strikethrough: span.strikethrough,
27
- inverse: span.inverse,
28
- };
29
- if (span.cursor === true && focused) {
30
- style.color = "black";
31
- style.backgroundColor = "white";
32
- style.inverse = false;
33
- }
34
- return style;
35
- }
36
-
37
- function renderSpan(
38
- span: EmbeddedTerminalSpan,
39
- index: number,
40
- focused: boolean,
41
- ): ReactElement {
42
- const style = resolveSpanStyle(span, focused);
43
- return (
44
- <Text
45
- key={`${index}:${span.text}`}
46
- color={style.color}
47
- backgroundColor={style.backgroundColor}
48
- dimColor={style.dimColor}
49
- bold={style.bold}
50
- italic={style.italic}
51
- underline={style.underline}
52
- strikethrough={style.strikethrough}
53
- inverse={style.inverse}
54
- >
55
- {span.text}
56
- </Text>
57
- );
58
- }
59
-
60
- export function OrchestratorPane(props: OrchestratorPaneProps): ReactElement {
61
- return (
62
- <Box
63
- flexGrow={1}
64
- flexDirection="column"
65
- borderStyle="single"
66
- marginRight={1}
67
- minHeight={16}
68
- paddingX={1}
69
- >
70
- <Text bold={true}>{props.title}</Text>
71
- <Box flexDirection="column" flexGrow={1}>
72
- {props.lines.length > 0 ? (
73
- props.lines.map((line, index) => {
74
- return (
75
- <Text key={String(index)} wrap="truncate-end">
76
- {line.spans.length > 0
77
- ? line.spans.map((span, spanIndex) =>
78
- renderSpan(span, spanIndex, props.focused),
79
- )
80
- : " "}
81
- </Text>
82
- );
83
- })
84
- ) : (
85
- <Text dimColor={true}>Waiting for Claude terminal…</Text>
86
- )}
87
- </Box>
88
- <Text color={props.focused ? "cyan" : "gray"} wrap="truncate-end">
89
- {props.focused
90
- ? "[Ctrl+g] sidebar · [Mouse wheel] scroll"
91
- : "[Ctrl+g] focus left terminal"}
92
- </Text>
93
- {!props.connected ? (
94
- <Text color="yellow">Embedded tmux client disconnected.</Text>
95
- ) : null}
96
- </Box>
97
- );
98
- }
@@ -1,34 +0,0 @@
1
- import type { ReactElement } from "react";
2
- import { Box, Text } from "ink";
3
-
4
- export interface SidebarHeaderProps {
5
- orchestraId: string;
6
- musicianCount: number;
7
- pendingCount: number;
8
- version: string;
9
- }
10
-
11
- export function SidebarHeader(props: SidebarHeaderProps): ReactElement {
12
- return (
13
- <Box
14
- flexDirection="column"
15
- borderStyle="round"
16
- borderBottom={true}
17
- paddingX={1}
18
- >
19
- <Text bold={true}>No Fluff Orchestra · {props.orchestraId}</Text>
20
- <Text bold={true}>v.{props.version}</Text>
21
- {props.pendingCount > 0 ? (
22
- <Text color="yellow">
23
- {props.musicianCount} musicians · {props.pendingCount} awaiting
24
- permission
25
- </Text>
26
- ) : (
27
- <Text dimColor={true}>
28
- {props.musicianCount} musicians · {props.pendingCount} awaiting
29
- permission
30
- </Text>
31
- )}
32
- </Box>
33
- );
34
- }
@@ -1,42 +0,0 @@
1
- import type { ReactElement } from "react";
2
- import { Box, Text } from "ink";
3
-
4
- export interface StatusBarProps {
5
- permissionLevel: string;
6
- tokenHint: string;
7
- pendingCount: number;
8
- dismissConfirmation?: string | null;
9
- orchestratorFocused: boolean;
10
- }
11
-
12
- export function StatusBar(props: StatusBarProps): ReactElement {
13
- return (
14
- <Box
15
- flexDirection="column"
16
- borderStyle="single"
17
- borderTop={true}
18
- paddingX={1}
19
- >
20
- {props.pendingCount > 0 ? (
21
- <Text color="yellow">
22
- ⚠ {props.pendingCount} awaiting permission · [p] jump to next
23
- </Text>
24
- ) : null}
25
- {props.dismissConfirmation ? (
26
- <Text color="red">{props.dismissConfirmation}</Text>
27
- ) : null}
28
- <Text>
29
- {props.permissionLevel} · {props.tokenHint}
30
- </Text>
31
- {props.orchestratorFocused ? (
32
- <Text dimColor={true}>[type] active terminal [Ctrl+g] sidebar</Text>
33
- ) : (
34
- <Text dimColor={true}>
35
- [↑↓] nav [⏎] open left pane [d] dismiss [p] pending [n] notes [Ctrl+g]
36
- terminal
37
- </Text>
38
- )}
39
- <Text dimColor={true}>[q] detach [?] help</Text>
40
- </Box>
41
- );
42
- }
@@ -1,93 +0,0 @@
1
- const LAST_N_LINES = 20;
2
- const MAX_TOOL_LEN = 60;
3
-
4
- const INTRO_PATTERNS: RegExp[] = [
5
- /allow\s+\S+/i,
6
- /do you want to/i,
7
- /permission required/i,
8
- /use this tool/i,
9
- ];
10
-
11
- export interface PermissionDetection {
12
- pending: boolean;
13
- tool: string | null;
14
- }
15
-
16
- function hasYesLine(lines: string[]): boolean {
17
- for (const line of lines) {
18
- const trimmed = line.trimStart();
19
- if (trimmed.startsWith('1.') || trimmed.startsWith('1)')) {
20
- return true;
21
- }
22
- }
23
- return false;
24
- }
25
-
26
- function hasNoLine(lines: string[]): boolean {
27
- for (const line of lines) {
28
- const trimmed = line.trimStart();
29
- const startsWithSmallDigit =
30
- trimmed.startsWith('2.') ||
31
- trimmed.startsWith('2)') ||
32
- trimmed.startsWith('3.') ||
33
- trimmed.startsWith('3)');
34
- if (startsWithSmallDigit && trimmed.includes('No')) {
35
- return true;
36
- }
37
- }
38
- return false;
39
- }
40
-
41
- function hasIntroPattern(lines: string[]): boolean {
42
- const block = lines.join('\n');
43
- for (const pattern of INTRO_PATTERNS) {
44
- if (pattern.test(block)) {
45
- return true;
46
- }
47
- }
48
- return false;
49
- }
50
-
51
- function extractTool(lines: string[]): string | null {
52
- try {
53
- const block = lines.join('\n');
54
- const nameMatch = /^Allow ([A-Z][A-Za-z]+)/m.exec(block);
55
- if (!nameMatch) {
56
- return null;
57
- }
58
- const toolName = nameMatch[1];
59
- // Find the full line that contains the Allow … match, to search for backticks.
60
- const matchIndex = nameMatch.index;
61
- const lineStart = matchIndex;
62
- const lineEnd = block.indexOf('\n', matchIndex);
63
- const fullLine = lineEnd === -1 ? block.slice(lineStart) : block.slice(lineStart, lineEnd);
64
- const backtickMatch = /`([^`]*)`/.exec(fullLine);
65
- let result: string;
66
- if (backtickMatch) {
67
- result = `${toolName}: \`${backtickMatch[1]}\``;
68
- } else {
69
- result = toolName;
70
- }
71
- if (result.length > MAX_TOOL_LEN) {
72
- return result.slice(0, MAX_TOOL_LEN - 1) + '…';
73
- }
74
- return result;
75
- } catch {
76
- return null;
77
- }
78
- }
79
-
80
- export function detectPermissionPrompt(paneText: string): PermissionDetection {
81
- const allLines = paneText.split('\n');
82
- const nonEmpty = allLines.filter((line) => { return line.trim().length > 0; });
83
- const lines = nonEmpty.slice(-LAST_N_LINES);
84
-
85
- const pending = hasYesLine(lines) && hasNoLine(lines) && hasIntroPattern(lines);
86
-
87
- if (!pending) {
88
- return { pending: false, tool: null };
89
- }
90
-
91
- const tool = extractTool(lines);
92
- return { pending: true, tool };
93
- }
@@ -1,44 +0,0 @@
1
- export interface EmbeddedSessionLease {
2
- readonly sessionName: string;
3
- readonly token: number;
4
- }
5
-
6
- const embeddedSessionTokens = new Map<string, number>();
7
- const embeddedSessionQueues = new Map<string, Promise<void>>();
8
-
9
- export function claimEmbeddedSessionLease(
10
- sessionName: string,
11
- ): EmbeddedSessionLease {
12
- const token = (embeddedSessionTokens.get(sessionName) ?? 0) + 1;
13
- embeddedSessionTokens.set(sessionName, token);
14
- return { sessionName, token };
15
- }
16
-
17
- export function embeddedSessionLeaseIsCurrent(
18
- lease: EmbeddedSessionLease,
19
- ): boolean {
20
- return embeddedSessionTokens.get(lease.sessionName) === lease.token;
21
- }
22
-
23
- export async function runEmbeddedSessionOperation<T>(
24
- sessionName: string,
25
- operation: () => Promise<T>,
26
- ): Promise<T> {
27
- const previous = embeddedSessionQueues.get(sessionName) ?? Promise.resolve();
28
- let releaseQueue: (() => void) | undefined;
29
- const current = new Promise<void>((resolve) => {
30
- releaseQueue = resolve;
31
- });
32
- embeddedSessionQueues.set(sessionName, current);
33
-
34
- await previous;
35
-
36
- try {
37
- return await operation();
38
- } finally {
39
- releaseQueue?.();
40
- if (embeddedSessionQueues.get(sessionName) === current) {
41
- embeddedSessionQueues.delete(sessionName);
42
- }
43
- }
44
- }