pi-crew 0.1.45 → 0.1.46

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 (198) hide show
  1. package/README.md +5 -5
  2. package/agents/analyst.md +1 -1
  3. package/agents/critic.md +1 -1
  4. package/agents/executor.md +1 -1
  5. package/agents/explorer.md +1 -1
  6. package/agents/planner.md +1 -1
  7. package/agents/reviewer.md +1 -1
  8. package/agents/security-reviewer.md +1 -1
  9. package/agents/test-engineer.md +1 -1
  10. package/agents/verifier.md +1 -1
  11. package/agents/writer.md +1 -1
  12. package/docs/next-upgrade-roadmap.md +733 -0
  13. package/docs/refactor-tasks-phase3.md +394 -394
  14. package/docs/refactor-tasks-phase4.md +564 -564
  15. package/docs/refactor-tasks-phase5.md +402 -402
  16. package/docs/refactor-tasks-phase6.md +662 -662
  17. package/docs/research-awesome-agent-skills-distillation.md +100 -0
  18. package/docs/research-extension-examples.md +297 -297
  19. package/docs/research-extension-system.md +324 -324
  20. package/docs/research-oh-my-pi-distillation.md +322 -0
  21. package/docs/research-optimization-plan.md +548 -548
  22. package/docs/research-phase10-distillation.md +198 -198
  23. package/docs/research-phase11-distillation.md +201 -201
  24. package/docs/research-pi-coding-agent.md +357 -357
  25. package/docs/research-source-pi-crew-reference.md +174 -174
  26. package/docs/runtime-flow.md +148 -148
  27. package/docs/source-runtime-refactor-map.md +107 -83
  28. package/docs/usage.md +3 -3
  29. package/index.ts +6 -6
  30. package/install.mjs +52 -8
  31. package/package.json +1 -1
  32. package/schema.json +2 -1
  33. package/skills/async-worker-recovery/SKILL.md +42 -0
  34. package/skills/context-artifact-hygiene/SKILL.md +52 -0
  35. package/skills/delegation-patterns/SKILL.md +54 -0
  36. package/skills/mailbox-interactive/SKILL.md +40 -0
  37. package/skills/model-routing-context/SKILL.md +39 -0
  38. package/skills/multi-perspective-review/SKILL.md +58 -0
  39. package/skills/observability-reliability/SKILL.md +41 -0
  40. package/skills/ownership-session-security/SKILL.md +41 -0
  41. package/skills/pi-extension-lifecycle/SKILL.md +39 -0
  42. package/skills/requirements-to-task-packet/SKILL.md +63 -0
  43. package/skills/resource-discovery-config/SKILL.md +41 -0
  44. package/skills/runtime-state-reader/SKILL.md +44 -0
  45. package/skills/secure-agent-orchestration-review/SKILL.md +45 -0
  46. package/skills/state-mutation-locking/SKILL.md +42 -0
  47. package/skills/systematic-debugging/SKILL.md +67 -0
  48. package/skills/ui-render-performance/SKILL.md +39 -0
  49. package/skills/verification-before-done/SKILL.md +57 -0
  50. package/skills/worktree-isolation/SKILL.md +39 -0
  51. package/src/agents/agent-serializer.ts +34 -34
  52. package/src/agents/discover-agents.ts +12 -11
  53. package/src/config/config.ts +48 -24
  54. package/src/config/defaults.ts +14 -0
  55. package/src/extension/cross-extension-rpc.ts +82 -82
  56. package/src/extension/project-init.ts +62 -2
  57. package/src/extension/register.ts +11 -9
  58. package/src/extension/registration/commands.ts +32 -25
  59. package/src/extension/registration/compaction-guard.ts +125 -125
  60. package/src/extension/registration/subagent-helpers.ts +8 -0
  61. package/src/extension/registration/subagent-tools.ts +149 -148
  62. package/src/extension/registration/team-tool.ts +8 -6
  63. package/src/extension/run-bundle-schema.ts +89 -89
  64. package/src/extension/run-index.ts +13 -5
  65. package/src/extension/run-maintenance.ts +62 -43
  66. package/src/extension/team-tool/api.ts +25 -8
  67. package/src/extension/team-tool/cancel.ts +33 -4
  68. package/src/extension/team-tool/context.ts +5 -0
  69. package/src/extension/team-tool/handle-settings.ts +188 -188
  70. package/src/extension/team-tool/inspect.ts +41 -41
  71. package/src/extension/team-tool/lifecycle-actions.ts +91 -79
  72. package/src/extension/team-tool/plan.ts +19 -19
  73. package/src/extension/team-tool/respond.ts +37 -17
  74. package/src/extension/team-tool/run.ts +52 -10
  75. package/src/extension/team-tool/status.ts +12 -1
  76. package/src/extension/team-tool-types.ts +2 -0
  77. package/src/extension/team-tool.ts +32 -11
  78. package/src/i18n.ts +184 -184
  79. package/src/observability/event-to-metric.ts +8 -1
  80. package/src/observability/exporters/otlp-exporter.ts +77 -77
  81. package/src/prompt/prompt-runtime.ts +72 -72
  82. package/src/runtime/agent-control.ts +63 -63
  83. package/src/runtime/agent-memory.ts +72 -72
  84. package/src/runtime/agent-observability.ts +114 -114
  85. package/src/runtime/async-marker.ts +26 -26
  86. package/src/runtime/attention-events.ts +28 -28
  87. package/src/runtime/background-runner.ts +59 -53
  88. package/src/runtime/cancellation.ts +51 -0
  89. package/src/runtime/child-pi.ts +457 -444
  90. package/src/runtime/completion-guard.ts +190 -190
  91. package/src/runtime/crash-recovery.ts +1 -0
  92. package/src/runtime/crew-agent-records.ts +38 -6
  93. package/src/runtime/deadletter.ts +1 -0
  94. package/src/runtime/delivery-coordinator.ts +46 -25
  95. package/src/runtime/direct-run.ts +35 -35
  96. package/src/runtime/effectiveness.ts +76 -0
  97. package/src/runtime/foreground-control.ts +82 -82
  98. package/src/runtime/green-contract.ts +46 -46
  99. package/src/runtime/group-join.ts +106 -106
  100. package/src/runtime/heartbeat-gradient.ts +28 -28
  101. package/src/runtime/heartbeat-watcher.ts +124 -124
  102. package/src/runtime/live-agent-control.ts +88 -87
  103. package/src/runtime/live-agent-manager.ts +103 -85
  104. package/src/runtime/live-control-realtime.ts +36 -36
  105. package/src/runtime/live-session-runtime.ts +309 -305
  106. package/src/runtime/manifest-cache.ts +17 -2
  107. package/src/runtime/model-fallback.ts +6 -4
  108. package/src/runtime/parallel-research.ts +44 -44
  109. package/src/runtime/pi-args.ts +18 -3
  110. package/src/runtime/pi-json-output.ts +111 -111
  111. package/src/runtime/policy-engine.ts +79 -79
  112. package/src/runtime/process-status.ts +5 -1
  113. package/src/runtime/progress-event-coalescer.ts +43 -43
  114. package/src/runtime/recovery-recipes.ts +74 -74
  115. package/src/runtime/retry-executor.ts +81 -64
  116. package/src/runtime/role-permission.ts +39 -39
  117. package/src/runtime/runtime-resolver.ts +22 -6
  118. package/src/runtime/session-resources.ts +25 -25
  119. package/src/runtime/session-snapshot.ts +59 -59
  120. package/src/runtime/session-usage.ts +79 -79
  121. package/src/runtime/sidechain-output.ts +29 -29
  122. package/src/runtime/skill-instructions.ts +222 -0
  123. package/src/runtime/stale-reconciler.ts +4 -14
  124. package/src/runtime/subagent-manager.ts +3 -0
  125. package/src/runtime/supervisor-contact.ts +59 -59
  126. package/src/runtime/task-display.ts +38 -38
  127. package/src/runtime/task-output-context.ts +127 -127
  128. package/src/runtime/task-runner/capabilities.ts +78 -0
  129. package/src/runtime/task-runner/live-executor.ts +105 -101
  130. package/src/runtime/task-runner/progress.ts +119 -119
  131. package/src/runtime/task-runner/prompt-builder.ts +3 -1
  132. package/src/runtime/task-runner/prompt-pipeline.ts +64 -0
  133. package/src/runtime/task-runner/result-utils.ts +14 -14
  134. package/src/runtime/task-runner/state-helpers.ts +22 -22
  135. package/src/runtime/task-runner.ts +44 -5
  136. package/src/runtime/team-runner.ts +78 -15
  137. package/src/runtime/worker-heartbeat.ts +21 -21
  138. package/src/runtime/worker-startup.ts +57 -57
  139. package/src/schema/config-schema.ts +1 -0
  140. package/src/schema/team-tool-schema.ts +3 -3
  141. package/src/state/active-run-registry.ts +165 -0
  142. package/src/state/contracts.ts +1 -1
  143. package/src/state/mailbox.ts +44 -4
  144. package/src/state/state-store.ts +8 -1
  145. package/src/state/task-claims.ts +44 -44
  146. package/src/state/types.ts +44 -2
  147. package/src/state/usage.ts +29 -29
  148. package/src/subagents/async-entry.ts +1 -1
  149. package/src/subagents/index.ts +3 -3
  150. package/src/subagents/live/control.ts +1 -1
  151. package/src/subagents/live/manager.ts +1 -1
  152. package/src/subagents/live/realtime.ts +1 -1
  153. package/src/subagents/live/session-runtime.ts +1 -1
  154. package/src/subagents/manager.ts +1 -1
  155. package/src/subagents/spawn.ts +1 -1
  156. package/src/teams/team-config.ts +1 -0
  157. package/src/teams/team-serializer.ts +38 -38
  158. package/src/types/diff.d.ts +18 -18
  159. package/src/ui/crew-footer.ts +101 -101
  160. package/src/ui/crew-select-list.ts +111 -111
  161. package/src/ui/crew-widget.ts +4 -3
  162. package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
  163. package/src/ui/dashboard-panes/progress-pane.ts +2 -0
  164. package/src/ui/dynamic-border.ts +25 -25
  165. package/src/ui/layout-primitives.ts +106 -106
  166. package/src/ui/loaders.ts +158 -158
  167. package/src/ui/render-diff.ts +119 -119
  168. package/src/ui/render-scheduler.ts +143 -143
  169. package/src/ui/run-snapshot-cache.ts +10 -2
  170. package/src/ui/snapshot-types.ts +2 -0
  171. package/src/ui/spinner.ts +17 -17
  172. package/src/ui/status-colors.ts +58 -58
  173. package/src/ui/syntax-highlight.ts +116 -116
  174. package/src/utils/atomic-write.ts +33 -33
  175. package/src/utils/completion-dedupe.ts +63 -63
  176. package/src/utils/frontmatter.ts +68 -68
  177. package/src/utils/git.ts +262 -262
  178. package/src/utils/ids.ts +12 -12
  179. package/src/utils/names.ts +27 -27
  180. package/src/utils/paths.ts +4 -2
  181. package/src/utils/redaction.ts +44 -44
  182. package/src/utils/safe-paths.ts +47 -47
  183. package/src/utils/sleep.ts +32 -32
  184. package/src/workflows/validate-workflow.ts +40 -40
  185. package/src/workflows/workflow-config.ts +1 -0
  186. package/src/worktree/branch-freshness.ts +45 -45
  187. package/teams/default.team.md +12 -12
  188. package/teams/fast-fix.team.md +11 -11
  189. package/teams/implementation.team.md +18 -18
  190. package/teams/parallel-research.team.md +14 -14
  191. package/teams/research.team.md +11 -11
  192. package/teams/review.team.md +12 -12
  193. package/workflows/default.workflow.md +29 -29
  194. package/workflows/fast-fix.workflow.md +22 -22
  195. package/workflows/implementation.workflow.md +38 -38
  196. package/workflows/parallel-research.workflow.md +46 -46
  197. package/workflows/research.workflow.md +22 -22
  198. package/workflows/review.workflow.md +30 -30
@@ -1,188 +1,188 @@
1
- import type { TeamContext } from "../team-tool/context.ts";
2
- import { loadConfig, updateConfig } from "../../config/config.ts";
3
- import { configPatchFromConfig } from "../team-tool/config-patch.ts";
4
- import { result } from "../team-tool/context.ts";
5
- import type { PiTeamsToolResult } from "../tool-result.ts";
6
-
7
- // ---------------------------------------------------------------------------
8
- // Helpers
9
- // ---------------------------------------------------------------------------
10
-
11
- function setNested(obj: Record<string, unknown>, path: string, value: unknown): void {
12
- const keys = path.split(".");
13
- let target: Record<string, unknown> = obj;
14
- for (let i = 0; i < keys.length - 1; i++) {
15
- if (!target[keys[i]] || typeof target[keys[i]] !== "object") {
16
- target[keys[i]] = {};
17
- }
18
- target = target[keys[i]] as Record<string, unknown>;
19
- }
20
- target[keys[keys.length - 1]] = value;
21
- }
22
-
23
- function getNested(obj: Record<string, unknown>, path: string): unknown {
24
- const keys = path.split(".");
25
- let current: unknown = obj;
26
- for (const key of keys) {
27
- if (!current || typeof current !== "object") return undefined;
28
- current = (current as Record<string, unknown>)[key];
29
- }
30
- return current;
31
- }
32
-
33
- function formatValue(value: unknown): string {
34
- if (value === undefined) return "<not set>";
35
- if (typeof value === "object") return JSON.stringify(value);
36
- return String(value);
37
- }
38
-
39
- function parseValue(raw: string): unknown {
40
- // JSON handles strings (quoted), numbers, booleans, null, arrays, objects.
41
- try { return JSON.parse(raw); } catch { /* keep as string */ }
42
- return raw;
43
- }
44
-
45
- // ---------------------------------------------------------------------------
46
- // Known config keys — mirrors config-schema.ts + config.ts.
47
- // When adding new config fields, add the dotted path here so team-settings
48
- // can discover and display them.
49
- // ---------------------------------------------------------------------------
50
-
51
- const KNOWN_KEYS = new Set([
52
- // top-level
53
- "asyncByDefault",
54
- "executeWorkers",
55
- "notifierIntervalMs",
56
- "requireCleanWorktreeLeader",
57
- // runtime
58
- "runtime.mode",
59
- "runtime.preferLiveSession",
60
- "runtime.allowChildProcessFallback",
61
- "runtime.maxTurns",
62
- "runtime.graceTurns",
63
- "runtime.inheritContext",
64
- "runtime.promptMode",
65
- "runtime.groupJoin",
66
- "runtime.groupJoinAckTimeoutMs",
67
- "runtime.requirePlanApproval",
68
- "runtime.completionMutationGuard",
69
- // limits
70
- "limits.maxConcurrentWorkers",
71
- "limits.allowUnboundedConcurrency",
72
- "limits.maxTaskDepth",
73
- "limits.maxChildrenPerTask",
74
- "limits.maxRunMinutes",
75
- "limits.maxRetriesPerTask",
76
- "limits.maxTasksPerRun",
77
- "limits.heartbeatStaleMs",
78
- // control
79
- "control.enabled",
80
- "control.needsAttentionAfterMs",
81
- // autonomous
82
- "autonomous.profile",
83
- "autonomous.enabled",
84
- "autonomous.injectPolicy",
85
- "autonomous.preferAsyncForLongTasks",
86
- "autonomous.allowWorktreeSuggestion",
87
- // tools
88
- "tools.enableClaudeStyleAliases",
89
- "tools.enableSteer",
90
- "tools.terminateOnForeground",
91
- // agents
92
- "agents.disableBuiltins",
93
- // observability
94
- "observability.prometheus.enabled",
95
- "observability.otlp.enabled",
96
- // worktree
97
- "worktree.enabled",
98
- ]);
99
-
100
- const KNOWN_SORTED = [...KNOWN_KEYS].sort();
101
-
102
- // ---------------------------------------------------------------------------
103
- // Detail objects – all require { action, status } from TeamToolDetails.
104
- // Extras (count, key, value, path) are passed as never to bypass the narrow
105
- // TeamToolDetails interface (consistent with the rest of the codebase).
106
- // ---------------------------------------------------------------------------
107
-
108
- const OK = { action: "settings", status: "ok" as const };
109
- const ERR = { action: "settings", status: "error" as const };
110
-
111
- // ---------------------------------------------------------------------------
112
- // Main handler
113
- // ---------------------------------------------------------------------------
114
-
115
- export function handleSettings(params: { config?: Record<string, unknown> }, ctx: TeamContext): PiTeamsToolResult {
116
- const cfg = (params.config ?? {}) as Record<string, unknown>;
117
- const args = typeof cfg.args === "string" ? cfg.args.trim() : "";
118
- const scope = cfg.scope === "project" ? "project" : "user";
119
- const loaded = loadConfig(ctx.cwd);
120
- const effective = loaded.config as Record<string, unknown>;
121
-
122
- // team-settings list
123
- if (!args || args === "list") {
124
- const lines = ["pi-crew settings:", `Path: ${loaded.path}`, ""];
125
- for (const key of KNOWN_SORTED) {
126
- const value = getNested(effective, key);
127
- lines.push(` ${key} = ${formatValue(value)}`);
128
- }
129
- lines.push("", "Usage: team-settings [list|get <key>|set <key> <value>|unset <key>|path|scope]");
130
- return result(lines.join("\n"), { ...OK, count: KNOWN_KEYS.size } as never);
131
- }
132
-
133
- // team-settings path
134
- if (args === "path") {
135
- return result(`pi-crew config path: ${loaded.path}`, { ...OK, path: loaded.path } as never);
136
- }
137
-
138
- // team-settings scope
139
- if (args === "scope") {
140
- return result(`Current scope: ${scope}\nConfig at: ${loaded.path}`, { ...OK, scope } as never);
141
- }
142
-
143
- // team-settings get <key>
144
- if (args.startsWith("get ")) {
145
- const key = args.slice(4).trim();
146
- if (!key) return result("Usage: team-settings get <key>", { ...ERR }, true);
147
- const value = getNested(effective, key);
148
- const note = KNOWN_KEYS.has(key) ? "" : " (unknown key — may not take effect)";
149
- return result(`${key} = ${formatValue(value)}${note}`, { ...OK, key, value } as never);
150
- }
151
-
152
- // team-settings unset <key>
153
- if (args.startsWith("unset ")) {
154
- const key = args.slice(6).trim();
155
- if (!key) return result("Usage: team-settings unset <key>", { ...ERR }, true);
156
- try {
157
- const saved = updateConfig({}, { cwd: ctx.cwd, scope, unsetPaths: [key] });
158
- return result(`Unset ${key}\nPath: ${saved.path}`, { ...OK, key } as never);
159
- } catch (error) {
160
- return result(error instanceof Error ? error.message : String(error), { ...ERR }, true);
161
- }
162
- }
163
-
164
- // team-settings set <key> <value>
165
- if (args.startsWith("set ")) {
166
- const rest = args.slice(4).trim();
167
- const spaceIdx = rest.indexOf(" ");
168
- if (spaceIdx === -1) return result("Usage: team-settings set <key> <value>", { ...ERR }, true);
169
- const key = rest.slice(0, spaceIdx);
170
- const rawValue = rest.slice(spaceIdx + 1).trim();
171
- if (!key) return result("Usage: team-settings set <key> <value>", { ...ERR }, true);
172
-
173
- const value = parseValue(rawValue);
174
- const patch = {};
175
- setNested(patch as Record<string, unknown>, key, value);
176
-
177
- try {
178
- const converted = configPatchFromConfig({ config: patch as Record<string, unknown> });
179
- const saved = updateConfig(converted, { cwd: ctx.cwd, scope });
180
- const warning = KNOWN_KEYS.has(key) ? "" : "\nWarning: unknown key — verify it exists in config schema.";
181
- return result(`Set ${key} = ${formatValue(value)}\nPath: ${saved.path}${warning}`, { ...OK, key, value } as never);
182
- } catch (error) {
183
- return result(error instanceof Error ? error.message : String(error), { ...ERR }, true);
184
- }
185
- }
186
-
187
- return result("Unknown subcommand. Usage: team-settings [list|get <key>|set <key> <value>|unset <key>|path|scope]", { ...ERR }, true);
188
- }
1
+ import type { TeamContext } from "../team-tool/context.ts";
2
+ import { loadConfig, updateConfig } from "../../config/config.ts";
3
+ import { configPatchFromConfig } from "../team-tool/config-patch.ts";
4
+ import { result } from "../team-tool/context.ts";
5
+ import type { PiTeamsToolResult } from "../tool-result.ts";
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // Helpers
9
+ // ---------------------------------------------------------------------------
10
+
11
+ function setNested(obj: Record<string, unknown>, path: string, value: unknown): void {
12
+ const keys = path.split(".");
13
+ let target: Record<string, unknown> = obj;
14
+ for (let i = 0; i < keys.length - 1; i++) {
15
+ if (!target[keys[i]] || typeof target[keys[i]] !== "object") {
16
+ target[keys[i]] = {};
17
+ }
18
+ target = target[keys[i]] as Record<string, unknown>;
19
+ }
20
+ target[keys[keys.length - 1]] = value;
21
+ }
22
+
23
+ function getNested(obj: Record<string, unknown>, path: string): unknown {
24
+ const keys = path.split(".");
25
+ let current: unknown = obj;
26
+ for (const key of keys) {
27
+ if (!current || typeof current !== "object") return undefined;
28
+ current = (current as Record<string, unknown>)[key];
29
+ }
30
+ return current;
31
+ }
32
+
33
+ function formatValue(value: unknown): string {
34
+ if (value === undefined) return "<not set>";
35
+ if (typeof value === "object") return JSON.stringify(value);
36
+ return String(value);
37
+ }
38
+
39
+ function parseValue(raw: string): unknown {
40
+ // JSON handles strings (quoted), numbers, booleans, null, arrays, objects.
41
+ try { return JSON.parse(raw); } catch { /* keep as string */ }
42
+ return raw;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Known config keys — mirrors config-schema.ts + config.ts.
47
+ // When adding new config fields, add the dotted path here so team-settings
48
+ // can discover and display them.
49
+ // ---------------------------------------------------------------------------
50
+
51
+ const KNOWN_KEYS = new Set([
52
+ // top-level
53
+ "asyncByDefault",
54
+ "executeWorkers",
55
+ "notifierIntervalMs",
56
+ "requireCleanWorktreeLeader",
57
+ // runtime
58
+ "runtime.mode",
59
+ "runtime.preferLiveSession",
60
+ "runtime.allowChildProcessFallback",
61
+ "runtime.maxTurns",
62
+ "runtime.graceTurns",
63
+ "runtime.inheritContext",
64
+ "runtime.promptMode",
65
+ "runtime.groupJoin",
66
+ "runtime.groupJoinAckTimeoutMs",
67
+ "runtime.requirePlanApproval",
68
+ "runtime.completionMutationGuard",
69
+ // limits
70
+ "limits.maxConcurrentWorkers",
71
+ "limits.allowUnboundedConcurrency",
72
+ "limits.maxTaskDepth",
73
+ "limits.maxChildrenPerTask",
74
+ "limits.maxRunMinutes",
75
+ "limits.maxRetriesPerTask",
76
+ "limits.maxTasksPerRun",
77
+ "limits.heartbeatStaleMs",
78
+ // control
79
+ "control.enabled",
80
+ "control.needsAttentionAfterMs",
81
+ // autonomous
82
+ "autonomous.profile",
83
+ "autonomous.enabled",
84
+ "autonomous.injectPolicy",
85
+ "autonomous.preferAsyncForLongTasks",
86
+ "autonomous.allowWorktreeSuggestion",
87
+ // tools
88
+ "tools.enableClaudeStyleAliases",
89
+ "tools.enableSteer",
90
+ "tools.terminateOnForeground",
91
+ // agents
92
+ "agents.disableBuiltins",
93
+ // observability
94
+ "observability.prometheus.enabled",
95
+ "observability.otlp.enabled",
96
+ // worktree
97
+ "worktree.enabled",
98
+ ]);
99
+
100
+ const KNOWN_SORTED = [...KNOWN_KEYS].sort();
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Detail objects – all require { action, status } from TeamToolDetails.
104
+ // Extras (count, key, value, path) are passed as never to bypass the narrow
105
+ // TeamToolDetails interface (consistent with the rest of the codebase).
106
+ // ---------------------------------------------------------------------------
107
+
108
+ const OK = { action: "settings", status: "ok" as const };
109
+ const ERR = { action: "settings", status: "error" as const };
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Main handler
113
+ // ---------------------------------------------------------------------------
114
+
115
+ export function handleSettings(params: { config?: Record<string, unknown> }, ctx: TeamContext): PiTeamsToolResult {
116
+ const cfg = (params.config ?? {}) as Record<string, unknown>;
117
+ const args = typeof cfg.args === "string" ? cfg.args.trim() : "";
118
+ const scope = cfg.scope === "project" ? "project" : "user";
119
+ const loaded = loadConfig(ctx.cwd);
120
+ const effective = loaded.config as Record<string, unknown>;
121
+
122
+ // team-settings list
123
+ if (!args || args === "list") {
124
+ const lines = ["pi-crew settings:", `Path: ${loaded.path}`, ""];
125
+ for (const key of KNOWN_SORTED) {
126
+ const value = getNested(effective, key);
127
+ lines.push(` ${key} = ${formatValue(value)}`);
128
+ }
129
+ lines.push("", "Usage: team-settings [list|get <key>|set <key> <value>|unset <key>|path|scope]");
130
+ return result(lines.join("\n"), { ...OK, count: KNOWN_KEYS.size } as never);
131
+ }
132
+
133
+ // team-settings path
134
+ if (args === "path") {
135
+ return result(`pi-crew config path: ${loaded.path}`, { ...OK, path: loaded.path } as never);
136
+ }
137
+
138
+ // team-settings scope
139
+ if (args === "scope") {
140
+ return result(`Current scope: ${scope}\nConfig at: ${loaded.path}`, { ...OK, scope } as never);
141
+ }
142
+
143
+ // team-settings get <key>
144
+ if (args.startsWith("get ")) {
145
+ const key = args.slice(4).trim();
146
+ if (!key) return result("Usage: team-settings get <key>", { ...ERR }, true);
147
+ const value = getNested(effective, key);
148
+ const note = KNOWN_KEYS.has(key) ? "" : " (unknown key — may not take effect)";
149
+ return result(`${key} = ${formatValue(value)}${note}`, { ...OK, key, value } as never);
150
+ }
151
+
152
+ // team-settings unset <key>
153
+ if (args.startsWith("unset ")) {
154
+ const key = args.slice(6).trim();
155
+ if (!key) return result("Usage: team-settings unset <key>", { ...ERR }, true);
156
+ try {
157
+ const saved = updateConfig({}, { cwd: ctx.cwd, scope, unsetPaths: [key] });
158
+ return result(`Unset ${key}\nPath: ${saved.path}`, { ...OK, key } as never);
159
+ } catch (error) {
160
+ return result(error instanceof Error ? error.message : String(error), { ...ERR }, true);
161
+ }
162
+ }
163
+
164
+ // team-settings set <key> <value>
165
+ if (args.startsWith("set ")) {
166
+ const rest = args.slice(4).trim();
167
+ const spaceIdx = rest.indexOf(" ");
168
+ if (spaceIdx === -1) return result("Usage: team-settings set <key> <value>", { ...ERR }, true);
169
+ const key = rest.slice(0, spaceIdx);
170
+ const rawValue = rest.slice(spaceIdx + 1).trim();
171
+ if (!key) return result("Usage: team-settings set <key> <value>", { ...ERR }, true);
172
+
173
+ const value = parseValue(rawValue);
174
+ const patch = {};
175
+ setNested(patch as Record<string, unknown>, key, value);
176
+
177
+ try {
178
+ const converted = configPatchFromConfig({ config: patch as Record<string, unknown> });
179
+ const saved = updateConfig(converted, { cwd: ctx.cwd, scope });
180
+ const warning = KNOWN_KEYS.has(key) ? "" : "\nWarning: unknown key — verify it exists in config schema.";
181
+ return result(`Set ${key} = ${formatValue(value)}\nPath: ${saved.path}${warning}`, { ...OK, key, value } as never);
182
+ } catch (error) {
183
+ return result(error instanceof Error ? error.message : String(error), { ...ERR }, true);
184
+ }
185
+ }
186
+
187
+ return result("Unknown subcommand. Usage: team-settings [list|get <key>|set <key> <value>|unset <key>|path|scope]", { ...ERR }, true);
188
+ }
@@ -1,41 +1,41 @@
1
- import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
2
- import { readEvents } from "../../state/event-log.ts";
3
- import { loadRunManifestById } from "../../state/state-store.ts";
4
- import { aggregateUsage, formatUsage } from "../../state/usage.ts";
5
- import type { PiTeamsToolResult } from "../tool-result.ts";
6
- import { result, type TeamContext } from "./context.ts";
7
-
8
- export function handleEvents(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
9
- if (!params.runId) return result("Events requires runId.", { action: "events", status: "error" }, true);
10
- const loaded = loadRunManifestById(ctx.cwd, params.runId);
11
- if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "events", status: "error" }, true);
12
- const events = readEvents(loaded.manifest.eventsPath);
13
- const lines = [`Events for ${loaded.manifest.runId}:`, ...(events.length ? events.map((event) => `${event.time} ${event.type}${event.taskId ? ` ${event.taskId}` : ""}${event.message ? `: ${event.message}` : ""}${event.data ? ` ${JSON.stringify(event.data)}` : ""}`) : ["(none)"])];
14
- return result(lines.join("\n"), { action: "events", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
15
- }
16
-
17
- export function handleArtifacts(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
18
- if (!params.runId) return result("Artifacts requires runId.", { action: "artifacts", status: "error" }, true);
19
- const loaded = loadRunManifestById(ctx.cwd, params.runId);
20
- if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "artifacts", status: "error" }, true);
21
- const lines = [`Artifacts for ${loaded.manifest.runId}:`, ...(loaded.manifest.artifacts.length ? loaded.manifest.artifacts.map((artifact) => `- ${artifact.kind}: ${artifact.path}${artifact.sizeBytes !== undefined ? ` (${artifact.sizeBytes} bytes)` : ""}${artifact.contentHash ? ` sha256=${artifact.contentHash.slice(0, 12)}` : ""}`) : ["- (none)"])];
22
- return result(lines.join("\n"), { action: "artifacts", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
23
- }
24
-
25
- export function handleSummary(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
26
- if (!params.runId) return result("Summary requires runId.", { action: "summary", status: "error" }, true);
27
- const loaded = loadRunManifestById(ctx.cwd, params.runId);
28
- if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "summary", status: "error" }, true);
29
- const usage = aggregateUsage(loaded.tasks);
30
- const lines = [
31
- `Summary for ${loaded.manifest.runId}`,
32
- `Status: ${loaded.manifest.status}`,
33
- `Team: ${loaded.manifest.team}`,
34
- `Workflow: ${loaded.manifest.workflow ?? "(none)"}`,
35
- `Goal: ${loaded.manifest.goal}`,
36
- `Usage: ${formatUsage(usage)}`,
37
- "Tasks:",
38
- ...loaded.tasks.map((task) => `- ${task.id}: ${task.status} (${task.role} -> ${task.agent})${task.error ? ` - ${task.error}` : ""}`),
39
- ];
40
- return result(lines.join("\n"), { action: "summary", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
41
- }
1
+ import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
2
+ import { readEvents } from "../../state/event-log.ts";
3
+ import { loadRunManifestById } from "../../state/state-store.ts";
4
+ import { aggregateUsage, formatUsage } from "../../state/usage.ts";
5
+ import type { PiTeamsToolResult } from "../tool-result.ts";
6
+ import { result, type TeamContext } from "./context.ts";
7
+
8
+ export function handleEvents(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
9
+ if (!params.runId) return result("Events requires runId.", { action: "events", status: "error" }, true);
10
+ const loaded = loadRunManifestById(ctx.cwd, params.runId);
11
+ if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "events", status: "error" }, true);
12
+ const events = readEvents(loaded.manifest.eventsPath);
13
+ const lines = [`Events for ${loaded.manifest.runId}:`, ...(events.length ? events.map((event) => `${event.time} ${event.type}${event.taskId ? ` ${event.taskId}` : ""}${event.message ? `: ${event.message}` : ""}${event.data ? ` ${JSON.stringify(event.data)}` : ""}`) : ["(none)"])];
14
+ return result(lines.join("\n"), { action: "events", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
15
+ }
16
+
17
+ export function handleArtifacts(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
18
+ if (!params.runId) return result("Artifacts requires runId.", { action: "artifacts", status: "error" }, true);
19
+ const loaded = loadRunManifestById(ctx.cwd, params.runId);
20
+ if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "artifacts", status: "error" }, true);
21
+ const lines = [`Artifacts for ${loaded.manifest.runId}:`, ...(loaded.manifest.artifacts.length ? loaded.manifest.artifacts.map((artifact) => `- ${artifact.kind}: ${artifact.path}${artifact.sizeBytes !== undefined ? ` (${artifact.sizeBytes} bytes)` : ""}${artifact.contentHash ? ` sha256=${artifact.contentHash.slice(0, 12)}` : ""}`) : ["- (none)"])];
22
+ return result(lines.join("\n"), { action: "artifacts", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
23
+ }
24
+
25
+ export function handleSummary(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
26
+ if (!params.runId) return result("Summary requires runId.", { action: "summary", status: "error" }, true);
27
+ const loaded = loadRunManifestById(ctx.cwd, params.runId);
28
+ if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "summary", status: "error" }, true);
29
+ const usage = aggregateUsage(loaded.tasks);
30
+ const lines = [
31
+ `Summary for ${loaded.manifest.runId}`,
32
+ `Status: ${loaded.manifest.status}`,
33
+ `Team: ${loaded.manifest.team}`,
34
+ `Workflow: ${loaded.manifest.workflow ?? "(none)"}`,
35
+ `Goal: ${loaded.manifest.goal}`,
36
+ `Usage: ${formatUsage(usage)}`,
37
+ "Tasks:",
38
+ ...loaded.tasks.map((task) => `- ${task.id}: ${task.status} (${task.role} -> ${task.agent})${task.error ? ` - ${task.error}` : ""}`),
39
+ ];
40
+ return result(lines.join("\n"), { action: "summary", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
41
+ }