@mmerterden/multi-agent-pipeline 20.36.0 → 20.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/docs/facts.json +2 -2
- package/docs/features.md +1 -1
- package/docs/recovery-guide.md +1 -1
- package/install/catalog-history.json +1 -1
- package/manifest.json +225 -148
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/SKILL.md +33 -154
- package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
- package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
- package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
- package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +12 -7
- package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
- package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
- package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
- package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
- package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
- package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
- package/pipeline/commands/multi-agent/sync/SKILL.md +35 -164
- package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
- package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
- package/pipeline/contract/CHANGELOG.md +9 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
- package/pipeline/contract/fixtures/runs-empty.json +1 -1
- package/pipeline/contract/fixtures/runs-failed.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
- package/pipeline/contract/fixtures/runs-running.json +1 -1
- package/pipeline/contract/manifest.json +5 -5
- package/pipeline/contract/types/index.d.ts +3 -2
- package/pipeline/multi-agent-refs/audit-guide.md +20 -0
- package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
- package/pipeline/multi-agent-refs/channels/generator.md +14 -0
- package/pipeline/multi-agent-refs/channels/preview.md +13 -0
- package/pipeline/multi-agent-refs/channels/reference.md +46 -0
- package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
- package/pipeline/multi-agent-refs/design-check/export.md +1 -0
- package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
- package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
- package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
- package/pipeline/multi-agent-refs/features/commit-multi-repo.md +63 -0
- package/pipeline/multi-agent-refs/features/dev-multi-repo.md +51 -0
- package/pipeline/multi-agent-refs/features/test-gap.md +40 -0
- package/pipeline/multi-agent-refs/features/worktree-finalize.md +1 -1
- package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
- package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
- package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
- package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
- package/pipeline/multi-agent-refs/orchestrator/phase-3-user-test.md +12 -9
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -4
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +8 -47
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +21 -65
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +9 -59
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +0 -31
- package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
- package/pipeline/multi-agent-refs/resume/details.md +115 -2
- package/pipeline/multi-agent-refs/review/inputs.md +33 -1
- package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
- package/pipeline/multi-agent-refs/review/panel.md +75 -0
- package/pipeline/multi-agent-refs/review/valven.md +5 -1
- package/pipeline/multi-agent-refs/rules.md +1 -1
- package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
- package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
- package/pipeline/multi-agent-refs/stack/background.md +72 -0
- package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
- package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
- package/pipeline/multi-agent-refs/sync/office.md +10 -0
- package/pipeline/multi-agent-refs/sync/overview.md +61 -0
- package/pipeline/multi-agent-refs/sync/repo.md +91 -0
- package/pipeline/multi-agent-refs/update/details.md +83 -0
- package/pipeline/office/index.html +6 -3
- package/pipeline/office/toolkit.json +1 -1
- package/pipeline/rules/figma-pipeline.md +6 -3
- package/pipeline/schemas/prefs.schema.json +1 -1
- package/pipeline/schemas/runs-index.schema.json +2 -1
- package/pipeline/scripts/_memory-policy.mjs +27 -5
- package/pipeline/scripts/checkpoint-fold.sh +59 -0
- package/pipeline/scripts/contract-server.mjs +1 -1
- package/pipeline/scripts/gate-ledger.mjs +1 -1
- package/pipeline/scripts/office-control.mjs +41 -15
- package/pipeline/scripts/offload-ref.sh +1 -1
- package/pipeline/scripts/rework-rulings.mjs +5 -3
- package/pipeline/scripts/runs-index.mjs +34 -7
- package/pipeline/scripts/scope-check-gate.mjs +15 -2
- package/pipeline/scripts/status-overview.mjs +401 -0
- package/pipeline/scripts/user-test-handoff.sh +203 -0
- package/pipeline/scripts/validate-planning.mjs +5 -3
- package/pipeline/scripts/validate-triage.mjs +2 -1
- package/pipeline/scripts/write-state.mjs +13 -4
- package/pipeline/skills/.skill-manifest.json +64 -64
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +7 -70
- package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
- package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +10 -72
- package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +12 -7
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +73 -217
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
- package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
- package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
- package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
- package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
- package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
- package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
- package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
- package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
- package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
- package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
- package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
- package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
- package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
- package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
- package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
- package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
- package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
- package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
- package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
- package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
- package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
- package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
- package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
- package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
- package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
- package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
- package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
- package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
- package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
- package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
- package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
- package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
- package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
- package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
- package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
- package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
- package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
- package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
- package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
- package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
- package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
- package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
- package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
- package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
- package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
- package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
- package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
- package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
- package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
- package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
|
@@ -81,7 +81,7 @@ const PHASE_WAITING_FROM = PHASE_CONTRACT.thresholds.waitingFromPhase;
|
|
|
81
81
|
*/
|
|
82
82
|
const LAST_PHASE = PHASE_CONTRACT.phases[PHASE_CONTRACT.phases.length - 1].id;
|
|
83
83
|
|
|
84
|
-
export const CONTRACT_VERSION = "1.
|
|
84
|
+
export const CONTRACT_VERSION = "1.4.0";
|
|
85
85
|
|
|
86
86
|
const STATE_MAX_BYTES = 16 * 1024 * 1024;
|
|
87
87
|
|
|
@@ -93,14 +93,22 @@ const GROUPS = {
|
|
|
93
93
|
stopped: "Stopped mid-development",
|
|
94
94
|
question: "Left at a question",
|
|
95
95
|
unknown: "Status not recorded",
|
|
96
|
+
done: "Finished",
|
|
96
97
|
};
|
|
97
98
|
|
|
99
|
+
/**
|
|
100
|
+
* A run in one of these finished or was given up; it asks nothing of anyone.
|
|
101
|
+
* `failed` is not here: a failed run can still be resumed or killed.
|
|
102
|
+
*/
|
|
103
|
+
const DONE_STATUSES = new Set(["complete", "completed", "done", "abandoned", "cancelled"]);
|
|
104
|
+
|
|
98
105
|
function parseArgs(argv) {
|
|
99
|
-
const flags = { json: false, redact: false, group: null, taskId: null };
|
|
106
|
+
const flags = { json: false, redact: false, all: false, group: null, taskId: null };
|
|
100
107
|
for (let i = 0; i < argv.length; i++) {
|
|
101
108
|
const a = argv[i];
|
|
102
109
|
if (a === "--json") flags.json = true;
|
|
103
110
|
else if (a === "--redact") flags.redact = true;
|
|
111
|
+
else if (a === "--all") flags.all = true;
|
|
104
112
|
else if (a === "--group") flags.group = argv[++i];
|
|
105
113
|
else if (a.startsWith("--group=")) flags.group = a.slice(8);
|
|
106
114
|
else if (a === "--task-id") flags.taskId = argv[++i];
|
|
@@ -379,11 +387,12 @@ function summarisePhases(tracker) {
|
|
|
379
387
|
* The group a run belongs to, per status/SKILL.md 3b.
|
|
380
388
|
*
|
|
381
389
|
* @param {object|null} state
|
|
382
|
-
* @returns {"waiting"|"stopped"|"question"|"unknown"}
|
|
390
|
+
* @returns {"waiting"|"stopped"|"question"|"unknown"|"done"}
|
|
383
391
|
*/
|
|
384
392
|
function groupOf(state) {
|
|
385
393
|
const status = state?.status;
|
|
386
394
|
if (!status) return "unknown";
|
|
395
|
+
if (DONE_STATUSES.has(status)) return "done";
|
|
387
396
|
const phase = Number(state?.currentPhase);
|
|
388
397
|
const prUrl = state?.pr?.url ?? state?.prUrl ?? null;
|
|
389
398
|
if (status === "awaiting_input" || status === "awaiting-user-test-main-checkout")
|
|
@@ -525,7 +534,17 @@ function fmtDuration(startedAt) {
|
|
|
525
534
|
return `${Math.floor(h / 24)}d`;
|
|
526
535
|
}
|
|
527
536
|
|
|
528
|
-
|
|
537
|
+
/**
|
|
538
|
+
* The Phase column. A phase past the contract's last one was recorded under an
|
|
539
|
+
* older, longer numbering; "current/last" would read as overflow, so it shows
|
|
540
|
+
* as the recorded number alone.
|
|
541
|
+
*/
|
|
542
|
+
export function phaseCell(phase) {
|
|
543
|
+
if (phase === null) return " -";
|
|
544
|
+
return phase > LAST_PHASE ? `${phase} old` : `${phase}/${LAST_PHASE}`;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function renderHuman(records, { all = false } = {}) {
|
|
529
548
|
const out = [];
|
|
530
549
|
const total = records.length;
|
|
531
550
|
out.push(`multi-agent runs - ${total} under ${logsRoot()}`);
|
|
@@ -541,13 +560,20 @@ function renderHuman(records) {
|
|
|
541
560
|
for (const key of Object.keys(GROUPS)) {
|
|
542
561
|
const rows = records.filter((r) => r.group === key);
|
|
543
562
|
if (!rows.length) continue;
|
|
563
|
+
// Neither group asks anything of anyone, and together they are most of a
|
|
564
|
+
// machine's runs: a count keeps the actionable groups readable.
|
|
565
|
+
if ((key === "done" || key === "unknown") && !all) {
|
|
566
|
+
out.push("");
|
|
567
|
+
out.push(`${GROUPS[key]} (${rows.length}) - --all lists them`);
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
544
570
|
out.push("");
|
|
545
571
|
out.push(`${GROUPS[key]} (${rows.length})`);
|
|
546
572
|
out.push(
|
|
547
573
|
" ID Phase Status Branch Age ~USD",
|
|
548
574
|
);
|
|
549
575
|
for (const r of rows) {
|
|
550
|
-
const phase = r.currentPhase
|
|
576
|
+
const phase = phaseCell(r.currentPhase);
|
|
551
577
|
out.push(
|
|
552
578
|
" " +
|
|
553
579
|
[
|
|
@@ -651,7 +677,7 @@ function main() {
|
|
|
651
677
|
const flags = parseArgs(process.argv.slice(2));
|
|
652
678
|
if (flags.help) {
|
|
653
679
|
process.stdout.write(
|
|
654
|
-
"Usage: runs-index.mjs [--json [--redact]] [--group waiting|stopped|question|unknown] [--task-id <id>]\n",
|
|
680
|
+
"Usage: runs-index.mjs [--json [--redact]] [--all] [--group waiting|stopped|question|unknown|done] [--task-id <id>]\n",
|
|
655
681
|
);
|
|
656
682
|
return 0;
|
|
657
683
|
}
|
|
@@ -670,7 +696,8 @@ function main() {
|
|
|
670
696
|
JSON.stringify(envelope(records, { redact: flags.redact }), null, 2) + "\n",
|
|
671
697
|
);
|
|
672
698
|
} else {
|
|
673
|
-
|
|
699
|
+
// A group asked for by name is listed, not folded into its count.
|
|
700
|
+
process.stdout.write(renderHuman(records, { all: flags.all || flags.group !== null }) + "\n");
|
|
674
701
|
}
|
|
675
702
|
return 0;
|
|
676
703
|
}
|
|
@@ -99,6 +99,17 @@ const LINE_COMMENT = /(\.(py|pyi|ya?ml|sh|bash|zsh|rb|toml|coffee)|(^|\/)(GNU)?[
|
|
|
99
99
|
const TIGHT_LANGS =
|
|
100
100
|
/\.(js|jsx|mjs|cjs|ts|tsx|java|go|cs|swift|kt|kts|json|rs|dart|scala|py|pyi|c|h|cc|cpp|hpp|m|mm)$/;
|
|
101
101
|
const LEADING_OPERATOR_LANGS = /\.(kt|kts|swift|go|scala)$/;
|
|
102
|
+
// A newline ends the statement here, so joining two lines changes the program.
|
|
103
|
+
const NEWLINE_TERMINATED = new RegExp(
|
|
104
|
+
[
|
|
105
|
+
String.raw`\.(sh|bash|zsh|ksh|fish|rb|rake|gemspec|toml|dockerfile)$`,
|
|
106
|
+
String.raw`(^|\/)\.envrc$`,
|
|
107
|
+
String.raw`(^|\/)[^/]*Rakefile$`,
|
|
108
|
+
String.raw`(^|\/)(Gemfile|Podfile|Fastfile|Appfile|Matchfile|Snapfile|Scanfile|Gymfile|Deliverfile|Pluginfile|Dangerfile|Brewfile|Vagrantfile|Jenkinsfile)$`,
|
|
109
|
+
String.raw`(^|\/)(Dockerfile|Containerfile)([.-][^/]*)?$`,
|
|
110
|
+
].join("|"),
|
|
111
|
+
"i",
|
|
112
|
+
);
|
|
102
113
|
const BREAK_KEEPERS = /\b(return|throw|yield|break|continue)$/;
|
|
103
114
|
const LEADING_OPERATOR = /^[+\-*/%&|^.?]/;
|
|
104
115
|
const TIGHT = /\s*([(){}[\],;:])\s*/g;
|
|
@@ -135,8 +146,9 @@ function foldLine(line, hashComments) {
|
|
|
135
146
|
/**
|
|
136
147
|
* The layout-free form of some lines. Each line is folded on its own (linear
|
|
137
148
|
* in the hunk), then the lines are joined by a space, except that a break
|
|
138
|
-
* after return, throw, yield, break or continue,
|
|
139
|
-
* Scala a break before a leading operator,
|
|
149
|
+
* after return, throw, yield, break or continue, in Kotlin, Swift, Go or
|
|
150
|
+
* Scala a break before a leading operator, and every break in shell, Ruby,
|
|
151
|
+
* TOML and Dockerfiles, stays a break. Space next to
|
|
140
152
|
* brackets, commas, semicolons and colons is dropped only in TIGHT_LANGS, and
|
|
141
153
|
* never in a hunk that holds a C preprocessor line.
|
|
142
154
|
*/
|
|
@@ -150,6 +162,7 @@ function outsideStrings(text, file = "") {
|
|
|
150
162
|
return;
|
|
151
163
|
}
|
|
152
164
|
const keepBreak =
|
|
165
|
+
NEWLINE_TERMINATED.test(file) ||
|
|
153
166
|
BREAK_KEEPERS.test(lines[i - 1]) ||
|
|
154
167
|
(LEADING_OPERATOR_LANGS.test(file) && LEADING_OPERATOR.test(line));
|
|
155
168
|
joined += (keepBreak ? "\u23ce" : " ") + line;
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file status-overview.mjs - what is happening on this machine right now,
|
|
4
|
+
* in one answer: the live Claude Code sessions and the run each one drives,
|
|
5
|
+
* the tokens they used, which accounts and credentials are set up, and the
|
|
6
|
+
* run list.
|
|
7
|
+
*
|
|
8
|
+
* Sources, all read on every call:
|
|
9
|
+
*
|
|
10
|
+
* 1. sessions: `claude agents --json` (lib/claude-sessions.mjs), matched to
|
|
11
|
+
* a run by the session id it recorded, else by a working directory inside
|
|
12
|
+
* the run's worktree (activity-snapshot.mjs matchRun);
|
|
13
|
+
* 2. tokens: the `usage` block of every assistant message in the session's
|
|
14
|
+
* transcript and its subagent transcripts, counted once per message id
|
|
15
|
+
* (a message split into several lines repeats its usage). The last-24h
|
|
16
|
+
* figure covers every transcript written in that window. Prices come from
|
|
17
|
+
* cost-table.json; cache writes are priced at the input rate, so the
|
|
18
|
+
* estimate errs low;
|
|
19
|
+
* 3. accounts: the github.com logins gh holds (usage-identity.mjs) and the
|
|
20
|
+
* author git would write in the current directory;
|
|
21
|
+
* 4. credentials: each logical name in prefs.global.keychainMapping is
|
|
22
|
+
* "registered" when its service name is in `credential-store.sh list`.
|
|
23
|
+
* Only service names are read: the store is never asked for a value, and
|
|
24
|
+
* nothing about a value (length, prefix) is printed;
|
|
25
|
+
* 5. runs: runs-index.mjs, the same records /multi-agent:status always used.
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
* status-overview.mjs [--json] [--all]
|
|
29
|
+
*
|
|
30
|
+
* Env: MA_CREDENTIAL_STORE (the store script), MA_PREFS (the preferences
|
|
31
|
+
* file), CLAUDE_PROJECTS_ROOT (the transcript root), MA_AP_AGENTS_JSON (the
|
|
32
|
+
* session list, see claude-sessions.mjs).
|
|
33
|
+
*
|
|
34
|
+
* Exit: 0 printed, 2 usage.
|
|
35
|
+
*
|
|
36
|
+
* @module pipeline/scripts/status-overview
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { createReadStream, existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
40
|
+
import { spawnSync } from "node:child_process";
|
|
41
|
+
import { homedir } from "node:os";
|
|
42
|
+
import { basename, join } from "node:path";
|
|
43
|
+
import { createInterface } from "node:readline";
|
|
44
|
+
|
|
45
|
+
import { listSessions } from "../lib/claude-sessions.mjs";
|
|
46
|
+
import { runMain } from "../lib/fatal.mjs";
|
|
47
|
+
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
48
|
+
import { costUsd } from "./_cost.mjs";
|
|
49
|
+
import { matchRun } from "./activity-snapshot.mjs";
|
|
50
|
+
import { buildIndex, phaseCell } from "./runs-index.mjs";
|
|
51
|
+
import { listAccounts } from "./usage-identity.mjs";
|
|
52
|
+
|
|
53
|
+
export const CONTRACT_VERSION = "1.0.0";
|
|
54
|
+
export const WINDOW_MS = 24 * 3_600_000;
|
|
55
|
+
const SAFE_SESSION_ID = /^[A-Za-z0-9-]{8,64}$/;
|
|
56
|
+
/**
|
|
57
|
+
* States of a session that is still open. Wider than claude-sessions.mjs
|
|
58
|
+
* LIVE_STATUSES, which asks whether a session is working: an idle or blocked
|
|
59
|
+
* session is not working, but it is open and it is what this overview counts.
|
|
60
|
+
*/
|
|
61
|
+
const OPEN = new Set(["busy", "running", "waiting", "idle", "blocked"]);
|
|
62
|
+
|
|
63
|
+
const projectsRoot = (env) => env.CLAUDE_PROJECTS_ROOT || join(homedir(), ".claude", "projects");
|
|
64
|
+
const prefsPath = (env) =>
|
|
65
|
+
env.MA_PREFS || join(homedir(), ".claude", "multi-agent-preferences.json");
|
|
66
|
+
const storePath = (env) =>
|
|
67
|
+
env.MA_CREDENTIAL_STORE || new URL("../lib/credential-store.sh", import.meta.url).pathname;
|
|
68
|
+
|
|
69
|
+
function listDir(dir) {
|
|
70
|
+
try {
|
|
71
|
+
return readdirSync(dir);
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let PRICES = null;
|
|
78
|
+
/** The cost-table rate for a wire model id, by its `modelId`, else by rung name in the id. */
|
|
79
|
+
export function rateFor(model) {
|
|
80
|
+
if (!PRICES) {
|
|
81
|
+
try {
|
|
82
|
+
PRICES = JSON.parse(
|
|
83
|
+
readFileSync(new URL("./cost-table.json", import.meta.url), "utf8"),
|
|
84
|
+
).prices;
|
|
85
|
+
} catch {
|
|
86
|
+
PRICES = {};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (typeof model !== "string" || !model) return null;
|
|
90
|
+
for (const rate of Object.values(PRICES)) if (rate?.modelId === model) return rate;
|
|
91
|
+
for (const [rung, rate] of Object.entries(PRICES)) {
|
|
92
|
+
if (/^[a-z]+$/.test(rung) && model.includes(`-${rung}-`)) return rate;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const emptyUsage = () => ({ in: 0, out: 0, cacheRead: 0, cacheWrite: 0, usd: 0, unpriced: 0 });
|
|
98
|
+
|
|
99
|
+
function addUsage(acc, model, u) {
|
|
100
|
+
const tin = Number(u.input_tokens) || 0;
|
|
101
|
+
const tout = Number(u.output_tokens) || 0;
|
|
102
|
+
const read = Number(u.cache_read_input_tokens) || 0;
|
|
103
|
+
const write = Number(u.cache_creation_input_tokens) || 0;
|
|
104
|
+
acc.in += tin;
|
|
105
|
+
acc.out += tout;
|
|
106
|
+
acc.cacheRead += read;
|
|
107
|
+
acc.cacheWrite += write;
|
|
108
|
+
const usd = costUsd(rateFor(model), tin + write, tout, read);
|
|
109
|
+
if (usd === null) acc.unpriced++;
|
|
110
|
+
else acc.usd += usd;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Token usage in one transcript: the whole file, and the part at or after
|
|
115
|
+
* `since`. Each message id counts once in the file; `windowSeen`, shared
|
|
116
|
+
* across files, keeps a message a resumed or forked session copied into a
|
|
117
|
+
* second transcript from counting twice in the window. A file that cannot be
|
|
118
|
+
* read adds nothing and comes back `readable: false`.
|
|
119
|
+
*
|
|
120
|
+
* @returns {Promise<{total: object, window: object, readable: boolean}>}
|
|
121
|
+
*/
|
|
122
|
+
export async function transcriptUsage(path, since = 0, windowSeen = new Set()) {
|
|
123
|
+
const total = emptyUsage();
|
|
124
|
+
const window = emptyUsage();
|
|
125
|
+
const seen = new Set();
|
|
126
|
+
try {
|
|
127
|
+
await scan(path, (rec, msg, id) => {
|
|
128
|
+
if (id) {
|
|
129
|
+
if (seen.has(id)) return;
|
|
130
|
+
seen.add(id);
|
|
131
|
+
}
|
|
132
|
+
addUsage(total, msg.model, msg.usage);
|
|
133
|
+
const t = Date.parse(rec.timestamp);
|
|
134
|
+
if (!Number.isFinite(t) || t < since) return;
|
|
135
|
+
if (id) {
|
|
136
|
+
if (windowSeen.has(id)) return;
|
|
137
|
+
windowSeen.add(id);
|
|
138
|
+
}
|
|
139
|
+
addUsage(window, msg.model, msg.usage);
|
|
140
|
+
});
|
|
141
|
+
} catch {
|
|
142
|
+
return { total: emptyUsage(), window: emptyUsage(), readable: false };
|
|
143
|
+
}
|
|
144
|
+
return { total, window, readable: true };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function scan(path, visit) {
|
|
148
|
+
const lines = createInterface({ input: createReadStream(path), crlfDelay: Infinity });
|
|
149
|
+
for await (const line of lines) {
|
|
150
|
+
if (!line.includes('"usage"')) continue;
|
|
151
|
+
let rec;
|
|
152
|
+
try {
|
|
153
|
+
rec = JSON.parse(line);
|
|
154
|
+
} catch {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const msg = rec?.message;
|
|
158
|
+
if (rec?.type !== "assistant" || !msg?.usage || typeof msg.usage !== "object") continue;
|
|
159
|
+
// The host writes its own notices as "<synthetic>" messages; no model ran.
|
|
160
|
+
if (msg.model === "<synthetic>") continue;
|
|
161
|
+
visit(rec, msg, typeof msg.id === "string" ? msg.id : null);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function merge(a, b) {
|
|
166
|
+
for (const k of Object.keys(a)) a[k] += b[k];
|
|
167
|
+
return a;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** The session transcript and its subagent transcripts, or [] when the id is unsafe or absent. */
|
|
171
|
+
export function sessionFiles(root, sessionId) {
|
|
172
|
+
if (typeof sessionId !== "string" || !SAFE_SESSION_ID.test(sessionId)) return [];
|
|
173
|
+
for (const slug of listDir(root)) {
|
|
174
|
+
const main = join(root, slug, `${sessionId}.jsonl`);
|
|
175
|
+
if (!existsSync(main)) continue;
|
|
176
|
+
const sub = join(root, slug, sessionId, "subagents");
|
|
177
|
+
return [
|
|
178
|
+
main,
|
|
179
|
+
...listDir(sub)
|
|
180
|
+
.filter((f) => f.endsWith(".jsonl"))
|
|
181
|
+
.map((f) => join(sub, f)),
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Every transcript under the root written at or after `since`. */
|
|
188
|
+
function recentFiles(root, since) {
|
|
189
|
+
const out = [];
|
|
190
|
+
const visit = (dir, depth) => {
|
|
191
|
+
for (const name of listDir(dir)) {
|
|
192
|
+
const p = join(dir, name);
|
|
193
|
+
let st;
|
|
194
|
+
try {
|
|
195
|
+
st = statSync(p);
|
|
196
|
+
} catch {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (st.isDirectory() && depth < 3) visit(p, depth + 1);
|
|
200
|
+
else if (st.isFile() && name.endsWith(".jsonl") && st.mtimeMs >= since) out.push(p);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
visit(root, 0);
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Logical credential names and whether each is registered; values are never read. */
|
|
208
|
+
export function credentialStatus({ env = process.env } = {}) {
|
|
209
|
+
let mapping;
|
|
210
|
+
try {
|
|
211
|
+
mapping = JSON.parse(readFileSync(prefsPath(env), "utf8"))?.global?.keychainMapping ?? {};
|
|
212
|
+
} catch {
|
|
213
|
+
return { readable: false, items: [] };
|
|
214
|
+
}
|
|
215
|
+
const listed = spawnSync("bash", [storePath(env), "list"], {
|
|
216
|
+
encoding: "utf8",
|
|
217
|
+
timeout: 15000,
|
|
218
|
+
env,
|
|
219
|
+
});
|
|
220
|
+
if (listed.status !== 0) return { readable: false, items: [] };
|
|
221
|
+
const services = new Set(String(listed.stdout).split("\n").filter(Boolean));
|
|
222
|
+
const items = Object.entries(mapping)
|
|
223
|
+
.filter(([k, v]) => typeof k === "string" && typeof v === "string" && v)
|
|
224
|
+
.map(([name, service]) => ({ name, registered: services.has(service) }))
|
|
225
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
226
|
+
return { readable: true, items };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** The author git would write in the current directory. */
|
|
230
|
+
function gitIdentity(env) {
|
|
231
|
+
const read = (key) => {
|
|
232
|
+
const r = spawnSync("git", ["config", "--get", key], { encoding: "utf8", timeout: 5000, env });
|
|
233
|
+
return r.status === 0 ? r.stdout.trim() || null : null;
|
|
234
|
+
};
|
|
235
|
+
const name = read("user.name");
|
|
236
|
+
return name ? { name, email: read("user.email") } : null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The whole overview as one document.
|
|
241
|
+
*
|
|
242
|
+
* @param {{env?: object, now?: number, all?: boolean}} [opts]
|
|
243
|
+
*/
|
|
244
|
+
export async function statusOverview({ env = process.env, now = Date.now(), all = false } = {}) {
|
|
245
|
+
const root = projectsRoot(env);
|
|
246
|
+
const since = now - WINDOW_MS;
|
|
247
|
+
const runs = buildIndex();
|
|
248
|
+
const rows = listSessions(env);
|
|
249
|
+
|
|
250
|
+
// One read per transcript: a live session's file is usually also one of the
|
|
251
|
+
// files written in the window.
|
|
252
|
+
const open = (Array.isArray(rows) ? rows : []).filter((s) => {
|
|
253
|
+
const state = s?.status ?? s?.state ?? null;
|
|
254
|
+
return !state || OPEN.has(state);
|
|
255
|
+
});
|
|
256
|
+
const files = new Map();
|
|
257
|
+
for (const s of open) for (const f of sessionFiles(root, s?.sessionId)) files.set(f, null);
|
|
258
|
+
for (const f of recentFiles(root, since)) files.set(f, null);
|
|
259
|
+
const windowSeen = new Set();
|
|
260
|
+
const last24h = emptyUsage();
|
|
261
|
+
let unreadable = 0;
|
|
262
|
+
for (const f of files.keys()) {
|
|
263
|
+
const u = await transcriptUsage(f, since, windowSeen);
|
|
264
|
+
if (!u.readable) unreadable++;
|
|
265
|
+
files.set(f, u);
|
|
266
|
+
merge(last24h, u.window);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const sessions = [];
|
|
270
|
+
for (const s of open) {
|
|
271
|
+
const state = s?.status ?? s?.state ?? null;
|
|
272
|
+
const { run, matchedBy } = matchRun(s?.sessionId, s?.cwd ?? null, null, runs);
|
|
273
|
+
const usage = emptyUsage();
|
|
274
|
+
for (const f of sessionFiles(root, s?.sessionId))
|
|
275
|
+
merge(usage, files.get(f)?.total ?? emptyUsage());
|
|
276
|
+
sessions.push({
|
|
277
|
+
sessionId: typeof s?.sessionId === "string" ? s.sessionId : null,
|
|
278
|
+
kind: typeof s?.kind === "string" ? s.kind : null,
|
|
279
|
+
state,
|
|
280
|
+
project: s?.cwd ? basename(s.cwd) : null,
|
|
281
|
+
startedAt: s?.startedAt ?? null,
|
|
282
|
+
run: run ? { taskId: run.taskId, currentPhase: run.currentPhase, status: run.status } : null,
|
|
283
|
+
matchedBy,
|
|
284
|
+
usage,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const accounts = listAccounts({ env });
|
|
289
|
+
return {
|
|
290
|
+
contractVersion: CONTRACT_VERSION,
|
|
291
|
+
generatedAt: new Date(now).toISOString(),
|
|
292
|
+
sessions,
|
|
293
|
+
sessionListReadable: Array.isArray(rows),
|
|
294
|
+
usage: {
|
|
295
|
+
liveSessions: sessions.reduce((acc, s) => merge(acc, { ...s.usage }), emptyUsage()),
|
|
296
|
+
last24h,
|
|
297
|
+
unreadableTranscripts: unreadable,
|
|
298
|
+
},
|
|
299
|
+
accounts: {
|
|
300
|
+
github: accounts.map((a) => ({ login: a.login, active: a.active })),
|
|
301
|
+
git: gitIdentity(env),
|
|
302
|
+
},
|
|
303
|
+
credentials: credentialStatus({ env }),
|
|
304
|
+
runs: all ? runs : runs.filter((r) => r.group !== "done" && r.group !== "unknown"),
|
|
305
|
+
runCounts: runs.reduce((acc, r) => ({ ...acc, [r.group]: (acc[r.group] ?? 0) + 1 }), {}),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const n = (x) =>
|
|
310
|
+
x >= 1e9
|
|
311
|
+
? `${(x / 1e9).toFixed(1)}B`
|
|
312
|
+
: x >= 1e6
|
|
313
|
+
? `${(x / 1e6).toFixed(1)}M`
|
|
314
|
+
: x >= 1e3
|
|
315
|
+
? `${(x / 1e3).toFixed(1)}k`
|
|
316
|
+
: String(x);
|
|
317
|
+
const usageLine = (u) =>
|
|
318
|
+
`in ${n(u.in)} out ${n(u.out)} cache read ${n(u.cacheRead)} cache write ${n(u.cacheWrite)} ~$${u.usd.toFixed(2)}` +
|
|
319
|
+
(u.unpriced ? ` (${u.unpriced} unpriced)` : "");
|
|
320
|
+
|
|
321
|
+
export function renderHuman(doc) {
|
|
322
|
+
const out = [];
|
|
323
|
+
const busy = doc.sessions.filter((s) => s.state === "busy" || s.state === "running").length;
|
|
324
|
+
out.push(`Live sessions (${doc.sessions.length}, ${busy} busy)`);
|
|
325
|
+
if (!doc.sessionListReadable)
|
|
326
|
+
out.push(" the session list could not be read (claude agents --json)");
|
|
327
|
+
for (const s of doc.sessions) {
|
|
328
|
+
const run = s.run ? `${s.run.taskId} ${phaseCell(s.run.currentPhase)}` : "no run";
|
|
329
|
+
out.push(
|
|
330
|
+
` ${(s.sessionId ?? "-").slice(0, 8)} ${(s.kind ?? "-").padEnd(11)} ${(s.state ?? "-").padEnd(7)} ` +
|
|
331
|
+
`${(s.project ?? "-").padEnd(24).slice(0, 24)} ${run.padEnd(22)} ~$${s.usage.usd.toFixed(2)}`,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
out.push("");
|
|
335
|
+
out.push("Tokens");
|
|
336
|
+
out.push(` live sessions ${usageLine(doc.usage.liveSessions)}`);
|
|
337
|
+
out.push(` last 24h ${usageLine(doc.usage.last24h)}`);
|
|
338
|
+
if (doc.usage.unreadableTranscripts)
|
|
339
|
+
out.push(
|
|
340
|
+
` ${doc.usage.unreadableTranscripts} transcript(s) could not be read; the totals leave them out`,
|
|
341
|
+
);
|
|
342
|
+
out.push("");
|
|
343
|
+
out.push("Accounts");
|
|
344
|
+
const gh = doc.accounts.github;
|
|
345
|
+
out.push(
|
|
346
|
+
` github ${gh.length ? gh.map((a) => (a.active ? `${a.login} (active)` : a.login)).join(", ") : "none logged in"}`,
|
|
347
|
+
);
|
|
348
|
+
if (doc.accounts.git)
|
|
349
|
+
out.push(` git ${doc.accounts.git.name} <${doc.accounts.git.email ?? "-"}>`);
|
|
350
|
+
out.push("");
|
|
351
|
+
const c = doc.credentials;
|
|
352
|
+
if (!c.readable) out.push("Credentials could not be read");
|
|
353
|
+
else {
|
|
354
|
+
const missing = c.items.filter((i) => !i.registered).map((i) => i.name);
|
|
355
|
+
out.push(`Credentials ${c.items.length - missing.length}/${c.items.length} registered`);
|
|
356
|
+
out.push(
|
|
357
|
+
` ${
|
|
358
|
+
c.items
|
|
359
|
+
.filter((i) => i.registered)
|
|
360
|
+
.map((i) => i.name)
|
|
361
|
+
.join(", ") || "-"
|
|
362
|
+
}`,
|
|
363
|
+
);
|
|
364
|
+
if (missing.length) out.push(` missing: ${missing.join(", ")}`);
|
|
365
|
+
}
|
|
366
|
+
out.push("");
|
|
367
|
+
const counts = Object.entries(doc.runCounts)
|
|
368
|
+
.map(([g, k]) => `${g} ${k}`)
|
|
369
|
+
.join(", ");
|
|
370
|
+
out.push(`Runs (${counts || "none"}) - runs-index.mjs lists them`);
|
|
371
|
+
const driven = new Set(doc.sessions.map((x) => x.run?.taskId).filter(Boolean));
|
|
372
|
+
for (const r of doc.runs.filter((x) => x.group === "waiting" || driven.has(x.taskId))) {
|
|
373
|
+
out.push(
|
|
374
|
+
` ${r.taskId.padEnd(30).slice(0, 30)} ${phaseCell(r.currentPhase).padStart(5)} ${r.group}`,
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
return out.join("\n");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function main() {
|
|
381
|
+
const args = process.argv.slice(2);
|
|
382
|
+
const flags = { json: false, all: false };
|
|
383
|
+
for (const a of args) {
|
|
384
|
+
if (a === "--json") flags.json = true;
|
|
385
|
+
else if (a === "--all") flags.all = true;
|
|
386
|
+
else if (a === "-h" || a === "--help") {
|
|
387
|
+
process.stdout.write("Usage: status-overview.mjs [--json] [--all]\n");
|
|
388
|
+
return 0;
|
|
389
|
+
} else {
|
|
390
|
+
process.stderr.write(`status-overview: unknown argument ${a}\n`);
|
|
391
|
+
return 2;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
const doc = await statusOverview({ all: flags.all });
|
|
395
|
+
process.stdout.write((flags.json ? JSON.stringify(doc, null, 2) : renderHuman(doc)) + "\n");
|
|
396
|
+
return 0;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (invokedDirectly(import.meta.url)) {
|
|
400
|
+
runMain("status-overview", main);
|
|
401
|
+
}
|