pi-crew 0.1.44 → 0.1.45

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 (142) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/agents/analyst.md +11 -11
  3. package/agents/critic.md +11 -11
  4. package/agents/executor.md +11 -11
  5. package/agents/explorer.md +11 -11
  6. package/agents/planner.md +11 -11
  7. package/agents/reviewer.md +11 -11
  8. package/agents/security-reviewer.md +11 -11
  9. package/agents/test-engineer.md +11 -11
  10. package/agents/verifier.md +11 -11
  11. package/agents/writer.md +11 -11
  12. package/docs/refactor-tasks-phase3.md +394 -394
  13. package/docs/refactor-tasks-phase4.md +564 -564
  14. package/docs/refactor-tasks-phase5.md +402 -402
  15. package/docs/refactor-tasks-phase6.md +662 -662
  16. package/docs/research-extension-examples.md +297 -297
  17. package/docs/research-extension-system.md +324 -324
  18. package/docs/research-optimization-plan.md +548 -548
  19. package/docs/research-phase10-distillation.md +198 -198
  20. package/docs/research-phase11-distillation.md +201 -201
  21. package/docs/research-pi-coding-agent.md +357 -357
  22. package/docs/research-source-pi-crew-reference.md +174 -174
  23. package/docs/runtime-flow.md +148 -148
  24. package/docs/source-runtime-refactor-map.md +83 -83
  25. package/index.ts +6 -6
  26. package/package.json +1 -1
  27. package/src/agents/agent-serializer.ts +34 -34
  28. package/src/extension/cross-extension-rpc.ts +82 -82
  29. package/src/extension/register.ts +8 -1
  30. package/src/extension/registration/commands.ts +18 -2
  31. package/src/extension/registration/compaction-guard.ts +125 -125
  32. package/src/extension/registration/subagent-tools.ts +148 -148
  33. package/src/extension/registration/team-tool.ts +26 -8
  34. package/src/extension/run-bundle-schema.ts +89 -89
  35. package/src/extension/run-maintenance.ts +43 -43
  36. package/src/extension/team-tool/cancel.ts +105 -102
  37. package/src/extension/team-tool/context.ts +1 -0
  38. package/src/extension/team-tool/handle-settings.ts +188 -188
  39. package/src/extension/team-tool/inspect.ts +41 -41
  40. package/src/extension/team-tool/lifecycle-actions.ts +79 -79
  41. package/src/extension/team-tool/plan.ts +19 -19
  42. package/src/extension/team-tool/respond.ts +83 -66
  43. package/src/extension/team-tool/run.ts +1 -0
  44. package/src/i18n.ts +184 -184
  45. package/src/observability/exporters/otlp-exporter.ts +77 -77
  46. package/src/prompt/prompt-runtime.ts +72 -72
  47. package/src/runtime/agent-control.ts +63 -63
  48. package/src/runtime/agent-memory.ts +72 -72
  49. package/src/runtime/agent-observability.ts +114 -114
  50. package/src/runtime/async-marker.ts +26 -26
  51. package/src/runtime/attention-events.ts +28 -28
  52. package/src/runtime/background-runner.ts +53 -53
  53. package/src/runtime/child-pi.ts +444 -444
  54. package/src/runtime/completion-guard.ts +190 -190
  55. package/src/runtime/crew-agent-records.ts +8 -0
  56. package/src/runtime/delivery-coordinator.ts +153 -142
  57. package/src/runtime/direct-run.ts +35 -35
  58. package/src/runtime/foreground-control.ts +82 -82
  59. package/src/runtime/green-contract.ts +46 -46
  60. package/src/runtime/group-join.ts +106 -106
  61. package/src/runtime/heartbeat-gradient.ts +28 -28
  62. package/src/runtime/heartbeat-watcher.ts +124 -124
  63. package/src/runtime/live-agent-control.ts +87 -87
  64. package/src/runtime/live-agent-manager.ts +85 -85
  65. package/src/runtime/live-control-realtime.ts +36 -36
  66. package/src/runtime/live-session-runtime.ts +305 -305
  67. package/src/runtime/overflow-recovery.ts +175 -156
  68. package/src/runtime/parallel-research.ts +44 -44
  69. package/src/runtime/pi-json-output.ts +111 -111
  70. package/src/runtime/policy-engine.ts +79 -79
  71. package/src/runtime/progress-event-coalescer.ts +43 -43
  72. package/src/runtime/recovery-recipes.ts +74 -74
  73. package/src/runtime/retry-executor.ts +64 -64
  74. package/src/runtime/role-permission.ts +39 -39
  75. package/src/runtime/session-resources.ts +25 -25
  76. package/src/runtime/session-snapshot.ts +59 -59
  77. package/src/runtime/session-usage.ts +79 -79
  78. package/src/runtime/sidechain-output.ts +29 -29
  79. package/src/runtime/stale-reconciler.ts +199 -179
  80. package/src/runtime/supervisor-contact.ts +59 -59
  81. package/src/runtime/task-display.ts +38 -38
  82. package/src/runtime/task-output-context.ts +127 -127
  83. package/src/runtime/task-runner/live-executor.ts +101 -101
  84. package/src/runtime/task-runner/progress.ts +119 -119
  85. package/src/runtime/task-runner/result-utils.ts +14 -14
  86. package/src/runtime/task-runner/state-helpers.ts +22 -22
  87. package/src/runtime/team-runner.ts +13 -4
  88. package/src/runtime/worker-heartbeat.ts +21 -21
  89. package/src/runtime/worker-startup.ts +57 -57
  90. package/src/state/state-store.ts +43 -0
  91. package/src/state/task-claims.ts +44 -44
  92. package/src/state/types.ts +2 -0
  93. package/src/state/usage.ts +29 -29
  94. package/src/subagents/async-entry.ts +1 -1
  95. package/src/subagents/index.ts +3 -3
  96. package/src/subagents/live/control.ts +1 -1
  97. package/src/subagents/live/manager.ts +1 -1
  98. package/src/subagents/live/realtime.ts +1 -1
  99. package/src/subagents/live/session-runtime.ts +1 -1
  100. package/src/subagents/manager.ts +1 -1
  101. package/src/subagents/spawn.ts +1 -1
  102. package/src/teams/team-serializer.ts +38 -38
  103. package/src/types/diff.d.ts +18 -18
  104. package/src/ui/crew-footer.ts +101 -101
  105. package/src/ui/crew-select-list.ts +111 -111
  106. package/src/ui/crew-widget.ts +5 -1
  107. package/src/ui/dashboard-panes/mailbox-pane.ts +2 -1
  108. package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
  109. package/src/ui/dynamic-border.ts +25 -25
  110. package/src/ui/layout-primitives.ts +106 -106
  111. package/src/ui/loaders.ts +158 -158
  112. package/src/ui/powerbar-publisher.ts +1 -1
  113. package/src/ui/render-diff.ts +119 -119
  114. package/src/ui/render-scheduler.ts +143 -143
  115. package/src/ui/run-snapshot-cache.ts +56 -37
  116. package/src/ui/snapshot-types.ts +5 -0
  117. package/src/ui/spinner.ts +17 -17
  118. package/src/ui/status-colors.ts +58 -58
  119. package/src/ui/syntax-highlight.ts +116 -116
  120. package/src/utils/atomic-write.ts +33 -33
  121. package/src/utils/completion-dedupe.ts +63 -63
  122. package/src/utils/frontmatter.ts +68 -68
  123. package/src/utils/git.ts +262 -262
  124. package/src/utils/ids.ts +12 -12
  125. package/src/utils/names.ts +27 -27
  126. package/src/utils/redaction.ts +44 -44
  127. package/src/utils/safe-paths.ts +47 -47
  128. package/src/utils/sleep.ts +32 -32
  129. package/src/workflows/validate-workflow.ts +40 -40
  130. package/src/worktree/branch-freshness.ts +45 -45
  131. package/teams/default.team.md +12 -12
  132. package/teams/fast-fix.team.md +11 -11
  133. package/teams/implementation.team.md +18 -18
  134. package/teams/parallel-research.team.md +14 -14
  135. package/teams/research.team.md +11 -11
  136. package/teams/review.team.md +12 -12
  137. package/workflows/default.workflow.md +29 -29
  138. package/workflows/fast-fix.workflow.md +22 -22
  139. package/workflows/implementation.workflow.md +38 -38
  140. package/workflows/parallel-research.workflow.md +46 -46
  141. package/workflows/research.workflow.md +22 -22
  142. package/workflows/review.workflow.md +30 -30
@@ -1,44 +1,44 @@
1
- const SECRET_KEY_PATTERN = /(?:^|[_.-])(token|api[-_]?key|password|passwd|secret|credential|authorization|private[-_]?key)(?:$|[_.-])/i;
2
- const INLINE_SECRET_PATTERN = /(^|[\s,{])(([A-Za-z0-9_.-]*(?:api[-_]?key|token|password|passwd|secret|credential|authorization|private[-_]?key)[A-Za-z0-9_.-]*)\s*[=:]\s*)([^\s,;"'}]+)/gi;
3
- const AUTH_HEADER_PATTERN = /\b(Authorization\s*:\s*(?:Bearer|Basic|Token)?\s*)([^\r\n]+)/gi;
4
- const BEARER_PATTERN = /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{8,})\b/g;
5
- const PEM_PRIVATE_KEY_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
6
-
7
- function isRecord(value: unknown): value is Record<string, unknown> {
8
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
9
- // Exclude built-in types whose Object.entries() would produce empty arrays.
10
- if (value instanceof Date || value instanceof RegExp || value instanceof Error || value instanceof Map || value instanceof Set) return false;
11
- return true;
12
- }
13
-
14
- function isSecretKey(keyName: string): boolean {
15
- return SECRET_KEY_PATTERN.test(keyName) || /^(token|apiKey|api_key|password|secret|credential|authorization|privateKey|private_key)$/i.test(keyName);
16
- }
17
-
18
- export function redactSecretString(value: string): string {
19
- return value
20
- .replace(PEM_PRIVATE_KEY_PATTERN, "***")
21
- .replace(AUTH_HEADER_PATTERN, "$1***")
22
- .replace(BEARER_PATTERN, "$1***")
23
- .replace(INLINE_SECRET_PATTERN, "$1$2***");
24
- }
25
-
26
- export function redactSecrets(value: unknown, keyName = ""): unknown {
27
- if (keyName && isSecretKey(keyName)) return "***";
28
- if (typeof value === "string") return redactSecretString(value);
29
- if (Array.isArray(value)) return value.map((item) => redactSecrets(item));
30
- if (isRecord(value)) {
31
- const output: Record<string, unknown> = {};
32
- for (const [key, entry] of Object.entries(value)) output[key] = redactSecrets(entry, key);
33
- return output;
34
- }
35
- return value;
36
- }
37
-
38
- export function redactJsonLine(line: string): string {
39
- try {
40
- return JSON.stringify(redactSecrets(JSON.parse(line) as unknown));
41
- } catch {
42
- return redactSecretString(line);
43
- }
44
- }
1
+ const SECRET_KEY_PATTERN = /(?:^|[_.-])(token|api[-_]?key|password|passwd|secret|credential|authorization|private[-_]?key)(?:$|[_.-])/i;
2
+ const INLINE_SECRET_PATTERN = /(^|[\s,{])(([A-Za-z0-9_.-]*(?:api[-_]?key|token|password|passwd|secret|credential|authorization|private[-_]?key)[A-Za-z0-9_.-]*)\s*[=:]\s*)([^\s,;"'}]+)/gi;
3
+ const AUTH_HEADER_PATTERN = /\b(Authorization\s*:\s*(?:Bearer|Basic|Token)?\s*)([^\r\n]+)/gi;
4
+ const BEARER_PATTERN = /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{8,})\b/g;
5
+ const PEM_PRIVATE_KEY_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
6
+
7
+ function isRecord(value: unknown): value is Record<string, unknown> {
8
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
9
+ // Exclude built-in types whose Object.entries() would produce empty arrays.
10
+ if (value instanceof Date || value instanceof RegExp || value instanceof Error || value instanceof Map || value instanceof Set) return false;
11
+ return true;
12
+ }
13
+
14
+ function isSecretKey(keyName: string): boolean {
15
+ return SECRET_KEY_PATTERN.test(keyName) || /^(token|apiKey|api_key|password|secret|credential|authorization|privateKey|private_key)$/i.test(keyName);
16
+ }
17
+
18
+ export function redactSecretString(value: string): string {
19
+ return value
20
+ .replace(PEM_PRIVATE_KEY_PATTERN, "***")
21
+ .replace(AUTH_HEADER_PATTERN, "$1***")
22
+ .replace(BEARER_PATTERN, "$1***")
23
+ .replace(INLINE_SECRET_PATTERN, "$1$2***");
24
+ }
25
+
26
+ export function redactSecrets(value: unknown, keyName = ""): unknown {
27
+ if (keyName && isSecretKey(keyName)) return "***";
28
+ if (typeof value === "string") return redactSecretString(value);
29
+ if (Array.isArray(value)) return value.map((item) => redactSecrets(item));
30
+ if (isRecord(value)) {
31
+ const output: Record<string, unknown> = {};
32
+ for (const [key, entry] of Object.entries(value)) output[key] = redactSecrets(entry, key);
33
+ return output;
34
+ }
35
+ return value;
36
+ }
37
+
38
+ export function redactJsonLine(line: string): string {
39
+ try {
40
+ return JSON.stringify(redactSecrets(JSON.parse(line) as unknown));
41
+ } catch {
42
+ return redactSecretString(line);
43
+ }
44
+ }
@@ -1,47 +1,47 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
-
4
- export function isSafePathId(value: string): boolean {
5
- return /^[A-Za-z0-9_-]+$/.test(value);
6
- }
7
-
8
- export function assertSafePathId(kind: string, value: string): string {
9
- if (!isSafePathId(value)) throw new Error(`Invalid ${kind}: ${value}`);
10
- return value;
11
- }
12
-
13
- export function resolveContainedPath(baseDir: string, targetPath: string): string {
14
- const base = path.resolve(baseDir);
15
- const resolved = path.isAbsolute(targetPath) ? path.resolve(targetPath) : path.resolve(base, targetPath);
16
- const relative = path.relative(base, resolved);
17
- if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`Path is outside ${baseDir}: ${targetPath}`);
18
- return resolved;
19
- }
20
-
21
- export function resolveRealContainedPath(baseDir: string, targetPath: string): string {
22
- const resolved = resolveContainedPath(baseDir, targetPath);
23
- let realBase: string;
24
- let realTarget: string;
25
- try {
26
- realBase = fs.realpathSync.native(baseDir);
27
- } catch (baseError) {
28
- throw new Error(`Cannot resolve real path of base directory ${baseDir}: ${baseError instanceof Error ? baseError.message : String(baseError)}`);
29
- }
30
- try {
31
- realTarget = fs.realpathSync.native(resolved);
32
- } catch (targetError) {
33
- if ((targetError as NodeJS.ErrnoException).code === "ENOENT") {
34
- throw new Error(`Path does not exist: ${resolved}`);
35
- }
36
- throw new Error(`Cannot resolve real path of ${resolved}: ${targetError instanceof Error ? targetError.message : String(targetError)}`);
37
- }
38
- const relative = path.relative(realBase, realTarget);
39
- if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`Path is outside ${baseDir}: ${targetPath}`);
40
- return realTarget;
41
- }
42
-
43
- export function resolveContainedRelativePath(baseDir: string, relativePath: string, kind = "path"): string {
44
- const normalized = relativePath.replaceAll("\\", "/").replace(/^\.\/+/, "");
45
- if (!normalized || normalized.split("/").some((segment) => segment === "..") || path.isAbsolute(normalized)) throw new Error(`Invalid ${kind}: ${relativePath}`);
46
- return resolveContainedPath(baseDir, path.resolve(baseDir, normalized));
47
- }
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+
4
+ export function isSafePathId(value: string): boolean {
5
+ return /^[A-Za-z0-9_-]+$/.test(value);
6
+ }
7
+
8
+ export function assertSafePathId(kind: string, value: string): string {
9
+ if (!isSafePathId(value)) throw new Error(`Invalid ${kind}: ${value}`);
10
+ return value;
11
+ }
12
+
13
+ export function resolveContainedPath(baseDir: string, targetPath: string): string {
14
+ const base = path.resolve(baseDir);
15
+ const resolved = path.isAbsolute(targetPath) ? path.resolve(targetPath) : path.resolve(base, targetPath);
16
+ const relative = path.relative(base, resolved);
17
+ if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`Path is outside ${baseDir}: ${targetPath}`);
18
+ return resolved;
19
+ }
20
+
21
+ export function resolveRealContainedPath(baseDir: string, targetPath: string): string {
22
+ const resolved = resolveContainedPath(baseDir, targetPath);
23
+ let realBase: string;
24
+ let realTarget: string;
25
+ try {
26
+ realBase = fs.realpathSync.native(baseDir);
27
+ } catch (baseError) {
28
+ throw new Error(`Cannot resolve real path of base directory ${baseDir}: ${baseError instanceof Error ? baseError.message : String(baseError)}`);
29
+ }
30
+ try {
31
+ realTarget = fs.realpathSync.native(resolved);
32
+ } catch (targetError) {
33
+ if ((targetError as NodeJS.ErrnoException).code === "ENOENT") {
34
+ throw new Error(`Path does not exist: ${resolved}`);
35
+ }
36
+ throw new Error(`Cannot resolve real path of ${resolved}: ${targetError instanceof Error ? targetError.message : String(targetError)}`);
37
+ }
38
+ const relative = path.relative(realBase, realTarget);
39
+ if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`Path is outside ${baseDir}: ${targetPath}`);
40
+ return realTarget;
41
+ }
42
+
43
+ export function resolveContainedRelativePath(baseDir: string, relativePath: string, kind = "path"): string {
44
+ const normalized = relativePath.replaceAll("\\", "/").replace(/^\.\/+/, "");
45
+ if (!normalized || normalized.split("/").some((segment) => segment === "..") || path.isAbsolute(normalized)) throw new Error(`Invalid ${kind}: ${relativePath}`);
46
+ return resolveContainedPath(baseDir, path.resolve(baseDir, normalized));
47
+ }
@@ -1,32 +1,32 @@
1
- /**
2
- * Sleep helper that respects abort signal.
3
- */
4
- export function sleep(ms: number, signal?: AbortSignal): Promise<void> {
5
- return new Promise((resolve, reject) => {
6
- if (signal?.aborted) {
7
- reject(signal.reason instanceof Error ? signal.reason : new Error("Aborted"));
8
- return;
9
- }
10
-
11
- let settled = false;
12
- const cleanup = (): void => {
13
- if (signal) signal.removeEventListener("abort", onAbort);
14
- };
15
- const timeout = setTimeout(() => {
16
- if (settled) return;
17
- settled = true;
18
- cleanup();
19
- resolve();
20
- }, ms);
21
-
22
- const onAbort = (): void => {
23
- if (settled) return;
24
- settled = true;
25
- clearTimeout(timeout);
26
- cleanup();
27
- reject(signal?.reason instanceof Error ? signal.reason : new Error("Aborted"));
28
- };
29
-
30
- signal?.addEventListener("abort", onAbort);
31
- });
32
- }
1
+ /**
2
+ * Sleep helper that respects abort signal.
3
+ */
4
+ export function sleep(ms: number, signal?: AbortSignal): Promise<void> {
5
+ return new Promise((resolve, reject) => {
6
+ if (signal?.aborted) {
7
+ reject(signal.reason instanceof Error ? signal.reason : new Error("Aborted"));
8
+ return;
9
+ }
10
+
11
+ let settled = false;
12
+ const cleanup = (): void => {
13
+ if (signal) signal.removeEventListener("abort", onAbort);
14
+ };
15
+ const timeout = setTimeout(() => {
16
+ if (settled) return;
17
+ settled = true;
18
+ cleanup();
19
+ resolve();
20
+ }, ms);
21
+
22
+ const onAbort = (): void => {
23
+ if (settled) return;
24
+ settled = true;
25
+ clearTimeout(timeout);
26
+ cleanup();
27
+ reject(signal?.reason instanceof Error ? signal.reason : new Error("Aborted"));
28
+ };
29
+
30
+ signal?.addEventListener("abort", onAbort);
31
+ });
32
+ }
@@ -1,40 +1,40 @@
1
- import type { TeamConfig } from "../teams/team-config.ts";
2
- import type { WorkflowConfig } from "./workflow-config.ts";
3
-
4
- export function validateWorkflowForTeam(workflow: WorkflowConfig, team: TeamConfig): string[] {
5
- const errors: string[] = [];
6
- const roles = new Set(team.roles.map((role) => role.name));
7
- const stepIds = new Set<string>();
8
-
9
- for (const step of workflow.steps) {
10
- if (stepIds.has(step.id)) errors.push(`Duplicate workflow step id '${step.id}'.`);
11
- stepIds.add(step.id);
12
- if (!roles.has(step.role)) errors.push(`Step '${step.id}' references unknown team role '${step.role}'.`);
13
- }
14
-
15
- for (const step of workflow.steps) {
16
- for (const dep of step.dependsOn ?? []) {
17
- if (!stepIds.has(dep)) errors.push(`Step '${step.id}' depends on unknown step '${dep}'.`);
18
- }
19
- }
20
-
21
- const visiting = new Set<string>();
22
- const visited = new Set<string>();
23
- const byId = new Map(workflow.steps.map((step) => [step.id, step]));
24
-
25
- function visit(id: string, trail: string[]): void {
26
- if (visited.has(id)) return;
27
- if (visiting.has(id)) {
28
- errors.push(`Workflow dependency cycle detected: ${[...trail, id].join(" -> ")}.`);
29
- return;
30
- }
31
- visiting.add(id);
32
- const step = byId.get(id);
33
- for (const dep of step?.dependsOn ?? []) visit(dep, [...trail, id]);
34
- visiting.delete(id);
35
- visited.add(id);
36
- }
37
-
38
- for (const step of workflow.steps) visit(step.id, []);
39
- return [...new Set(errors)];
40
- }
1
+ import type { TeamConfig } from "../teams/team-config.ts";
2
+ import type { WorkflowConfig } from "./workflow-config.ts";
3
+
4
+ export function validateWorkflowForTeam(workflow: WorkflowConfig, team: TeamConfig): string[] {
5
+ const errors: string[] = [];
6
+ const roles = new Set(team.roles.map((role) => role.name));
7
+ const stepIds = new Set<string>();
8
+
9
+ for (const step of workflow.steps) {
10
+ if (stepIds.has(step.id)) errors.push(`Duplicate workflow step id '${step.id}'.`);
11
+ stepIds.add(step.id);
12
+ if (!roles.has(step.role)) errors.push(`Step '${step.id}' references unknown team role '${step.role}'.`);
13
+ }
14
+
15
+ for (const step of workflow.steps) {
16
+ for (const dep of step.dependsOn ?? []) {
17
+ if (!stepIds.has(dep)) errors.push(`Step '${step.id}' depends on unknown step '${dep}'.`);
18
+ }
19
+ }
20
+
21
+ const visiting = new Set<string>();
22
+ const visited = new Set<string>();
23
+ const byId = new Map(workflow.steps.map((step) => [step.id, step]));
24
+
25
+ function visit(id: string, trail: string[]): void {
26
+ if (visited.has(id)) return;
27
+ if (visiting.has(id)) {
28
+ errors.push(`Workflow dependency cycle detected: ${[...trail, id].join(" -> ")}.`);
29
+ return;
30
+ }
31
+ visiting.add(id);
32
+ const step = byId.get(id);
33
+ for (const dep of step?.dependsOn ?? []) visit(dep, [...trail, id]);
34
+ visiting.delete(id);
35
+ visited.add(id);
36
+ }
37
+
38
+ for (const step of workflow.steps) visit(step.id, []);
39
+ return [...new Set(errors)];
40
+ }
@@ -1,45 +1,45 @@
1
- import { execFileSync } from "node:child_process";
2
-
3
- export type BranchFreshnessStatus = "fresh" | "stale" | "diverged" | "unknown";
4
- export type StaleBranchPolicy = "warn" | "block" | "auto_rebase" | "auto_merge_forward";
5
-
6
- export interface BranchFreshness {
7
- status: BranchFreshnessStatus;
8
- branch?: string;
9
- mainRef: string;
10
- ahead: number;
11
- behind: number;
12
- missingFixes: string[];
13
- message: string;
14
- error?: string;
15
- }
16
-
17
- function git(cwd: string, args: string[]): string {
18
- return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).trim();
19
- }
20
-
21
- function count(cwd: string, range: string): number {
22
- const raw = git(cwd, ["rev-list", "--count", range]);
23
- const parsed = Number.parseInt(raw, 10);
24
- return Number.isFinite(parsed) ? parsed : 0;
25
- }
26
-
27
- export function checkBranchFreshness(cwd: string, mainRef = "main"): BranchFreshness {
28
- try {
29
- git(cwd, ["rev-parse", "--is-inside-work-tree"]);
30
- const branch = git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
31
- const behind = count(cwd, `${branch}..${mainRef}`);
32
- const ahead = count(cwd, `${mainRef}..${branch}`);
33
- const missingFixes = behind > 0 ? git(cwd, ["log", "--format=%s", `${branch}..${mainRef}`]).split("\n").map((line) => line.trim()).filter(Boolean) : [];
34
- if (behind === 0) return { status: "fresh", branch, mainRef, ahead, behind, missingFixes, message: `Branch '${branch}' is fresh against ${mainRef}.` };
35
- if (ahead > 0) return { status: "diverged", branch, mainRef, ahead, behind, missingFixes, message: `Branch '${branch}' diverged from ${mainRef}: ahead=${ahead}, behind=${behind}.` };
36
- return { status: "stale", branch, mainRef, ahead, behind, missingFixes, message: `Branch '${branch}' is ${behind} commit(s) behind ${mainRef}.` };
37
- } catch (error) {
38
- const message = error instanceof Error ? error.message : String(error);
39
- return { status: "unknown", mainRef, ahead: 0, behind: 0, missingFixes: [], message: "Branch freshness could not be determined.", error: message };
40
- }
41
- }
42
-
43
- export function shouldBlockForBranchFreshness(freshness: BranchFreshness, policy: StaleBranchPolicy = "warn"): boolean {
44
- return policy === "block" && (freshness.status === "stale" || freshness.status === "diverged");
45
- }
1
+ import { execFileSync } from "node:child_process";
2
+
3
+ export type BranchFreshnessStatus = "fresh" | "stale" | "diverged" | "unknown";
4
+ export type StaleBranchPolicy = "warn" | "block" | "auto_rebase" | "auto_merge_forward";
5
+
6
+ export interface BranchFreshness {
7
+ status: BranchFreshnessStatus;
8
+ branch?: string;
9
+ mainRef: string;
10
+ ahead: number;
11
+ behind: number;
12
+ missingFixes: string[];
13
+ message: string;
14
+ error?: string;
15
+ }
16
+
17
+ function git(cwd: string, args: string[]): string {
18
+ return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).trim();
19
+ }
20
+
21
+ function count(cwd: string, range: string): number {
22
+ const raw = git(cwd, ["rev-list", "--count", range]);
23
+ const parsed = Number.parseInt(raw, 10);
24
+ return Number.isFinite(parsed) ? parsed : 0;
25
+ }
26
+
27
+ export function checkBranchFreshness(cwd: string, mainRef = "main"): BranchFreshness {
28
+ try {
29
+ git(cwd, ["rev-parse", "--is-inside-work-tree"]);
30
+ const branch = git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
31
+ const behind = count(cwd, `${branch}..${mainRef}`);
32
+ const ahead = count(cwd, `${mainRef}..${branch}`);
33
+ const missingFixes = behind > 0 ? git(cwd, ["log", "--format=%s", `${branch}..${mainRef}`]).split("\n").map((line) => line.trim()).filter(Boolean) : [];
34
+ if (behind === 0) return { status: "fresh", branch, mainRef, ahead, behind, missingFixes, message: `Branch '${branch}' is fresh against ${mainRef}.` };
35
+ if (ahead > 0) return { status: "diverged", branch, mainRef, ahead, behind, missingFixes, message: `Branch '${branch}' diverged from ${mainRef}: ahead=${ahead}, behind=${behind}.` };
36
+ return { status: "stale", branch, mainRef, ahead, behind, missingFixes, message: `Branch '${branch}' is ${behind} commit(s) behind ${mainRef}.` };
37
+ } catch (error) {
38
+ const message = error instanceof Error ? error.message : String(error);
39
+ return { status: "unknown", mainRef, ahead: 0, behind: 0, missingFixes: [], message: "Branch freshness could not be determined.", error: message };
40
+ }
41
+ }
42
+
43
+ export function shouldBlockForBranchFreshness(freshness: BranchFreshness, policy: StaleBranchPolicy = "warn"): boolean {
44
+ return policy === "block" && (freshness.status === "stale" || freshness.status === "diverged");
45
+ }
@@ -1,12 +1,12 @@
1
- ---
2
- name: default
3
- description: Balanced team for ordinary implementation tasks
4
- defaultWorkflow: default
5
- workspaceMode: single
6
- maxConcurrency: 2
7
- ---
8
-
9
- - explorer: agent=explorer fast discovery
10
- - planner: agent=planner plan the work
11
- - executor: agent=executor implement changes
12
- - verifier: agent=verifier verify completion
1
+ ---
2
+ name: default
3
+ description: Balanced team for ordinary implementation tasks
4
+ defaultWorkflow: default
5
+ workspaceMode: single
6
+ maxConcurrency: 2
7
+ ---
8
+
9
+ - explorer: agent=explorer fast discovery
10
+ - planner: agent=planner plan the work
11
+ - executor: agent=executor implement changes
12
+ - verifier: agent=verifier verify completion
@@ -1,11 +1,11 @@
1
- ---
2
- name: fast-fix
3
- description: Small team for quick bug fixes
4
- defaultWorkflow: fast-fix
5
- workspaceMode: single
6
- maxConcurrency: 1
7
- ---
8
-
9
- - explorer: agent=explorer find the relevant files
10
- - executor: agent=executor make the fix
11
- - verifier: agent=verifier verify the fix
1
+ ---
2
+ name: fast-fix
3
+ description: Small team for quick bug fixes
4
+ defaultWorkflow: fast-fix
5
+ workspaceMode: single
6
+ maxConcurrency: 1
7
+ ---
8
+
9
+ - explorer: agent=explorer find the relevant files
10
+ - executor: agent=executor make the fix
11
+ - verifier: agent=verifier verify the fix
@@ -1,18 +1,18 @@
1
- ---
2
- name: implementation
3
- description: Full implementation team with parallel specialists, critique, execution, review, and verification
4
- defaultWorkflow: implementation
5
- workspaceMode: single
6
- maxConcurrency: 3
7
- ---
8
-
9
- - explorer: agent=explorer map the codebase
10
- - analyst: agent=analyst clarify requirements and constraints
11
- - planner: agent=planner create execution plan
12
- - critic: agent=critic challenge and synthesize specialist findings
13
- - executor: agent=executor implement the plan
14
- - reviewer: agent=reviewer review the implementation
15
- - security-reviewer: agent=security-reviewer review security and trust boundaries
16
- - test-engineer: agent=test-engineer design and run verification
17
- - verifier: agent=verifier verify done
18
- - writer: agent=writer summarize documentation or release notes when needed
1
+ ---
2
+ name: implementation
3
+ description: Full implementation team with parallel specialists, critique, execution, review, and verification
4
+ defaultWorkflow: implementation
5
+ workspaceMode: single
6
+ maxConcurrency: 3
7
+ ---
8
+
9
+ - explorer: agent=explorer map the codebase
10
+ - analyst: agent=analyst clarify requirements and constraints
11
+ - planner: agent=planner create execution plan
12
+ - critic: agent=critic challenge and synthesize specialist findings
13
+ - executor: agent=executor implement the plan
14
+ - reviewer: agent=reviewer review the implementation
15
+ - security-reviewer: agent=security-reviewer review security and trust boundaries
16
+ - test-engineer: agent=test-engineer design and run verification
17
+ - verifier: agent=verifier verify done
18
+ - writer: agent=writer summarize documentation or release notes when needed
@@ -1,14 +1,14 @@
1
- ---
2
- name: parallel-research
3
- description: Parallel research team for multi-project/source audits
4
- workspaceMode: single
5
- defaultWorkflow: parallel-research
6
- maxConcurrency: 4
7
- triggers: đọc sâu, deep read, deep research, source audit, multiple projects, parallel research, pi-*
8
- category: research
9
- cost: cheap
10
- ---
11
-
12
- - explorer: agent=explorer gather source facts in parallel shards
13
- - analyst: agent=analyst synthesize shard findings
14
- - writer: agent=writer produce final notes
1
+ ---
2
+ name: parallel-research
3
+ description: Parallel research team for multi-project/source audits
4
+ workspaceMode: single
5
+ defaultWorkflow: parallel-research
6
+ maxConcurrency: 4
7
+ triggers: đọc sâu, deep read, deep research, source audit, multiple projects, parallel research, pi-*
8
+ category: research
9
+ cost: cheap
10
+ ---
11
+
12
+ - explorer: agent=explorer gather source facts in parallel shards
13
+ - analyst: agent=analyst synthesize shard findings
14
+ - writer: agent=writer produce final notes
@@ -1,11 +1,11 @@
1
- ---
2
- name: research
3
- description: Team for investigation and documentation
4
- defaultWorkflow: research
5
- workspaceMode: single
6
- maxConcurrency: 2
7
- ---
8
-
9
- - explorer: agent=explorer gather codebase facts
10
- - analyst: agent=analyst analyze findings
11
- - writer: agent=writer produce final notes
1
+ ---
2
+ name: research
3
+ description: Team for investigation and documentation
4
+ defaultWorkflow: research
5
+ workspaceMode: single
6
+ maxConcurrency: 2
7
+ ---
8
+
9
+ - explorer: agent=explorer gather codebase facts
10
+ - analyst: agent=analyst analyze findings
11
+ - writer: agent=writer produce final notes
@@ -1,12 +1,12 @@
1
- ---
2
- name: review
3
- description: Team for code review and security review
4
- defaultWorkflow: review
5
- workspaceMode: single
6
- maxConcurrency: 2
7
- ---
8
-
9
- - explorer: agent=explorer understand changed areas
10
- - reviewer: agent=reviewer review correctness and maintainability
11
- - security-reviewer: agent=security-reviewer review security risks
12
- - verifier: agent=verifier summarize pass/fail
1
+ ---
2
+ name: review
3
+ description: Team for code review and security review
4
+ defaultWorkflow: review
5
+ workspaceMode: single
6
+ maxConcurrency: 2
7
+ ---
8
+
9
+ - explorer: agent=explorer understand changed areas
10
+ - reviewer: agent=reviewer review correctness and maintainability
11
+ - security-reviewer: agent=security-reviewer review security risks
12
+ - verifier: agent=verifier summarize pass/fail
@@ -1,29 +1,29 @@
1
- ---
2
- name: default
3
- description: Explore, plan, execute, and verify
4
- ---
5
-
6
- ## explore
7
- role: explorer
8
-
9
- Explore the codebase for the goal: {goal}
10
-
11
- ## plan
12
- role: planner
13
- dependsOn: explore
14
- output: plan.md
15
-
16
- Create a concise implementation plan for: {goal}
17
-
18
- ## execute
19
- role: executor
20
- dependsOn: plan
21
-
22
- Implement the plan for: {goal}
23
-
24
- ## verify
25
- role: verifier
26
- dependsOn: execute
27
- verify: true
28
-
29
- Verify completion for: {goal}
1
+ ---
2
+ name: default
3
+ description: Explore, plan, execute, and verify
4
+ ---
5
+
6
+ ## explore
7
+ role: explorer
8
+
9
+ Explore the codebase for the goal: {goal}
10
+
11
+ ## plan
12
+ role: planner
13
+ dependsOn: explore
14
+ output: plan.md
15
+
16
+ Create a concise implementation plan for: {goal}
17
+
18
+ ## execute
19
+ role: executor
20
+ dependsOn: plan
21
+
22
+ Implement the plan for: {goal}
23
+
24
+ ## verify
25
+ role: verifier
26
+ dependsOn: execute
27
+ verify: true
28
+
29
+ Verify completion for: {goal}