@dadado/agent-kit-cli 5.5.0 → 5.7.0
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/README.md +11 -1
- package/dashboard/README.md +17 -0
- package/dashboard/dashboard-data.mjs +215 -5
- package/dashboard/dashboard.html +285 -1
- package/dashboard/lib/live-refresh.d.mts +23 -0
- package/dashboard/lib/semantic-model.mjs +12 -5
- package/dashboard/social-preview.png +0 -0
- package/dashboard/social-preview.svg +18 -0
- package/dist/index.js +702 -161
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @dadado/agent-kit-cli
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@dadado/agent-kit-cli)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
[](https://github.com/agent-kit-startup/agent-kit/releases/latest)
|
|
7
|
+
|
|
8
|
+
<img src="https://raw.githubusercontent.com/agent-kit-startup/agent-kit/main/dashboard/logo.svg" alt="Mission Kit helmet mark" width="120">
|
|
9
|
+
|
|
3
10
|
Agent Kit CLI: HITL operating-layer install and tooling for AI-assisted IDEs (rules, skills, plan/handoff, staging-to-prod, memory). It installs local workspace contracts; it is not a hosted control plane or graph workflow runtime.
|
|
4
11
|
|
|
5
12
|
## Install
|
|
@@ -36,6 +43,8 @@ agent-kit dashboard
|
|
|
36
43
|
|
|
37
44
|
The panel binds to loopback by default, serves its own static files, and snapshots the current workspace. L0 install does **not** copy `dashboard/` into your app; `agent-kit dashboard` resolves the panel from the installed package.
|
|
38
45
|
|
|
46
|
+
Browser-free: `agent-kit mission-control` is a third surface (ASCII Mission, Flight Log, Checklist, Crew Monitor) that reuses the same snapshot builders without starting the HTTP server. `agent-kit mission-control --once` prints one frame (Claude Code `/agent-kit`). The web dashboard stays shipped.
|
|
47
|
+
|
|
39
48
|
Older tags before 4.8.2 do not include those assets. Prefer a current pin, or point `MISSION_CONTROL_KIT_ROOT` / `AGENT_KIT_HOME` at an agent-kit checkout that contains `dashboard/`.
|
|
40
49
|
|
|
41
50
|
## Bare invoke (welcome)
|
|
@@ -66,7 +75,8 @@ On an interactive TTY, long-running commands (`init`, `install`, `doctor`, `upda
|
|
|
66
75
|
| `agent-kit doctor` | Diagnose repository readiness (`--json` includes an `env` pillar: bin-on-PATH, npm prefix writability, Node version, shell profile) |
|
|
67
76
|
| `agent-kit setup-global` | Self-heal a root-owned npm global prefix (relocate to `~/.npm-global`, fix `PATH`, reinstall) |
|
|
68
77
|
| `agent-kit update` | Re-apply L0/packs/skills from the registry |
|
|
69
|
-
| `agent-kit dashboard` | Start Mission Control for this workspace |
|
|
78
|
+
| `agent-kit dashboard` | Start Mission Control for this workspace (browser panel) |
|
|
79
|
+
| `agent-kit mission-control` | ASCII Mission Control TUI (`--once` for one frame) |
|
|
70
80
|
| `agent-kit add <id>` | Install a skill or L1 pack |
|
|
71
81
|
| `agent-kit run-plan` | Headless continuous plan runner (never promotes to production) |
|
|
72
82
|
|
package/dashboard/README.md
CHANGED
|
@@ -23,3 +23,20 @@ before it fans out to the workspace packages).
|
|
|
23
23
|
|
|
24
24
|
`dashboard.html` is outside Biome's scope; CSS/HTML-only changes are covered by
|
|
25
25
|
`packages/cli/src/dashboard/plugin-ux-validation.test.ts` instead.
|
|
26
|
+
|
|
27
|
+
## Git tab and DevOps tab
|
|
28
|
+
|
|
29
|
+
The Git tab (`#git`) keeps the pre-rendered `git log --graph` markdown block and adds a
|
|
30
|
+
second, state-colored visual tree next to it, reusing the `.now-stepper`/`.now-step-marker`
|
|
31
|
+
timeline component built for the Current Mission panel (`renderGitVisualTree` in
|
|
32
|
+
`dashboard.html`, parsing `SNAPSHOT.git.graph`). It is single-lane by design — the markdown
|
|
33
|
+
block keeps the true branch-lane geometry; the stepper trades that for an at-a-glance
|
|
34
|
+
promotion read (HEAD, promoted to `origin/main`/`origin/staging`, or neither).
|
|
35
|
+
|
|
36
|
+
The DevOps tab (`#devops`) is scoped to CI/CD + deploy signal, separate from the
|
|
37
|
+
local-process-only Processes tab. `dashboard-data.mjs`'s `collectPipelineRuns()` shells to
|
|
38
|
+
`gh run list` (budget-guarded via `withinSnapshotBudget()`, fails soft to an honest
|
|
39
|
+
empty-state when `gh` is unavailable/unauthenticated); `collectDeploySignal()` reads `v*`
|
|
40
|
+
git tags plus the latest non-`Unreleased` `CHANGELOG.md` entry as a best-effort "what
|
|
41
|
+
shipped recently" proxy. Neither collector polls live infra/hosting — the DevOps tab never
|
|
42
|
+
implies monitoring it does not perform.
|
|
@@ -49,13 +49,33 @@ const MAX_TERMINALS = 20;
|
|
|
49
49
|
const MAX_PROCESSES = 25;
|
|
50
50
|
const MAX_GIT_GRAPH_LINES = 25;
|
|
51
51
|
const MAX_GIT_GRAPH_LINE_CHARS = 160;
|
|
52
|
+
const MAX_PIPELINE_RUNS = 5;
|
|
53
|
+
const MAX_PIPELINE_NAME_CHARS = 80;
|
|
54
|
+
// Live-measured (2026-08-24, this repo, 8 fresh `gh run list` samples via the
|
|
55
|
+
// exact trimmed command): 3476-6584ms, one earlier ad-hoc sample 7674ms.
|
|
56
|
+
// Prior review sessions (.cursor/memory/plan-monitor-mc-git-tab-visual-tree-and-devops-panel.md)
|
|
57
|
+
// spanned 2.1-9.9s. 3500ms had no headroom at all over that range; 12000ms
|
|
58
|
+
// gives real margin over the observed worst case (~2.1s over the historical
|
|
59
|
+
// 9.9s max, ~4.3s over this session's tight-sample max) while staying well
|
|
60
|
+
// under the 60s outer child-process timeout (serve.mjs DATA_SCRIPT_TIMEOUT_MS).
|
|
61
|
+
const GH_RUN_LIST_TIMEOUT_MS = 12_000;
|
|
62
|
+
const MAX_DEPLOY_TAGS = 5;
|
|
63
|
+
const MAX_DEPLOY_TAG_NAME_CHARS = 64;
|
|
64
|
+
const MAX_CHANGELOG_ITEMS = 4;
|
|
65
|
+
const MAX_CHANGELOG_ITEM_CHARS = 140;
|
|
52
66
|
|
|
53
67
|
/** Soft wall-clock budget for optional collectors (transcripts, reports, ps). */
|
|
54
68
|
const SNAPSHOT_STARTED_MS = Date.now();
|
|
69
|
+
// Default raised from 12000ms by the same delta GH_RUN_LIST_TIMEOUT_MS grew
|
|
70
|
+
// (3500ms -> 12000ms, +8500ms): collectPipelineRuns() now runs last among the
|
|
71
|
+
// budget-guarded collectors and reserves GH_RUN_LIST_TIMEOUT_MS + 400ms of
|
|
72
|
+
// budget before attempting `gh run list` (see withinSnapshotBudget call
|
|
73
|
+
// there), so the total budget must grow by the same amount the reserve did
|
|
74
|
+
// or the larger timeout can never actually be used.
|
|
55
75
|
const SNAPSHOT_BUDGET_MS = (() => {
|
|
56
76
|
const raw = process.env.AGENT_KIT_DASHBOARD_DATA_BUDGET_MS;
|
|
57
|
-
const n = raw != null && raw !== "" ? Number(raw) :
|
|
58
|
-
return Number.isFinite(n) && n > 0 ? n :
|
|
77
|
+
const n = raw != null && raw !== "" ? Number(raw) : 20_500;
|
|
78
|
+
return Number.isFinite(n) && n > 0 ? n : 20_500;
|
|
59
79
|
})();
|
|
60
80
|
function withinSnapshotBudget(reserveMs = 400) {
|
|
61
81
|
return Date.now() - SNAPSHOT_STARTED_MS + reserveMs < SNAPSHOT_BUDGET_MS;
|
|
@@ -104,7 +124,7 @@ function redactTerminalOutput(text) {
|
|
|
104
124
|
|
|
105
125
|
const SNAPSHOT = {
|
|
106
126
|
_schema: {
|
|
107
|
-
version: "1.
|
|
127
|
+
version: "1.3.0",
|
|
108
128
|
description: "Mission Control dashboard data model",
|
|
109
129
|
fields: {
|
|
110
130
|
generatedAt: "ISO-8601 timestamp of snapshot generation",
|
|
@@ -117,6 +137,8 @@ const SNAPSHOT = {
|
|
|
117
137
|
memory:
|
|
118
138
|
"Memory records: error count, decision count, recent decisions, recent parsed errors, error-o-meter stats",
|
|
119
139
|
git: "Git repository state: branch, dirty status, commit, ahead/behind, bounded files[], promotion flow vs staging/main, graph lines, staging hygiene",
|
|
140
|
+
devops:
|
|
141
|
+
"Best-effort DevOps signal: pipeline ({available, runs[], reason?} recent gh run list rows; reason is 'budget' when the shared snapshot budget ran out before gh was attempted, or 'call-failed' when gh was attempted and missing/unauthenticated/timed out/errored; reason is only present when available is false) and deploy ({tags[], changelog} v* git tags + latest CHANGELOG release entry as a 'what shipped recently' proxy, not a live infra poll)",
|
|
120
142
|
terminals:
|
|
121
143
|
"Active Cursor terminal sessions with metadata, output line count, and capped lastOutput",
|
|
122
144
|
processes:
|
|
@@ -127,7 +149,7 @@ const SNAPSHOT = {
|
|
|
127
149
|
},
|
|
128
150
|
},
|
|
129
151
|
generatedAt: new Date().toISOString(),
|
|
130
|
-
dashboardDataVersion: "1.
|
|
152
|
+
dashboardDataVersion: "1.4.0",
|
|
131
153
|
plans: [],
|
|
132
154
|
system: {
|
|
133
155
|
repoRoot: ROOT,
|
|
@@ -246,6 +268,7 @@ const kitCommandPaths = new Set();
|
|
|
246
268
|
const kitSkillDirs = new Set();
|
|
247
269
|
const kitAgentPaths = new Set();
|
|
248
270
|
const registryFile = join(ROOT, "registry", "registry.json");
|
|
271
|
+
let registryHadCommands = false;
|
|
249
272
|
if (existsSync(registryFile)) {
|
|
250
273
|
try {
|
|
251
274
|
const registry = JSON.parse(readFileSync(registryFile, "utf8"));
|
|
@@ -265,10 +288,58 @@ if (existsSync(registryFile)) {
|
|
|
265
288
|
kitAgentPaths.add(entry.path);
|
|
266
289
|
}
|
|
267
290
|
}
|
|
291
|
+
registryHadCommands = kitCommandPaths.size > 0;
|
|
268
292
|
} catch {
|
|
269
293
|
// Unreadable registry: degrade to all-editable rather than locking everything.
|
|
270
294
|
}
|
|
271
295
|
}
|
|
296
|
+
// `registry/registry.json` is a factory-only artifact index — consumer installs
|
|
297
|
+
// never ship it, so the block above always leaves kitCommandPaths empty there and
|
|
298
|
+
// every command reads kitManaged: false even though `update` owns it. Fall back to
|
|
299
|
+
// the locally-tracked kit markers: `.cursor/agent-kit.managed-hashes.json` (already
|
|
300
|
+
// keyed by the same repo-relative path as c.path / a.path) and, as a secondary
|
|
301
|
+
// source, `.cursor/agent-kit.json`'s `protected[]` list (exact paths only; glob
|
|
302
|
+
// entries like `.cursor/plans/**` don't identify individual kit-managed commands so
|
|
303
|
+
// they're skipped). Only engages when the registry is absent or yielded no commands,
|
|
304
|
+
// so factory behavior (registry present + populated) is unchanged.
|
|
305
|
+
if (!registryHadCommands) {
|
|
306
|
+
const managedHashesFile = join(ROOT, ".cursor", "agent-kit.managed-hashes.json");
|
|
307
|
+
const agentKitConfigFile = join(ROOT, ".cursor", "agent-kit.json");
|
|
308
|
+
const fallbackPaths = new Set();
|
|
309
|
+
if (existsSync(managedHashesFile)) {
|
|
310
|
+
try {
|
|
311
|
+
const managed = JSON.parse(readFileSync(managedHashesFile, "utf8"));
|
|
312
|
+
const hashes = managed?.hashes && typeof managed.hashes === "object" ? managed.hashes : {};
|
|
313
|
+
for (const path of Object.keys(hashes)) {
|
|
314
|
+
fallbackPaths.add(path);
|
|
315
|
+
}
|
|
316
|
+
} catch {
|
|
317
|
+
// Unreadable managed-hashes.json: fall through to protected[] (if any).
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (existsSync(agentKitConfigFile)) {
|
|
321
|
+
try {
|
|
322
|
+
const agentKitConfig = JSON.parse(readFileSync(agentKitConfigFile, "utf8"));
|
|
323
|
+
const protectedList = Array.isArray(agentKitConfig?.protected)
|
|
324
|
+
? agentKitConfig.protected
|
|
325
|
+
: [];
|
|
326
|
+
for (const entry of protectedList) {
|
|
327
|
+
if (typeof entry === "string" && !entry.includes("*")) fallbackPaths.add(entry);
|
|
328
|
+
}
|
|
329
|
+
} catch {
|
|
330
|
+
// Unreadable agent-kit.json: managed-hashes.json (if any) still applies.
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (const path of fallbackPaths) {
|
|
334
|
+
if (path.startsWith(".cursor/commands/")) {
|
|
335
|
+
kitCommandPaths.add(path);
|
|
336
|
+
} else if (path.startsWith(".cursor/agents/")) {
|
|
337
|
+
kitAgentPaths.add(path);
|
|
338
|
+
} else if (path.startsWith(".cursor/skills/") && path.endsWith("/SKILL.md")) {
|
|
339
|
+
kitSkillDirs.add(path.slice(0, -"/SKILL.md".length));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
272
343
|
for (const c of SNAPSHOT.commands) {
|
|
273
344
|
c.kitManaged = kitCommandPaths.has(c.path);
|
|
274
345
|
}
|
|
@@ -402,7 +473,12 @@ if (existsSync(memoryDecisionsDir)) {
|
|
|
402
473
|
try {
|
|
403
474
|
const gitOpts = { cwd: ROOT, encoding: "utf-8", timeout: 5000 };
|
|
404
475
|
const branch = execSync("git rev-parse --abbrev-ref HEAD", gitOpts).trim();
|
|
405
|
-
|
|
476
|
+
// Only strip the trailing newline(s) here — `git status --short` uses
|
|
477
|
+
// positional XY status columns, so an unstaged-only row starts with a
|
|
478
|
+
// literal leading space (e.g. " M path"). A full-string .trim() eats that
|
|
479
|
+
// leading space on line 1 only, shifting parseGitStatusShort's staged /
|
|
480
|
+
// unstaged read for the first row.
|
|
481
|
+
const status = execSync("git status --short", gitOpts).replace(/\n+$/, "");
|
|
406
482
|
const lastCommit = execSync("git log -1 --oneline", gitOpts).trim();
|
|
407
483
|
let ahead = 0;
|
|
408
484
|
let behind = 0;
|
|
@@ -487,6 +563,128 @@ try {
|
|
|
487
563
|
SNAPSHOT._gitRecentLog = [];
|
|
488
564
|
}
|
|
489
565
|
|
|
566
|
+
// 6b. DevOps: CI/CD pipeline status (best-effort, `gh` CLI) + a "what shipped
|
|
567
|
+
// recently" deploy-activity proxy from v* git tags and the latest CHANGELOG
|
|
568
|
+
// release entry. Both fail soft to an empty/unavailable shape; this snapshot
|
|
569
|
+
// never blocks or errors on either signal, and never claims live
|
|
570
|
+
// infra/hosting monitoring it does not perform.
|
|
571
|
+
function collectPipelineRuns() {
|
|
572
|
+
// gh run list is a network call, unlike the local-only git collectors
|
|
573
|
+
// above; the budget reserve must cover its own timeout, not just a nominal
|
|
574
|
+
// buffer (see .cursor/memory/errors/2026-07-26_mission-control-dashboard-data-timeout.md
|
|
575
|
+
// for why this file's soft-budget guards exist).
|
|
576
|
+
if (!withinSnapshotBudget(GH_RUN_LIST_TIMEOUT_MS + 400)) {
|
|
577
|
+
// `gh` was never even attempted this snapshot — the shared budget ran out
|
|
578
|
+
// first. Distinguished from a failed/timed-out call below so the UI does
|
|
579
|
+
// not misattribute this (the dominant real case; see live measurements
|
|
580
|
+
// in .cursor/memory/plan-monitor-mc-git-tab-visual-tree-and-devops-panel.md
|
|
581
|
+
// and the residual note this fix landed from) to an auth/install problem.
|
|
582
|
+
return { available: false, runs: [], reason: "budget" };
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
// Trimmed to the fields renderDevopsPipelineCard() / navDevopsDot actually
|
|
586
|
+
// read (workflow, status, conclusion, branch, event, createdAt). databaseId
|
|
587
|
+
// and url are dropped: the pipeline card is display-only with no
|
|
588
|
+
// paste-destination for a run URL (ADR 2026-07-25 copy-only convention),
|
|
589
|
+
// so requesting them only adds payload cost without a consumer.
|
|
590
|
+
const out = execSync(
|
|
591
|
+
`gh run list --limit ${MAX_PIPELINE_RUNS} --json workflowName,status,conclusion,headBranch,event,createdAt`,
|
|
592
|
+
{ cwd: ROOT, encoding: "utf-8", timeout: GH_RUN_LIST_TIMEOUT_MS },
|
|
593
|
+
);
|
|
594
|
+
const rows = JSON.parse(out);
|
|
595
|
+
if (!Array.isArray(rows)) return { available: false, runs: [], reason: "call-failed" };
|
|
596
|
+
const runs = rows.slice(0, MAX_PIPELINE_RUNS).map((r) => ({
|
|
597
|
+
workflow: truncateStr(String(r.workflowName || ""), MAX_PIPELINE_NAME_CHARS),
|
|
598
|
+
status: String(r.status || ""),
|
|
599
|
+
conclusion: r.conclusion ? String(r.conclusion) : null,
|
|
600
|
+
branch: truncateStr(String(r.headBranch || ""), MAX_STRING.branch),
|
|
601
|
+
event: String(r.event || ""),
|
|
602
|
+
createdAt: r.createdAt || null,
|
|
603
|
+
}));
|
|
604
|
+
return { available: true, runs };
|
|
605
|
+
} catch {
|
|
606
|
+
// gh missing, unauthenticated, its own execSync timeout, or non-repo cwd
|
|
607
|
+
// all read the same to the caller: no pipeline signal for this snapshot.
|
|
608
|
+
// Not further split (e.g. timeout vs auth) — that would need parsing the
|
|
609
|
+
// execSync error shape, which is more taxonomy than the empty-state copy
|
|
610
|
+
// needs; "call-failed" is enough to stop the UI from guessing "auth".
|
|
611
|
+
return { available: false, runs: [], reason: "call-failed" };
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/** Markdown decoration removed, not converted (mirrors scripts/build-landing.mjs's
|
|
616
|
+
* stripMdInline): safe against unclosed tokens after the item-length cap below. */
|
|
617
|
+
function stripMdInlineForDeploySignal(s) {
|
|
618
|
+
return s
|
|
619
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
|
|
620
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
621
|
+
.replace(/`([^`]+)`/g, "$1");
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** First non-"Unreleased" `## [x.y.z] - date` entry and a capped bullet list. */
|
|
625
|
+
function parseLatestChangelogEntryForDeploySignal(changelog) {
|
|
626
|
+
const headerRe = /^## \[([^\]]+)\](?:\s*-\s*(.+))?$/gm;
|
|
627
|
+
const headers = [...changelog.matchAll(headerRe)];
|
|
628
|
+
const latest = headers.find((m) => m[1].toLowerCase() !== "unreleased");
|
|
629
|
+
if (!latest) return null;
|
|
630
|
+
const start = latest.index + latest[0].length;
|
|
631
|
+
const next = headers.find((m) => m.index > latest.index);
|
|
632
|
+
const body = changelog.slice(start, next ? next.index : undefined);
|
|
633
|
+
const items = [...body.matchAll(/^- (.+)$/gm)]
|
|
634
|
+
.map((m) => stripMdInlineForDeploySignal(m[1].trim()))
|
|
635
|
+
.filter(Boolean)
|
|
636
|
+
.slice(0, MAX_CHANGELOG_ITEMS)
|
|
637
|
+
.map((line) =>
|
|
638
|
+
line.length > MAX_CHANGELOG_ITEM_CHARS
|
|
639
|
+
? `${line.slice(0, MAX_CHANGELOG_ITEM_CHARS).trimEnd()}…`
|
|
640
|
+
: line,
|
|
641
|
+
);
|
|
642
|
+
return { version: latest[1], date: (latest[2] || "").trim() || null, items };
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function collectDeploySignal() {
|
|
646
|
+
if (!withinSnapshotBudget(400)) return { tags: [], changelog: null };
|
|
647
|
+
const gitOpts = { cwd: ROOT, encoding: "utf-8", timeout: 5000 };
|
|
648
|
+
let tags = [];
|
|
649
|
+
try {
|
|
650
|
+
// v*-only: an archive/* or other non-release tag under "what shipped
|
|
651
|
+
// recently" would be exactly the misleading signal this phase avoids.
|
|
652
|
+
const out = execSync(
|
|
653
|
+
`git for-each-ref --sort=-creatordate --format='%(refname:short)|%(creatordate:short)' --count=${MAX_DEPLOY_TAGS} 'refs/tags/v*'`,
|
|
654
|
+
gitOpts,
|
|
655
|
+
).trim();
|
|
656
|
+
tags = out
|
|
657
|
+
? out
|
|
658
|
+
.split("\n")
|
|
659
|
+
.filter(Boolean)
|
|
660
|
+
.map((line) => {
|
|
661
|
+
const [name, date] = line.split("|");
|
|
662
|
+
return { name: truncateStr(name || "", MAX_DEPLOY_TAG_NAME_CHARS), date: date || null };
|
|
663
|
+
})
|
|
664
|
+
: [];
|
|
665
|
+
} catch {
|
|
666
|
+
tags = [];
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
let changelog = null;
|
|
670
|
+
try {
|
|
671
|
+
const changelogPath = join(ROOT, "CHANGELOG.md");
|
|
672
|
+
if (existsSync(changelogPath)) {
|
|
673
|
+
changelog = parseLatestChangelogEntryForDeploySignal(readFileSync(changelogPath, "utf-8"));
|
|
674
|
+
}
|
|
675
|
+
} catch {
|
|
676
|
+
changelog = null;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return { tags, changelog };
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// SNAPSHOT.devops is assigned near the end of the snapshot build (after the
|
|
683
|
+
// transcripts/reports/ps collectors below), not here — gh run list is a
|
|
684
|
+
// network call that must not run ahead of and starve the local-only
|
|
685
|
+
// budget-guarded collectors that share SNAPSHOT_BUDGET_MS. See the call site
|
|
686
|
+
// right before the missionControl assembly for the ordering rationale.
|
|
687
|
+
|
|
490
688
|
// 7. Terminals (read from Cursor terminal files)
|
|
491
689
|
const terminalsDir = resolve(process.env.HOME || "~", ".cursor", "projects");
|
|
492
690
|
// Derive project path from ROOT rather than hardcoding a specific slug
|
|
@@ -1142,4 +1340,16 @@ function readPreviousInventory() {
|
|
|
1142
1340
|
}
|
|
1143
1341
|
SNAPSHOT._gitRecentLog = undefined;
|
|
1144
1342
|
|
|
1343
|
+
// DevOps: CI/CD pipeline status (best-effort, `gh` CLI) + a "what shipped
|
|
1344
|
+
// recently" deploy-activity proxy. Deliberately collected last, after every
|
|
1345
|
+
// other budget-guarded collector above (processes, detached-audit-sessions,
|
|
1346
|
+
// agentPrompts, externalReports, subagentRuns): collectPipelineRuns() makes
|
|
1347
|
+
// a network call (gh run list) unlike every other collector in this file, so
|
|
1348
|
+
// it must not run ahead of and eat into the shared SNAPSHOT_BUDGET_MS budget
|
|
1349
|
+
// that the local-only collectors also need.
|
|
1350
|
+
SNAPSHOT.devops = {
|
|
1351
|
+
pipeline: collectPipelineRuns(),
|
|
1352
|
+
deploy: collectDeploySignal(),
|
|
1353
|
+
};
|
|
1354
|
+
|
|
1145
1355
|
process.stdout.write(JSON.stringify(SNAPSHOT, null, 2));
|