@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 @@
|
|
|
1
|
+
{"version":3,"file":"proc-scan.d.ts","sourceRoot":"","sources":["../../src/cli/proc-scan.ts"],"names":[],"mappings":"AAmBA,sDAAsD;AACtD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,gBAAgB,CAAC;AAE1D,sEAAsE;AACtE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAClC,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,SAAS,MAAM,EAAE,CAAC;IAC3C,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD,wDAAwD;IACxD,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACrD,wDAAwD;IACxD,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACtD;AAED,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/C,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAEvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQ1E;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQ3F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACpC;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;CAAE,GAAG,IAAI,CAWvE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAMnE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,WAAW,EAAE,CA0B5E;AAWD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,UA8B/B,CAAC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity-scoped `/proc` scanning for MACF process diagnostics (macf#556).
|
|
3
|
+
*
|
|
4
|
+
* Single-tenant instruments (`pgrep claude | head -1`) are WRONG on a
|
|
5
|
+
* multi-tenant host: `head -1` grabs an arbitrary one of several `claude`
|
|
6
|
+
* processes, producing a "no OTEL" false negative against a sibling agent.
|
|
7
|
+
* This module is correct-by-construction: it enumerates EVERY MACF process
|
|
8
|
+
* and keys each one by its own `readlink /proc/<pid>/cwd` + per-process
|
|
9
|
+
* environment, so the caller can disambiguate by workspace / agent identity
|
|
10
|
+
* instead of picking one at random.
|
|
11
|
+
*
|
|
12
|
+
* Linux `/proc` only. The `ProcReader` seam keeps the scan a PURE function of
|
|
13
|
+
* injectable inputs so tests feed synthetic `/proc` data (no real running
|
|
14
|
+
* processes), and the real reader degrades gracefully (every read returns
|
|
15
|
+
* `null` on EACCES / ESRCH races; `available()` is false off-Linux).
|
|
16
|
+
*/
|
|
17
|
+
import { readdirSync, readlinkSync, readFileSync, existsSync } from 'node:fs';
|
|
18
|
+
import { basename } from 'node:path';
|
|
19
|
+
/** Split a NUL-separated `/proc` blob (cmdline / environ) into tokens. */
|
|
20
|
+
export function splitNul(raw) {
|
|
21
|
+
return raw.split('\0').filter((t) => t.length > 0);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parse a NUL-separated `/proc/<pid>/environ` blob into a key→value map.
|
|
25
|
+
* Splits each entry on the FIRST `=` (values may contain `=`).
|
|
26
|
+
*/
|
|
27
|
+
export function parseEnviron(raw) {
|
|
28
|
+
const env = {};
|
|
29
|
+
for (const entry of splitNul(raw)) {
|
|
30
|
+
const eq = entry.indexOf('=');
|
|
31
|
+
if (eq <= 0)
|
|
32
|
+
continue;
|
|
33
|
+
env[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
34
|
+
}
|
|
35
|
+
return env;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Parse an `OTEL_RESOURCE_ATTRIBUTES` value (`k1=v1,k2=v2`) into a map.
|
|
39
|
+
* Used to recover `gen_ai.agent.name`, the OTEL-canonical agent identity.
|
|
40
|
+
*/
|
|
41
|
+
export function parseOtelResourceAttributes(value) {
|
|
42
|
+
const attrs = {};
|
|
43
|
+
for (const pair of value.split(',')) {
|
|
44
|
+
const eq = pair.indexOf('=');
|
|
45
|
+
if (eq <= 0)
|
|
46
|
+
continue;
|
|
47
|
+
attrs[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
|
|
48
|
+
}
|
|
49
|
+
return attrs;
|
|
50
|
+
}
|
|
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 function agentIdentityFromEnv(env) {
|
|
59
|
+
const ra = env['OTEL_RESOURCE_ATTRIBUTES'];
|
|
60
|
+
if (ra) {
|
|
61
|
+
const name = parseOtelResourceAttributes(ra)['gen_ai.agent.name'];
|
|
62
|
+
if (name)
|
|
63
|
+
return { identity: name, source: 'otel' };
|
|
64
|
+
}
|
|
65
|
+
const envName = env['MACF_AGENT_NAME'];
|
|
66
|
+
if (envName)
|
|
67
|
+
return { identity: envName, source: 'env-name' };
|
|
68
|
+
const envLabel = env['MACF_ROUTING_LABEL'];
|
|
69
|
+
if (envLabel)
|
|
70
|
+
return { identity: envLabel, source: 'env-label' };
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Classify a raw cmdline blob as a MACF process kind, or null if it's neither.
|
|
75
|
+
* channel-server wins over claude (a server is a node process that never
|
|
76
|
+
* carries a `claude` argv0). claude matches when any argv token's basename is
|
|
77
|
+
* exactly `claude` (covers `claude`, `/usr/local/bin/claude`, `.../bin/claude`).
|
|
78
|
+
*/
|
|
79
|
+
export function classifyCmdline(raw) {
|
|
80
|
+
const tokens = splitNul(raw);
|
|
81
|
+
if (tokens.length === 0)
|
|
82
|
+
return null;
|
|
83
|
+
if (tokens.some((t) => t.includes('macf-channel-server')))
|
|
84
|
+
return 'channel-server';
|
|
85
|
+
if (tokens.some((t) => basename(t) === 'claude'))
|
|
86
|
+
return 'claude';
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Enumerate every MACF claude / channel-server process via the reader.
|
|
91
|
+
* PURE w.r.t. the reader: no real-process dependency. Each process is keyed by
|
|
92
|
+
* its OWN cwd + environment — never `head -1`. Returns a stable ordering
|
|
93
|
+
* (by cwd, then kind, then numeric pid) for readable, diffable output.
|
|
94
|
+
*/
|
|
95
|
+
export function scanMacfProcesses(reader) {
|
|
96
|
+
if (!reader.available())
|
|
97
|
+
return [];
|
|
98
|
+
const out = [];
|
|
99
|
+
for (const pid of reader.listPids()) {
|
|
100
|
+
const cmdline = reader.readCmdline(pid);
|
|
101
|
+
if (cmdline === null)
|
|
102
|
+
continue;
|
|
103
|
+
const kind = classifyCmdline(cmdline);
|
|
104
|
+
if (!kind)
|
|
105
|
+
continue;
|
|
106
|
+
const cwd = reader.readCwd(pid);
|
|
107
|
+
const environRaw = reader.readEnviron(pid);
|
|
108
|
+
const env = environRaw ? parseEnviron(environRaw) : {};
|
|
109
|
+
const id = agentIdentityFromEnv(env);
|
|
110
|
+
out.push({
|
|
111
|
+
pid,
|
|
112
|
+
kind,
|
|
113
|
+
cwd,
|
|
114
|
+
cwdBasename: cwd ? basename(cwd) : null,
|
|
115
|
+
agentIdentity: id?.identity ?? null,
|
|
116
|
+
identitySource: id?.source ?? null,
|
|
117
|
+
port: env['MACF_PORT'] ?? null,
|
|
118
|
+
otelEndpoint: env['OTEL_EXPORTER_OTLP_ENDPOINT'] ?? null,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return [...out].sort(compareProcesses);
|
|
122
|
+
}
|
|
123
|
+
/** Stable ordering: cwd (nulls last) → kind → numeric pid. */
|
|
124
|
+
function compareProcesses(a, b) {
|
|
125
|
+
const ca = a.cwd ?? '';
|
|
126
|
+
const cb = b.cwd ?? '';
|
|
127
|
+
if (ca !== cb)
|
|
128
|
+
return ca < cb ? -1 : 1;
|
|
129
|
+
if (a.kind !== b.kind)
|
|
130
|
+
return a.kind < b.kind ? -1 : 1;
|
|
131
|
+
return Number(a.pid) - Number(b.pid);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Real `/proc` reader. Each accessor swallows errors → null so a PID racing
|
|
135
|
+
* away mid-scan or an unreadable (other-user) process degrades gracefully.
|
|
136
|
+
*/
|
|
137
|
+
export const defaultProcReader = {
|
|
138
|
+
available: () => process.platform === 'linux' && existsSync('/proc'),
|
|
139
|
+
listPids: () => {
|
|
140
|
+
try {
|
|
141
|
+
return readdirSync('/proc').filter((n) => /^\d+$/.test(n));
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
readCwd: (pid) => {
|
|
148
|
+
try {
|
|
149
|
+
return readlinkSync(`/proc/${pid}/cwd`);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
readCmdline: (pid) => {
|
|
156
|
+
try {
|
|
157
|
+
return readFileSync(`/proc/${pid}/cmdline`, 'utf-8');
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
readEnviron: (pid) => {
|
|
164
|
+
try {
|
|
165
|
+
return readFileSync(`/proc/${pid}/environ`, 'utf-8');
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=proc-scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proc-scan.js","sourceRoot":"","sources":["../../src/cli/proc-scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** Env var carrying the project-rules source (operator-managed config). */
|
|
2
|
+
export declare const PROJECT_RULES_SOURCE_ENV = "MACF_PROJECT_RULES_SOURCE";
|
|
3
|
+
/**
|
|
4
|
+
* Destination directory for project-tier rules within a workspace:
|
|
5
|
+
* `<workspace>/.claude/rules/project/`. A SUBDIR of the universal-rule dir so
|
|
6
|
+
* the two tiers are distinguishable on disk.
|
|
7
|
+
*/
|
|
8
|
+
export declare function workspaceProjectRulesDir(workspaceDir: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Parsed shape of a `MACF_PROJECT_RULES_SOURCE` value.
|
|
11
|
+
*
|
|
12
|
+
* - `kind: 'github'` — `<owner>/<repo>//<path>` form: fetched via git clone.
|
|
13
|
+
* - `kind: 'local'` — a local directory path: copied directly.
|
|
14
|
+
*/
|
|
15
|
+
export type ProjectRulesSource = {
|
|
16
|
+
readonly kind: 'github';
|
|
17
|
+
readonly owner: string;
|
|
18
|
+
readonly repo: string;
|
|
19
|
+
readonly subpath: string;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'local';
|
|
22
|
+
readonly dir: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Parse a `MACF_PROJECT_RULES_SOURCE` value into a discriminated source.
|
|
26
|
+
*
|
|
27
|
+
* The `//` separator disambiguates the two forms unambiguously: a GitHub
|
|
28
|
+
* `<owner>/<repo>//<path>` always contains `//`, a local filesystem path
|
|
29
|
+
* never does (`//` collapses to `/` on POSIX and isn't a meaningful path
|
|
30
|
+
* token). So presence of `//` selects the github form; absence selects local.
|
|
31
|
+
*
|
|
32
|
+
* Returns `undefined` for an empty/whitespace-only value (caller treats as
|
|
33
|
+
* no-op) or a malformed github form (missing owner/repo/path segment) —
|
|
34
|
+
* malformed is surfaced by the caller as a warning, not a parse crash.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseProjectRulesSource(raw: string | undefined): ProjectRulesSource | undefined;
|
|
37
|
+
export interface FetchProjectRulesOptions {
|
|
38
|
+
/** Override the env value (defaults to process.env[MACF_PROJECT_RULES_SOURCE]). */
|
|
39
|
+
readonly source?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Override the GitHub base URL for the github-source form (tests point this
|
|
42
|
+
* at a local bare repo). Defaults to `https://github.com`.
|
|
43
|
+
*/
|
|
44
|
+
readonly githubBaseUrl?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Distribute project-tier rules into `<workspace>/.claude/rules/project/`.
|
|
48
|
+
*
|
|
49
|
+
* Resolves the source from `MACF_PROJECT_RULES_SOURCE` (or `options.source`):
|
|
50
|
+
* - Unset/empty/whitespace → NO-OP, returns `[]`. The tier is optional.
|
|
51
|
+
* - `<owner>/<repo>//<path>` → shallow git clone the default branch (mirrors
|
|
52
|
+
* plugin-fetcher.ts), copy `<clone>/<path>/*.md` to the dest subdir.
|
|
53
|
+
* - a local directory path → copy `<dir>/*.md` directly.
|
|
54
|
+
*
|
|
55
|
+
* Only `*.md` files are copied (a `.example` template, a README, etc. in the
|
|
56
|
+
* source are ignored — same filter as the universal-rule copy). Each copied
|
|
57
|
+
* file gets the PROJECT_RULE_HEADER prepended (unless it already opens with an
|
|
58
|
+
* HTML comment, to avoid double-stacking).
|
|
59
|
+
*
|
|
60
|
+
* **Idempotent + non-destructive to other tiers.** The dest is a fresh subdir;
|
|
61
|
+
* the universal `.claude/rules/*.md` files are never touched (different path).
|
|
62
|
+
* Re-running overwrites the project subdir's `*.md` files with the current
|
|
63
|
+
* source content. A `.example` seed (from `macf init`) is preserved — only
|
|
64
|
+
* `*.md` files are managed here.
|
|
65
|
+
*
|
|
66
|
+
* Throws only on a genuine fetch failure (git clone error, source path is a
|
|
67
|
+
* file not a directory, the github subpath is missing in the clone). A
|
|
68
|
+
* malformed source string is a no-op-with-warning, NOT a throw, so a typo'd
|
|
69
|
+
* config can't break `macf update`.
|
|
70
|
+
*
|
|
71
|
+
* @returns the list of copied basenames (empty when the source is unset or
|
|
72
|
+
* the source dir has no `*.md` files).
|
|
73
|
+
*/
|
|
74
|
+
export declare function fetchProjectRules(workspaceDir: string, options?: FetchProjectRulesOptions): readonly string[];
|
|
75
|
+
/**
|
|
76
|
+
* Basename of the seed template `macf init` drops into the project-rules
|
|
77
|
+
* subdir. The `.example` suffix keeps it OUT of the live-rule set (only `*.md`
|
|
78
|
+
* are loaded as rules + managed by `fetchProjectRules`), so it self-documents
|
|
79
|
+
* the tier + gives the DR-026 §4 auditor a format to match without becoming a
|
|
80
|
+
* rule itself.
|
|
81
|
+
*/
|
|
82
|
+
export declare const PROJECT_RULE_SEED_NAME = "EXAMPLE.project-rule.md.example";
|
|
83
|
+
/**
|
|
84
|
+
* Generic, deployment-agnostic seed content for the project-rules tier.
|
|
85
|
+
*
|
|
86
|
+
* CRITICAL: this ships via `macf init` to EVERY deployment, so it must NOT be
|
|
87
|
+
* macf-specific. A genomics deployment must not receive a macf `dev.mk` rule.
|
|
88
|
+
* The body demonstrates the FORMAT + the precedence contract with neutral
|
|
89
|
+
* placeholders only — never a concrete macf rule.
|
|
90
|
+
*/
|
|
91
|
+
export declare function projectRuleSeedContent(): string;
|
|
92
|
+
/**
|
|
93
|
+
* Seed the project-rules subdir with the generic `.example` template
|
|
94
|
+
* (`macf init`). Creates `.claude/rules/project/` (mkdir -p) and writes the
|
|
95
|
+
* `.example` file. Idempotent: overwrites the `.example` with the current
|
|
96
|
+
* canonical template — it's a managed example, not operator state. Returns the
|
|
97
|
+
* seeded basename.
|
|
98
|
+
*
|
|
99
|
+
* Deliberately does NOT fetch from `MACF_PROJECT_RULES_SOURCE` — `macf init`
|
|
100
|
+
* only lays down the tier + its self-documenting seed; `macf update` (and
|
|
101
|
+
* `macf rules refresh`) pull the actual rules from the source. This keeps init
|
|
102
|
+
* generic (it ships to every deployment) and offline-safe.
|
|
103
|
+
*/
|
|
104
|
+
export declare function seedProjectRulesDir(workspaceDir: string): string;
|
|
105
|
+
//# sourceMappingURL=project-rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-rules.d.ts","sourceRoot":"","sources":["../../src/cli/project-rules.ts"],"names":[],"mappings":"AAiDA,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,8BAA8B,CAAC;AAsBpE;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,kBAAkB,GAAG,SAAS,CAmB/F;AAED,MAAM,WAAW,wBAAwB;IACvC,mFAAmF;IACnF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,wBAA6B,GACrC,SAAS,MAAM,EAAE,CA0DnB;AAwBD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,oCAAoC,CAAC;AAExE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CA8D/C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKhE"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-tier rule distribution (groundnuty/macf#501, DR-026 F3).
|
|
3
|
+
*
|
|
4
|
+
* The three-tier rule model (DR-026 §3) splits coordination knowledge into:
|
|
5
|
+
* 1. Universal / product rules — shipped IN the CLI at `plugin/rules/*.md`,
|
|
6
|
+
* copied to `.claude/rules/*.md` (see rules.ts `copyCanonicalRules`).
|
|
7
|
+
* 2. **Project rules** — per-deployment, NOT shipped in the npm product. This
|
|
8
|
+
* module. Sourced from an explicit config and copied to a SUBDIR
|
|
9
|
+
* `.claude/rules/project/*.md` so the universal and project tiers are
|
|
10
|
+
* distinguishable on disk (DR-026 §4's subordination check needs a clean
|
|
11
|
+
* target to match against).
|
|
12
|
+
* 3. Agent-private rules — an agent's own `.claude/rules/` / memory; out of
|
|
13
|
+
* scope here.
|
|
14
|
+
*
|
|
15
|
+
* **Why explicit config, not "the coordination repo".** A multi-repo deployment
|
|
16
|
+
* has no single repo to implicitly pull project rules from, so the source MUST
|
|
17
|
+
* be configured. `MACF_PROJECT_RULES_SOURCE` carries it, in one of two forms:
|
|
18
|
+
*
|
|
19
|
+
* - `<owner>/<repo>//<path>` — a GitHub repo + subdir (e.g.
|
|
20
|
+
* `groundnuty/macf//project-rules`). Fetched via
|
|
21
|
+
* a shallow `git clone` of the default branch,
|
|
22
|
+
* mirroring plugin-fetcher.ts (reuse, don't
|
|
23
|
+
* reinvent). The `//` separates repo from path.
|
|
24
|
+
* - a local directory path — anything WITHOUT the `//` separator that
|
|
25
|
+
* resolves to a directory on disk. Copied
|
|
26
|
+
* directly (no clone). Lets a single-host
|
|
27
|
+
* deployment point at a checked-out repo or a
|
|
28
|
+
* plain rules directory.
|
|
29
|
+
*
|
|
30
|
+
* **Optional tier — never errors on absence.** When the env var is unset/empty,
|
|
31
|
+
* `fetchProjectRules` is a no-op returning `[]`. The tier is optional, exactly
|
|
32
|
+
* like a workspace that has no universal rules: absence is normal, not a fault.
|
|
33
|
+
*
|
|
34
|
+
* **No shadow of the universal tier.** Project rules land under
|
|
35
|
+
* `.claude/rules/project/`, a different subdir from the universal
|
|
36
|
+
* `.claude/rules/*.md`. The universal-rule copy path (rules.ts) is untouched,
|
|
37
|
+
* so this can never overwrite a universal rule.
|
|
38
|
+
*
|
|
39
|
+
* **Precedence (documented, NOT enforced here).** Project rules *add to /
|
|
40
|
+
* specialize* the universal protocol; they never weaken it. The §4
|
|
41
|
+
* invariant-check that enforces "never contradict" is a separate (gated) slice
|
|
42
|
+
* of DR-026 — F3 only lays down the on-disk tiers. See
|
|
43
|
+
* `design/project-tier-rules.md`.
|
|
44
|
+
*/
|
|
45
|
+
import { execFileSync } from 'node:child_process';
|
|
46
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
47
|
+
import { tmpdir } from 'node:os';
|
|
48
|
+
import { join, resolve } from 'node:path';
|
|
49
|
+
/** Env var carrying the project-rules source (operator-managed config). */
|
|
50
|
+
export const PROJECT_RULES_SOURCE_ENV = 'MACF_PROJECT_RULES_SOURCE';
|
|
51
|
+
/**
|
|
52
|
+
* Header prepended to each copied project rule, marking the on-disk tier +
|
|
53
|
+
* the managed-from-source contract. Distinct wording from the universal-rule
|
|
54
|
+
* MANAGED_HEADER (rules.ts) so an operator inspecting a file knows which tier
|
|
55
|
+
* it belongs to + where it came from.
|
|
56
|
+
*/
|
|
57
|
+
const PROJECT_RULE_HEADER = [
|
|
58
|
+
'<!--',
|
|
59
|
+
' PROJECT-TIER RULE (DR-026 §3 tier 2). Distributed by `macf` from this',
|
|
60
|
+
` deployment's project-rules source (\`${PROJECT_RULES_SOURCE_ENV}\`). Do not`,
|
|
61
|
+
' edit this workspace copy — edits are overwritten on the next `macf update`.',
|
|
62
|
+
' Edit the rule at the source, then re-run `macf update` here.',
|
|
63
|
+
'',
|
|
64
|
+
' Project rules ADD TO / SPECIALIZE the universal rules in',
|
|
65
|
+
' .claude/rules/*.md; they must never contradict or weaken them',
|
|
66
|
+
' (DR-026 §4). See design/project-tier-rules.md.',
|
|
67
|
+
'-->',
|
|
68
|
+
'',
|
|
69
|
+
].join('\n');
|
|
70
|
+
/**
|
|
71
|
+
* Destination directory for project-tier rules within a workspace:
|
|
72
|
+
* `<workspace>/.claude/rules/project/`. A SUBDIR of the universal-rule dir so
|
|
73
|
+
* the two tiers are distinguishable on disk.
|
|
74
|
+
*/
|
|
75
|
+
export function workspaceProjectRulesDir(workspaceDir) {
|
|
76
|
+
return join(resolve(workspaceDir), '.claude', 'rules', 'project');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse a `MACF_PROJECT_RULES_SOURCE` value into a discriminated source.
|
|
80
|
+
*
|
|
81
|
+
* The `//` separator disambiguates the two forms unambiguously: a GitHub
|
|
82
|
+
* `<owner>/<repo>//<path>` always contains `//`, a local filesystem path
|
|
83
|
+
* never does (`//` collapses to `/` on POSIX and isn't a meaningful path
|
|
84
|
+
* token). So presence of `//` selects the github form; absence selects local.
|
|
85
|
+
*
|
|
86
|
+
* Returns `undefined` for an empty/whitespace-only value (caller treats as
|
|
87
|
+
* no-op) or a malformed github form (missing owner/repo/path segment) —
|
|
88
|
+
* malformed is surfaced by the caller as a warning, not a parse crash.
|
|
89
|
+
*/
|
|
90
|
+
export function parseProjectRulesSource(raw) {
|
|
91
|
+
const value = (raw ?? '').trim();
|
|
92
|
+
if (value === '')
|
|
93
|
+
return undefined;
|
|
94
|
+
const sepIdx = value.indexOf('//');
|
|
95
|
+
if (sepIdx < 0) {
|
|
96
|
+
// No `//` → local directory path.
|
|
97
|
+
return { kind: 'local', dir: value };
|
|
98
|
+
}
|
|
99
|
+
const repoPart = value.slice(0, sepIdx);
|
|
100
|
+
const subpath = value.slice(sepIdx + 2).replace(/^\/+|\/+$/g, '');
|
|
101
|
+
const ownerRepo = repoPart.split('/');
|
|
102
|
+
if (ownerRepo.length !== 2 || !ownerRepo[0] || !ownerRepo[1] || subpath === '') {
|
|
103
|
+
// Malformed github form (e.g. "owner//path" with no repo, or trailing
|
|
104
|
+
// empty subpath). Don't throw — caller warns + skips.
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return { kind: 'github', owner: ownerRepo[0], repo: ownerRepo[1], subpath };
|
|
108
|
+
}
|
|
109
|
+
const DEFAULT_GITHUB_BASE_URL = 'https://github.com';
|
|
110
|
+
/**
|
|
111
|
+
* Distribute project-tier rules into `<workspace>/.claude/rules/project/`.
|
|
112
|
+
*
|
|
113
|
+
* Resolves the source from `MACF_PROJECT_RULES_SOURCE` (or `options.source`):
|
|
114
|
+
* - Unset/empty/whitespace → NO-OP, returns `[]`. The tier is optional.
|
|
115
|
+
* - `<owner>/<repo>//<path>` → shallow git clone the default branch (mirrors
|
|
116
|
+
* plugin-fetcher.ts), copy `<clone>/<path>/*.md` to the dest subdir.
|
|
117
|
+
* - a local directory path → copy `<dir>/*.md` directly.
|
|
118
|
+
*
|
|
119
|
+
* Only `*.md` files are copied (a `.example` template, a README, etc. in the
|
|
120
|
+
* source are ignored — same filter as the universal-rule copy). Each copied
|
|
121
|
+
* file gets the PROJECT_RULE_HEADER prepended (unless it already opens with an
|
|
122
|
+
* HTML comment, to avoid double-stacking).
|
|
123
|
+
*
|
|
124
|
+
* **Idempotent + non-destructive to other tiers.** The dest is a fresh subdir;
|
|
125
|
+
* the universal `.claude/rules/*.md` files are never touched (different path).
|
|
126
|
+
* Re-running overwrites the project subdir's `*.md` files with the current
|
|
127
|
+
* source content. A `.example` seed (from `macf init`) is preserved — only
|
|
128
|
+
* `*.md` files are managed here.
|
|
129
|
+
*
|
|
130
|
+
* Throws only on a genuine fetch failure (git clone error, source path is a
|
|
131
|
+
* file not a directory, the github subpath is missing in the clone). A
|
|
132
|
+
* malformed source string is a no-op-with-warning, NOT a throw, so a typo'd
|
|
133
|
+
* config can't break `macf update`.
|
|
134
|
+
*
|
|
135
|
+
* @returns the list of copied basenames (empty when the source is unset or
|
|
136
|
+
* the source dir has no `*.md` files).
|
|
137
|
+
*/
|
|
138
|
+
export function fetchProjectRules(workspaceDir, options = {}) {
|
|
139
|
+
const raw = options.source ?? process.env[PROJECT_RULES_SOURCE_ENV];
|
|
140
|
+
const parsed = parseProjectRulesSource(raw);
|
|
141
|
+
if (parsed === undefined) {
|
|
142
|
+
if ((raw ?? '').trim() !== '') {
|
|
143
|
+
// Non-empty but unparseable — surface it; don't silently drop.
|
|
144
|
+
process.stderr.write(`Warning: ${PROJECT_RULES_SOURCE_ENV}="${raw}" is malformed.\n` +
|
|
145
|
+
` Expected "<owner>/<repo>//<path>" (e.g. groundnuty/macf//project-rules)\n` +
|
|
146
|
+
` or a local directory path. Skipping project-rule distribution.\n`);
|
|
147
|
+
}
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
if (parsed.kind === 'local') {
|
|
151
|
+
const srcDir = resolve(parsed.dir);
|
|
152
|
+
if (!existsSync(srcDir)) {
|
|
153
|
+
throw new Error(`${PROJECT_RULES_SOURCE_ENV} local source does not exist: ${srcDir}`);
|
|
154
|
+
}
|
|
155
|
+
if (!statSync(srcDir).isDirectory()) {
|
|
156
|
+
throw new Error(`${PROJECT_RULES_SOURCE_ENV} local source is not a directory: ${srcDir}`);
|
|
157
|
+
}
|
|
158
|
+
return copyMarkdownRules(srcDir, workspaceProjectRulesDir(workspaceDir));
|
|
159
|
+
}
|
|
160
|
+
// github form — shallow-clone the default branch (mirrors plugin-fetcher.ts),
|
|
161
|
+
// copy the subpath's *.md files, discard the clone.
|
|
162
|
+
const baseUrl = options.githubBaseUrl ?? DEFAULT_GITHUB_BASE_URL;
|
|
163
|
+
const repoUrl = `${baseUrl}/${parsed.owner}/${parsed.repo}`;
|
|
164
|
+
const tmpClone = mkdtempSync(join(tmpdir(), 'macf-project-rules-clone-'));
|
|
165
|
+
try {
|
|
166
|
+
execFileSync('git', ['clone', '--depth', '1', repoUrl, tmpClone], {
|
|
167
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
168
|
+
});
|
|
169
|
+
const srcDir = join(tmpClone, parsed.subpath);
|
|
170
|
+
if (!existsSync(srcDir) || !statSync(srcDir).isDirectory()) {
|
|
171
|
+
throw new Error(`Project-rules subpath "${parsed.subpath}" not found in ${repoUrl}. ` +
|
|
172
|
+
`Check ${PROJECT_RULES_SOURCE_ENV} and the repo layout.`);
|
|
173
|
+
}
|
|
174
|
+
return copyMarkdownRules(srcDir, workspaceProjectRulesDir(workspaceDir));
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
if (err instanceof Error && err.message.includes('subpath'))
|
|
178
|
+
throw err;
|
|
179
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
180
|
+
throw new Error(`Failed to fetch project rules from ${repoUrl}: ${msg}. ` +
|
|
181
|
+
`Check network access + that ${PROJECT_RULES_SOURCE_ENV} is correct.`, { cause: err });
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
rmSync(tmpClone, { recursive: true, force: true });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Copy every `*.md` file from `srcDir` to `destDir`, prepending the
|
|
189
|
+
* project-rule header. Creates `destDir` (mkdir -p). Returns copied basenames.
|
|
190
|
+
* Shared by both the local and github source paths.
|
|
191
|
+
*/
|
|
192
|
+
function copyMarkdownRules(srcDir, destDir) {
|
|
193
|
+
mkdirSync(destDir, { recursive: true });
|
|
194
|
+
const copied = [];
|
|
195
|
+
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
196
|
+
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
197
|
+
continue;
|
|
198
|
+
const content = readFileSync(join(srcDir, entry.name), 'utf-8');
|
|
199
|
+
const out = content.startsWith('<!--') ? content : PROJECT_RULE_HEADER + content;
|
|
200
|
+
writeFileSync(join(destDir, entry.name), out);
|
|
201
|
+
copied.push(entry.name);
|
|
202
|
+
}
|
|
203
|
+
return copied;
|
|
204
|
+
}
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// Seed (.example) template — written by `macf init`
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
/**
|
|
209
|
+
* Basename of the seed template `macf init` drops into the project-rules
|
|
210
|
+
* subdir. The `.example` suffix keeps it OUT of the live-rule set (only `*.md`
|
|
211
|
+
* are loaded as rules + managed by `fetchProjectRules`), so it self-documents
|
|
212
|
+
* the tier + gives the DR-026 §4 auditor a format to match without becoming a
|
|
213
|
+
* rule itself.
|
|
214
|
+
*/
|
|
215
|
+
export const PROJECT_RULE_SEED_NAME = 'EXAMPLE.project-rule.md.example';
|
|
216
|
+
/**
|
|
217
|
+
* Generic, deployment-agnostic seed content for the project-rules tier.
|
|
218
|
+
*
|
|
219
|
+
* CRITICAL: this ships via `macf init` to EVERY deployment, so it must NOT be
|
|
220
|
+
* macf-specific. A genomics deployment must not receive a macf `dev.mk` rule.
|
|
221
|
+
* The body demonstrates the FORMAT + the precedence contract with neutral
|
|
222
|
+
* placeholders only — never a concrete macf rule.
|
|
223
|
+
*/
|
|
224
|
+
export function projectRuleSeedContent() {
|
|
225
|
+
return [
|
|
226
|
+
'<!--',
|
|
227
|
+
' EXAMPLE project-tier rule (template, not active).',
|
|
228
|
+
'',
|
|
229
|
+
' This file ends in `.example`, so `macf` does NOT load it as a live rule',
|
|
230
|
+
' and does NOT overwrite it on `macf update` (only `*.md` files in this',
|
|
231
|
+
' directory are managed). It self-documents the project-rules tier and',
|
|
232
|
+
' gives the DR-026 §4 auditor a format to match.',
|
|
233
|
+
'-->',
|
|
234
|
+
'',
|
|
235
|
+
'# Project-tier rules (`.claude/rules/project/`)',
|
|
236
|
+
'',
|
|
237
|
+
'This directory holds **project-tier** coordination rules (DR-026 §3, tier 2):',
|
|
238
|
+
'per-deployment rules that ADD TO or SPECIALIZE the universal rules in the',
|
|
239
|
+
'parent `.claude/rules/*.md` directory.',
|
|
240
|
+
'',
|
|
241
|
+
'## Where these come from',
|
|
242
|
+
'',
|
|
243
|
+
'Project rules are distributed by `macf` from this deployment\'s configured',
|
|
244
|
+
'source, set via the `MACF_PROJECT_RULES_SOURCE` operator config in',
|
|
245
|
+
'`.claude/.macf/env.project-rules`. The source is one of:',
|
|
246
|
+
'',
|
|
247
|
+
'- `<owner>/<repo>//<path>` — a GitHub repo + subdir',
|
|
248
|
+
' (e.g. `your-org/your-coordination-repo//project-rules`)',
|
|
249
|
+
'- a local directory path — for single-host deployments',
|
|
250
|
+
'',
|
|
251
|
+
'When `MACF_PROJECT_RULES_SOURCE` is unset, this tier is empty (it is',
|
|
252
|
+
'optional). Authored rules live at the SOURCE, not in this workspace copy —',
|
|
253
|
+
'edit them there, then run `macf update` here.',
|
|
254
|
+
'',
|
|
255
|
+
'## Precedence (the one hard contract)',
|
|
256
|
+
'',
|
|
257
|
+
'Project rules may **add to / specialize** the universal protocol but must',
|
|
258
|
+
'**never contradict or weaken** its protected invariants (e.g. reporter-owns',
|
|
259
|
+
'closure, the identity↔attribution guarantee, the no-self-merge / LGTM gate).',
|
|
260
|
+
'See `design/project-tier-rules.md` for the full precedence model.',
|
|
261
|
+
'',
|
|
262
|
+
'## Format to follow (replace this example)',
|
|
263
|
+
'',
|
|
264
|
+
'A project rule is a Markdown file. Name it `*.md` (this `.example` is a',
|
|
265
|
+
'template, not a rule). Suggested shape — adapt to your deployment:',
|
|
266
|
+
'',
|
|
267
|
+
'```markdown',
|
|
268
|
+
'# <Short rule title>',
|
|
269
|
+
'',
|
|
270
|
+
'## Applies to',
|
|
271
|
+
'',
|
|
272
|
+
'<Which agents / repos / situations this project rule governs.>',
|
|
273
|
+
'',
|
|
274
|
+
'## Rule',
|
|
275
|
+
'',
|
|
276
|
+
'<The specialization. State what it ADDS to the universal protocol — never',
|
|
277
|
+
'what it weakens. If it looks like it relaxes a universal invariant, it is',
|
|
278
|
+
'wrong by construction (DR-026 §4).>',
|
|
279
|
+
'',
|
|
280
|
+
'## Rationale',
|
|
281
|
+
'',
|
|
282
|
+
'<Why this deployment needs it. Link the incident / pattern it codifies.>',
|
|
283
|
+
'```',
|
|
284
|
+
'',
|
|
285
|
+
].join('\n');
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Seed the project-rules subdir with the generic `.example` template
|
|
289
|
+
* (`macf init`). Creates `.claude/rules/project/` (mkdir -p) and writes the
|
|
290
|
+
* `.example` file. Idempotent: overwrites the `.example` with the current
|
|
291
|
+
* canonical template — it's a managed example, not operator state. Returns the
|
|
292
|
+
* seeded basename.
|
|
293
|
+
*
|
|
294
|
+
* Deliberately does NOT fetch from `MACF_PROJECT_RULES_SOURCE` — `macf init`
|
|
295
|
+
* only lays down the tier + its self-documenting seed; `macf update` (and
|
|
296
|
+
* `macf rules refresh`) pull the actual rules from the source. This keeps init
|
|
297
|
+
* generic (it ships to every deployment) and offline-safe.
|
|
298
|
+
*/
|
|
299
|
+
export function seedProjectRulesDir(workspaceDir) {
|
|
300
|
+
const destDir = workspaceProjectRulesDir(workspaceDir);
|
|
301
|
+
mkdirSync(destDir, { recursive: true });
|
|
302
|
+
writeFileSync(join(destDir, PROJECT_RULE_SEED_NAME), projectRuleSeedContent());
|
|
303
|
+
return PROJECT_RULE_SEED_NAME;
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=project-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-rules.js","sourceRoot":"","sources":["../../src/cli/project-rules.ts"],"names":[],"mappings":"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"}
|