pi-crew 0.9.67 → 0.10.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 (222) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/agents/analyst.md +1 -1
  3. package/agents/cold-verifier.md +3 -1
  4. package/agents/critic.md +1 -1
  5. package/agents/executor.md +1 -1
  6. package/agents/explorer.md +1 -1
  7. package/agents/planner.md +1 -1
  8. package/agents/reviewer.md +1 -1
  9. package/agents/security-reviewer.md +1 -1
  10. package/agents/test-engineer.md +1 -1
  11. package/agents/verifier.md +1 -1
  12. package/agents/writer.md +1 -1
  13. package/dist/index.mjs +65847 -60331
  14. package/docs/README.md +2 -0
  15. package/docs/actions-reference.md +31 -0
  16. package/docs/commands-reference.md +17 -6
  17. package/docs/resource-formats.md +13 -0
  18. package/package.json +9 -6
  19. package/schema.json +15 -1
  20. package/scripts/resource-sampler.mjs +36 -2
  21. package/skills/requirements-to-task-packet/SKILL.md +26 -0
  22. package/skills/widget-rendering/SKILL.md +7 -7
  23. package/src/agents/agent-config.ts +36 -14
  24. package/src/agents/discover-agents.ts +23 -14
  25. package/src/config/config-merge.ts +183 -0
  26. package/src/config/config-validation.ts +681 -0
  27. package/src/config/config.ts +22 -864
  28. package/src/config/defaults.ts +35 -1
  29. package/src/config/drift-detector.ts +1 -1
  30. package/src/config/env-vars.ts +691 -0
  31. package/src/config/role-tools.ts +8 -8
  32. package/src/config/sanitize-project-config.ts +172 -0
  33. package/src/config/types.ts +30 -0
  34. package/src/extension/async-notifier.ts +25 -2
  35. package/src/extension/crew-vibes/config.ts +2 -1
  36. package/src/extension/plan-orchestrate.ts +132 -0
  37. package/src/extension/registration/commands/dashboard.ts +158 -0
  38. package/src/extension/registration/commands/index.ts +35 -0
  39. package/src/extension/registration/commands/manage.ts +303 -0
  40. package/src/extension/registration/commands/run.ts +214 -0
  41. package/src/extension/registration/commands/shared.ts +631 -0
  42. package/src/extension/registration/commands/status.ts +60 -0
  43. package/src/extension/registration/commands.ts +13 -1224
  44. package/src/extension/registration/lifecycle-handlers.ts +126 -15
  45. package/src/extension/registration/subagent-tools.ts +218 -9
  46. package/src/extension/rpc-hmac.ts +5 -3
  47. package/src/extension/team-tool/api/heartbeat.ts +47 -10
  48. package/src/extension/team-tool/api/plan-approval.ts +9 -0
  49. package/src/extension/team-tool/api/task-claims.ts +109 -40
  50. package/src/extension/team-tool/cancel.ts +84 -50
  51. package/src/extension/team-tool/dispatch/index.ts +1 -0
  52. package/src/extension/team-tool/dispatch/run.ts +4 -1
  53. package/src/extension/team-tool/doctor.ts +103 -1
  54. package/src/extension/team-tool/orchestrate.ts +66 -1
  55. package/src/extension/team-tool/plans.ts +192 -0
  56. package/src/extension/team-tool/respond.ts +197 -65
  57. package/src/extension/team-tool/run-deadline.ts +16 -1
  58. package/src/extension/team-tool/run-intent.ts +63 -0
  59. package/src/extension/team-tool/run.ts +48 -18
  60. package/src/extension/team-tool/status.ts +84 -26
  61. package/src/extension/team-tool.ts +11 -2
  62. package/src/hooks/registry.ts +1 -6
  63. package/src/i18n.ts +9 -0
  64. package/src/prompt/prompt-runtime.ts +521 -2
  65. package/src/prompt/worker-events-channel.ts +173 -0
  66. package/src/runtime/README.md +8 -8
  67. package/src/runtime/async-runner.ts +7 -3
  68. package/src/runtime/background-runner.ts +42 -14
  69. package/src/runtime/broker/broker-issuer.ts +9 -2
  70. package/src/runtime/broker/crew-broker-tokens.ts +43 -6
  71. package/src/runtime/broker/crew-broker.ts +838 -10
  72. package/src/runtime/broker/wait-status-cache.ts +157 -0
  73. package/src/runtime/budget-enforcement.ts +281 -0
  74. package/src/runtime/child-pi/child-pi-spawn.ts +60 -14
  75. package/src/runtime/child-pi/child-pi-timers.ts +324 -0
  76. package/src/runtime/child-pi/child-pi.ts +97 -201
  77. package/src/runtime/child-pi/mock-fixtures.ts +16 -2
  78. package/src/runtime/crew-agent-records.ts +259 -14
  79. package/src/runtime/delegate-spawn.ts +148 -0
  80. package/src/runtime/deterministic-ast.ts +2 -1
  81. package/src/runtime/dispatch-batch.ts +945 -0
  82. package/src/runtime/finalize-run.ts +557 -0
  83. package/src/runtime/goal-workflow/adaptive-plan.ts +87 -13
  84. package/src/runtime/goal-workflow/dynamic-workflow-runner.ts +3 -2
  85. package/src/runtime/goal-workflow/goal-state-store.ts +1 -1
  86. package/src/runtime/group-join.ts +11 -125
  87. package/src/runtime/live-session/live-session-runtime.ts +26 -1
  88. package/src/runtime/merge-gate.ts +7 -1
  89. package/src/runtime/merge-loop.ts +130 -0
  90. package/src/runtime/model/model-budget-summary.ts +53 -0
  91. package/src/runtime/model/model-fallback.ts +32 -2
  92. package/src/runtime/model/pi-args.ts +10 -0
  93. package/src/runtime/model/provider-extensions.ts +10 -0
  94. package/src/runtime/orphan-worker-registry.ts +1 -1
  95. package/src/runtime/output/output-validator.ts +45 -0
  96. package/src/runtime/parent-guard.ts +3 -1
  97. package/src/runtime/peer-dep.ts +2 -1
  98. package/src/runtime/per-write-validator.ts +0 -5
  99. package/src/runtime/pi-spawn.ts +61 -15
  100. package/src/runtime/plan-approval.ts +125 -0
  101. package/src/runtime/plan-replan.ts +151 -0
  102. package/src/runtime/recovery/checkpoint.ts +0 -18
  103. package/src/runtime/recovery/crash-recovery.ts +95 -46
  104. package/src/runtime/scheduler-context.ts +98 -0
  105. package/src/runtime/scheduling/coalesce-tasks.ts +5 -0
  106. package/src/runtime/scheduling/global-worker-cap.ts +2 -1
  107. package/src/runtime/scheduling/nested-slots.ts +70 -0
  108. package/src/runtime/scheduling/run-coalesced-task-group.ts +64 -13
  109. package/src/runtime/scheduling/task-graph-scheduler.ts +0 -10
  110. package/src/runtime/scratchpad/README.md +10 -5
  111. package/src/runtime/scratchpad/guest.ts +103 -2
  112. package/src/runtime/scratchpad/protocol.ts +13 -1
  113. package/src/runtime/scratchpad/transform.ts +206 -12
  114. package/src/runtime/settings-store.ts +219 -0
  115. package/src/runtime/spawn-policy.ts +217 -0
  116. package/src/runtime/stale-reconciler.ts +87 -6
  117. package/src/runtime/subagent-manager.ts +25 -1
  118. package/src/runtime/task-output-context.ts +230 -9
  119. package/src/runtime/task-packet.ts +23 -1
  120. package/src/runtime/task-runner/child-executor.ts +106 -7
  121. package/src/runtime/task-runner/post-execution.ts +125 -1
  122. package/src/runtime/task-runner/pre-execution.ts +39 -1
  123. package/src/runtime/task-runner/prompt-builder.ts +51 -1
  124. package/src/runtime/task-runner/retrieval-orchestrator.ts +72 -18
  125. package/src/runtime/task-runner/spec-evidence.ts +403 -0
  126. package/src/runtime/task-runner/state-helpers.ts +26 -24
  127. package/src/runtime/task-runner.ts +11 -0
  128. package/src/runtime/team-runner.ts +129 -1671
  129. package/src/runtime/verification/spec-sandbox.ts +255 -0
  130. package/src/runtime/verification/verification-gates.ts +3 -2
  131. package/src/runtime/verification/verification-worktree.ts +2 -1
  132. package/src/runtime/workflow-phase-advance.ts +100 -0
  133. package/src/runtime/workspace-tree.ts +9 -0
  134. package/src/schema/config-schema.ts +65 -25
  135. package/src/schema/sensitive-config-paths.ts +64 -0
  136. package/src/schema/team-tool-schema.ts +13 -3
  137. package/src/state/README.md +4 -10
  138. package/src/state/atomic-write.ts +20 -3
  139. package/src/state/contracts.ts +38 -0
  140. package/src/state/coordination/mailbox.ts +12 -2
  141. package/src/state/event-log/cursor.ts +223 -0
  142. package/src/state/event-log/event-log-rotation.ts +12 -4
  143. package/src/state/event-log/event-log.ts +152 -369
  144. package/src/state/event-log/sequence-cache.ts +373 -0
  145. package/src/state/event-log/worker-atomic-writer.ts +2 -1
  146. package/src/state/stores/active-run-registry.ts +3 -2
  147. package/src/state/stores/manifest-io.ts +237 -0
  148. package/src/state/stores/ownership-map.ts +162 -0
  149. package/src/state/stores/plan-store.ts +241 -0
  150. package/src/state/stores/run-cache.ts +0 -90
  151. package/src/state/stores/spec-store.ts +189 -0
  152. package/src/state/stores/state-store.ts +66 -229
  153. package/src/state/types.ts +197 -0
  154. package/src/ui/dashboard-panes/plan-pane.ts +136 -0
  155. package/src/ui/dashboard-panes/progress-pane.ts +6 -0
  156. package/src/ui/dashboard-panes/transcript-pane.ts +31 -0
  157. package/src/ui/heartbeat-aggregator.ts +9 -1
  158. package/src/ui/keybinding-map.ts +54 -13
  159. package/src/ui/powerbar-publisher.ts +52 -1
  160. package/src/ui/run-dashboard.ts +31 -5
  161. package/src/ui/run-snapshot-cache.ts +57 -30
  162. package/src/ui/snapshot-types.ts +6 -1
  163. package/src/ui/widget/widget-renderer.ts +9 -1
  164. package/src/utils/file-coalescer.ts +0 -4
  165. package/src/utils/fs-errno.ts +66 -0
  166. package/src/utils/fs-watch.ts +1 -1
  167. package/src/utils/internal-error.ts +3 -1
  168. package/src/utils/paths.ts +11 -3
  169. package/src/utils/task-name-generator.ts +1 -8
  170. package/src/workflows/discover-workflows.ts +19 -2
  171. package/src/workflows/validate-workflow.ts +7 -1
  172. package/src/workflows/workflow-config.ts +10 -0
  173. package/src/workflows/workflow-serializer.ts +3 -0
  174. package/src/worktree/worktree-manager.ts +22 -0
  175. package/src/agents/agent-search.ts +0 -98
  176. package/src/benchmark/benchmark-runner.ts +0 -313
  177. package/src/benchmark/feedback-loop.ts +0 -73
  178. package/src/config/resilient-parser.ts +0 -117
  179. package/src/extension/crew-vibes/cat-frames.ts +0 -18
  180. package/src/extension/result-watcher.ts +0 -139
  181. package/src/observability/exporters/prometheus-exporter.ts +0 -54
  182. package/src/observability/metric-retention.ts +0 -64
  183. package/src/runtime/compaction/compaction-summary.ts +0 -278
  184. package/src/runtime/errors/crew-errors.ts +0 -162
  185. package/src/runtime/live-session/intercom-bridge.ts +0 -187
  186. package/src/runtime/loop-gates.ts +0 -128
  187. package/src/runtime/metric-parser.ts +0 -36
  188. package/src/runtime/output/stream-preview.ts +0 -184
  189. package/src/runtime/output/tool-progress.ts +0 -278
  190. package/src/runtime/phase-tracker.ts +0 -385
  191. package/src/runtime/pipeline-runner.ts +0 -523
  192. package/src/runtime/process/process-lifecycle.ts +0 -491
  193. package/src/runtime/recovery/retry-runner.ts +0 -330
  194. package/src/runtime/run-drift.ts +0 -219
  195. package/src/runtime/scratchpad/snapshot-hmac.ts +0 -167
  196. package/src/runtime/task-quality.ts +0 -199
  197. package/src/runtime/task-runner/run-projection.ts +0 -128
  198. package/src/runtime/verification/post-checks.ts +0 -142
  199. package/src/state/coordination/schedule.ts +0 -166
  200. package/src/state/event-log/jsonl-writer.ts +0 -115
  201. package/src/state/hook-instinct-bridge.ts +0 -94
  202. package/src/state/hook-integrations.ts +0 -51
  203. package/src/state/session-state-map.ts +0 -51
  204. package/src/state/stores/blob-store.ts +0 -308
  205. package/src/state/stores/instinct-store.ts +0 -275
  206. package/src/state/stores/observation-store.ts +0 -176
  207. package/src/state/tiered-eval.ts +0 -480
  208. package/src/state/types-eval.ts +0 -58
  209. package/src/tools/safe-bash-extension.ts +0 -54
  210. package/src/tools/safe-bash.ts +0 -505
  211. package/src/ui/agent-management-overlay.ts +0 -160
  212. package/src/ui/crew-footer.ts +0 -102
  213. package/src/ui/crew-select-list.ts +0 -114
  214. package/src/ui/dashboard-panes/capability-pane.ts +0 -77
  215. package/src/ui/transcript-entries.ts +0 -256
  216. package/src/utils/conflict-detect.ts +0 -721
  217. package/src/utils/fingerprint.ts +0 -180
  218. package/src/utils/gh-protocol.ts +0 -556
  219. package/src/utils/project-detector.ts +0 -160
  220. package/src/utils/sse-parser.ts +0 -131
  221. package/src/workflows/cost-estimator.ts +0 -34
  222. package/src/workflows/intermediate-store.ts +0 -166
@@ -1,160 +0,0 @@
1
- /**
2
- * Agent Management Overlay — displays discovered agents with their configuration.
3
- * Read-only view of agent definitions from builtin/user/project sources.
4
- * Future: enable/disable toggle, model override editing.
5
- */
6
- import type { AgentConfig, ResourceSource } from "../agents/agent-config.ts";
7
- import { truncate } from "../utils/visual.ts";
8
-
9
- export interface AgentEntry {
10
- name: string;
11
- description: string;
12
- source: ResourceSource;
13
- model?: string;
14
- thinking?: string;
15
- loadMode?: string;
16
- contextMode?: string;
17
- disabled?: boolean;
18
- filePath: string;
19
- }
20
-
21
- export function agentToEntry(agent: AgentConfig): AgentEntry {
22
- return {
23
- name: agent.name,
24
- description: agent.description,
25
- source: agent.source,
26
- model: agent.model,
27
- thinking: agent.thinking,
28
- loadMode: agent.loadMode,
29
- contextMode: agent.contextMode,
30
- disabled: agent.disabled,
31
- filePath: agent.filePath,
32
- };
33
- }
34
-
35
- function sourceIcon(source: ResourceSource): string {
36
- switch (source) {
37
- case "builtin":
38
- return "📦";
39
- case "user":
40
- return "👤";
41
- case "project":
42
- return "📂";
43
- case "git":
44
- return "🔗";
45
- case "dynamic":
46
- return "⚡";
47
- default:
48
- return "❓";
49
- }
50
- }
51
-
52
- function sourceLabel(source: ResourceSource): string {
53
- switch (source) {
54
- case "builtin":
55
- return "builtin";
56
- case "user":
57
- return "user";
58
- case "project":
59
- return "project";
60
- case "git":
61
- return "git";
62
- case "dynamic":
63
- return "dynamic";
64
- default:
65
- return "unknown";
66
- }
67
- }
68
-
69
- export interface AgentOverlayState {
70
- entries: AgentEntry[];
71
- selectedIndex: number;
72
- scrollOffset: number;
73
- expanded: Set<number>;
74
- maxVisible: number;
75
- }
76
-
77
- export function createAgentOverlayState(entries: AgentEntry[], maxVisible = 20): AgentOverlayState {
78
- return {
79
- entries: entries.sort((a, b) => {
80
- const order: Record<ResourceSource, number> = {
81
- project: 0,
82
- "project-pi": 1,
83
- user: 2,
84
- git: 3,
85
- builtin: 4,
86
- dynamic: 5,
87
- };
88
- const diff = (order[a.source] ?? 4) - (order[b.source] ?? 4);
89
- return diff !== 0 ? diff : a.name.localeCompare(b.name);
90
- }),
91
- selectedIndex: 0,
92
- scrollOffset: 0,
93
- expanded: new Set(),
94
- maxVisible,
95
- };
96
- }
97
-
98
- export function moveSelection(state: AgentOverlayState, direction: -1 | 1): AgentOverlayState {
99
- const next = Math.max(0, Math.min(state.entries.length - 1, state.selectedIndex + direction));
100
- const visibleStart = state.scrollOffset;
101
- const visibleEnd = state.scrollOffset + state.maxVisible;
102
- const newScroll = next < visibleStart ? next : next >= visibleEnd ? Math.max(0, next - state.maxVisible + 1) : state.scrollOffset;
103
- return { ...state, selectedIndex: next, scrollOffset: newScroll };
104
- }
105
-
106
- export function toggleExpand(state: AgentOverlayState): AgentOverlayState {
107
- const expanded = new Set(state.expanded);
108
- if (expanded.has(state.selectedIndex)) {
109
- expanded.delete(state.selectedIndex);
110
- } else {
111
- expanded.add(state.selectedIndex);
112
- }
113
- return { ...state, expanded };
114
- }
115
-
116
- export function renderAgentOverlay(state: AgentOverlayState, width: number): string[] {
117
- const lines: string[] = [];
118
- const header = ` Agent Configuration (${state.entries.length} agents)`;
119
- lines.push(truncate(header, width));
120
- lines.push(truncate("─".repeat(Math.min(width, 60)), width));
121
-
122
- if (state.entries.length === 0) {
123
- lines.push(truncate(" No agents discovered.", width));
124
- return lines;
125
- }
126
-
127
- const visible = state.entries.slice(state.scrollOffset, state.scrollOffset + state.maxVisible);
128
-
129
- for (const [i, entry] of visible.entries()) {
130
- const globalIndex = state.scrollOffset + i;
131
- const isSelected = globalIndex === state.selectedIndex;
132
- const isExpanded = state.expanded.has(globalIndex);
133
- const cursor = isSelected ? "▸" : " ";
134
- const disabled = entry.disabled ? " [disabled]" : "";
135
- const model = entry.model ? ` (${entry.model})` : "";
136
-
137
- const summary = `${cursor} ${sourceIcon(entry.source)} ${entry.name}${model}${disabled}`;
138
- lines.push(truncate(summary, width));
139
-
140
- if (isExpanded) {
141
- const desc = ` ${entry.description}`;
142
- lines.push(truncate(desc, width));
143
- const meta: string[] = [` source: ${sourceLabel(entry.source)}`];
144
- if (entry.model) meta.push(`model: ${entry.model}`);
145
- if (entry.thinking) meta.push(`thinking: ${entry.thinking}`);
146
- if (entry.loadMode) meta.push(`loadMode: ${entry.loadMode}`);
147
- if (entry.contextMode) meta.push(`context: ${entry.contextMode}`);
148
- meta.push(`file: ${entry.filePath}`);
149
- lines.push(truncate(meta.join(" · "), width));
150
- lines.push(truncate("─".repeat(Math.min(width - 4, 50)), width));
151
- }
152
- }
153
-
154
- if (state.scrollOffset + state.maxVisible < state.entries.length) {
155
- const remaining = state.entries.length - state.scrollOffset - state.maxVisible;
156
- lines.push(truncate(` … +${remaining} more`, width));
157
- }
158
-
159
- return lines;
160
- }
@@ -1,102 +0,0 @@
1
- import type { UsageState } from "../state/types.ts";
2
- import { pad, truncate } from "../utils/visual.ts";
3
- import type { RunStatus } from "./status-colors.ts";
4
- import type { CrewTheme } from "./theme-adapter.ts";
5
-
6
- export interface CrewFooterData {
7
- pwd: string;
8
- branch?: string;
9
- runId?: string;
10
- status?: RunStatus;
11
- usage?: UsageState;
12
- contextWindow?: number;
13
- contextPercent?: number;
14
- badges?: string[];
15
- }
16
-
17
- function formatCount(value: number | undefined): string {
18
- if (value === undefined || !Number.isFinite(value)) return "?";
19
- if (Math.abs(value) >= 1000) return `${(value / 1000).toFixed(Math.abs(value) >= 10_000 ? 0 : 1)}k`;
20
- return `${value}`;
21
- }
22
-
23
- function formatCost(value: number | undefined): string {
24
- return value === undefined || !Number.isFinite(value) ? "$0.0000" : `$${value.toFixed(4)}`;
25
- }
26
-
27
- function displayPwd(pwd: string): string {
28
- const home = process.env.HOME || process.env.USERPROFILE;
29
- if (home && pwd.startsWith(home)) return `~${pwd.slice(home.length) || "/"}`;
30
- return pwd || ".";
31
- }
32
-
33
- function contextText(data: CrewFooterData): string {
34
- const windowText = data.contextWindow && Number.isFinite(data.contextWindow) ? formatCount(data.contextWindow) : "window";
35
- const percent = data.contextPercent;
36
- if (percent === undefined || !Number.isFinite(percent)) return `?/${windowText}`;
37
- return `${percent.toFixed(1)}%/${windowText}`;
38
- }
39
-
40
- export class CrewFooter {
41
- private data: CrewFooterData;
42
- private readonly theme: CrewTheme;
43
- private cacheKey = "";
44
- private cacheWidth = 0;
45
- private cacheLines: string[] = [];
46
-
47
- constructor(data: CrewFooterData, theme: CrewTheme) {
48
- this.data = data;
49
- this.theme = theme;
50
- }
51
-
52
- setData(data: CrewFooterData): void {
53
- this.data = data;
54
- this.invalidate();
55
- }
56
-
57
- invalidate(): void {
58
- this.cacheKey = "";
59
- this.cacheLines = [];
60
- }
61
-
62
- render(width: number): string[] {
63
- const key = JSON.stringify(this.data);
64
- if (this.cacheKey === key && this.cacheWidth === width && this.cacheLines.length) return this.cacheLines;
65
- const lineWidth = Math.max(1, width);
66
- const firstParts = [
67
- displayPwd(this.data.pwd),
68
- this.data.branch ? `(${this.data.branch})` : undefined,
69
- this.data.runId,
70
- this.data.status,
71
- ].filter((part): part is string => Boolean(part));
72
- const usage = this.data.usage;
73
- const context = contextText(this.data);
74
- const contextPercent = this.data.contextPercent;
75
- const contextColor =
76
- contextPercent !== undefined && Number.isFinite(contextPercent)
77
- ? contextPercent > 90
78
- ? "error"
79
- : contextPercent > 70
80
- ? "warning"
81
- : undefined
82
- : undefined;
83
- const contextRendered = contextColor ? this.theme.fg(contextColor, context) : context;
84
- const usageLine = [
85
- `↑${formatCount(usage?.input)}`,
86
- `↓${formatCount(usage?.output)}`,
87
- `R ${formatCount(usage?.cacheRead)} cache`,
88
- `W ${formatCount(usage?.cacheWrite)} cache`,
89
- formatCost(usage?.cost),
90
- contextRendered,
91
- ].join(" • ");
92
- const badges = this.data.badges?.length ? this.data.badges.map((badge) => `[${badge}]`).join(" ") : "";
93
- this.cacheLines = [
94
- this.theme.fg("dim", pad(truncate(firstParts.join(" • "), lineWidth), lineWidth)),
95
- this.theme.fg("dim", pad(truncate(usageLine, lineWidth), lineWidth)),
96
- this.theme.fg("dim", pad(truncate(badges, lineWidth), lineWidth)),
97
- ];
98
- this.cacheKey = key;
99
- this.cacheWidth = width;
100
- return this.cacheLines;
101
- }
102
- }
@@ -1,114 +0,0 @@
1
- import { pad, truncate } from "../utils/visual.ts";
2
- import { keyOf } from "./key-utils.ts";
3
- import type { CrewTheme } from "./theme-adapter.ts";
4
-
5
- export interface CrewSelectItem<T = string> {
6
- value: T;
7
- label: string;
8
- description?: string;
9
- }
10
-
11
- export interface CrewSelectListOptions<T = string> {
12
- onSelect: (item: CrewSelectItem<T>) => void;
13
- onCancel: () => void;
14
- onPreview?: (item: CrewSelectItem<T>) => void;
15
- maxHeight?: number;
16
- }
17
-
18
- export class CrewSelectList<T = string> {
19
- private readonly items: CrewSelectItem<T>[];
20
- private readonly theme: CrewTheme;
21
- private readonly options: CrewSelectListOptions<T>;
22
- private selectedIndex = 0;
23
- private scrollOffset = 0;
24
-
25
- constructor(items: CrewSelectItem<T>[], theme: CrewTheme, options: CrewSelectListOptions<T>) {
26
- this.items = [...items];
27
- this.theme = theme;
28
- this.options = options;
29
- this.selectedIndex = this.items.length ? 0 : -1;
30
- }
31
-
32
- invalidate(): void {
33
- /* no-op */
34
- }
35
-
36
- getSelected(): CrewSelectItem<T> | undefined {
37
- return this.selectedIndex >= 0 ? this.items[this.selectedIndex] : undefined;
38
- }
39
-
40
- setSelectedIndex(index: number): void {
41
- if (!this.items.length) {
42
- this.selectedIndex = -1;
43
- this.scrollOffset = 0;
44
- return;
45
- }
46
- const next = Math.min(this.items.length - 1, Math.max(0, index));
47
- const changed = next !== this.selectedIndex;
48
- this.selectedIndex = next;
49
- this.ensureVisible();
50
- if (changed) {
51
- const selected = this.getSelected();
52
- if (selected) this.options.onPreview?.(selected);
53
- }
54
- }
55
-
56
- handleInput(data: string): void {
57
- if (data === "q" || data === "\u001b") {
58
- this.options.onCancel();
59
- return;
60
- }
61
- if (data === "j" || keyOf(data) === "down") {
62
- this.setSelectedIndex(this.selectedIndex + 1);
63
- return;
64
- }
65
- if (data === "k" || keyOf(data) === "up") {
66
- this.setSelectedIndex(this.selectedIndex - 1);
67
- return;
68
- }
69
- if (data === "\r" || data === "\n") {
70
- const selected = this.getSelected();
71
- if (selected) this.options.onSelect(selected);
72
- }
73
- }
74
-
75
- render(width: number): string[] {
76
- if (!this.items.length) return [this.theme.fg("muted", "(no items)")];
77
- const maxHeight = Math.max(1, Math.floor(this.options.maxHeight ?? this.items.length));
78
- this.ensureVisible();
79
- const hasTop = this.scrollOffset > 0;
80
- const availableWithoutBottom = Math.max(1, maxHeight - (hasTop ? 1 : 0));
81
- const hasBottom = this.scrollOffset + availableWithoutBottom < this.items.length;
82
- const slots = this.visibleItemSlots(maxHeight, hasTop, hasBottom);
83
- const visibleItems = this.items.slice(this.scrollOffset, this.scrollOffset + slots);
84
- const lines: string[] = [];
85
- if (hasTop) lines.push(this.theme.fg("muted", `↑ ${this.scrollOffset} more`));
86
- for (const [offset, item] of visibleItems.entries()) {
87
- const index = this.scrollOffset + offset;
88
- const prefix = index === this.selectedIndex ? " → " : " ";
89
- const suffix = item.description ? this.theme.fg("dim", ` — ${item.description}`) : "";
90
- const raw = `${prefix}${item.label}${suffix}`;
91
- const line = index === this.selectedIndex ? (this.theme.inverse?.(raw) ?? raw) : raw;
92
- lines.push(pad(truncate(line, width), Math.max(1, width)));
93
- }
94
- if (hasBottom) lines.push(this.theme.fg("muted", `↓ ${this.items.length - (this.scrollOffset + slots)} more`));
95
- return lines.slice(0, maxHeight);
96
- }
97
-
98
- private visibleItemSlots(maxHeight: number, hasTop: boolean, hasBottom: boolean): number {
99
- return Math.max(1, maxHeight - (hasTop ? 1 : 0) - (hasBottom ? 1 : 0));
100
- }
101
-
102
- private ensureVisible(): void {
103
- if (this.selectedIndex < 0) return;
104
- const maxHeight = Math.max(1, Math.floor(this.options.maxHeight ?? this.items.length));
105
- const reservedTop = this.scrollOffset > 0 ? 1 : 0;
106
- const visibleSlots = Math.max(1, maxHeight - reservedTop - 1);
107
- if (this.selectedIndex < this.scrollOffset) {
108
- this.scrollOffset = this.selectedIndex;
109
- } else if (this.selectedIndex >= this.scrollOffset + visibleSlots) {
110
- this.scrollOffset = Math.max(0, this.selectedIndex - visibleSlots + 1);
111
- }
112
- this.scrollOffset = Math.min(this.scrollOffset, Math.max(0, this.items.length - 1));
113
- }
114
- }
@@ -1,77 +0,0 @@
1
- import type { PiTeamsConfig } from "../../config/config.ts";
2
- import type { CapabilityItem } from "../../runtime/capability-inventory.ts";
3
- import { buildCapabilityInventory } from "../../runtime/capability-inventory.ts";
4
-
5
- export interface CapabilityPaneOptions {
6
- config?: PiTeamsConfig;
7
- filter?: string;
8
- }
9
-
10
- function kindIcon(kind: string): string {
11
- switch (kind) {
12
- case "team":
13
- return "👥";
14
- case "workflow":
15
- return "📋";
16
- case "agent":
17
- return "🤖";
18
- case "skill":
19
- return "🔧";
20
- case "tool":
21
- return "🛠";
22
- case "runtime":
23
- return "⚙";
24
- default:
25
- return "•";
26
- }
27
- }
28
-
29
- function stateLabel(state: string): string {
30
- switch (state) {
31
- case "active":
32
- return "";
33
- case "disabled":
34
- return " [DISABLED]";
35
- case "shadowed":
36
- return " [SHADOWED]";
37
- case "missing":
38
- return " [MISSING]";
39
- default:
40
- return "";
41
- }
42
- }
43
-
44
- export function renderCapabilityPane(cwd: string, opts: CapabilityPaneOptions = {}): string[] {
45
- const inventory = buildCapabilityInventory(cwd, opts.config);
46
- const filtered = opts.filter
47
- ? inventory.filter(
48
- (item) =>
49
- item.kind.includes(opts.filter!.toLowerCase()) ||
50
- item.name.toLowerCase().includes(opts.filter!.toLowerCase()) ||
51
- item.id.toLowerCase().includes(opts.filter!.toLowerCase()),
52
- )
53
- : inventory;
54
-
55
- if (filtered.length === 0) return ["Capability pane: no items found"];
56
-
57
- const byKind = new Map<string, CapabilityItem[]>();
58
- for (const item of filtered) {
59
- const group = byKind.get(item.kind) ?? [];
60
- group.push(item);
61
- byKind.set(item.kind, group);
62
- }
63
-
64
- const lines = [`Capability pane: ${filtered.length} item(s) (filter: ${opts.filter ?? "none"})`];
65
- for (const [kind, items] of byKind) {
66
- lines.push(` ${kindIcon(kind)} ${kind} (${items.length}):`);
67
- for (const item of items.slice(0, 10)) {
68
- const icon = item.state === "active" ? "✓" : "✗";
69
- lines.push(` ${icon} ${item.name}${stateLabel(item.state)} [${item.source}]`);
70
- }
71
- if (items.length > 10) lines.push(` ... and ${items.length - 10} more`);
72
- }
73
-
74
- const disabled = filtered.filter((i) => i.state === "disabled").length;
75
- if (disabled > 0) lines.push(` Disabled: ${disabled}`);
76
- return lines;
77
- }