@groundnuty/macf 0.2.36 → 0.2.38
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/dist/.build-info.json +2 -2
- package/dist/cli/claude-sh.d.ts +12 -10
- package/dist/cli/claude-sh.d.ts.map +1 -1
- package/dist/cli/claude-sh.js +13 -11
- package/dist/cli/claude-sh.js.map +1 -1
- package/dist/cli/commands/certs.d.ts.map +1 -1
- package/dist/cli/commands/certs.js +6 -2
- package/dist/cli/commands/certs.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +102 -3
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +349 -55
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.d.ts +24 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +81 -8
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/monitor.d.ts +16 -0
- package/dist/cli/commands/monitor.d.ts.map +1 -0
- package/dist/cli/commands/monitor.js +96 -0
- package/dist/cli/commands/monitor.js.map +1 -0
- package/dist/cli/commands/propose.d.ts +21 -0
- package/dist/cli/commands/propose.d.ts.map +1 -0
- package/dist/cli/commands/propose.js +128 -0
- package/dist/cli/commands/propose.js.map +1 -0
- package/dist/cli/commands/ps.d.ts +17 -0
- package/dist/cli/commands/ps.d.ts.map +1 -0
- package/dist/cli/commands/ps.js +69 -0
- package/dist/cli/commands/ps.js.map +1 -0
- package/dist/cli/commands/registry-prune.d.ts +44 -0
- package/dist/cli/commands/registry-prune.d.ts.map +1 -0
- package/dist/cli/commands/registry-prune.js +124 -0
- package/dist/cli/commands/registry-prune.js.map +1 -0
- package/dist/cli/commands/rules-refresh.d.ts +1 -0
- package/dist/cli/commands/rules-refresh.d.ts.map +1 -1
- package/dist/cli/commands/rules-refresh.js +22 -1
- package/dist/cli/commands/rules-refresh.js.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +23 -2
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/config.d.ts +2 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +16 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/env-files-update.d.ts.map +1 -1
- package/dist/cli/env-files-update.js +5 -1
- package/dist/cli/env-files-update.js.map +1 -1
- package/dist/cli/env-files.d.ts +38 -13
- package/dist/cli/env-files.d.ts.map +1 -1
- package/dist/cli/env-files.js +84 -14
- package/dist/cli/env-files.js.map +1 -1
- package/dist/cli/index.js +142 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/monitor/digest.d.ts +89 -0
- package/dist/cli/monitor/digest.d.ts.map +1 -0
- package/dist/cli/monitor/digest.js +232 -0
- package/dist/cli/monitor/digest.js.map +1 -0
- package/dist/cli/monitor/github-reader.d.ts +38 -0
- package/dist/cli/monitor/github-reader.d.ts.map +1 -0
- package/dist/cli/monitor/github-reader.js +65 -0
- package/dist/cli/monitor/github-reader.js.map +1 -0
- package/dist/cli/monitor/reflections.d.ts +18 -0
- package/dist/cli/monitor/reflections.d.ts.map +1 -0
- package/dist/cli/monitor/reflections.js +72 -0
- package/dist/cli/monitor/reflections.js.map +1 -0
- package/dist/cli/monitor/run.d.ts +30 -0
- package/dist/cli/monitor/run.d.ts.map +1 -0
- package/dist/cli/monitor/run.js +67 -0
- package/dist/cli/monitor/run.js.map +1 -0
- package/dist/cli/proc-scan.d.ts +81 -0
- package/dist/cli/proc-scan.d.ts.map +1 -0
- package/dist/cli/proc-scan.js +172 -0
- package/dist/cli/proc-scan.js.map +1 -0
- package/dist/cli/project-rules.d.ts +105 -0
- package/dist/cli/project-rules.d.ts.map +1 -0
- package/dist/cli/project-rules.js +305 -0
- package/dist/cli/project-rules.js.map +1 -0
- package/dist/cli/propose/candidates.d.ts +95 -0
- package/dist/cli/propose/candidates.d.ts.map +1 -0
- package/dist/cli/propose/candidates.js +117 -0
- package/dist/cli/propose/candidates.js.map +1 -0
- package/dist/cli/propose/invariants.d.ts +49 -0
- package/dist/cli/propose/invariants.d.ts.map +1 -0
- package/dist/cli/propose/invariants.js +154 -0
- package/dist/cli/propose/invariants.js.map +1 -0
- package/dist/cli/propose/proposal-writer.d.ts +33 -0
- package/dist/cli/propose/proposal-writer.d.ts.map +1 -0
- package/dist/cli/propose/proposal-writer.js +53 -0
- package/dist/cli/propose/proposal-writer.js.map +1 -0
- package/dist/cli/propose/report.d.ts +49 -0
- package/dist/cli/propose/report.d.ts.map +1 -0
- package/dist/cli/propose/report.js +227 -0
- package/dist/cli/propose/report.js.map +1 -0
- package/dist/cli/propose/run.d.ts +41 -0
- package/dist/cli/propose/run.d.ts.map +1 -0
- package/dist/cli/propose/run.js +62 -0
- package/dist/cli/propose/run.js.map +1 -0
- package/dist/cli/role-settings-model.d.ts +70 -0
- package/dist/cli/role-settings-model.d.ts.map +1 -0
- package/dist/cli/role-settings-model.js +90 -0
- package/dist/cli/role-settings-model.js.map +1 -0
- package/dist/cli/settings-writer.d.ts +103 -6
- package/dist/cli/settings-writer.d.ts.map +1 -1
- package/dist/cli/settings-writer.js +259 -8
- package/dist/cli/settings-writer.js.map +1 -1
- package/dist/reconciler/reconcile.d.ts +31 -0
- package/dist/reconciler/reconcile.d.ts.map +1 -1
- package/dist/reconciler/reconcile.js +47 -3
- package/dist/reconciler/reconcile.js.map +1 -1
- package/dist/reconciler/run.d.ts +21 -1
- package/dist/reconciler/run.d.ts.map +1 -1
- package/dist/reconciler/run.js +106 -17
- package/dist/reconciler/run.js.map +1 -1
- package/package.json +2 -2
- package/plugin/rules/gh-token-attribution-traps.md +4 -0
- package/plugin/rules/observability-wiring.md +3 -3
- package/plugin/rules/reflection-staging.md +65 -0
- package/plugin/rules/silent-fallback-hazards.md +21 -4
- package/scripts/check-auditor-never-acts.sh +167 -0
- package/scripts/check-gh-attribution.sh +254 -0
- package/scripts/emit-turn-receipt.sh +1 -1
- package/scripts/harvest-reflection.sh +125 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
2
|
+
/** Assignment-label heuristic: labels ending in `-agent` are who-owns-it. */
|
|
3
|
+
function isAssignmentLabel(label) {
|
|
4
|
+
return label.endsWith('-agent');
|
|
5
|
+
}
|
|
6
|
+
function ageInDays(createdAtIso, nowMs) {
|
|
7
|
+
const created = Date.parse(createdAtIso);
|
|
8
|
+
if (!Number.isFinite(created))
|
|
9
|
+
return 0;
|
|
10
|
+
return Math.floor((nowMs - created) / DAY_MS);
|
|
11
|
+
}
|
|
12
|
+
/** Compute the stale-issue set: open issues older than `sinceDays`. */
|
|
13
|
+
export function computeStaleIssues(issues, sinceDays, nowMs) {
|
|
14
|
+
const stale = [];
|
|
15
|
+
for (const issue of issues) {
|
|
16
|
+
const ageDays = ageInDays(issue.createdAt, nowMs);
|
|
17
|
+
if (ageDays <= sinceDays)
|
|
18
|
+
continue;
|
|
19
|
+
const owner = issue.labels.find(isAssignmentLabel) ?? 'unassigned';
|
|
20
|
+
stale.push({ number: issue.number, title: issue.title, ageDays, owner });
|
|
21
|
+
}
|
|
22
|
+
// Oldest first — the operator's attention should go to the longest-stalled.
|
|
23
|
+
return stale.sort((a, b) => b.ageDays - a.ageDays);
|
|
24
|
+
}
|
|
25
|
+
/** Bucket open PRs by the action they need. */
|
|
26
|
+
export function computePrBuckets(prs) {
|
|
27
|
+
const awaitingReview = [];
|
|
28
|
+
const approvedUnmerged = [];
|
|
29
|
+
const changesRequested = [];
|
|
30
|
+
const drafts = [];
|
|
31
|
+
for (const pr of prs) {
|
|
32
|
+
if (pr.isDraft) {
|
|
33
|
+
drafts.push(pr);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
switch (pr.reviewDecision) {
|
|
37
|
+
case 'APPROVED':
|
|
38
|
+
approvedUnmerged.push(pr);
|
|
39
|
+
break;
|
|
40
|
+
case 'CHANGES_REQUESTED':
|
|
41
|
+
changesRequested.push(pr);
|
|
42
|
+
break;
|
|
43
|
+
// REVIEW_REQUIRED, null, or any other state → still needs a review.
|
|
44
|
+
default:
|
|
45
|
+
awaitingReview.push(pr);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { awaitingReview, approvedUnmerged, changesRequested, drafts };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Aggregate rule-evolution signals across every reflection record, grouped by
|
|
53
|
+
* (proposed_tier, dedup-handle). The dedup handle is the signal's `key` when
|
|
54
|
+
* present (the cross-agent dedup handle per the F2 schema) and the signal text
|
|
55
|
+
* otherwise. `count` is the number of DISTINCT AGENTS that raised the signal —
|
|
56
|
+
* the cross-agent corroboration count, NOT raw occurrences (five hits from one
|
|
57
|
+
* agent count as one). This is the occurrence-vs-distinct-agent distinction the
|
|
58
|
+
* auditor's N>1 promotion gate hinges on (macf#503/#514); the digest's "×N
|
|
59
|
+
* agents" label must reflect distinct agents, not it would imply a corroboration
|
|
60
|
+
* it never measured. `occurrences` keeps the raw record count for context.
|
|
61
|
+
*/
|
|
62
|
+
export function aggregateSignals(records) {
|
|
63
|
+
const byHandle = new Map();
|
|
64
|
+
for (const rec of records) {
|
|
65
|
+
const agentName = rec.agent.name;
|
|
66
|
+
for (const sig of rec.rule_evolution_signals) {
|
|
67
|
+
const hasKey = typeof sig.key === 'string' && sig.key.length > 0;
|
|
68
|
+
const handle = hasKey ? sig.key : sig.signal;
|
|
69
|
+
// Tier is part of the grouping key so the same handle proposed at two
|
|
70
|
+
// tiers shows up as two distinct candidate signals.
|
|
71
|
+
const mapKey = JSON.stringify([sig.proposed_tier, handle]);
|
|
72
|
+
const existing = byHandle.get(mapKey);
|
|
73
|
+
if (existing) {
|
|
74
|
+
existing.agents.add(agentName);
|
|
75
|
+
existing.occurrences += 1;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
byHandle.set(mapKey, {
|
|
79
|
+
handle,
|
|
80
|
+
proposedTier: sig.proposed_tier,
|
|
81
|
+
signal: sig.signal,
|
|
82
|
+
agents: new Set([agentName]),
|
|
83
|
+
occurrences: 1,
|
|
84
|
+
hasKey,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Strongest cross-agent corroboration (distinct agents) first, then by tier.
|
|
90
|
+
return [...byHandle.values()]
|
|
91
|
+
.map((g) => ({
|
|
92
|
+
handle: g.handle,
|
|
93
|
+
proposedTier: g.proposedTier,
|
|
94
|
+
signal: g.signal,
|
|
95
|
+
count: g.agents.size,
|
|
96
|
+
occurrences: g.occurrences,
|
|
97
|
+
hasKey: g.hasKey,
|
|
98
|
+
}))
|
|
99
|
+
.sort((a, b) => b.count - a.count || a.proposedTier.localeCompare(b.proposedTier));
|
|
100
|
+
}
|
|
101
|
+
function countBreaches(records) {
|
|
102
|
+
return records.reduce((n, r) => n + r.breaches.length, 0);
|
|
103
|
+
}
|
|
104
|
+
function countUnresolved(records) {
|
|
105
|
+
return records.reduce((n, r) => n + r.unresolved.length, 0);
|
|
106
|
+
}
|
|
107
|
+
/** ISO8601 UTC, seconds precision (matches the reflection schema's timestamp). */
|
|
108
|
+
function isoUtc(ms) {
|
|
109
|
+
return new Date(ms).toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Build the full Markdown protocol-health digest.
|
|
113
|
+
*
|
|
114
|
+
* Sections: header → Stale issues → PRs awaiting action → Aggregated reflection
|
|
115
|
+
* signals → Summary. Drift / stale state / candidate signals are SURFACED, not
|
|
116
|
+
* acted on; a one-line note records that proposing/actuation is gated (G1).
|
|
117
|
+
*/
|
|
118
|
+
export function buildDigest(input) {
|
|
119
|
+
const nowMs = input.now();
|
|
120
|
+
const stale = computeStaleIssues(input.issues, input.sinceDays, nowMs);
|
|
121
|
+
const buckets = computePrBuckets(input.prs);
|
|
122
|
+
const signals = aggregateSignals(input.reflections);
|
|
123
|
+
const breachCount = countBreaches(input.reflections);
|
|
124
|
+
const unresolvedCount = countUnresolved(input.reflections);
|
|
125
|
+
const lines = [];
|
|
126
|
+
// --- Header ---
|
|
127
|
+
lines.push(`# Protocol-Health Digest — ${input.project}`);
|
|
128
|
+
lines.push('');
|
|
129
|
+
lines.push(`- Repo: \`${input.repo}\``);
|
|
130
|
+
lines.push(`- Generated at: ${isoUtc(nowMs)}`);
|
|
131
|
+
lines.push(`- Stale threshold: ${input.sinceDays} days`);
|
|
132
|
+
lines.push(`- Read-only auditor (DR-026 F4). Drift + candidate signals below are ` +
|
|
133
|
+
`**surfaced, not acted on** — proposing/actuation is gated (DR-026 G1).`);
|
|
134
|
+
lines.push('');
|
|
135
|
+
// --- Stale issues ---
|
|
136
|
+
lines.push('## Stale issues');
|
|
137
|
+
lines.push('');
|
|
138
|
+
if (stale.length === 0) {
|
|
139
|
+
lines.push(`_No open issues older than ${input.sinceDays} days._`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const byOwner = new Map();
|
|
143
|
+
for (const s of stale) {
|
|
144
|
+
const arr = byOwner.get(s.owner) ?? [];
|
|
145
|
+
arr.push(s);
|
|
146
|
+
byOwner.set(s.owner, arr);
|
|
147
|
+
}
|
|
148
|
+
for (const owner of [...byOwner.keys()].sort()) {
|
|
149
|
+
lines.push(`### ${owner}`);
|
|
150
|
+
lines.push('');
|
|
151
|
+
for (const s of byOwner.get(owner)) {
|
|
152
|
+
lines.push(`- #${s.number} — ${s.title} (${s.ageDays}d open)`);
|
|
153
|
+
}
|
|
154
|
+
lines.push('');
|
|
155
|
+
}
|
|
156
|
+
// Trailing blank from the loop is fine; normalize below.
|
|
157
|
+
}
|
|
158
|
+
lines.push('');
|
|
159
|
+
// --- PRs awaiting action ---
|
|
160
|
+
lines.push('## PRs awaiting action');
|
|
161
|
+
lines.push('');
|
|
162
|
+
const prSection = (heading, prs) => {
|
|
163
|
+
lines.push(`### ${heading}`);
|
|
164
|
+
lines.push('');
|
|
165
|
+
if (prs.length === 0) {
|
|
166
|
+
lines.push('_none_');
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
for (const pr of prs) {
|
|
170
|
+
lines.push(`- #${pr.number} — ${pr.title}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
lines.push('');
|
|
174
|
+
};
|
|
175
|
+
prSection('Awaiting review', buckets.awaitingReview);
|
|
176
|
+
prSection('Approved, unmerged', buckets.approvedUnmerged);
|
|
177
|
+
prSection('Changes requested', buckets.changesRequested);
|
|
178
|
+
if (buckets.drafts.length > 0) {
|
|
179
|
+
prSection('Drafts', buckets.drafts);
|
|
180
|
+
}
|
|
181
|
+
// --- Aggregated reflection signals ---
|
|
182
|
+
lines.push('## Aggregated reflection signals');
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push(`_Candidate rule-evolution signals harvested from F2 reflections — ` +
|
|
185
|
+
`surfaced for operator awareness, not proposed (G1)._`);
|
|
186
|
+
lines.push('');
|
|
187
|
+
if (signals.length === 0) {
|
|
188
|
+
lines.push('_No rule-evolution signals in the reflection ledgers._');
|
|
189
|
+
lines.push('');
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const byTier = new Map();
|
|
193
|
+
for (const sig of signals) {
|
|
194
|
+
const arr = byTier.get(sig.proposedTier) ?? [];
|
|
195
|
+
arr.push(sig);
|
|
196
|
+
byTier.set(sig.proposedTier, arr);
|
|
197
|
+
}
|
|
198
|
+
for (const tier of [...byTier.keys()].sort()) {
|
|
199
|
+
lines.push(`### proposed_tier: ${tier}`);
|
|
200
|
+
lines.push('');
|
|
201
|
+
for (const sig of byTier.get(tier)) {
|
|
202
|
+
const dedup = sig.hasKey
|
|
203
|
+
? ` [key: \`${sig.handle}\`]`
|
|
204
|
+
: '';
|
|
205
|
+
const corroboration = sig.count > 1 ? ` (×${sig.count} agents)` : '';
|
|
206
|
+
lines.push(`- ${sig.signal}${dedup}${corroboration}`);
|
|
207
|
+
}
|
|
208
|
+
lines.push('');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// --- Summary ---
|
|
212
|
+
lines.push('## Summary');
|
|
213
|
+
lines.push('');
|
|
214
|
+
lines.push(`- Open issues: ${input.issues.length} (stale: ${stale.length})`);
|
|
215
|
+
lines.push(`- Open PRs: ${input.prs.length} ` +
|
|
216
|
+
`(awaiting review: ${buckets.awaitingReview.length}, ` +
|
|
217
|
+
`approved-unmerged: ${buckets.approvedUnmerged.length}, ` +
|
|
218
|
+
`changes-requested: ${buckets.changesRequested.length})`);
|
|
219
|
+
lines.push(`- Reflection records: ${input.reflections.length} ` +
|
|
220
|
+
`across ${input.reflectionFiles} ledger file(s) ` +
|
|
221
|
+
`(skipped malformed lines: ${input.reflectionsSkipped})`);
|
|
222
|
+
lines.push(`- Candidate signals: ${signals.length} | ` +
|
|
223
|
+
`breaches: ${breachCount} | unresolved: ${unresolvedCount}`);
|
|
224
|
+
lines.push(`- Project-tier rules present: ${input.projectRulesCount}`);
|
|
225
|
+
lines.push('');
|
|
226
|
+
lines.push(`> This is a read-only protocol-health report. No issues were created, ` +
|
|
227
|
+
`commented on, closed, or merged. Proposing rule changes / actuation is a ` +
|
|
228
|
+
`separate, ratification-gated step (DR-026 G1).`);
|
|
229
|
+
// Collapse any accidental 3+ blank-line runs to a single blank line.
|
|
230
|
+
return lines.join('\n').replace(/\n{3,}/g, '\n\n') + '\n';
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=digest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest.js","sourceRoot":"","sources":["../../../src/cli/monitor/digest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** A single open issue, as read from `gh issue list --json`. */
|
|
2
|
+
export interface MonitorIssue {
|
|
3
|
+
readonly number: number;
|
|
4
|
+
readonly title: string;
|
|
5
|
+
/** ISO8601 creation timestamp (for stale detection). */
|
|
6
|
+
readonly createdAt: string;
|
|
7
|
+
/** Assignment + status labels (we group stale issues by assignment label). */
|
|
8
|
+
readonly labels: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
/** A single open PR, as read from `gh pr list --json`. */
|
|
11
|
+
export interface MonitorPR {
|
|
12
|
+
readonly number: number;
|
|
13
|
+
readonly title: string;
|
|
14
|
+
readonly createdAt: string;
|
|
15
|
+
/** GitHub's review decision: APPROVED / CHANGES_REQUESTED / REVIEW_REQUIRED / null. */
|
|
16
|
+
readonly reviewDecision: string | null;
|
|
17
|
+
/** GitHub's mergeable state string (e.g. CLEAN, BLOCKED, DIRTY, UNKNOWN). */
|
|
18
|
+
readonly mergeStateStatus: string | null;
|
|
19
|
+
readonly isDraft: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The read-only GitHub seam. Implementations MUST perform only reads. The
|
|
23
|
+
* Monitor command depends on this interface (not on `gh` directly) so tests can
|
|
24
|
+
* inject a fake + assert no mutation ever happens.
|
|
25
|
+
*/
|
|
26
|
+
export interface GitHubReader {
|
|
27
|
+
listOpenIssues(repo: string): Promise<readonly MonitorIssue[]>;
|
|
28
|
+
listOpenPRs(repo: string): Promise<readonly MonitorPR[]>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The production GitHub reader: shells out to `gh` for issue/PR LISTS only.
|
|
32
|
+
*
|
|
33
|
+
* `token` is forwarded as `GH_TOKEN` in the subprocess env — the canonical bot
|
|
34
|
+
* auth posture. Both calls are pure reads (`gh issue list`, `gh pr list`); this
|
|
35
|
+
* class has no write path at all.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createGhReader(token: string): GitHubReader;
|
|
38
|
+
//# sourceMappingURL=github-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-reader.d.ts","sourceRoot":"","sources":["../../../src/cli/monitor/github-reader.ts"],"names":[],"mappings":"AAoBA,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,0DAA0D;AAC1D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uFAAuF;IACvF,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,6EAA6E;IAC7E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC,CAAC;IAC/D,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAAC;CAC1D;AAoBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAgD1D"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only GitHub seam for the auditor Monitor (groundnuty/macf#502, DR-026 F4).
|
|
3
|
+
*
|
|
4
|
+
* THE LOAD-BEARING INVARIANT: the Monitor is strictly READ-ONLY. This interface
|
|
5
|
+
* exposes ONLY read operations — `listOpenIssues` + `listOpenPRs`. There is
|
|
6
|
+
* deliberately NO create/comment/close/merge/edit method on the seam, so a
|
|
7
|
+
* mutation is not merely "discouraged" but *unrepresentable* through the type.
|
|
8
|
+
* Tests inject a fake implementation and assert it received reads only.
|
|
9
|
+
*
|
|
10
|
+
* The default implementation shells out to the `gh` CLI via `execFile` — the
|
|
11
|
+
* same canonical read pattern as `src/plugin/lib/work.ts#checkIssues`. It only
|
|
12
|
+
* ever invokes `gh issue list` / `gh pr list` (both pure reads). The token is
|
|
13
|
+
* passed via the `GH_TOKEN` env (the house auth posture), never baked into a
|
|
14
|
+
* remote or used for a write.
|
|
15
|
+
*/
|
|
16
|
+
import { execFile } from 'node:child_process';
|
|
17
|
+
import { promisify } from 'node:util';
|
|
18
|
+
const execFileAsync = promisify(execFile);
|
|
19
|
+
/**
|
|
20
|
+
* The production GitHub reader: shells out to `gh` for issue/PR LISTS only.
|
|
21
|
+
*
|
|
22
|
+
* `token` is forwarded as `GH_TOKEN` in the subprocess env — the canonical bot
|
|
23
|
+
* auth posture. Both calls are pure reads (`gh issue list`, `gh pr list`); this
|
|
24
|
+
* class has no write path at all.
|
|
25
|
+
*/
|
|
26
|
+
export function createGhReader(token) {
|
|
27
|
+
const env = { ...process.env, GH_TOKEN: token };
|
|
28
|
+
return {
|
|
29
|
+
async listOpenIssues(repo) {
|
|
30
|
+
const { stdout } = await execFileAsync('gh', [
|
|
31
|
+
'issue', 'list',
|
|
32
|
+
'--repo', repo,
|
|
33
|
+
'--state', 'open',
|
|
34
|
+
'--limit', '500',
|
|
35
|
+
'--json', 'number,title,createdAt,labels',
|
|
36
|
+
], { encoding: 'utf-8', env, maxBuffer: 32 * 1024 * 1024 });
|
|
37
|
+
const rows = JSON.parse(stdout);
|
|
38
|
+
return rows.map((r) => ({
|
|
39
|
+
number: r.number,
|
|
40
|
+
title: r.title,
|
|
41
|
+
createdAt: r.createdAt,
|
|
42
|
+
labels: (r.labels ?? []).map((l) => l.name),
|
|
43
|
+
}));
|
|
44
|
+
},
|
|
45
|
+
async listOpenPRs(repo) {
|
|
46
|
+
const { stdout } = await execFileAsync('gh', [
|
|
47
|
+
'pr', 'list',
|
|
48
|
+
'--repo', repo,
|
|
49
|
+
'--state', 'open',
|
|
50
|
+
'--limit', '500',
|
|
51
|
+
'--json', 'number,title,createdAt,reviewDecision,mergeStateStatus,isDraft',
|
|
52
|
+
], { encoding: 'utf-8', env, maxBuffer: 32 * 1024 * 1024 });
|
|
53
|
+
const rows = JSON.parse(stdout);
|
|
54
|
+
return rows.map((r) => ({
|
|
55
|
+
number: r.number,
|
|
56
|
+
title: r.title,
|
|
57
|
+
createdAt: r.createdAt,
|
|
58
|
+
reviewDecision: r.reviewDecision ?? null,
|
|
59
|
+
mergeStateStatus: r.mergeStateStatus ?? null,
|
|
60
|
+
isDraft: r.isDraft ?? false,
|
|
61
|
+
}));
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=github-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-reader.js","sourceRoot":"","sources":["../../../src/cli/monitor/github-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAoD1C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAEhD,OAAO;QACL,KAAK,CAAC,cAAc,CAAC,IAAY;YAC/B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CACpC,IAAI,EACJ;gBACE,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,IAAI;gBACd,SAAS,EAAE,MAAM;gBACjB,SAAS,EAAE,KAAK;gBAChB,QAAQ,EAAE,+BAA+B;aAC1C,EACD,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CACxD,CAAC;YACF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAA0B,CAAC;YACzD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACtB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAC5C,CAAC,CAAC,CAAC;QACN,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,IAAY;YAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CACpC,IAAI,EACJ;gBACE,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,IAAI;gBACd,SAAS,EAAE,MAAM;gBACjB,SAAS,EAAE,KAAK;gBAChB,QAAQ,EAAE,gEAAgE;aAC3E,EACD,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CACxD,CAAC;YACF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAuB,CAAC;YACtD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACtB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,cAAc,EAAE,CAAC,CAAC,cAAc,IAAI,IAAI;gBACxC,gBAAgB,EAAE,CAAC,CAAC,gBAAgB,IAAI,IAAI;gBAC5C,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK;aAC5B,CAAC,CAAC,CAAC;QACN,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReflectionRecord } from '@groundnuty/macf-core';
|
|
2
|
+
export interface ReflectionsReadResult {
|
|
3
|
+
/** All successfully-parsed reflection records across every ledger file. */
|
|
4
|
+
readonly records: readonly ReflectionRecord[];
|
|
5
|
+
/** Count of JSONL lines that failed to parse (surfaced, never fatal). */
|
|
6
|
+
readonly skipped: number;
|
|
7
|
+
/** Number of `*.jsonl` ledger files read. */
|
|
8
|
+
readonly files: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Read all reflection records from the JSONL ledgers under `dir`.
|
|
12
|
+
*
|
|
13
|
+
* Returns an empty result (no throw) when `dir` doesn't exist or contains no
|
|
14
|
+
* `*.jsonl` files — a project that hasn't compacted yet is a valid state, not
|
|
15
|
+
* an error.
|
|
16
|
+
*/
|
|
17
|
+
export declare function readReflections(dir: string): ReflectionsReadResult;
|
|
18
|
+
//# sourceMappingURL=reflections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflections.d.ts","sourceRoot":"","sources":["../../../src/cli/monitor/reflections.ts"],"names":[],"mappings":"AAcA,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEtF,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CA+ClE"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read + parse F2 reflection JSONL ledgers for the auditor Monitor
|
|
3
|
+
* (groundnuty/macf#502, DR-026 F4).
|
|
4
|
+
*
|
|
5
|
+
* Reads every `*.jsonl` file under the reflections directory, parses each line
|
|
6
|
+
* with `ReflectionRecordSchema`, and skips malformed lines DEFENSIVELY — a
|
|
7
|
+
* single bad line (truncated write, hand-edit, schema drift) must never make the
|
|
8
|
+
* whole digest fatal. Skipped-line counts are surfaced in the result so the
|
|
9
|
+
* digest can note them rather than hide them.
|
|
10
|
+
*
|
|
11
|
+
* This is a pure read: it only opens files for reading. No mutation.
|
|
12
|
+
*/
|
|
13
|
+
import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { ReflectionRecordSchema } from '@groundnuty/macf-core';
|
|
16
|
+
/**
|
|
17
|
+
* Read all reflection records from the JSONL ledgers under `dir`.
|
|
18
|
+
*
|
|
19
|
+
* Returns an empty result (no throw) when `dir` doesn't exist or contains no
|
|
20
|
+
* `*.jsonl` files — a project that hasn't compacted yet is a valid state, not
|
|
21
|
+
* an error.
|
|
22
|
+
*/
|
|
23
|
+
export function readReflections(dir) {
|
|
24
|
+
if (!existsSync(dir)) {
|
|
25
|
+
return { records: [], skipped: 0, files: 0 };
|
|
26
|
+
}
|
|
27
|
+
let entries;
|
|
28
|
+
try {
|
|
29
|
+
entries = readdirSync(dir).filter((f) => f.endsWith('.jsonl'));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { records: [], skipped: 0, files: 0 };
|
|
33
|
+
}
|
|
34
|
+
const records = [];
|
|
35
|
+
let skipped = 0;
|
|
36
|
+
let files = 0;
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const path = join(dir, entry);
|
|
39
|
+
let raw;
|
|
40
|
+
try {
|
|
41
|
+
if (!statSync(path).isFile())
|
|
42
|
+
continue;
|
|
43
|
+
raw = readFileSync(path, 'utf-8');
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
continue; // unreadable file — skip the whole file, not fatal
|
|
47
|
+
}
|
|
48
|
+
files += 1;
|
|
49
|
+
for (const line of raw.split('\n')) {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
if (trimmed.length === 0)
|
|
52
|
+
continue; // blank lines are not failures
|
|
53
|
+
let parsedJson;
|
|
54
|
+
try {
|
|
55
|
+
parsedJson = JSON.parse(trimmed);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
skipped += 1; // not valid JSON — skip defensively
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const result = ReflectionRecordSchema.safeParse(parsedJson);
|
|
62
|
+
if (result.success) {
|
|
63
|
+
records.push(result.data);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
skipped += 1; // valid JSON but wrong shape — skip defensively
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { records, skipped, files };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=reflections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflections.js","sourceRoot":"","sources":["../../../src/cli/monitor/reflections.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAyB,MAAM,uBAAuB,CAAC;AAWtF;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC/C,CAAC;IAED,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9B,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE;gBAAE,SAAS;YACvC,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,mDAAmD;QAC/D,CAAC;QACD,KAAK,IAAI,CAAC,CAAC;QAEX,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS,CAAC,+BAA+B;YACnE,IAAI,UAAmB,CAAC;YACxB,IAAI,CAAC;gBACH,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,CAAC,CAAC,oCAAoC;gBAClD,SAAS;YACX,CAAC;YACD,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YAC5D,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,OAAO,IAAI,CAAC,CAAC,CAAC,gDAAgD;YAChE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Clock } from './digest.js';
|
|
2
|
+
import type { GitHubReader } from './github-reader.js';
|
|
3
|
+
export interface RunMonitorOptions {
|
|
4
|
+
readonly project: string;
|
|
5
|
+
readonly repo: string;
|
|
6
|
+
readonly sinceDays: number;
|
|
7
|
+
/** Directory holding the F2 reflection JSONL ledgers. */
|
|
8
|
+
readonly reflectionsDir: string;
|
|
9
|
+
/**
|
|
10
|
+
* Workspace root used to locate `.claude/rules/project/*.md` for the
|
|
11
|
+
* (optional, defensive) project-tier rule count. F3 (`project-rules.ts`) is
|
|
12
|
+
* not on this branch, so we check the filesystem directly rather than import.
|
|
13
|
+
*/
|
|
14
|
+
readonly workspaceDir: string;
|
|
15
|
+
readonly reader: GitHubReader;
|
|
16
|
+
readonly now: Clock;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Count `.claude/rules/project/*.md` files. Defensive: returns 0 (never throws)
|
|
20
|
+
* when the directory is absent — F3 isn't merged, so its absence is normal.
|
|
21
|
+
*/
|
|
22
|
+
export declare function countProjectRules(workspaceDir: string): number;
|
|
23
|
+
/**
|
|
24
|
+
* Run the Monitor: read everything, build the digest, return the Markdown.
|
|
25
|
+
*
|
|
26
|
+
* Only READS are performed. Any GitHub read failure is surfaced as a thrown
|
|
27
|
+
* error to the caller (the CLI command), which decides how to report it.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runMonitor(opts: RunMonitorOptions): Promise<string>;
|
|
30
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/monitor/run.ts"],"names":[],"mappings":"AAcA,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAe9D;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBzE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only Monitor orchestrator for the auditor (groundnuty/macf#502, DR-026 F4).
|
|
3
|
+
*
|
|
4
|
+
* Aggregates a project's coordination substrate — GitHub (open issues + open
|
|
5
|
+
* PRs, via an injectable read-only seam), F2 reflection ledgers, and a defensive
|
|
6
|
+
* project-tier rule count — and renders a protocol-health digest via the pure
|
|
7
|
+
* `buildDigest`.
|
|
8
|
+
*
|
|
9
|
+
* Everything here is a READ. The GitHub seam is `GitHubReader` (list-only by
|
|
10
|
+
* type); reflections + project-rule counts are filesystem reads. The clock is
|
|
11
|
+
* injected so output is deterministic under test.
|
|
12
|
+
*/
|
|
13
|
+
import { readdirSync, existsSync, statSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { buildDigest } from './digest.js';
|
|
16
|
+
import { readReflections } from './reflections.js';
|
|
17
|
+
/**
|
|
18
|
+
* Count `.claude/rules/project/*.md` files. Defensive: returns 0 (never throws)
|
|
19
|
+
* when the directory is absent — F3 isn't merged, so its absence is normal.
|
|
20
|
+
*/
|
|
21
|
+
export function countProjectRules(workspaceDir) {
|
|
22
|
+
const dir = join(workspaceDir, '.claude', 'rules', 'project');
|
|
23
|
+
if (!existsSync(dir))
|
|
24
|
+
return 0;
|
|
25
|
+
try {
|
|
26
|
+
return readdirSync(dir).filter((f) => {
|
|
27
|
+
if (!f.endsWith('.md'))
|
|
28
|
+
return false;
|
|
29
|
+
try {
|
|
30
|
+
return statSync(join(dir, f)).isFile();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}).length;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run the Monitor: read everything, build the digest, return the Markdown.
|
|
43
|
+
*
|
|
44
|
+
* Only READS are performed. Any GitHub read failure is surfaced as a thrown
|
|
45
|
+
* error to the caller (the CLI command), which decides how to report it.
|
|
46
|
+
*/
|
|
47
|
+
export async function runMonitor(opts) {
|
|
48
|
+
const [issues, prs] = await Promise.all([
|
|
49
|
+
opts.reader.listOpenIssues(opts.repo),
|
|
50
|
+
opts.reader.listOpenPRs(opts.repo),
|
|
51
|
+
]);
|
|
52
|
+
const reflections = readReflections(opts.reflectionsDir);
|
|
53
|
+
const projectRulesCount = countProjectRules(opts.workspaceDir);
|
|
54
|
+
return buildDigest({
|
|
55
|
+
project: opts.project,
|
|
56
|
+
repo: opts.repo,
|
|
57
|
+
sinceDays: opts.sinceDays,
|
|
58
|
+
issues,
|
|
59
|
+
prs,
|
|
60
|
+
reflections: reflections.records,
|
|
61
|
+
reflectionsSkipped: reflections.skipped,
|
|
62
|
+
reflectionFiles: reflections.files,
|
|
63
|
+
projectRulesCount,
|
|
64
|
+
now: opts.now,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/cli/monitor/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAc,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAkBnD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,YAAoB;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACnC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YACrC,IAAI,CAAC;gBACH,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC,CAAC,CAAC,MAAM,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAuB;IACtD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;KACnC,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACzD,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAE/D,OAAO,WAAW,CAAC;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,MAAM;QACN,GAAG;QACH,WAAW,EAAE,WAAW,CAAC,OAAO;QAChC,kBAAkB,EAAE,WAAW,CAAC,OAAO;QACvC,eAAe,EAAE,WAAW,CAAC,KAAK;QAClC,iBAAiB;QACjB,GAAG,EAAE,IAAI,CAAC,GAAG;KACd,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** The MACF process kinds this scanner recognises. */
|
|
2
|
+
export type MacfProcessKind = 'claude' | 'channel-server';
|
|
3
|
+
/** Where an agent identity was sourced from (precedence high→low). */
|
|
4
|
+
export type IdentitySource = 'otel' | 'env-name' | 'env-label';
|
|
5
|
+
/**
|
|
6
|
+
* Injectable `/proc` reader. The default implementation reads the real
|
|
7
|
+
* filesystem; tests pass a synthetic one. Every accessor returns `null`
|
|
8
|
+
* rather than throwing so a PID vanishing mid-scan (ESRCH) or owned by
|
|
9
|
+
* another user (EACCES) degrades to "unknown", not a crash.
|
|
10
|
+
*/
|
|
11
|
+
export interface ProcReader {
|
|
12
|
+
/** Is `/proc`-based introspection available on this host? */
|
|
13
|
+
readonly available: () => boolean;
|
|
14
|
+
/** Numeric PID directory names under `/proc`. */
|
|
15
|
+
readonly listPids: () => readonly string[];
|
|
16
|
+
/** `readlink /proc/<pid>/cwd` — absolute path, or null. */
|
|
17
|
+
readonly readCwd: (pid: string) => string | null;
|
|
18
|
+
/** Raw NUL-separated `/proc/<pid>/cmdline`, or null. */
|
|
19
|
+
readonly readCmdline: (pid: string) => string | null;
|
|
20
|
+
/** Raw NUL-separated `/proc/<pid>/environ`, or null. */
|
|
21
|
+
readonly readEnviron: (pid: string) => string | null;
|
|
22
|
+
}
|
|
23
|
+
/** One discovered MACF process, keyed by its own cwd + identity. */
|
|
24
|
+
export interface MacfProcess {
|
|
25
|
+
readonly pid: string;
|
|
26
|
+
readonly kind: MacfProcessKind;
|
|
27
|
+
/** Absolute cwd from `/proc/<pid>/cwd` (the disambiguation key). */
|
|
28
|
+
readonly cwd: string | null;
|
|
29
|
+
/** `basename(cwd)` for compact display. */
|
|
30
|
+
readonly cwdBasename: string | null;
|
|
31
|
+
/** Resolved agent identity (see `agentIdentityFromEnv`). */
|
|
32
|
+
readonly agentIdentity: string | null;
|
|
33
|
+
readonly identitySource: IdentitySource | null;
|
|
34
|
+
/** `MACF_PORT` from the process environment. */
|
|
35
|
+
readonly port: string | null;
|
|
36
|
+
/** `OTEL_EXPORTER_OTLP_ENDPOINT` from the process environment. */
|
|
37
|
+
readonly otelEndpoint: string | null;
|
|
38
|
+
}
|
|
39
|
+
/** Split a NUL-separated `/proc` blob (cmdline / environ) into tokens. */
|
|
40
|
+
export declare function splitNul(raw: string): readonly string[];
|
|
41
|
+
/**
|
|
42
|
+
* Parse a NUL-separated `/proc/<pid>/environ` blob into a key→value map.
|
|
43
|
+
* Splits each entry on the FIRST `=` (values may contain `=`).
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseEnviron(raw: string): Readonly<Record<string, string>>;
|
|
46
|
+
/**
|
|
47
|
+
* Parse an `OTEL_RESOURCE_ATTRIBUTES` value (`k1=v1,k2=v2`) into a map.
|
|
48
|
+
* Used to recover `gen_ai.agent.name`, the OTEL-canonical agent identity.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseOtelResourceAttributes(value: string): Readonly<Record<string, string>>;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve an agent identity from a process environment, in precedence order:
|
|
53
|
+
* 1. `gen_ai.agent.name` inside `OTEL_RESOURCE_ATTRIBUTES` (telemetry-canonical)
|
|
54
|
+
* 2. `MACF_AGENT_NAME`
|
|
55
|
+
* 3. `MACF_ROUTING_LABEL`
|
|
56
|
+
* Returns null when none is present.
|
|
57
|
+
*/
|
|
58
|
+
export declare function agentIdentityFromEnv(env: Readonly<Record<string, string>>): {
|
|
59
|
+
readonly identity: string;
|
|
60
|
+
readonly source: IdentitySource;
|
|
61
|
+
} | null;
|
|
62
|
+
/**
|
|
63
|
+
* Classify a raw cmdline blob as a MACF process kind, or null if it's neither.
|
|
64
|
+
* channel-server wins over claude (a server is a node process that never
|
|
65
|
+
* carries a `claude` argv0). claude matches when any argv token's basename is
|
|
66
|
+
* exactly `claude` (covers `claude`, `/usr/local/bin/claude`, `.../bin/claude`).
|
|
67
|
+
*/
|
|
68
|
+
export declare function classifyCmdline(raw: string): MacfProcessKind | null;
|
|
69
|
+
/**
|
|
70
|
+
* Enumerate every MACF claude / channel-server process via the reader.
|
|
71
|
+
* PURE w.r.t. the reader: no real-process dependency. Each process is keyed by
|
|
72
|
+
* its OWN cwd + environment — never `head -1`. Returns a stable ordering
|
|
73
|
+
* (by cwd, then kind, then numeric pid) for readable, diffable output.
|
|
74
|
+
*/
|
|
75
|
+
export declare function scanMacfProcesses(reader: ProcReader): readonly MacfProcess[];
|
|
76
|
+
/**
|
|
77
|
+
* Real `/proc` reader. Each accessor swallows errors → null so a PID racing
|
|
78
|
+
* away mid-scan or an unreadable (other-user) process degrades gracefully.
|
|
79
|
+
*/
|
|
80
|
+
export declare const defaultProcReader: ProcReader;
|
|
81
|
+
//# sourceMappingURL=proc-scan.d.ts.map
|