pi-crew 0.9.55 → 0.9.57
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/AGENTS.md +1 -1
- package/CHANGELOG.md +58 -0
- package/README.md +53 -5
- package/dist/index.mjs +5666 -4370
- package/docs/README.md +54 -0
- package/package.json +12 -13
- package/skills/real-test-pi-crew/SKILL.md +93 -3
- package/src/agents/discover-agents.ts +68 -10
- package/src/benchmark/feedback-loop.ts +1 -1
- package/src/config/config.ts +2 -1
- package/src/config/types.ts +12 -0
- package/src/extension/action-suggestions.ts +16 -63
- package/src/extension/async-notifier.ts +3 -3
- package/src/extension/command-completions.ts +2 -2
- package/src/extension/crew-cleanup.ts +3 -3
- package/src/extension/crew-vibes/footer.ts +4 -2
- package/src/extension/cross-extension-rpc.ts +10 -5
- package/src/extension/knowledge-injection.ts +57 -10
- package/src/extension/register.ts +2 -2
- package/src/extension/registration/artifact-cleanup.ts +41 -3
- package/src/extension/registration/commands.ts +4 -4
- package/src/extension/registration/context-builder.ts +10 -10
- package/src/extension/registration/crash-recovery-cache.ts +3 -3
- package/src/extension/registration/foreground-run-controller.ts +1 -1
- package/src/extension/registration/lazy-configurers.ts +2 -2
- package/src/extension/registration/lifecycle-handlers.ts +12 -12
- package/src/extension/registration/lifecycle.ts +2 -2
- package/src/extension/registration/observability.ts +2 -2
- package/src/extension/registration/registration-types.ts +3 -3
- package/src/extension/registration/runtime-cleanup.ts +1 -1
- package/src/extension/registration/subagent-helpers.ts +2 -2
- package/src/extension/registration/subagent-manager-setup.ts +2 -2
- package/src/extension/registration/subagent-tools.ts +21 -16
- package/src/extension/registration/team-tool.ts +109 -17
- package/src/extension/registration/viewers.ts +3 -4
- package/src/extension/registration/wire-cross-extension.ts +7 -7
- package/src/extension/rpc-hmac.ts +3 -2
- package/src/extension/run-bundle-schema.ts +13 -4
- package/src/extension/run-export.ts +2 -2
- package/src/extension/run-index.ts +2 -3
- package/src/extension/run-maintenance.ts +57 -1
- package/src/extension/team-manager-command.ts +3 -1
- package/src/extension/team-tool/anchor.ts +8 -2
- package/src/extension/team-tool/api.ts +117 -39
- package/src/extension/team-tool/cancel.ts +23 -8
- package/src/extension/team-tool/chain-executor.ts +1 -1
- package/src/extension/team-tool/dispatch/automate.ts +17 -2
- package/src/extension/team-tool/dispatch/control.ts +17 -2
- package/src/extension/team-tool/dispatch/manage.ts +34 -2
- package/src/extension/team-tool/dispatch/run.ts +17 -2
- package/src/extension/team-tool/dispatch/status.ts +37 -5
- package/src/extension/team-tool/doctor.ts +2 -2
- package/src/extension/team-tool/explain.ts +1 -1
- package/src/extension/team-tool/goal-wrap.ts +5 -5
- package/src/extension/team-tool/goal.ts +30 -9
- package/src/extension/team-tool/handle-schedule.ts +37 -19
- package/src/extension/team-tool/inspect.ts +21 -5
- package/src/extension/team-tool/lifecycle-actions.ts +53 -10
- package/src/extension/team-tool/parallel-dispatch.ts +14 -5
- package/src/extension/team-tool/param-error.ts +138 -0
- package/src/extension/team-tool/respond.ts +20 -6
- package/src/extension/team-tool/run.ts +27 -17
- package/src/extension/team-tool/status.ts +17 -6
- package/src/extension/team-tool.ts +26 -19
- package/src/hooks/registry.ts +1 -1
- package/src/i18n.ts +22 -0
- package/src/observability/event-bus.ts +1 -1
- package/src/prompt/prompt-runtime.ts +2 -2
- package/src/runtime/README.md +35 -0
- package/src/runtime/agent-control.ts +1 -1
- package/src/runtime/anchor-manager.ts +0 -2
- package/src/runtime/async-runner.ts +1 -1
- package/src/runtime/attention-events.ts +3 -2
- package/src/runtime/auto-summarize.ts +0 -2
- package/src/runtime/background-runner.ts +103 -30
- package/src/runtime/{crew-broker-child.ts → broker/crew-broker-child.ts} +2 -2
- package/src/runtime/{crew-broker-client.ts → broker/crew-broker-client.ts} +4 -7
- package/src/runtime/{crew-broker.ts → broker/crew-broker.ts} +13 -21
- package/src/runtime/chain-runner.ts +50 -5
- package/src/runtime/{child-pi-constants.ts → child-pi/child-pi-constants.ts} +1 -1
- package/src/runtime/{child-pi-kill.ts → child-pi/child-pi-kill.ts} +3 -3
- package/src/runtime/{child-pi-spawn.ts → child-pi/child-pi-spawn.ts} +28 -12
- package/src/runtime/{child-pi-steering.ts → child-pi/child-pi-steering.ts} +1 -1
- package/src/runtime/{child-pi-streams.ts → child-pi/child-pi-streams.ts} +23 -4
- package/src/runtime/{child-pi-transcript.ts → child-pi/child-pi-transcript.ts} +6 -6
- package/src/runtime/{child-pi.ts → child-pi/child-pi.ts} +47 -78
- package/src/runtime/compaction/compact-stages/bounded-tail.ts +84 -0
- package/src/runtime/{compact-stages → compaction/compact-stages}/index.ts +2 -2
- package/src/runtime/{compaction-summary.ts → compaction/compaction-summary.ts} +3 -3
- package/src/runtime/{tool-output-pruner.ts → compaction/tool-output-pruner.ts} +1 -1
- package/src/runtime/crew-agent-records.ts +48 -2
- package/src/runtime/custom-tools/irc-tool.ts +2 -2
- package/src/runtime/delivery-coordinator.ts +0 -3
- package/src/runtime/diagnostic-export.ts +5 -4
- package/src/runtime/foreground-control.ts +1 -1
- package/src/runtime/foreground-watchdog.ts +1 -1
- package/src/runtime/{adaptive-plan.ts → goal-workflow/adaptive-plan.ts} +8 -8
- package/src/runtime/{dynamic-workflow-context.ts → goal-workflow/dynamic-workflow-context.ts} +16 -16
- package/src/runtime/{dynamic-workflow-runner.ts → goal-workflow/dynamic-workflow-runner.ts} +10 -10
- package/src/runtime/{goal-achievement.ts → goal-workflow/goal-achievement.ts} +3 -3
- package/src/runtime/{goal-evaluator.ts → goal-workflow/goal-evaluator.ts} +8 -8
- package/src/runtime/{goal-loop-runner.ts → goal-workflow/goal-loop-runner.ts} +27 -25
- package/src/runtime/{goal-state-store.ts → goal-workflow/goal-state-store.ts} +8 -8
- package/src/runtime/{plan-templates.ts → goal-workflow/plan-templates.ts} +1 -1
- package/src/runtime/group-join.ts +3 -3
- package/src/runtime/handoff-manager.ts +0 -2
- package/src/runtime/{heartbeat-watcher.ts → heartbeat/heartbeat-watcher.ts} +7 -7
- package/src/runtime/iteration-hooks.ts +1 -1
- package/src/runtime/{live-agent-control.ts → live-session/live-agent-control.ts} +2 -2
- package/src/runtime/{live-agent-manager.ts → live-session/live-agent-manager.ts} +4 -4
- package/src/runtime/{live-session-runtime.ts → live-session/live-session-runtime.ts} +39 -41
- package/src/runtime/manifest-cache.ts +6 -2
- package/src/runtime/{model-fallback.ts → model/model-fallback.ts} +1 -1
- package/src/runtime/{pi-args.ts → model/pi-args.ts} +20 -5
- package/src/runtime/model/provider-extensions.ts +116 -0
- package/src/runtime/{runtime-policy.ts → model/runtime-policy.ts} +2 -2
- package/src/runtime/{runtime-resolver.ts → model/runtime-resolver.ts} +3 -3
- package/src/runtime/{runtime-warmup.ts → model/runtime-warmup.ts} +8 -8
- package/src/runtime/orphan-worker-registry.ts +1 -1
- package/src/runtime/{progress-tracker.ts → output/progress-tracker.ts} +1 -1
- package/src/runtime/{result-extractor.ts → output/result-extractor.ts} +4 -4
- package/src/runtime/{sidechain-output.ts → output/sidechain-output.ts} +2 -2
- package/src/runtime/{stream-preview.ts → output/stream-preview.ts} +1 -1
- package/src/runtime/{streaming-output.ts → output/streaming-output.ts} +2 -2
- package/src/runtime/{tool-progress.ts → output/tool-progress.ts} +1 -1
- package/src/runtime/parent-guard.ts +30 -10
- package/src/runtime/pipeline-runner.ts +2 -2
- package/src/runtime/policy-engine.ts +2 -2
- package/src/runtime/{cancellation.ts → process/cancellation.ts} +1 -1
- package/src/runtime/{post-exit-stdio-guard.ts → process/post-exit-stdio-guard.ts} +1 -1
- package/src/runtime/{process-lifecycle.ts → process/process-lifecycle.ts} +3 -3
- package/src/runtime/{checkpoint.ts → recovery/checkpoint.ts} +4 -4
- package/src/runtime/{crash-recovery.ts → recovery/crash-recovery.ts} +93 -21
- package/src/runtime/{overflow-recovery.ts → recovery/overflow-recovery.ts} +1 -1
- package/src/runtime/{recovery-recipes.ts → recovery/recovery-recipes.ts} +1 -1
- package/src/runtime/{retry-executor.ts → recovery/retry-executor.ts} +2 -2
- package/src/runtime/{retry-runner.ts → recovery/retry-runner.ts} +2 -8
- package/src/runtime/run-tracker.ts +1 -1
- package/src/runtime/run-worker.ts +3 -3
- package/src/runtime/{coalesce-tasks.ts → scheduling/coalesce-tasks.ts} +3 -3
- package/src/runtime/{concurrency.ts → scheduling/concurrency.ts} +9 -3
- package/src/runtime/{parallel-research.ts → scheduling/parallel-research.ts} +4 -2
- package/src/runtime/{run-coalesced-task-group.ts → scheduling/run-coalesced-task-group.ts} +95 -36
- package/src/runtime/{semaphore.ts → scheduling/semaphore.ts} +4 -1
- package/src/runtime/{task-graph-scheduler.ts → scheduling/task-graph-scheduler.ts} +1 -1
- package/src/runtime/session-resources.ts +1 -1
- package/src/runtime/settings-store.ts +1 -1
- package/src/runtime/single-agent-compose.ts +1 -0
- package/src/runtime/stale-reconciler.ts +1 -1
- package/src/runtime/subagent-manager.ts +1 -1
- package/src/runtime/supervisor-contact.ts +28 -13
- package/src/runtime/task-output-context.ts +7 -5
- package/src/runtime/task-runner/child-executor.ts +67 -31
- package/src/runtime/task-runner/live-executor.ts +8 -8
- package/src/runtime/task-runner/post-execution.ts +12 -12
- package/src/runtime/task-runner/pre-execution.ts +6 -6
- package/src/runtime/task-runner/progress.ts +1 -1
- package/src/runtime/task-runner/run-projection.ts +1 -1
- package/src/runtime/task-runner/scaffold-executor.ts +1 -1
- package/src/runtime/task-runner/state-helpers.ts +2 -2
- package/src/runtime/task-runner.ts +4 -4
- package/src/runtime/team-runner.ts +441 -175
- package/src/runtime/{completion-guard.ts → verification/completion-guard.ts} +1 -1
- package/src/runtime/{green-contract.ts → verification/green-contract.ts} +1 -1
- package/src/runtime/{post-checks.ts → verification/post-checks.ts} +4 -4
- package/src/runtime/{verification-gates.ts → verification/verification-gates.ts} +5 -5
- package/src/runtime/{verification-integrity.ts → verification/verification-integrity.ts} +1 -1
- package/src/schema/config-schema.ts +1 -0
- package/src/schema/team-tool-schema.ts +149 -185
- package/src/state/README.md +44 -0
- package/src/state/atomic-write.ts +24 -3
- package/src/state/{locks.ts → coordination/locks.ts} +118 -38
- package/src/state/{mailbox.ts → coordination/mailbox.ts} +14 -8
- package/src/state/{schedule.ts → coordination/schedule.ts} +3 -3
- package/src/state/{task-claims.ts → coordination/task-claims.ts} +1 -1
- package/src/state/decision-ledger.ts +1 -1
- package/src/state/{event-log-rotation.ts → event-log/event-log-rotation.ts} +189 -31
- package/src/state/{event-log.ts → event-log/event-log.ts} +77 -39
- package/src/state/{jsonl-writer.ts → event-log/jsonl-writer.ts} +14 -6
- package/src/state/hook-instinct-bridge.ts +2 -2
- package/src/state/{active-run-registry.ts → stores/active-run-registry.ts} +7 -7
- package/src/state/{artifact-store.ts → stores/artifact-store.ts} +62 -4
- package/src/state/{blob-store.ts → stores/blob-store.ts} +3 -3
- package/src/state/{health-store.ts → stores/health-store.ts} +3 -3
- package/src/state/{instinct-store.ts → stores/instinct-store.ts} +3 -3
- package/src/state/{observation-store.ts → stores/observation-store.ts} +2 -2
- package/src/state/{run-cache.ts → stores/run-cache.ts} +4 -4
- package/src/state/{run-graph.ts → stores/run-graph.ts} +4 -4
- package/src/state/{run-metrics.ts → stores/run-metrics.ts} +3 -3
- package/src/state/{state-store.ts → stores/state-store.ts} +254 -21
- package/src/state/types.ts +27 -3
- package/src/ui/component.ts +55 -0
- package/src/ui/crew-select-list.ts +3 -1
- package/src/ui/dashboard-panes/agents-pane.ts +1 -1
- package/src/ui/dwf-phase-display.ts +1 -1
- package/src/ui/heartbeat-aggregator.ts +1 -1
- package/src/ui/keybinding-map.ts +179 -6
- package/src/ui/layout-primitives.ts +9 -5
- package/src/ui/live-conversation-overlay.ts +1 -1
- package/src/ui/live-run-sidebar.ts +1 -1
- package/src/ui/overlay-stack.ts +148 -0
- package/src/ui/overlays/agent-picker-overlay.ts +1 -1
- package/src/ui/overlays/mailbox-compose-overlay.ts +1 -1
- package/src/ui/overlays/mailbox-detail-overlay.ts +2 -2
- package/src/ui/powerbar-publisher.ts +1 -1
- package/src/ui/render-scheduler.ts +1 -1
- package/src/ui/run-action-dispatcher.ts +3 -3
- package/src/ui/run-dashboard.ts +5 -33
- package/src/ui/run-event-bus.ts +3 -3
- package/src/ui/run-snapshot-cache.ts +37 -18
- package/src/ui/settings-overlay.ts +13 -21
- package/src/ui/snapshot-types.ts +1 -1
- package/src/ui/status-colors.ts +0 -2
- package/src/ui/terminal-status.ts +1 -1
- package/src/ui/theme-adapter.ts +33 -13
- package/src/ui/tool-renderers/index.ts +8 -0
- package/src/ui/transcript-viewer.ts +37 -13
- package/src/ui/widget/index.ts +32 -9
- package/src/ui/widget/widget-formatters.ts +62 -4
- package/src/ui/widget/widget-model.ts +2 -2
- package/src/ui/widget/widget-renderer.ts +1 -1
- package/src/utils/conflict-detect.ts +58 -5
- package/src/utils/ndjson.ts +2 -2
- package/src/utils/redaction.ts +39 -0
- package/src/utils/socket-path.ts +1 -1
- package/src/workflows/workflow-config.ts +1 -1
- package/src/worktree/cleanup.ts +1 -1
- package/src/worktree/worktree-manager.ts +111 -47
- package/workflows/chain.workflow.md +2 -2
- package/dist/build-meta.json +0 -16807
- package/index.bundle.ts +0 -25
- package/scripts/bench-check.mjs +0 -96
- package/scripts/bench-cold-start.mjs +0 -216
- package/scripts/build-bundle.mjs +0 -92
- package/scripts/check-all-skills.ts +0 -294
- package/scripts/check-bundle-size.mjs +0 -38
- package/scripts/check-bundle-staleness.mjs +0 -97
- package/scripts/check-conflict-markers.mjs +0 -91
- package/scripts/check-lazy-imports.mjs +0 -28
- package/scripts/dev-runner.mjs +0 -106
- package/scripts/profile-startup.mjs +0 -125
- package/scripts/release-smoke.mjs +0 -74
- package/scripts/run-bench.mjs +0 -47
- package/scripts/run-real-chain.ts +0 -41
- package/scripts/test-issue-29-crash.ts +0 -111
- package/scripts/test-issue-29-e2e.ts +0 -183
- package/scripts/test-issue-29-real-runtime.ts +0 -330
- package/scripts/test-issue-29-real-tasks.ts +0 -387
- package/scripts/test-issue-29-team-tool.ts +0 -105
- package/scripts/test-runner.mjs +0 -84
- package/scripts/verify-flicker-fix.ts +0 -109
- package/scripts/verify-skill.ts +0 -550
- package/scripts/watch-bundle.mjs +0 -259
- package/scripts/watchdog-harness.ts +0 -119
- package/src/adapters/claude-adapter.ts +0 -23
- package/src/adapters/codex-adapter.ts +0 -21
- package/src/adapters/cursor-adapter.ts +0 -17
- package/src/adapters/export-util.ts +0 -143
- package/src/adapters/index.ts +0 -15
- package/src/adapters/registry.ts +0 -18
- package/src/adapters/types.ts +0 -23
- package/src/runtime/code-summary.ts +0 -292
- package/src/runtime/cross-extension-rpc.ts +0 -143
- package/src/runtime/hidden-handoff.ts +0 -407
- package/src/runtime/prose-compressor.ts +0 -162
- package/src/subagents/async-entry.ts +0 -1
- package/src/subagents/index.ts +0 -3
- package/src/subagents/live/control.ts +0 -1
- package/src/subagents/live/manager.ts +0 -1
- package/src/subagents/live/realtime.ts +0 -1
- package/src/subagents/live/session-runtime.ts +0 -1
- package/src/subagents/manager.ts +0 -1
- package/src/subagents/spawn.ts +0 -1
- package/test-integration-check.ts +0 -118
- /package/src/runtime/{broker-issuer.ts → broker/broker-issuer.ts} +0 -0
- /package/src/runtime/{crew-broker-tokens.ts → broker/crew-broker-tokens.ts} +0 -0
- /package/src/runtime/{child-pi-pool.ts → child-pi/child-pi-pool.ts} +0 -0
- /package/src/runtime/{compact-pipeline.ts → compaction/compact-pipeline.ts} +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/ansi-strip-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/blank-collapse-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/deduplicate-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/head-snap-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/tail-capture-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/truncation-stage.ts +0 -0
- /package/src/runtime/{important-line-classifier.ts → compaction/important-line-classifier.ts} +0 -0
- /package/src/runtime/{heartbeat-gradient.ts → heartbeat/heartbeat-gradient.ts} +0 -0
- /package/src/runtime/{worker-heartbeat.ts → heartbeat/worker-heartbeat.ts} +0 -0
- /package/src/runtime/{worker-startup.ts → heartbeat/worker-startup.ts} +0 -0
- /package/src/runtime/{intercom-bridge.ts → live-session/intercom-bridge.ts} +0 -0
- /package/src/runtime/{live-control-realtime.ts → live-session/live-control-realtime.ts} +0 -0
- /package/src/runtime/{live-extension-bridge.ts → live-session/live-extension-bridge.ts} +0 -0
- /package/src/runtime/{live-irc.ts → live-session/live-irc.ts} +0 -0
- /package/src/runtime/{live-session-health.ts → live-session/live-session-health.ts} +0 -0
- /package/src/runtime/{model-resolver.ts → model/model-resolver.ts} +0 -0
- /package/src/runtime/{model-scope.ts → model/model-scope.ts} +0 -0
- /package/src/runtime/{output-validator.ts → output/output-validator.ts} +0 -0
- /package/src/runtime/{pi-json-output.ts → output/pi-json-output.ts} +0 -0
- /package/src/runtime/{progress-event-coalescer.ts → output/progress-event-coalescer.ts} +0 -0
- /package/src/runtime/{cancellation-token.ts → process/cancellation-token.ts} +0 -0
- /package/src/runtime/{zombie-scanner.ts → process/zombie-scanner.ts} +0 -0
- /package/src/runtime/{crash-classification.ts → recovery/crash-classification.ts} +0 -0
- /package/src/runtime/{batch-barrier.ts → scheduling/batch-barrier.ts} +0 -0
- /package/src/runtime/{global-worker-cap.ts → scheduling/global-worker-cap.ts} +0 -0
- /package/src/runtime/{parallel-utils.ts → scheduling/parallel-utils.ts} +0 -0
- /package/src/runtime/{scheduler.ts → scheduling/scheduler.ts} +0 -0
- /package/src/runtime/{task-graph.ts → scheduling/task-graph.ts} +0 -0
- /package/src/runtime/{verification-worktree.ts → verification/verification-worktree.ts} +0 -0
- /package/src/state/{event-reconstructor.ts → event-log/event-reconstructor.ts} +0 -0
- /package/src/state/{worker-atomic-writer.ts → event-log/worker-atomic-writer.ts} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { redactSecrets } from "
|
|
4
|
-
import { isSafePathId } from "
|
|
3
|
+
import { redactSecrets } from "../../utils/redaction.ts";
|
|
4
|
+
import { isSafePathId } from "../../utils/safe-paths.ts";
|
|
5
5
|
|
|
6
6
|
export interface SidechainEntry {
|
|
7
7
|
isSidechain: true;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Captures incremental worker output during task execution for live preview.
|
|
3
3
|
// Used by the UI layer to show partial results before task completion.
|
|
4
4
|
|
|
5
|
-
import { TAIL_CAPTURE_STREAM_STAGE } from "
|
|
5
|
+
import { TAIL_CAPTURE_STREAM_STAGE } from "../compaction/compact-stages/index.ts";
|
|
6
6
|
import type { ParsedPiUsage } from "./pi-json-output.ts";
|
|
7
7
|
|
|
8
8
|
export interface ToolCallPreview {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import type { TeamRunManifest } from "
|
|
4
|
-
import { isSafePathId } from "
|
|
3
|
+
import type { TeamRunManifest } from "../../state/types.ts";
|
|
4
|
+
import { isSafePathId } from "../../utils/safe-paths.ts";
|
|
5
5
|
|
|
6
6
|
export interface StreamingOutputHandle {
|
|
7
7
|
write(text: string): void;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 4. This module provides structured types and utilities for the event system
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import type { CrewAgentProgress } from "
|
|
13
|
+
import type { CrewAgentProgress } from "../../state/types.ts";
|
|
14
14
|
|
|
15
15
|
// ── Event Types ─────────────────────────────────────────────────────────
|
|
16
16
|
|
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Parent liveness guard for pi-crew
|
|
2
|
+
* Parent liveness guard for pi-crew background-runner process.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* REALITY (verified by grep, RT-19/RT-2): `startParentGuard` is called in
|
|
5
|
+
* exactly ONE production location — `background-runner.ts:508`. The
|
|
6
|
+
* background-runner watches the main pi session that spawned it. If that
|
|
7
|
+
* parent dies, the runner self-terminates so it does not leak.
|
|
8
|
+
*
|
|
9
|
+
* Child pi workers do NOT call `startParentGuard`. They are the external
|
|
10
|
+
* `pi` binary (`@earendil-works/pi-coding-agent`) — pi-crew does not control
|
|
11
|
+
* its entry point. Although `PI_CREW_PARENT_PID` is set in the child's env
|
|
12
|
+
* (`child-pi-spawn.ts:134`), it has ZERO consumers: grep of the Pi binary
|
|
13
|
+
* dist for `PI_CREW_PARENT_PID` / `startParentGuard` / `parent-guard` = 0
|
|
14
|
+
* matches. This means child workers are NOT protected by this guard.
|
|
15
|
+
*
|
|
16
|
+
* The deeper fix (wiring `startParentGuard` into the pi worker entry point) is
|
|
17
|
+
* DEFERRED because workers are an external binary pi-crew doesn't control.
|
|
18
|
+
*
|
|
19
|
+
* Orphan-mitigation for this gap relies on:
|
|
20
|
+
* 1. The RT-2 SIGINT fix in `background-runner.ts` (abort + exitCode pattern
|
|
21
|
+
* lets the finally/runCleanup block terminate child-pi processes).
|
|
22
|
+
* 2. The reactive `zombie-scanner.ts` sweep (finds workers whose
|
|
23
|
+
* `PI_CREW_PARENT_PID` points at a dead PID and reports them).
|
|
8
24
|
*
|
|
9
25
|
* Note: `process.kill(pid, 0)` works on both Unix and Windows in Node.js
|
|
10
26
|
* for checking process existence. On Windows, it may throw for processes
|
|
11
27
|
* owned by other users (permission error), but correctly detects dead PIDs.
|
|
12
28
|
*
|
|
13
|
-
* Usage in
|
|
29
|
+
* Usage in background-runner.ts (the ONLY consumer):
|
|
14
30
|
* ```ts
|
|
15
31
|
* const parentPid = Number(process.env.PI_CREW_PARENT_PID);
|
|
16
32
|
* if (parentPid > 0) startParentGuard(parentPid);
|
|
@@ -81,12 +97,16 @@ function selfTerminate(parentPid: number): never {
|
|
|
81
97
|
|
|
82
98
|
/**
|
|
83
99
|
* Start a lightweight poll that checks if the parent process is still alive.
|
|
84
|
-
* If the parent dies,
|
|
100
|
+
* If the parent dies, the calling process exits immediately with code 124.
|
|
101
|
+
*
|
|
102
|
+
* CURRENT REALITY: the only production caller is `background-runner.ts`, which
|
|
103
|
+
* uses this to self-terminate when the main pi session dies. Child pi workers
|
|
104
|
+
* do NOT call this (see module header for the DEFERRED deeper fix).
|
|
85
105
|
*
|
|
86
106
|
* FIX: Removed unref() — the guard interval MUST keep the event loop alive
|
|
87
|
-
* to prevent premature
|
|
88
|
-
*
|
|
89
|
-
* a
|
|
107
|
+
* to prevent premature runner exit when the parent is still alive but the
|
|
108
|
+
* runner has no other pending work (LLM calls, timers, I/O). Without this,
|
|
109
|
+
* a runner in pure CPU wait could exit even though its parent is alive.
|
|
90
110
|
*/
|
|
91
111
|
export function startParentGuard(parentPid: number): void {
|
|
92
112
|
if (!parentPid || !Number.isFinite(parentPid) || parentPid <= 0) return;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { errors } from "../errors.ts";
|
|
2
|
-
import { appendEventAsync, flushEventLogBuffer } from "../state/event-log.ts";
|
|
2
|
+
import { appendEventAsync, flushEventLogBuffer } from "../state/event-log/event-log.ts";
|
|
3
3
|
import { errorMessage } from "../utils/guards.ts";
|
|
4
4
|
import type { WorkflowConfig } from "../workflows/workflow-config.ts";
|
|
5
|
-
import { mapConcurrent } from "./parallel-utils.ts";
|
|
5
|
+
import { mapConcurrent } from "./scheduling/parallel-utils.ts";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Pipeline stage configuration.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CrewLimitsConfig } from "../config/config.ts";
|
|
2
2
|
import type { PolicyDecision, PolicyDecisionAction, PolicyDecisionReason, TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { isWorkerHeartbeatStale } from "./heartbeat/worker-heartbeat.ts";
|
|
4
|
+
import { evaluateGreenContract } from "./verification/green-contract.ts";
|
|
5
5
|
|
|
6
6
|
export interface PolicyEngineInput {
|
|
7
7
|
manifest: TeamRunManifest;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Owned-process lifecycle abstraction (P0 item #3).
|
|
3
3
|
*
|
|
4
|
-
* Distilled and adapted from gajae-code's `runtime/process-lifecycle.ts`.
|
|
4
|
+
* Distilled and adapted from gajae-code's `runtime/process/process-lifecycle.ts`.
|
|
5
5
|
*
|
|
6
6
|
* Two complementary primitives:
|
|
7
7
|
*
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* See `.crew/knowledge.md` gotchas: BSD/Windows signal handling differs.
|
|
31
31
|
*/
|
|
32
32
|
import { type ChildProcess, type SpawnOptions, spawn } from "node:child_process";
|
|
33
|
-
import { logInternalError } from "
|
|
33
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
34
34
|
|
|
35
35
|
// ── tunables ──────────────────────────────────────────────────────────────────
|
|
36
36
|
|
|
@@ -197,7 +197,7 @@ export class OwnedProcess {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
private deregisterFn: () => void = () =>
|
|
200
|
+
private deregisterFn: () => void = () => undefined;
|
|
201
201
|
|
|
202
202
|
/** `true` once `dispose()` has started. */
|
|
203
203
|
get isDisposed(): boolean {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { atomicWriteFile } from "
|
|
4
|
-
import { logInternalError } from "
|
|
5
|
-
import { projectCrewRoot } from "
|
|
6
|
-
import { assertSafePathId } from "
|
|
3
|
+
import { atomicWriteFile } from "../../state/atomic-write.ts";
|
|
4
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
5
|
+
import { projectCrewRoot } from "../../utils/paths.ts";
|
|
6
|
+
import { assertSafePathId } from "../../utils/safe-paths.ts";
|
|
7
7
|
|
|
8
8
|
export interface Checkpoint {
|
|
9
9
|
runId: string;
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { appendHookEvent, executeHook } from "
|
|
5
|
-
import type { MetricRegistry } from "
|
|
6
|
-
import {
|
|
7
|
-
import { appendEvent, scanSequence } from "
|
|
8
|
-
import {
|
|
9
|
-
import { loadRunManifestById, saveRunTasks, updateRunStatus } from "
|
|
10
|
-
import type { TeamTaskState } from "
|
|
11
|
-
import { logInternalError } from "
|
|
12
|
-
import { projectCrewRoot, userCrewRoot } from "
|
|
13
|
-
import { resolveRealContainedPath } from "
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
4
|
+
import { appendHookEvent, executeHook } from "../../hooks/registry.ts";
|
|
5
|
+
import type { MetricRegistry } from "../../observability/metric-registry.ts";
|
|
6
|
+
import { withRunLockSync } from "../../state/coordination/locks.ts";
|
|
7
|
+
import { appendEvent, scanSequence } from "../../state/event-log/event-log.ts";
|
|
8
|
+
import { readActiveRunRegistry, unregisterActiveRun } from "../../state/stores/active-run-registry.ts";
|
|
9
|
+
import { loadRunManifestById, saveRunTasks, updateRunStatus } from "../../state/stores/state-store.ts";
|
|
10
|
+
import type { TeamTaskState } from "../../state/types.ts";
|
|
11
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
12
|
+
import { projectCrewRoot, userCrewRoot } from "../../utils/paths.ts";
|
|
13
|
+
import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
14
|
+
import { sleepSync } from "../../utils/sleep.ts";
|
|
15
|
+
import { recordFromTask, upsertCrewAgent } from "../crew-agent-records.ts";
|
|
16
|
+
import { isWorkerHeartbeatStale } from "../heartbeat/worker-heartbeat.ts";
|
|
17
|
+
import { terminateLiveAgentsForRun } from "../live-session/live-agent-manager.ts";
|
|
18
|
+
import type { ManifestCache } from "../manifest-cache.ts";
|
|
19
|
+
import { checkProcessLiveness } from "../process-status.ts";
|
|
20
|
+
import { isPlanApprovalPending, type ReconcileResult, reconcileStaleRun } from "../stale-reconciler.ts";
|
|
20
21
|
|
|
21
22
|
export interface RecoveryPlan {
|
|
22
23
|
runId: string;
|
|
@@ -35,6 +36,68 @@ function isTerminalTask(task: TeamTaskState): boolean {
|
|
|
35
36
|
);
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Errno codes that represent transient filesystem conditions (e.g. Windows AV
|
|
41
|
+
* scan temporarily locking the file, or a brief permission window). On these
|
|
42
|
+
* errors the manifest is NOT corrupt — retrying the read after a short backoff
|
|
43
|
+
* will succeed.
|
|
44
|
+
*/
|
|
45
|
+
const TRANSIENT_READ_ERRNO_CODES = new Set(["EBUSY", "EACCES", "EAGAIN", "EPERM", "ENFILE", "EMFILE"]);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Raw file-read function used by readManifestWithTransientRetry. Separated as
|
|
49
|
+
* a module-level binding so tests can inject transient-error mocks without
|
|
50
|
+
* needing to mutate the frozen ESM `fs` namespace.
|
|
51
|
+
*/
|
|
52
|
+
let _readManifestFileSync: (filePath: string) => string = (p) => fs.readFileSync(p, "utf-8");
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @internal — test seam for readManifestWithTransientRetry. Pass `null` to reset.
|
|
56
|
+
*/
|
|
57
|
+
export function _setReadManifestFileSyncForTest(fn: ((filePath: string) => string) | null): void {
|
|
58
|
+
_readManifestFileSync = fn ?? ((p) => fs.readFileSync(p, "utf-8"));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Read and JSON-parse a manifest file, retrying transient read errors
|
|
63
|
+
* (EBUSY/EACCES/EAGAIN — common on Windows when AV software briefly locks the
|
|
64
|
+
* file) with exponential backoff.
|
|
65
|
+
*
|
|
66
|
+
* ST-6: Previously, a single `catch` quarantined the manifest on ALL errors,
|
|
67
|
+
* including transient I/O failures that left a perfectly healthy manifest
|
|
68
|
+
* unparseable for a few milliseconds. This renamed the valid file to
|
|
69
|
+
* `.corrupt-*`, making the run permanently unloadable.
|
|
70
|
+
*
|
|
71
|
+
* Behavior:
|
|
72
|
+
* - Success → returns parsed object
|
|
73
|
+
* - SyntaxError (genuinely corrupt JSON) → rethrown immediately (caller quarantines)
|
|
74
|
+
* - Transient ErrnoException → retried up to `maxRetries` times; if still
|
|
75
|
+
* failing, rethrown so the caller can skip WITHOUT quarantining
|
|
76
|
+
* - Other ErrnoException (ENOENT, etc.) → rethrown immediately
|
|
77
|
+
*/
|
|
78
|
+
export function readManifestWithTransientRetry(manifestPath: string, maxRetries = 3, baseDelayMs = 100): Record<string, unknown> {
|
|
79
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(_readManifestFileSync(manifestPath)) as Record<string, unknown>;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
// SyntaxError = genuinely corrupt JSON → rethrow immediately
|
|
84
|
+
if (err instanceof SyntaxError) throw err;
|
|
85
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
86
|
+
// Transient read error → retry with exponential backoff
|
|
87
|
+
if (code !== undefined && TRANSIENT_READ_ERRNO_CODES.has(code)) {
|
|
88
|
+
if (attempt < maxRetries) {
|
|
89
|
+
sleepSync(baseDelayMs * 2 ** attempt);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Non-transient error, or retries exhausted → rethrow
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Unreachable: the loop either returns or throws on every iteration.
|
|
98
|
+
throw new Error(`unreachable: readManifestWithTransientRetry exhausted for ${manifestPath}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
38
101
|
function shouldRecoverTask(task: TeamTaskState, deadMs: number): boolean {
|
|
39
102
|
if (task.status !== "running") return false;
|
|
40
103
|
if (!task.heartbeat) return true;
|
|
@@ -375,11 +438,20 @@ export function purgeStaleActiveRunIndex(staleThresholdMs = 300_000, now = Date.
|
|
|
375
438
|
}
|
|
376
439
|
| undefined;
|
|
377
440
|
try {
|
|
378
|
-
manifest =
|
|
379
|
-
} catch {
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
441
|
+
manifest = readManifestWithTransientRetry(entry.manifestPath) as typeof manifest;
|
|
442
|
+
} catch (err) {
|
|
443
|
+
// ST-6: Transient read errors (EBUSY/EACCES/EAGAIN from Windows AV
|
|
444
|
+
// scans) must NOT quarantine a healthy manifest — the file is fine,
|
|
445
|
+
// just temporarily locked. Skip this entry and try again next cycle.
|
|
446
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
447
|
+
const isTransient = code !== undefined && TRANSIENT_READ_ERRNO_CODES.has(code);
|
|
448
|
+
if (isTransient) continue;
|
|
449
|
+
|
|
450
|
+
// R-01: Quarantine the corrupt manifest instead of deleting all run
|
|
451
|
+
// state. A single corrupted byte must NOT cause total data loss —
|
|
452
|
+
// events.jsonl, task data, and artifacts are preserved for manual
|
|
453
|
+
// recovery. SyntaxError (genuinely corrupt JSON) and unexpected
|
|
454
|
+
// non-transient errors fall through to here.
|
|
383
455
|
try {
|
|
384
456
|
// Include pid for uniqueness across concurrent quarantines in the same ms.
|
|
385
457
|
fs.renameSync(entry.manifestPath, entry.manifestPath + ".corrupt-" + Date.now() + "-" + process.pid);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { sleep } from "
|
|
2
|
-
import { throwIfCancelled } from "
|
|
1
|
+
import { sleep } from "../../utils/sleep.ts";
|
|
2
|
+
import { throwIfCancelled } from "../process/cancellation.ts";
|
|
3
3
|
|
|
4
4
|
export interface RetryPolicy {
|
|
5
5
|
maxAttempts: number;
|
|
@@ -7,11 +7,9 @@
|
|
|
7
7
|
* - Accumulates context from previous attempts
|
|
8
8
|
* - Supports exponential backoff
|
|
9
9
|
* - Limits handoff accumulation to prevent memory leaks
|
|
10
|
-
*
|
|
11
|
-
* @see docs/pi-boomerang-integration-plan.md
|
|
12
10
|
*/
|
|
13
11
|
|
|
14
|
-
import type { HandoffManager, HandoffSummary, TaskPacket, TaskResult } from "
|
|
12
|
+
import type { HandoffManager, HandoffSummary, TaskPacket, TaskResult } from "../handoff-manager.ts";
|
|
15
13
|
|
|
16
14
|
/**
|
|
17
15
|
* Retry configuration.
|
|
@@ -73,7 +71,6 @@ export class RetryRunner {
|
|
|
73
71
|
|
|
74
72
|
private taskRunner: TaskRunnerLike;
|
|
75
73
|
private handoffManager: HandoffManager;
|
|
76
|
-
private _handoffs: HandoffSummary[] = [];
|
|
77
74
|
|
|
78
75
|
constructor(taskRunner: TaskRunnerLike, handoffManager: HandoffManager) {
|
|
79
76
|
this.taskRunner = taskRunner;
|
|
@@ -100,7 +97,7 @@ export class RetryRunner {
|
|
|
100
97
|
* Useful when you want to reset state without disposing.
|
|
101
98
|
*/
|
|
102
99
|
clearHandoffs(): void {
|
|
103
|
-
|
|
100
|
+
/* no-op: _handoffs field removed (was never read) */
|
|
104
101
|
}
|
|
105
102
|
|
|
106
103
|
/**
|
|
@@ -139,9 +136,6 @@ export class RetryRunner {
|
|
|
139
136
|
const startTime = Date.now();
|
|
140
137
|
const maxHandoffs = this.getMaxHandoffs(config);
|
|
141
138
|
|
|
142
|
-
// Clear previous handoffs at start of each retry run
|
|
143
|
-
this._handoffs = [];
|
|
144
|
-
|
|
145
139
|
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
146
140
|
if (this._disposed) {
|
|
147
141
|
throw new Error("RetryRunner was disposed during retry");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { loadRunManifestById } from "../state/state-store.ts";
|
|
3
|
+
import { loadRunManifestById } from "../state/stores/state-store.ts";
|
|
4
4
|
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
5
5
|
import { projectCrewRoot } from "../utils/paths.ts";
|
|
6
6
|
import { isFinishedRunStatus } from "./process-status.ts";
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
* caller-owned (they need task/manifest state that runWorker does not see).
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import type { ChildPiRunInput, ChildPiRunResult } from "./child-pi.ts";
|
|
30
|
-
import { runChildPi } from "./child-pi.ts";
|
|
31
|
-
import { withWorkerSlot } from "./global-worker-cap.ts";
|
|
29
|
+
import type { ChildPiRunInput, ChildPiRunResult } from "./child-pi/child-pi.ts";
|
|
30
|
+
import { runChildPi } from "./child-pi/child-pi.ts";
|
|
31
|
+
import { withWorkerSlot } from "./scheduling/global-worker-cap.ts";
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Input for {@link runWorker}. Extends {@link ChildPiRunInput} with the `cap`
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
* grouping many micro-tasks into one worker prompt.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import type { TeamTaskState } from "
|
|
31
|
-
import type { WorkflowConfig, WorkflowStep } from "
|
|
32
|
-
import { permissionForRole } from "
|
|
30
|
+
import type { TeamTaskState } from "../../state/types.ts";
|
|
31
|
+
import type { WorkflowConfig, WorkflowStep } from "../../workflows/workflow-config.ts";
|
|
32
|
+
import { permissionForRole } from "../role-permission.ts";
|
|
33
33
|
|
|
34
34
|
/** Default cap on group size to prevent context-budget overflow. */
|
|
35
35
|
export const DEFAULT_MAX_GROUP_SIZE = 5;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_CONCURRENCY } from "
|
|
1
|
+
import { DEFAULT_CONCURRENCY } from "../../config/defaults.ts";
|
|
2
|
+
import { getWorkerCapCapacity } from "./global-worker-cap.ts";
|
|
2
3
|
|
|
3
4
|
export interface ResolveBatchConcurrencyInput {
|
|
4
5
|
workflowName: string;
|
|
@@ -7,6 +8,8 @@ export interface ResolveBatchConcurrencyInput {
|
|
|
7
8
|
limitMaxConcurrentWorkers?: number;
|
|
8
9
|
allowUnboundedConcurrency?: boolean;
|
|
9
10
|
hardCap?: number;
|
|
11
|
+
/** Optional override for the global worker cap (test determinism). Defaults to getWorkerCapCapacity(). */
|
|
12
|
+
workerCap?: number;
|
|
10
13
|
readyCount: number;
|
|
11
14
|
workspaceMode?: "single" | "worktree";
|
|
12
15
|
readyRoles?: string[];
|
|
@@ -45,9 +48,12 @@ export function resolveBatchConcurrency(input: ResolveBatchConcurrencyInput): Ba
|
|
|
45
48
|
else if (teamMax !== undefined) source = "team";
|
|
46
49
|
else source = "workflow";
|
|
47
50
|
const hardCap = positiveInteger(input.hardCap) ?? DEFAULT_CONCURRENCY.hardCap;
|
|
48
|
-
|
|
51
|
+
// P1-7: consult the global worker cap so the scheduler doesn't over-dispatch
|
|
52
|
+
// (e.g. dispatch 4 while the global semaphore holds 2 on a 4-core machine).
|
|
53
|
+
const workerCap = positiveInteger(input.workerCap) ?? getWorkerCapCapacity();
|
|
54
|
+
const maxConcurrent = input.allowUnboundedConcurrency ? requested : Math.min(requested, hardCap, workerCap);
|
|
49
55
|
const readyCount = Math.max(0, Math.trunc(Number.isFinite(input.readyCount) ? input.readyCount : 0));
|
|
50
|
-
const cappedReason = maxConcurrent < requested ? `;capped
|
|
56
|
+
const cappedReason = maxConcurrent < requested ? `;capped:hard=${hardCap},worker=${workerCap}` : "";
|
|
51
57
|
const unboundedReason = input.allowUnboundedConcurrency && requested > hardCap ? `;unbounded:${hardCap}` : "";
|
|
52
58
|
return {
|
|
53
59
|
maxConcurrent,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import type { WorkflowConfig, WorkflowStep } from "
|
|
3
|
+
import type { WorkflowConfig, WorkflowStep } from "../../workflows/workflow-config.ts";
|
|
4
4
|
|
|
5
5
|
export function sourcePiProjects(cwd: string): string[] {
|
|
6
6
|
const sourceDir = path.join(cwd, "Source");
|
|
@@ -17,7 +17,9 @@ export function sourcePiProjects(cwd: string): string[] {
|
|
|
17
17
|
|
|
18
18
|
export function chunkProjects(projects: string[], target = 6): string[][] {
|
|
19
19
|
const chunks = Array.from({ length: Math.min(Math.max(1, target), Math.max(1, projects.length)) }, () => [] as string[]);
|
|
20
|
-
projects.forEach((project, index) =>
|
|
20
|
+
projects.forEach((project, index) => {
|
|
21
|
+
chunks[index % chunks.length]!.push(project);
|
|
22
|
+
});
|
|
21
23
|
return chunks.filter((chunk) => chunk.length > 0);
|
|
22
24
|
}
|
|
23
25
|
|