pi-crew 0.1.24 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/refactor-tasks-phase3.md +394 -0
- package/docs/refactor-tasks-phase4.md +564 -0
- package/docs/refactor-tasks-phase5.md +402 -0
- package/docs/refactor-tasks.md +1484 -0
- package/package.json +98 -95
- package/src/agents/agent-config.ts +30 -30
- package/src/config/config.ts +153 -89
- package/src/config/defaults.ts +60 -0
- package/src/extension/autonomous-policy.ts +1 -1
- package/src/extension/help.ts +1 -0
- package/src/extension/management.ts +15 -2
- package/src/extension/register.ts +124 -170
- package/src/extension/registration/command-utils.ts +54 -0
- package/src/extension/registration/subagent-helpers.ts +70 -0
- package/src/extension/registration/viewers.ts +32 -0
- package/src/extension/result-watcher.ts +98 -89
- package/src/extension/team-tool/api.ts +276 -0
- package/src/extension/team-tool/config-patch.ts +36 -0
- package/src/extension/team-tool/context.ts +48 -0
- package/src/extension/team-tool/doctor.ts +178 -0
- package/src/extension/team-tool/run.ts +133 -0
- package/src/extension/team-tool-types.ts +6 -0
- package/src/extension/team-tool.ts +31 -623
- package/src/extension/tool-result.ts +16 -16
- package/src/runtime/async-runner.ts +42 -60
- package/src/runtime/child-pi.ts +434 -332
- package/src/runtime/concurrency.ts +50 -42
- package/src/runtime/crew-agent-records.ts +166 -156
- package/src/runtime/manifest-cache.ts +214 -0
- package/src/runtime/parallel-utils.ts +99 -0
- package/src/runtime/post-exit-stdio-guard.ts +86 -0
- package/src/runtime/runtime-resolver.ts +77 -74
- package/src/runtime/subagent-manager.ts +291 -236
- package/src/runtime/task-graph-scheduler.ts +122 -107
- package/src/runtime/team-runner.ts +46 -51
- package/src/schema/config-schema.ts +92 -0
- package/src/state/artifact-store.ts +108 -36
- package/src/state/atomic-write.ts +114 -49
- package/src/state/event-log.ts +189 -138
- package/src/state/jsonl-writer.ts +77 -0
- package/src/state/locks.ts +149 -40
- package/src/state/mailbox.ts +200 -188
- package/src/state/state-store.ts +104 -15
- package/src/teams/discover-teams.ts +94 -84
- package/src/teams/team-config.ts +26 -22
- package/src/ui/crew-footer.ts +101 -0
- package/src/ui/crew-select-list.ts +111 -0
- package/src/ui/crew-widget.ts +285 -219
- package/src/ui/dynamic-border.ts +25 -0
- package/src/ui/layout-primitives.ts +106 -0
- package/src/ui/live-run-sidebar.ts +163 -95
- package/src/ui/loaders.ts +158 -0
- package/src/ui/mascot.ts +441 -0
- package/src/ui/powerbar-publisher.ts +94 -71
- package/src/ui/render-diff.ts +119 -0
- package/src/ui/run-dashboard.ts +155 -120
- package/src/ui/status-colors.ts +54 -0
- package/src/ui/syntax-highlight.ts +116 -0
- package/src/ui/theme-adapter.ts +190 -0
- package/src/ui/transcript-viewer.ts +194 -111
- package/src/utils/completion-dedupe.ts +63 -0
- package/src/utils/file-coalescer.ts +84 -33
- package/src/utils/fs-watch.ts +31 -0
- package/src/utils/git.ts +262 -0
- package/src/utils/internal-error.ts +6 -0
- package/src/utils/paths.ts +33 -15
- package/src/utils/sleep.ts +32 -0
- package/src/utils/timings.ts +31 -0
- package/src/utils/visual.ts +159 -0
- package/src/workflows/discover-workflows.ts +109 -101
- package/src/workflows/workflow-config.ts +25 -24
- package/src/workflows/workflow-serializer.ts +32 -31
- package/tsconfig.json +19 -19
package/package.json
CHANGED
|
@@ -1,95 +1,98 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pi-crew",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
|
|
5
|
-
"author": "baphuongna",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/baphuongna/pi-crew.git"
|
|
10
|
-
},
|
|
11
|
-
"homepage": "https://github.com/baphuongna/pi-crew#readme",
|
|
12
|
-
"bugs": {
|
|
13
|
-
"url": "https://github.com/baphuongna/pi-crew/issues"
|
|
14
|
-
},
|
|
15
|
-
"type": "module",
|
|
16
|
-
"bin": {
|
|
17
|
-
"pi-crew": "install.mjs"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"pi-package",
|
|
21
|
-
"pi",
|
|
22
|
-
"pi-coding-agent",
|
|
23
|
-
"teams",
|
|
24
|
-
"agents",
|
|
25
|
-
"multi-agent",
|
|
26
|
-
"orchestration"
|
|
27
|
-
],
|
|
28
|
-
"files": [
|
|
29
|
-
"*.ts",
|
|
30
|
-
"*.mjs",
|
|
31
|
-
"src/**/*.ts",
|
|
32
|
-
"agents/",
|
|
33
|
-
"teams/",
|
|
34
|
-
"workflows/",
|
|
35
|
-
"skills/**/*",
|
|
36
|
-
"README.md",
|
|
37
|
-
"AGENTS.md",
|
|
38
|
-
"docs/",
|
|
39
|
-
"tsconfig.json",
|
|
40
|
-
"schema.json",
|
|
41
|
-
"CHANGELOG.md",
|
|
42
|
-
"LICENSE",
|
|
43
|
-
"NOTICE.md"
|
|
44
|
-
],
|
|
45
|
-
"scripts": {
|
|
46
|
-
"check": "npm run ci",
|
|
47
|
-
"ci": "npm run typecheck && npm test && npm pack --dry-run",
|
|
48
|
-
"typecheck": "tsc --noEmit && node --experimental-strip-types -e \"await import('./index.ts'); console.log('strip-types import ok')\"",
|
|
49
|
-
"test": "npm run test:unit",
|
|
50
|
-
"test:unit": "node --experimental-strip-types --test test/unit/*.test.ts",
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"@mariozechner/pi-
|
|
67
|
-
"@mariozechner/pi-
|
|
68
|
-
"@mariozechner/pi-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"@mariozechner/pi-
|
|
85
|
-
"optional": true
|
|
86
|
-
},
|
|
87
|
-
"@mariozechner/pi-
|
|
88
|
-
"optional": true
|
|
89
|
-
},
|
|
90
|
-
"@mariozechner/pi-
|
|
91
|
-
"optional": true
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-crew",
|
|
3
|
+
"version": "0.1.26",
|
|
4
|
+
"description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
|
|
5
|
+
"author": "baphuongna",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/baphuongna/pi-crew.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/baphuongna/pi-crew#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/baphuongna/pi-crew/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"pi-crew": "install.mjs"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"pi-package",
|
|
21
|
+
"pi",
|
|
22
|
+
"pi-coding-agent",
|
|
23
|
+
"teams",
|
|
24
|
+
"agents",
|
|
25
|
+
"multi-agent",
|
|
26
|
+
"orchestration"
|
|
27
|
+
],
|
|
28
|
+
"files": [
|
|
29
|
+
"*.ts",
|
|
30
|
+
"*.mjs",
|
|
31
|
+
"src/**/*.ts",
|
|
32
|
+
"agents/",
|
|
33
|
+
"teams/",
|
|
34
|
+
"workflows/",
|
|
35
|
+
"skills/**/*",
|
|
36
|
+
"README.md",
|
|
37
|
+
"AGENTS.md",
|
|
38
|
+
"docs/",
|
|
39
|
+
"tsconfig.json",
|
|
40
|
+
"schema.json",
|
|
41
|
+
"CHANGELOG.md",
|
|
42
|
+
"LICENSE",
|
|
43
|
+
"NOTICE.md"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"check": "npm run ci",
|
|
47
|
+
"ci": "npm run typecheck && npm test && npm pack --dry-run",
|
|
48
|
+
"typecheck": "tsc --noEmit && node --experimental-strip-types -e \"await import('./index.ts'); console.log('strip-types import ok')\"",
|
|
49
|
+
"test": "npm run test:unit && npm run test:integration",
|
|
50
|
+
"test:unit": "node --experimental-strip-types --test --test-timeout=30000 test/unit/*.test.ts",
|
|
51
|
+
"test:integration": "node --experimental-strip-types --test --test-timeout=120000 test/integration/*.test.ts",
|
|
52
|
+
"smoke:pi": "pi install ."
|
|
53
|
+
},
|
|
54
|
+
"exports": {
|
|
55
|
+
"./schema.json": "./schema.json"
|
|
56
|
+
},
|
|
57
|
+
"pi": {
|
|
58
|
+
"extensions": [
|
|
59
|
+
"./index.ts"
|
|
60
|
+
],
|
|
61
|
+
"skills": [
|
|
62
|
+
"./skills"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"@mariozechner/pi-agent-core": "*",
|
|
67
|
+
"@mariozechner/pi-ai": "*",
|
|
68
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
69
|
+
"@mariozechner/pi-tui": "*"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"cli-highlight": "^2.1.11",
|
|
73
|
+
"diff": "^5.2.0",
|
|
74
|
+
"jiti": "^2.6.1",
|
|
75
|
+
"typebox": "^1.1.24"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@mariozechner/pi-agent-core": "^0.65.0",
|
|
79
|
+
"@mariozechner/pi-ai": "^0.65.0",
|
|
80
|
+
"@mariozechner/pi-coding-agent": "^0.65.0",
|
|
81
|
+
"typescript": "^5.9.3"
|
|
82
|
+
},
|
|
83
|
+
"peerDependenciesMeta": {
|
|
84
|
+
"@mariozechner/pi-agent-core": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
"@mariozechner/pi-ai": {
|
|
88
|
+
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"@mariozechner/pi-coding-agent": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
93
|
+
"@mariozechner/pi-tui": {
|
|
94
|
+
"optional": true
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"readmeFilename": "README.md"
|
|
98
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
export type ResourceSource = "builtin" | "user" | "project";
|
|
2
|
-
|
|
3
|
-
export interface RoutingMetadata {
|
|
4
|
-
triggers?: string[];
|
|
5
|
-
useWhen?: string[];
|
|
6
|
-
avoidWhen?: string[];
|
|
7
|
-
cost?: "free" | "cheap" | "expensive";
|
|
8
|
-
category?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface AgentConfig {
|
|
12
|
-
name: string;
|
|
13
|
-
description: string;
|
|
14
|
-
source: ResourceSource;
|
|
15
|
-
filePath: string;
|
|
16
|
-
systemPrompt: string;
|
|
17
|
-
model?: string;
|
|
18
|
-
fallbackModels?: string[];
|
|
19
|
-
thinking?: string;
|
|
20
|
-
tools?: string[];
|
|
21
|
-
extensions?: string[];
|
|
22
|
-
skills?: string[];
|
|
23
|
-
systemPromptMode?: "replace" | "append";
|
|
24
|
-
inheritProjectContext?: boolean;
|
|
25
|
-
inheritSkills?: boolean;
|
|
26
|
-
routing?: RoutingMetadata;
|
|
27
|
-
memory?: "user" | "project" | "local";
|
|
28
|
-
disabled?: boolean;
|
|
29
|
-
override?: { source: "config"; path: string };
|
|
30
|
-
}
|
|
1
|
+
export type ResourceSource = "builtin" | "user" | "project" | "git";
|
|
2
|
+
|
|
3
|
+
export interface RoutingMetadata {
|
|
4
|
+
triggers?: string[];
|
|
5
|
+
useWhen?: string[];
|
|
6
|
+
avoidWhen?: string[];
|
|
7
|
+
cost?: "free" | "cheap" | "expensive";
|
|
8
|
+
category?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AgentConfig {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
source: ResourceSource;
|
|
15
|
+
filePath: string;
|
|
16
|
+
systemPrompt: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
fallbackModels?: string[];
|
|
19
|
+
thinking?: string;
|
|
20
|
+
tools?: string[];
|
|
21
|
+
extensions?: string[];
|
|
22
|
+
skills?: string[];
|
|
23
|
+
systemPromptMode?: "replace" | "append";
|
|
24
|
+
inheritProjectContext?: boolean;
|
|
25
|
+
inheritSkills?: boolean;
|
|
26
|
+
routing?: RoutingMetadata;
|
|
27
|
+
memory?: "user" | "project" | "local";
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
override?: { source: "config"; path: string };
|
|
30
|
+
}
|
package/src/config/config.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { Type, type Static, type TSchema } from "typebox";
|
|
2
|
+
import { Value } from "typebox/value";
|
|
1
3
|
import * as fs from "node:fs";
|
|
2
4
|
import * as os from "node:os";
|
|
3
5
|
import * as path from "node:path";
|
|
6
|
+
import { PiTeamsAutonomyProfileSchema, PiTeamsConfigSchema } from "../schema/config-schema.ts";
|
|
4
7
|
|
|
5
8
|
export type PiTeamsAutonomyProfile = "manual" | "suggested" | "assisted" | "aggressive";
|
|
6
9
|
|
|
@@ -59,6 +62,8 @@ export interface CrewUiConfig {
|
|
|
59
62
|
showModel?: boolean;
|
|
60
63
|
showTokens?: boolean;
|
|
61
64
|
showTools?: boolean;
|
|
65
|
+
mascotStyle?: "cat" | "armin";
|
|
66
|
+
mascotEffect?: "random" | "none" | "typewriter" | "scanline" | "rain" | "fade" | "crt" | "glitch" | "dissolve";
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
export interface AgentOverrideConfig {
|
|
@@ -93,6 +98,12 @@ export interface LoadedPiTeamsConfig {
|
|
|
93
98
|
path: string;
|
|
94
99
|
paths: string[];
|
|
95
100
|
error?: string;
|
|
101
|
+
warnings?: string[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface ConfigValidationResult {
|
|
105
|
+
config: PiTeamsConfig;
|
|
106
|
+
warnings: string[];
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
export interface SavedPiTeamsConfig {
|
|
@@ -119,6 +130,24 @@ function withoutUndefined<T extends Record<string, unknown>>(value: T): Partial<
|
|
|
119
130
|
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as Partial<T>;
|
|
120
131
|
}
|
|
121
132
|
|
|
133
|
+
function errorPathFromValidation(error: unknown): string {
|
|
134
|
+
if (error && typeof error === "object") {
|
|
135
|
+
if (typeof (error as { path?: unknown }).path === "string") return (error as { path: string }).path;
|
|
136
|
+
if (typeof (error as { instancePath?: unknown }).instancePath === "string") return (error as { instancePath: string }).instancePath;
|
|
137
|
+
if (typeof (error as { keyword?: unknown }).keyword === "string" && typeof (error as { schemaPath?: unknown }).schemaPath === "string") return (error as { schemaPath: string }).schemaPath;
|
|
138
|
+
}
|
|
139
|
+
return "config";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function validateConfigWithWarnings(raw: unknown): string[] {
|
|
143
|
+
if (!Value.Check(PiTeamsConfigSchema, raw)) {
|
|
144
|
+
return [...Value.Errors(PiTeamsConfigSchema, raw)].map((error) => {
|
|
145
|
+
return `${errorPathFromValidation(error)}: ${(error as { message?: unknown }).message ?? "invalid value"}`;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
|
|
122
151
|
function mergeConfig(base: PiTeamsConfig, override: PiTeamsConfig): PiTeamsConfig {
|
|
123
152
|
const merged: PiTeamsConfig = { ...base, ...withoutUndefined(override as Record<string, unknown>) };
|
|
124
153
|
if (base.autonomous || override.autonomous) {
|
|
@@ -171,8 +200,47 @@ function mergeConfig(base: PiTeamsConfig, override: PiTeamsConfig): PiTeamsConfi
|
|
|
171
200
|
return merged;
|
|
172
201
|
}
|
|
173
202
|
|
|
174
|
-
|
|
175
|
-
|
|
203
|
+
const LIMIT_CEILINGS = {
|
|
204
|
+
maxConcurrentWorkers: 1024,
|
|
205
|
+
maxTaskDepth: 100,
|
|
206
|
+
maxChildrenPerTask: 1000,
|
|
207
|
+
maxRunMinutes: 1440,
|
|
208
|
+
maxRetriesPerTask: 100,
|
|
209
|
+
maxTasksPerRun: 10_000,
|
|
210
|
+
heartbeatStaleMs: 24 * 60 * 60 * 1000,
|
|
211
|
+
runtimeMaxTurns: 10_000,
|
|
212
|
+
runtimeGraceTurns: 1_000,
|
|
213
|
+
} as const;
|
|
214
|
+
|
|
215
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
216
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
217
|
+
return value as Record<string, unknown>;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function parseWithSchema<T extends TSchema>(schema: T, value: unknown): Static<T> | undefined {
|
|
221
|
+
if (!Value.Check(schema, value)) return undefined;
|
|
222
|
+
return Value.Decode(schema, value);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function parsePositiveInteger(value: unknown, max = Number.MAX_SAFE_INTEGER): number | undefined {
|
|
226
|
+
return parseWithSchema(Type.Integer({ minimum: 1, maximum: max }), value);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function parseProfile(value: unknown): PiTeamsAutonomyProfile | undefined {
|
|
230
|
+
return parseWithSchema(PiTeamsAutonomyProfileSchema, value);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function parseStringList(value: unknown): string[] | undefined {
|
|
234
|
+
const items = parseWithSchema(Type.Array(Type.String()), value);
|
|
235
|
+
if (!items || items.length === 0) return undefined;
|
|
236
|
+
const normalized = items.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
237
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function parseStringArrayOrFalse(value: unknown): string[] | false | undefined {
|
|
241
|
+
if (value === false) return false;
|
|
242
|
+
if (typeof value === "string") return parseStringList(value.split(","));
|
|
243
|
+
return parseStringList(value);
|
|
176
244
|
}
|
|
177
245
|
|
|
178
246
|
export function effectiveAutonomousConfig(config: PiTeamsAutonomousConfig | undefined): Required<Pick<PiTeamsAutonomousConfig, "profile" | "enabled" | "injectPolicy" | "preferAsyncForLongTasks" | "allowWorktreeSuggestion">> & Pick<PiTeamsAutonomousConfig, "magicKeywords"> {
|
|
@@ -195,48 +263,33 @@ export function effectiveAutonomousConfig(config: PiTeamsAutonomousConfig | unde
|
|
|
195
263
|
}
|
|
196
264
|
|
|
197
265
|
function parseStringArrayRecord(value: unknown): Record<string, string[]> | undefined {
|
|
198
|
-
|
|
266
|
+
const record = parseWithSchema(Type.Record(Type.String({ minLength: 1 }), Type.Array(Type.String())), value);
|
|
267
|
+
if (!record) return undefined;
|
|
199
268
|
const result: Record<string, string[]> = {};
|
|
200
|
-
for (const [key, rawValues] of Object.entries(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (values.length > 0) result[key] = values;
|
|
269
|
+
for (const [key, rawValues] of Object.entries(record)) {
|
|
270
|
+
const parsed = parseStringList(rawValues);
|
|
271
|
+
if (parsed && parsed.length > 0) result[key] = parsed;
|
|
204
272
|
}
|
|
205
273
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
206
274
|
}
|
|
207
275
|
|
|
208
276
|
function parseAutonomousConfig(value: unknown): PiTeamsAutonomousConfig | undefined {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
profile:
|
|
213
|
-
enabled:
|
|
214
|
-
injectPolicy:
|
|
215
|
-
preferAsyncForLongTasks:
|
|
216
|
-
allowWorktreeSuggestion:
|
|
277
|
+
const obj = asRecord(value);
|
|
278
|
+
if (!obj) return undefined;
|
|
279
|
+
const config: PiTeamsAutonomousConfig = {
|
|
280
|
+
profile: parseProfile(obj.profile),
|
|
281
|
+
enabled: parseWithSchema(Type.Boolean(), obj.enabled),
|
|
282
|
+
injectPolicy: parseWithSchema(Type.Boolean(), obj.injectPolicy),
|
|
283
|
+
preferAsyncForLongTasks: parseWithSchema(Type.Boolean(), obj.preferAsyncForLongTasks),
|
|
284
|
+
allowWorktreeSuggestion: parseWithSchema(Type.Boolean(), obj.allowWorktreeSuggestion),
|
|
217
285
|
magicKeywords: parseStringArrayRecord(obj.magicKeywords),
|
|
218
286
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const LIMIT_CEILINGS = {
|
|
222
|
-
maxConcurrentWorkers: 1024,
|
|
223
|
-
maxTaskDepth: 100,
|
|
224
|
-
maxChildrenPerTask: 1000,
|
|
225
|
-
maxRunMinutes: 1440,
|
|
226
|
-
maxRetriesPerTask: 100,
|
|
227
|
-
maxTasksPerRun: 10_000,
|
|
228
|
-
heartbeatStaleMs: 24 * 60 * 60 * 1000,
|
|
229
|
-
runtimeMaxTurns: 10_000,
|
|
230
|
-
runtimeGraceTurns: 1_000,
|
|
231
|
-
} as const;
|
|
232
|
-
|
|
233
|
-
function parsePositiveInteger(value: unknown, max = Number.MAX_SAFE_INTEGER): number | undefined {
|
|
234
|
-
return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= max ? value : undefined;
|
|
287
|
+
return Object.values(config).some((entry) => entry !== undefined) ? config : undefined;
|
|
235
288
|
}
|
|
236
289
|
|
|
237
290
|
function parseLimitsConfig(value: unknown): CrewLimitsConfig | undefined {
|
|
238
|
-
|
|
239
|
-
|
|
291
|
+
const obj = asRecord(value);
|
|
292
|
+
if (!obj) return undefined;
|
|
240
293
|
const limits: CrewLimitsConfig = {
|
|
241
294
|
maxConcurrentWorkers: parsePositiveInteger(obj.maxConcurrentWorkers, LIMIT_CEILINGS.maxConcurrentWorkers),
|
|
242
295
|
maxTaskDepth: parsePositiveInteger(obj.maxTaskDepth, LIMIT_CEILINGS.maxTaskDepth),
|
|
@@ -249,111 +302,106 @@ function parseLimitsConfig(value: unknown): CrewLimitsConfig | undefined {
|
|
|
249
302
|
return Object.values(limits).some((entry) => entry !== undefined) ? limits : undefined;
|
|
250
303
|
}
|
|
251
304
|
|
|
252
|
-
function parseRuntimeMode(value: unknown): CrewRuntimeMode | undefined {
|
|
253
|
-
return value === "auto" || value === "scaffold" || value === "child-process" || value === "live-session" ? value : undefined;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
305
|
function parseRuntimeConfig(value: unknown): CrewRuntimeConfig | undefined {
|
|
257
|
-
|
|
258
|
-
|
|
306
|
+
const obj = asRecord(value);
|
|
307
|
+
if (!obj) return undefined;
|
|
259
308
|
const runtime: CrewRuntimeConfig = {
|
|
260
|
-
mode:
|
|
261
|
-
preferLiveSession:
|
|
262
|
-
allowChildProcessFallback:
|
|
309
|
+
mode: parseWithSchema(Type.Union([Type.Literal("auto"), Type.Literal("scaffold"), Type.Literal("child-process"), Type.Literal("live-session")]), obj.mode),
|
|
310
|
+
preferLiveSession: parseWithSchema(Type.Boolean(), obj.preferLiveSession),
|
|
311
|
+
allowChildProcessFallback: parseWithSchema(Type.Boolean(), obj.allowChildProcessFallback),
|
|
263
312
|
maxTurns: parsePositiveInteger(obj.maxTurns, LIMIT_CEILINGS.runtimeMaxTurns),
|
|
264
313
|
graceTurns: parsePositiveInteger(obj.graceTurns, LIMIT_CEILINGS.runtimeGraceTurns),
|
|
265
|
-
inheritContext:
|
|
266
|
-
promptMode:
|
|
267
|
-
groupJoin:
|
|
314
|
+
inheritContext: parseWithSchema(Type.Boolean(), obj.inheritContext),
|
|
315
|
+
promptMode: parseWithSchema(Type.Union([Type.Literal("replace"), Type.Literal("append")]), obj.promptMode),
|
|
316
|
+
groupJoin: parseWithSchema(Type.Union([Type.Literal("off"), Type.Literal("group"), Type.Literal("smart")]), obj.groupJoin),
|
|
268
317
|
};
|
|
269
318
|
return Object.values(runtime).some((entry) => entry !== undefined) ? runtime : undefined;
|
|
270
319
|
}
|
|
271
320
|
|
|
272
321
|
function parseControlConfig(value: unknown): CrewControlConfig | undefined {
|
|
273
|
-
|
|
274
|
-
|
|
322
|
+
const obj = asRecord(value);
|
|
323
|
+
if (!obj) return undefined;
|
|
275
324
|
const control: CrewControlConfig = {
|
|
276
|
-
enabled:
|
|
325
|
+
enabled: parseWithSchema(Type.Boolean(), obj.enabled),
|
|
277
326
|
needsAttentionAfterMs: parsePositiveInteger(obj.needsAttentionAfterMs),
|
|
278
327
|
};
|
|
279
328
|
return Object.values(control).some((entry) => entry !== undefined) ? control : undefined;
|
|
280
329
|
}
|
|
281
330
|
|
|
282
331
|
function parseWorktreeConfig(value: unknown): CrewWorktreeConfig | undefined {
|
|
283
|
-
|
|
284
|
-
|
|
332
|
+
const obj = asRecord(value);
|
|
333
|
+
if (!obj) return undefined;
|
|
334
|
+
const rawSetupHook = parseWithSchema(Type.String(), obj.setupHook);
|
|
335
|
+
const setupHook = rawSetupHook?.trim();
|
|
285
336
|
const worktree: CrewWorktreeConfig = {
|
|
286
|
-
setupHook:
|
|
337
|
+
setupHook: setupHook ? setupHook : undefined,
|
|
287
338
|
setupHookTimeoutMs: parsePositiveInteger(obj.setupHookTimeoutMs, 300_000),
|
|
288
|
-
linkNodeModules:
|
|
339
|
+
linkNodeModules: parseWithSchema(Type.Boolean(), obj.linkNodeModules),
|
|
289
340
|
};
|
|
290
341
|
return Object.values(worktree).some((entry) => entry !== undefined) ? worktree : undefined;
|
|
291
342
|
}
|
|
292
343
|
|
|
293
|
-
function parseStringArrayOrFalse(value: unknown): string[] | false | undefined {
|
|
294
|
-
if (value === false) return false;
|
|
295
|
-
if (typeof value === "string") return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
296
|
-
if (Array.isArray(value)) return value.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0).map((entry) => entry.trim());
|
|
297
|
-
return undefined;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
344
|
function parseAgentOverride(value: unknown): AgentOverrideConfig | undefined {
|
|
301
|
-
|
|
302
|
-
|
|
345
|
+
const obj = asRecord(value);
|
|
346
|
+
if (!obj) return undefined;
|
|
303
347
|
const override: AgentOverrideConfig = {
|
|
304
|
-
disabled:
|
|
305
|
-
model:
|
|
348
|
+
disabled: parseWithSchema(Type.Boolean(), obj.disabled),
|
|
349
|
+
model: parseWithSchema(Type.Union([Type.String(), Type.Literal(false)]), obj.model),
|
|
306
350
|
fallbackModels: parseStringArrayOrFalse(obj.fallbackModels),
|
|
307
|
-
thinking:
|
|
351
|
+
thinking: parseWithSchema(Type.Union([Type.String(), Type.Literal(false)]), obj.thinking),
|
|
308
352
|
tools: parseStringArrayOrFalse(obj.tools),
|
|
309
353
|
};
|
|
310
354
|
return Object.values(override).some((entry) => entry !== undefined) ? override : undefined;
|
|
311
355
|
}
|
|
312
356
|
|
|
313
357
|
function parseUiConfig(value: unknown): CrewUiConfig | undefined {
|
|
314
|
-
|
|
315
|
-
|
|
358
|
+
const obj = asRecord(value);
|
|
359
|
+
if (!obj) return undefined;
|
|
360
|
+
const rawWidgetPlacement = parseWithSchema(Type.Union([Type.Literal("aboveEditor"), Type.Literal("belowEditor")]), obj.widgetPlacement);
|
|
361
|
+
const rawDashboardPlacement = parseWithSchema(Type.Union([Type.Literal("center"), Type.Literal("right")]), obj.dashboardPlacement);
|
|
316
362
|
const ui: CrewUiConfig = {
|
|
317
|
-
widgetPlacement:
|
|
363
|
+
widgetPlacement: rawWidgetPlacement,
|
|
318
364
|
widgetMaxLines: parsePositiveInteger(obj.widgetMaxLines, 50),
|
|
319
|
-
powerbar:
|
|
320
|
-
dashboardPlacement:
|
|
365
|
+
powerbar: parseWithSchema(Type.Boolean(), obj.powerbar),
|
|
366
|
+
dashboardPlacement: rawDashboardPlacement,
|
|
321
367
|
dashboardWidth: parsePositiveInteger(obj.dashboardWidth, 120),
|
|
322
368
|
dashboardLiveRefreshMs: parsePositiveInteger(obj.dashboardLiveRefreshMs, 60_000),
|
|
323
|
-
autoOpenDashboard:
|
|
324
|
-
autoOpenDashboardForForegroundRuns:
|
|
325
|
-
showModel:
|
|
326
|
-
showTokens:
|
|
327
|
-
showTools:
|
|
369
|
+
autoOpenDashboard: parseWithSchema(Type.Boolean(), obj.autoOpenDashboard),
|
|
370
|
+
autoOpenDashboardForForegroundRuns: parseWithSchema(Type.Boolean(), obj.autoOpenDashboardForForegroundRuns),
|
|
371
|
+
showModel: parseWithSchema(Type.Boolean(), obj.showModel),
|
|
372
|
+
showTokens: parseWithSchema(Type.Boolean(), obj.showTokens),
|
|
373
|
+
showTools: parseWithSchema(Type.Boolean(), obj.showTools),
|
|
374
|
+
mascotStyle: parseWithSchema(Type.Union([Type.Literal("cat"), Type.Literal("armin")]), obj.mascotStyle),
|
|
375
|
+
mascotEffect: parseWithSchema(Type.Union([Type.Literal("random"), Type.Literal("none"), Type.Literal("typewriter"), Type.Literal("scanline"), Type.Literal("rain"), Type.Literal("fade"), Type.Literal("crt"), Type.Literal("glitch"), Type.Literal("dissolve")]), obj.mascotEffect),
|
|
328
376
|
};
|
|
329
377
|
return Object.values(ui).some((entry) => entry !== undefined) ? ui : undefined;
|
|
330
378
|
}
|
|
331
379
|
|
|
332
380
|
function parseAgentsConfig(value: unknown): CrewAgentsConfig | undefined {
|
|
333
|
-
|
|
334
|
-
|
|
381
|
+
const obj = asRecord(value);
|
|
382
|
+
if (!obj) return undefined;
|
|
335
383
|
const overrides: Record<string, AgentOverrideConfig> = {};
|
|
336
384
|
if (obj.overrides && typeof obj.overrides === "object" && !Array.isArray(obj.overrides)) {
|
|
337
|
-
for (const [name, rawOverride] of Object.entries(obj.overrides)) {
|
|
385
|
+
for (const [name, rawOverride] of Object.entries(obj.overrides as Record<string, unknown>)) {
|
|
338
386
|
const parsed = parseAgentOverride(rawOverride);
|
|
339
|
-
if (parsed) overrides[name] = parsed;
|
|
387
|
+
if (parsed && name.trim()) overrides[name.trim()] = parsed;
|
|
340
388
|
}
|
|
341
389
|
}
|
|
342
390
|
const agents: CrewAgentsConfig = {
|
|
343
|
-
disableBuiltins:
|
|
391
|
+
disableBuiltins: parseWithSchema(Type.Boolean(), obj.disableBuiltins),
|
|
344
392
|
overrides: Object.keys(overrides).length > 0 ? overrides : undefined,
|
|
345
393
|
};
|
|
346
394
|
return Object.values(agents).some((entry) => entry !== undefined) ? agents : undefined;
|
|
347
395
|
}
|
|
348
396
|
|
|
349
|
-
function parseConfig(raw: unknown): PiTeamsConfig {
|
|
350
|
-
|
|
351
|
-
|
|
397
|
+
export function parseConfig(raw: unknown): PiTeamsConfig {
|
|
398
|
+
const obj = asRecord(raw);
|
|
399
|
+
if (!obj) return {};
|
|
352
400
|
return {
|
|
353
|
-
asyncByDefault:
|
|
354
|
-
executeWorkers:
|
|
355
|
-
notifierIntervalMs:
|
|
356
|
-
requireCleanWorktreeLeader:
|
|
401
|
+
asyncByDefault: parseWithSchema(Type.Boolean(), obj.asyncByDefault),
|
|
402
|
+
executeWorkers: parseWithSchema(Type.Boolean(), obj.executeWorkers),
|
|
403
|
+
notifierIntervalMs: parseWithSchema(Type.Number({ minimum: 1_000 }), obj.notifierIntervalMs),
|
|
404
|
+
requireCleanWorktreeLeader: parseWithSchema(Type.Boolean(), obj.requireCleanWorktreeLeader),
|
|
357
405
|
autonomous: parseAutonomousConfig(obj.autonomous),
|
|
358
406
|
limits: parseLimitsConfig(obj.limits),
|
|
359
407
|
runtime: parseRuntimeConfig(obj.runtime),
|
|
@@ -364,6 +412,14 @@ function parseConfig(raw: unknown): PiTeamsConfig {
|
|
|
364
412
|
};
|
|
365
413
|
}
|
|
366
414
|
|
|
415
|
+
export function parseConfigWithWarnings(raw: unknown): ConfigValidationResult {
|
|
416
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { config: {}, warnings: [] };
|
|
417
|
+
const parsed = parseConfig(raw);
|
|
418
|
+
const warnings = validateConfigWithWarnings(raw as Record<string, unknown>);
|
|
419
|
+
return { config: parsed, warnings };
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
|
|
367
423
|
function unsetPath(record: Record<string, unknown>, dottedPath: string): void {
|
|
368
424
|
const parts = dottedPath.split(".").filter(Boolean);
|
|
369
425
|
if (parts.length === 0) return;
|
|
@@ -387,9 +443,17 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
|
|
|
387
443
|
const filePath = configPath();
|
|
388
444
|
const paths = cwd ? [filePath, projectConfigPath(cwd)] : [filePath];
|
|
389
445
|
try {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
446
|
+
const userRaw = readConfigRecord(filePath);
|
|
447
|
+
const userConfig = parseConfigWithWarnings(userRaw);
|
|
448
|
+
let config = userConfig.config;
|
|
449
|
+
const warnings: string[] = userConfig.warnings.map((warning) => `${filePath}: ${warning}`);
|
|
450
|
+
if (cwd) {
|
|
451
|
+
const projectPath = projectConfigPath(cwd);
|
|
452
|
+
const projectConfig = parseConfigWithWarnings(readConfigRecord(projectPath));
|
|
453
|
+
warnings.push(...projectConfig.warnings.map((warning) => `${projectPath}: ${warning}`));
|
|
454
|
+
config = mergeConfig(config, projectConfig.config);
|
|
455
|
+
}
|
|
456
|
+
return { path: filePath, paths, config, warnings: warnings.length > 0 ? warnings : undefined };
|
|
393
457
|
} catch (error) {
|
|
394
458
|
const message = error instanceof Error ? error.message : String(error);
|
|
395
459
|
return { path: filePath, paths, config: {}, error: message };
|