@groundnuty/macf 0.2.0-rc.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.
Files changed (124) hide show
  1. package/dist/.build-info.json +4 -0
  2. package/dist/cli/build-info.d.ts +38 -0
  3. package/dist/cli/build-info.d.ts.map +1 -0
  4. package/dist/cli/build-info.js +119 -0
  5. package/dist/cli/build-info.js.map +1 -0
  6. package/dist/cli/claude-sh.d.ts +42 -0
  7. package/dist/cli/claude-sh.d.ts.map +1 -0
  8. package/dist/cli/claude-sh.js +247 -0
  9. package/dist/cli/claude-sh.js.map +1 -0
  10. package/dist/cli/commands/cd.d.ts +6 -0
  11. package/dist/cli/commands/cd.d.ts.map +1 -0
  12. package/dist/cli/commands/cd.js +17 -0
  13. package/dist/cli/commands/cd.js.map +1 -0
  14. package/dist/cli/commands/certs.d.ts +33 -0
  15. package/dist/cli/commands/certs.d.ts.map +1 -0
  16. package/dist/cli/commands/certs.js +233 -0
  17. package/dist/cli/commands/certs.js.map +1 -0
  18. package/dist/cli/commands/doctor.d.ts +91 -0
  19. package/dist/cli/commands/doctor.d.ts.map +1 -0
  20. package/dist/cli/commands/doctor.js +235 -0
  21. package/dist/cli/commands/doctor.js.map +1 -0
  22. package/dist/cli/commands/init.d.ts +37 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -0
  24. package/dist/cli/commands/init.js +279 -0
  25. package/dist/cli/commands/init.js.map +1 -0
  26. package/dist/cli/commands/list.d.ts +5 -0
  27. package/dist/cli/commands/list.d.ts.map +1 -0
  28. package/dist/cli/commands/list.js +21 -0
  29. package/dist/cli/commands/list.js.map +1 -0
  30. package/dist/cli/commands/migrate-ca-key.d.ts +36 -0
  31. package/dist/cli/commands/migrate-ca-key.d.ts.map +1 -0
  32. package/dist/cli/commands/migrate-ca-key.js +92 -0
  33. package/dist/cli/commands/migrate-ca-key.js.map +1 -0
  34. package/dist/cli/commands/peers.d.ts +8 -0
  35. package/dist/cli/commands/peers.d.ts.map +1 -0
  36. package/dist/cli/commands/peers.js +45 -0
  37. package/dist/cli/commands/peers.js.map +1 -0
  38. package/dist/cli/commands/repo-init.d.ts +43 -0
  39. package/dist/cli/commands/repo-init.d.ts.map +1 -0
  40. package/dist/cli/commands/repo-init.js +304 -0
  41. package/dist/cli/commands/repo-init.js.map +1 -0
  42. package/dist/cli/commands/rules-refresh.d.ts +14 -0
  43. package/dist/cli/commands/rules-refresh.d.ts.map +1 -0
  44. package/dist/cli/commands/rules-refresh.js +67 -0
  45. package/dist/cli/commands/rules-refresh.js.map +1 -0
  46. package/dist/cli/commands/self-update.d.ts +14 -0
  47. package/dist/cli/commands/self-update.d.ts.map +1 -0
  48. package/dist/cli/commands/self-update.js +112 -0
  49. package/dist/cli/commands/self-update.js.map +1 -0
  50. package/dist/cli/commands/status.d.ts +9 -0
  51. package/dist/cli/commands/status.d.ts.map +1 -0
  52. package/dist/cli/commands/status.js +90 -0
  53. package/dist/cli/commands/status.js.map +1 -0
  54. package/dist/cli/commands/update.d.ts +25 -0
  55. package/dist/cli/commands/update.d.ts.map +1 -0
  56. package/dist/cli/commands/update.js +316 -0
  57. package/dist/cli/commands/update.js.map +1 -0
  58. package/dist/cli/config.d.ts +103 -0
  59. package/dist/cli/config.d.ts.map +1 -0
  60. package/dist/cli/config.js +224 -0
  61. package/dist/cli/config.js.map +1 -0
  62. package/dist/cli/index.d.ts +3 -0
  63. package/dist/cli/index.d.ts.map +1 -0
  64. package/dist/cli/index.js +245 -0
  65. package/dist/cli/index.js.map +1 -0
  66. package/dist/cli/plugin-fetcher.d.ts +20 -0
  67. package/dist/cli/plugin-fetcher.d.ts.map +1 -0
  68. package/dist/cli/plugin-fetcher.js +83 -0
  69. package/dist/cli/plugin-fetcher.js.map +1 -0
  70. package/dist/cli/prompt.d.ts +17 -0
  71. package/dist/cli/prompt.d.ts.map +1 -0
  72. package/dist/cli/prompt.js +109 -0
  73. package/dist/cli/prompt.js.map +1 -0
  74. package/dist/cli/registry-helper.d.ts +11 -0
  75. package/dist/cli/registry-helper.d.ts.map +1 -0
  76. package/dist/cli/registry-helper.js +18 -0
  77. package/dist/cli/registry-helper.js.map +1 -0
  78. package/dist/cli/rules.d.ts +39 -0
  79. package/dist/cli/rules.d.ts.map +1 -0
  80. package/dist/cli/rules.js +112 -0
  81. package/dist/cli/rules.js.map +1 -0
  82. package/dist/cli/settings-writer.d.ts +97 -0
  83. package/dist/cli/settings-writer.d.ts.map +1 -0
  84. package/dist/cli/settings-writer.js +270 -0
  85. package/dist/cli/settings-writer.js.map +1 -0
  86. package/dist/cli/version-resolver.d.ts +73 -0
  87. package/dist/cli/version-resolver.d.ts.map +1 -0
  88. package/dist/cli/version-resolver.js +238 -0
  89. package/dist/cli/version-resolver.js.map +1 -0
  90. package/dist/index.d.ts +19 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +22 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/plugin/bin/macf-plugin-cli.d.ts +13 -0
  95. package/dist/plugin/bin/macf-plugin-cli.d.ts.map +1 -0
  96. package/dist/plugin/bin/macf-plugin-cli.js +127 -0
  97. package/dist/plugin/bin/macf-plugin-cli.js.map +1 -0
  98. package/dist/plugin/lib/format.d.ts +40 -0
  99. package/dist/plugin/lib/format.d.ts.map +1 -0
  100. package/dist/plugin/lib/format.js +137 -0
  101. package/dist/plugin/lib/format.js.map +1 -0
  102. package/dist/plugin/lib/health.d.ts +2 -0
  103. package/dist/plugin/lib/health.d.ts.map +1 -0
  104. package/dist/plugin/lib/health.js +6 -0
  105. package/dist/plugin/lib/health.js.map +1 -0
  106. package/dist/plugin/lib/index.d.ts +7 -0
  107. package/dist/plugin/lib/index.d.ts.map +1 -0
  108. package/dist/plugin/lib/index.js +5 -0
  109. package/dist/plugin/lib/index.js.map +1 -0
  110. package/dist/plugin/lib/registry.d.ts +18 -0
  111. package/dist/plugin/lib/registry.d.ts.map +1 -0
  112. package/dist/plugin/lib/registry.js +17 -0
  113. package/dist/plugin/lib/registry.js.map +1 -0
  114. package/dist/plugin/lib/work.d.ts +13 -0
  115. package/dist/plugin/lib/work.d.ts.map +1 -0
  116. package/dist/plugin/lib/work.js +27 -0
  117. package/dist/plugin/lib/work.js.map +1 -0
  118. package/package.json +43 -0
  119. package/plugin/rules/coordination.md +224 -0
  120. package/scripts/check-gh-token.sh +102 -0
  121. package/scripts/macf-gh-token.sh +130 -0
  122. package/scripts/macf-whoami.sh +51 -0
  123. package/scripts/tmux-send-to-claude.sh +51 -0
  124. package/scripts/write-build-info.mjs +48 -0
@@ -0,0 +1,304 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { generateToken } from '@groundnuty/macf-core';
5
+ const STATUS_LABELS = [
6
+ { name: 'in-progress', color: 'fbca04', description: 'Actively being worked on' },
7
+ { name: 'in-review', color: '0e8a16', description: 'PR created, awaiting review' },
8
+ { name: 'blocked', color: 'e11d48', description: 'Needs help or input' },
9
+ { name: 'agent-offline', color: 'b60205', description: 'Agent VM unreachable' },
10
+ ];
11
+ const AGENT_LABEL_COLOR = '1d76db';
12
+ function ensureDir(filePath) {
13
+ const dir = dirname(filePath);
14
+ if (!existsSync(dir))
15
+ mkdirSync(dir, { recursive: true });
16
+ }
17
+ /**
18
+ * Detect owner/repo from git remote. Uses execFileSync (no shell injection).
19
+ */
20
+ function detectRepoFromGit(cwd) {
21
+ try {
22
+ const remote = execFileSync('git', ['remote', 'get-url', 'origin'], {
23
+ cwd,
24
+ encoding: 'utf-8',
25
+ stdio: ['pipe', 'pipe', 'pipe'],
26
+ }).trim();
27
+ const match = /github\.com[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?$/.exec(remote);
28
+ if (match)
29
+ return `${match[1]}/${match[2]}`;
30
+ return null;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ function validateVersion(version) {
37
+ const validPatterns = [/^v\d+$/, /^v\d+\.\d+$/, /^v\d+\.\d+\.\d+$/];
38
+ const isTag = validPatterns.some(p => p.test(version));
39
+ if (!isTag && version !== 'main') {
40
+ process.stderr.write(`Warning: "${version}" is not a tag ref. Production repos should pin to a tag (v1, v1.0, or v1.0.0).\n`);
41
+ }
42
+ }
43
+ export function generateWorkflow(actionsVersion) {
44
+ return [
45
+ 'name: Agent Router',
46
+ 'on:',
47
+ ' issues:',
48
+ ' types: [labeled, closed]',
49
+ ' issue_comment:',
50
+ ' types: [created]',
51
+ ' pull_request:',
52
+ ' types: [opened]',
53
+ ' pull_request_review:',
54
+ ' types: [submitted]',
55
+ 'jobs:',
56
+ ' route:',
57
+ ` uses: groundnuty/macf-actions/.github/workflows/agent-router.yml@${actionsVersion}`,
58
+ ' secrets: inherit',
59
+ '',
60
+ ].join('\n');
61
+ }
62
+ const DEFAULT_LABEL_TO_STATUS = {
63
+ 'in-progress': 'In Progress',
64
+ 'in-review': 'In Review',
65
+ 'blocked': 'Blocked',
66
+ };
67
+ function makeAgentEntry(agent, useWindows, sessionName, defaults) {
68
+ const sshUser = 'ubuntu';
69
+ const entry = {
70
+ app_name: agent,
71
+ host: '<agent-host-ip>',
72
+ tmux_session: useWindows ? sessionName : agent,
73
+ ssh_user: sshUser,
74
+ tmux_bin: 'tmux',
75
+ ssh_key_secret: 'AGENT_SSH_KEY',
76
+ };
77
+ if (useWindows)
78
+ entry.tmux_window = agent;
79
+ // Default workspace_dir = /home/<ssh_user>/repos/<owner>/<repo>. Covers
80
+ // the common case where agents are cloned into ~/repos/<owner>/<repo>
81
+ // on the host. Users override per-agent if their layout differs.
82
+ if (defaults) {
83
+ entry.workspace_dir = `/home/${sshUser}/repos/${defaults.owner}/${defaults.repo}`;
84
+ }
85
+ return entry;
86
+ }
87
+ export function generateAgentConfig(agents, sessionName, defaults) {
88
+ if (agents.length === 0) {
89
+ return JSON.stringify({
90
+ agents: {
91
+ '<agent-name>': {
92
+ app_name: '<github-app-name>',
93
+ host: '<agent-host-ip>',
94
+ tmux_session: '<tmux-session-name>',
95
+ ssh_user: 'ubuntu',
96
+ tmux_bin: 'tmux',
97
+ ssh_key_secret: 'AGENT_SSH_KEY',
98
+ workspace_dir: '/home/ubuntu/repos/<owner>/<repo>',
99
+ },
100
+ },
101
+ label_to_status: { ...DEFAULT_LABEL_TO_STATUS },
102
+ }, null, 2) + '\n';
103
+ }
104
+ const useWindows = !!sessionName && agents.length > 1;
105
+ const agentEntries = {};
106
+ for (const agent of agents) {
107
+ agentEntries[agent] = makeAgentEntry(agent, useWindows, sessionName, defaults);
108
+ }
109
+ return JSON.stringify({
110
+ agents: agentEntries,
111
+ label_to_status: { ...DEFAULT_LABEL_TO_STATUS },
112
+ }, null, 2) + '\n';
113
+ }
114
+ /**
115
+ * Merge-preserving regenerate for #76: update only tmux_session/tmux_window
116
+ * fields from user input, preserve app_name/host/ssh_key_secret/ssh_user
117
+ * /tmux_bin/unknown-fields, preserve top-level label_to_status and extras.
118
+ * Agents not in the --agents list are left alone.
119
+ */
120
+ export function patchAgentConfig(existingJson, agents, sessionName, defaults) {
121
+ let parsed;
122
+ try {
123
+ parsed = JSON.parse(existingJson);
124
+ }
125
+ catch {
126
+ throw new Error('Existing agent-config.json is not valid JSON; aborting rather than overwrite.');
127
+ }
128
+ if (!parsed.agents || typeof parsed.agents !== 'object') {
129
+ throw new Error('Existing agent-config.json has no `agents` object; aborting.');
130
+ }
131
+ const useWindows = !!sessionName && agents.length > 1;
132
+ const agentEntries = { ...parsed.agents };
133
+ for (const agent of agents) {
134
+ const existing = parsed.agents[agent];
135
+ if (!existing) {
136
+ agentEntries[agent] = makeAgentEntry(agent, useWindows, sessionName, defaults);
137
+ continue;
138
+ }
139
+ const patched = { ...existing };
140
+ patched.tmux_session = useWindows ? sessionName : agent;
141
+ if (useWindows) {
142
+ patched.tmux_window = agent;
143
+ }
144
+ else {
145
+ delete patched.tmux_window;
146
+ }
147
+ if (!patched.ssh_key_secret)
148
+ patched.ssh_key_secret = 'AGENT_SSH_KEY';
149
+ // Inject workspace_dir default for old entries that lack it, so
150
+ // existing configs self-upgrade to enable helper invocation without
151
+ // requiring a hand-edit. Users can customize afterwards.
152
+ if (!patched.workspace_dir && defaults) {
153
+ patched.workspace_dir = `/home/${patched.ssh_user || 'ubuntu'}/repos/${defaults.owner}/${defaults.repo}`;
154
+ }
155
+ agentEntries[agent] = patched;
156
+ }
157
+ const out = { ...parsed, agents: agentEntries };
158
+ if (!out.label_to_status) {
159
+ out.label_to_status = { ...DEFAULT_LABEL_TO_STATUS };
160
+ }
161
+ return JSON.stringify(out, null, 2) + '\n';
162
+ }
163
+ export async function createLabel(owner, repo, token, spec) {
164
+ const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/labels`, {
165
+ method: 'POST',
166
+ headers: {
167
+ 'Accept': 'application/vnd.github+json',
168
+ 'Authorization': `Bearer ${token}`,
169
+ 'X-GitHub-Api-Version': '2022-11-28',
170
+ 'Content-Type': 'application/json',
171
+ },
172
+ body: JSON.stringify({
173
+ name: spec.name,
174
+ color: spec.color,
175
+ description: spec.description,
176
+ }),
177
+ });
178
+ if (res.status === 201)
179
+ return 'created';
180
+ if (res.status === 422)
181
+ return 'exists';
182
+ return 'failed';
183
+ }
184
+ function writeFileSafe(path, content, force) {
185
+ if (existsSync(path) && !force) {
186
+ process.stderr.write(`Skipping existing file (use --force to overwrite): ${path}\n`);
187
+ return 'skipped';
188
+ }
189
+ ensureDir(path);
190
+ writeFileSync(path, content);
191
+ return 'created';
192
+ }
193
+ /**
194
+ * Bootstrap a repo for MACF routing.
195
+ */
196
+ export async function repoInit(projectDir, opts) {
197
+ const absDir = resolve(projectDir);
198
+ validateVersion(opts.actionsVersion);
199
+ const repo = opts.repo ?? detectRepoFromGit(absDir);
200
+ if (!repo) {
201
+ throw new Error('--repo required (or run from a git repo with a GitHub origin remote)');
202
+ }
203
+ const parts = repo.split('/');
204
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
205
+ throw new Error(`Invalid repo format: "${repo}". Expected "owner/repo".`);
206
+ }
207
+ const [owner, repoName] = parts;
208
+ const agentList = opts.agents ? opts.agents.split(',').map(s => s.trim()).filter(Boolean) : [];
209
+ const workflowPath = join(absDir, '.github', 'workflows', 'agent-router.yml');
210
+ const configPath = join(absDir, '.github', 'agent-config.json');
211
+ const workflowResult = writeFileSafe(workflowPath, generateWorkflow(opts.actionsVersion), opts.force);
212
+ // Agent-config handling: always merge-preserve when the file exists,
213
+ // regardless of --force (#82). Previously --force was required even to
214
+ // add new agents to an existing config; the "fresh template wins"
215
+ // semantic was a UX trap — users running `macf repo-init --agents foo`
216
+ // on an existing repo saw "Skipping existing file" and thought agents
217
+ // were scaffolded when nothing changed.
218
+ //
219
+ // --force now only controls the workflow file (agent-router.yml) — the
220
+ // workflow is regenerated from scratch (no fields to preserve), so the
221
+ // old "don't overwrite" guard still makes sense there.
222
+ //
223
+ // Patch is safe to call repeatedly: unchanged inputs produce the same
224
+ // output (idempotent), new agents are added, existing agent entries
225
+ // preserve app_name/host/ssh_key_secret/ssh_user/tmux_bin/workspace_dir,
226
+ // and top-level label_to_status + unknown keys pass through.
227
+ const entryDefaults = { owner: owner, repo: repoName };
228
+ let configResult;
229
+ if (existsSync(configPath)) {
230
+ const patched = patchAgentConfig(readFileSync(configPath, 'utf-8'), agentList, opts.sessionName, entryDefaults);
231
+ writeFileSync(configPath, patched);
232
+ configResult = 'updated';
233
+ }
234
+ else {
235
+ const fresh = generateAgentConfig(agentList, opts.sessionName, entryDefaults);
236
+ const writeRes = writeFileSafe(configPath, fresh, false);
237
+ configResult = writeRes; // 'created' (file didn't exist) is the expected path
238
+ }
239
+ const allLabels = [...STATUS_LABELS];
240
+ for (const agent of agentList) {
241
+ allLabels.push({
242
+ name: agent,
243
+ color: AGENT_LABEL_COLOR,
244
+ description: `Assigned to ${agent}[bot]`,
245
+ });
246
+ }
247
+ let token;
248
+ try {
249
+ token = await generateToken();
250
+ }
251
+ catch (err) {
252
+ process.stderr.write(`Warning: could not generate token (${err instanceof Error ? err.message : 'unknown'}). Skipping label creation.\n`);
253
+ printResults(workflowResult, configResult, [], [], []);
254
+ printNextSteps(configResult, agentList);
255
+ return;
256
+ }
257
+ const created = [];
258
+ const existed = [];
259
+ const failed = [];
260
+ for (const spec of allLabels) {
261
+ const result = await createLabel(owner, repoName, token, spec);
262
+ if (result === 'created')
263
+ created.push(spec.name);
264
+ else if (result === 'exists')
265
+ existed.push(spec.name);
266
+ else
267
+ failed.push(spec.name);
268
+ }
269
+ printResults(workflowResult, configResult, created, existed, failed);
270
+ printNextSteps(configResult, agentList);
271
+ }
272
+ function printResults(workflowResult, configResult, created, existed, failed) {
273
+ if (workflowResult === 'created')
274
+ console.log('✓ Created .github/workflows/agent-router.yml');
275
+ if (configResult === 'created')
276
+ console.log('✓ Created .github/agent-config.json');
277
+ if (configResult === 'updated')
278
+ console.log('✓ Patched .github/agent-config.json (preserving existing entries)');
279
+ if (created.length > 0)
280
+ console.log(`✓ Created labels: ${created.join(', ')}`);
281
+ if (existed.length > 0)
282
+ console.log(` Labels already exist: ${existed.join(', ')}`);
283
+ if (failed.length > 0)
284
+ console.error(`✗ Failed to create labels: ${failed.join(', ')}`);
285
+ }
286
+ function printNextSteps(configResult, agentList) {
287
+ console.log('\nNext steps:\n');
288
+ if (configResult === 'created' && agentList.length === 0) {
289
+ console.log(' 1. Edit .github/agent-config.json to set your agents\' hosts and tmux sessions');
290
+ }
291
+ else if (configResult === 'created') {
292
+ console.log(' 1. Edit .github/agent-config.json and replace <agent-host-ip> placeholders');
293
+ }
294
+ else if (configResult === 'updated') {
295
+ console.log(' 1. Review .github/agent-config.json — existing entries preserved, only tmux fields updated');
296
+ }
297
+ console.log(' 2. Set repo secrets (Settings → Secrets and variables → Actions):');
298
+ console.log(' - AGENT_SSH_KEY: SSH private key for connecting to agent hosts');
299
+ console.log(' - TS_OAUTH_CLIENT_ID: Tailscale OAuth client ID');
300
+ console.log(' - TS_OAUTH_SECRET: Tailscale OAuth secret');
301
+ console.log(' 3. Install your agent GitHub Apps on this repo');
302
+ console.log(' 4. Commit and push: git add .github/ && git commit -m "chore: bootstrap MACF routing"');
303
+ }
304
+ //# sourceMappingURL=repo-init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-init.js","sourceRoot":"","sources":["../../../src/cli/commands/repo-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAsBtD,MAAM,aAAa,GAAyB;IAC1C,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACjF,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAClF,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACxE,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE;CAChF,CAAC;AAEF,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAEnC,SAAS,SAAS,CAAC,QAAgB;IACjC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,GAAW;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE;YAClE,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,iDAAiD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7E,IAAI,KAAK;YAAE,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACtC,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,aAAa,EAAE,kBAAkB,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,aAAa,OAAO,mFAAmF,CACxG,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,cAAsB;IACrD,OAAO;QACL,oBAAoB;QACpB,KAAK;QACL,WAAW;QACX,8BAA8B;QAC9B,kBAAkB;QAClB,sBAAsB;QACtB,iBAAiB;QACjB,qBAAqB;QACrB,wBAAwB;QACxB,wBAAwB;QACxB,OAAO;QACP,UAAU;QACV,wEAAwE,cAAc,EAAE;QACxF,sBAAsB;QACtB,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAwCD,MAAM,uBAAuB,GAAqC;IAChE,aAAa,EAAE,aAAa;IAC5B,WAAW,EAAE,WAAW;IACxB,SAAS,EAAE,SAAS;CACrB,CAAC;AAQF,SAAS,cAAc,CACrB,KAAa,EACb,UAAmB,EACnB,WAA+B,EAC/B,QAA6B;IAE7B,MAAM,OAAO,GAAG,QAAQ,CAAC;IACzB,MAAM,KAAK,GAAqB;QAC9B,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,iBAAiB;QACvB,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC,WAAY,CAAC,CAAC,CAAC,KAAK;QAC/C,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,MAAM;QAChB,cAAc,EAAE,eAAe;KAChC,CAAC;IACF,IAAI,UAAU;QAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC;IAC1C,wEAAwE;IACxE,sEAAsE;IACtE,iEAAiE;IACjE,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,aAAa,GAAG,SAAS,OAAO,UAAU,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;IACpF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,MAAyB,EACzB,WAAoB,EACpB,QAA6B;IAE7B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,MAAM,EAAE;gBACN,cAAc,EAAE;oBACd,QAAQ,EAAE,mBAAmB;oBAC7B,IAAI,EAAE,iBAAiB;oBACvB,YAAY,EAAE,qBAAqB;oBACnC,QAAQ,EAAE,QAAQ;oBAClB,QAAQ,EAAE,MAAM;oBAChB,cAAc,EAAE,eAAe;oBAC/B,aAAa,EAAE,mCAAmC;iBACnD;aACF;YACD,eAAe,EAAE,EAAE,GAAG,uBAAuB,EAAE;SAChD,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,CAAC,WAAW,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAEtD,MAAM,YAAY,GAAqC,EAAE,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,YAAY,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,MAAM,EAAE,YAAY;QACpB,eAAe,EAAE,EAAE,GAAG,uBAAuB,EAAE;KAChD,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,YAAoB,EACpB,MAAyB,EACzB,WAAoB,EACpB,QAA6B;IAE7B,IAAI,MAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAoB,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;IACnG,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,CAAC,WAAW,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACtD,MAAM,YAAY,GAAqC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;IAE5E,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,YAAY,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;YAC/E,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAqB,EAAE,GAAG,QAAQ,EAAE,CAAC;QAClD,OAAO,CAAC,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,WAAY,CAAC,CAAC,CAAC,KAAK,CAAC;QACzD,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,OAAO,OAAO,CAAC,WAAW,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,cAAc;YAAE,OAAO,CAAC,cAAc,GAAG,eAAe,CAAC;QACtE,gEAAgE;QAChE,oEAAoE;QACpE,yDAAyD;QACzD,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,QAAQ,EAAE,CAAC;YACvC,OAAO,CAAC,aAAa,GAAG,SAAS,OAAO,CAAC,QAAQ,IAAI,QAAQ,UAAU,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC3G,CAAC;QACD,YAAY,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAChC,CAAC;IAED,MAAM,GAAG,GAAoB,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACjE,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC;QACzB,GAAG,CAAC,eAAe,GAAG,EAAE,GAAG,uBAAuB,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,IAAY,EACZ,KAAa,EACb,IAAe;IAEf,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,gCAAgC,KAAK,IAAI,IAAI,SAAS,EAAE;QAC9E,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,QAAQ,EAAE,6BAA6B;YACvC,eAAe,EAAE,UAAU,KAAK,EAAE;YAClC,sBAAsB,EAAE,YAAY;YACpC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IACxC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,OAAe,EAAE,KAAc;IAClE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sDAAsD,IAAI,IAAI,CAAC,CAAC;QACrF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,SAAS,CAAC,IAAI,CAAC,CAAC;IAChB,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,UAAkB,EAClB,IAAqB;IAErB,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEnC,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAErC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,2BAA2B,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC;IAEhC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/F,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAEhE,MAAM,cAAc,GAAG,aAAa,CAClC,YAAY,EACZ,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,EACrC,IAAI,CAAC,KAAK,CACX,CAAC;IAEF,qEAAqE;IACrE,uEAAuE;IACvE,kEAAkE;IAClE,uEAAuE;IACvE,sEAAsE;IACtE,wCAAwC;IACxC,EAAE;IACF,uEAAuE;IACvE,uEAAuE;IACvE,uDAAuD;IACvD,EAAE;IACF,sEAAsE;IACtE,oEAAoE;IACpE,yEAAyE;IACzE,6DAA6D;IAC7D,MAAM,aAAa,GAAuB,EAAE,KAAK,EAAE,KAAM,EAAE,IAAI,EAAE,QAAS,EAAE,CAAC;IAC7E,IAAI,YAA+C,CAAC;IACpD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,gBAAgB,CAC9B,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,SAAS,EACT,IAAI,CAAC,WAAW,EAChB,aAAa,CACd,CAAC;QACF,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACnC,YAAY,GAAG,SAAS,CAAC;IAC3B,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,GAAG,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QAC9E,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QACzD,YAAY,GAAG,QAAQ,CAAC,CAAE,qDAAqD;IACjF,CAAC;IAED,MAAM,SAAS,GAAgB,CAAC,GAAG,aAAa,CAAC,CAAC;IAClD,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,iBAAiB;YACxB,WAAW,EAAE,eAAe,KAAK,OAAO;SACzC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAa,CAAC;IAClB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,aAAa,EAAE,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,sCAAsC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,+BAA+B,CACpH,CAAC;QACF,YAAY,CAAC,cAAc,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACvD,cAAc,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC/D,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aAC7C,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACjD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,YAAY,CAAC,cAAc,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACrE,cAAc,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,YAAY,CACnB,cAAqC,EACrC,YAA+C,EAC/C,OAA0B,EAC1B,OAA0B,EAC1B,MAAyB;IAEzB,IAAI,cAAc,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;IAC9F,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnF,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC;IACjH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,2BAA2B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,KAAK,CAAC,8BAA8B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,SAAS,cAAc,CACrB,YAA+C,EAC/C,SAA4B;IAE5B,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/B,IAAI,YAAY,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,kFAAkF,CAAC,CAAC;IAClG,CAAC;SAAM,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,8EAA8E,CAAC,CAAC;IAC9F,CAAC;SAAM,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,8FAA8F,CAAC,CAAC;IAC9G,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,qEAAqE,CAAC,CAAC;IACnF,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACtE,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,yFAAyF,CAAC,CAAC;AACzG,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface RulesRefreshResult {
2
+ readonly rules: readonly string[];
3
+ readonly scripts: readonly string[];
4
+ readonly hookInstalled: boolean;
5
+ }
6
+ /**
7
+ * Copy canonical rules + scripts into <targetDir>/.claude/. Target must
8
+ * exist and be a directory. Returns copied filenames for caller logging.
9
+ *
10
+ * Unlike `macf update`, this does not read `.macf/macf-agent.json` — it
11
+ * runs against any Claude Code workspace, MACF-init'd or not.
12
+ */
13
+ export declare function rulesRefresh(targetDir: string): RulesRefreshResult;
14
+ //# sourceMappingURL=rules-refresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules-refresh.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/rules-refresh.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,CAyClE"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * macf rules refresh: distribute canonical coordination rules + helper
3
+ * scripts to a workspace, without requiring full `macf init`.
4
+ *
5
+ * `macf update` refreshes these assets too, but only for workspaces that
6
+ * already have a `.macf/macf-agent.json`. Several Claude Code workspaces
7
+ * coordinate with MACF agents but don't (or can't) run full `macf init`:
8
+ *
9
+ * - groundnuty/macf — the framework repo where code-agent lives; its
10
+ * .claude/ is hand-curated and predates `macf init`.
11
+ * - groundnuty/macf-science-agent — same situation for science-agent.
12
+ * - Any workspace operated by a bot that isn't a MACF-registered agent
13
+ * but still wants the canonical coordination rules (escalation,
14
+ * mergeStateStatus interpretation, @mention routing, etc.).
15
+ *
16
+ * For those workspaces, `macf rules refresh --dir <path>` copies the same
17
+ * canonical files that `macf init` / `macf update` would copy, with no
18
+ * dependence on App credentials, registry, certs, or pin state.
19
+ */
20
+ import { existsSync, statSync } from 'node:fs';
21
+ import { copyCanonicalRules, copyCanonicalScripts } from '../rules.js';
22
+ import { installGhTokenHook, installPluginSkillPermissions, installSandboxFdAllowRead } from '../settings-writer.js';
23
+ /**
24
+ * Copy canonical rules + scripts into <targetDir>/.claude/. Target must
25
+ * exist and be a directory. Returns copied filenames for caller logging.
26
+ *
27
+ * Unlike `macf update`, this does not read `.macf/macf-agent.json` — it
28
+ * runs against any Claude Code workspace, MACF-init'd or not.
29
+ */
30
+ export function rulesRefresh(targetDir) {
31
+ if (!existsSync(targetDir)) {
32
+ throw new Error(`Target directory does not exist: ${targetDir}`);
33
+ }
34
+ if (!statSync(targetDir).isDirectory()) {
35
+ throw new Error(`Target is not a directory: ${targetDir}`);
36
+ }
37
+ const rules = copyCanonicalRules(targetDir);
38
+ const scripts = copyCanonicalScripts(targetDir);
39
+ // Refresh the attribution-trap PreToolUse hook entry (merge-preserving,
40
+ // per #140). Keeps non-init'd workspaces (the macf repo itself, CV,
41
+ // etc.) in sync with the same structural guard as macf-init'd agents.
42
+ installGhTokenHook(targetDir);
43
+ // Pre-approve macf-agent plugin skills so SessionStart auto-pickup
44
+ // + /macf-status / /macf-issues don't hit interactive approval
45
+ // dialogs. See macf#189 sub-item 2.
46
+ installPluginSkillPermissions(targetDir);
47
+ // Install /proc/self/fd/** in sandbox.filesystem.allowRead so
48
+ // every Bash tool call stops failing with permission-denied on
49
+ // the harness fd. macf#200.
50
+ installSandboxFdAllowRead(targetDir);
51
+ if (rules.length > 0) {
52
+ console.log(`Refreshed ${rules.length} canonical rule file(s) in .claude/rules/:`);
53
+ for (const name of rules)
54
+ console.log(` ${name}`);
55
+ }
56
+ else {
57
+ console.log('No canonical rule files found in CLI package (nothing to copy).');
58
+ }
59
+ if (scripts.length > 0) {
60
+ console.log(`Refreshed ${scripts.length} helper script(s) in .claude/scripts/:`);
61
+ for (const name of scripts)
62
+ console.log(` ${name}`);
63
+ }
64
+ console.log('Refreshed gh-token guard hook in .claude/settings.json');
65
+ return { rules, scripts, hookInstalled: true };
66
+ }
67
+ //# sourceMappingURL=rules-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules-refresh.js","sourceRoot":"","sources":["../../../src/cli/commands/rules-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAQrH;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,oCAAoC,SAAS,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,8BAA8B,SAAS,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAEhD,wEAAwE;IACxE,oEAAoE;IACpE,sEAAsE;IACtE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAE9B,mEAAmE;IACnE,+DAA+D;IAC/D,oCAAoC;IACpC,6BAA6B,CAAC,SAAS,CAAC,CAAC;IAEzC,8DAA8D;IAC9D,+DAA+D;IAC/D,4BAA4B;IAC5B,yBAAyB,CAAC,SAAS,CAAC,CAAC;IAErC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,CAAC,MAAM,4CAA4C,CAAC,CAAC;QACnF,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;IACjF,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,CAAC,MAAM,wCAAwC,CAAC,CAAC;QACjF,KAAK,MAAM,IAAI,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IAEtE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;AACjD,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface SelfUpdateResult {
2
+ readonly updated: boolean;
3
+ readonly previousCommit: string;
4
+ readonly newCommit: string;
5
+ }
6
+ /**
7
+ * Run the self-update sequence in `sourceRepoDir` (typically the
8
+ * result of findCliPackageRoot()).
9
+ *
10
+ * Set MACF_SELF_UPDATE_SKIP_BUILD=1 to skip the `npm run build` step
11
+ * (used by unit tests that don't want to exercise npm).
12
+ */
13
+ export declare function selfUpdate(sourceRepoDir: string): SelfUpdateResult;
14
+ //# sourceMappingURL=self-update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-update.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/self-update.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAUD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,gBAAgB,CA4FlE"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * macf self-update — pulls main + rebuilds the CLI's own dist/ (#144).
3
+ *
4
+ * Solves the stale-dist recurrence where operators `npm link`-ed to the
5
+ * macf source repo hit silent no-op behavior after a CLI-behavior PR
6
+ * merged: the installed binary points at an older `dist/cli/index.js`
7
+ * because no one ran `npm run build`.
8
+ *
9
+ * This command fetches + ff-merges origin/main into the local source
10
+ * checkout, then runs `npm run build` to refresh `dist/`. No-op when
11
+ * already up-to-date. Refuses to run on a dirty tree or divergent
12
+ * local commits — operator cleans up by hand; we don't guess.
13
+ *
14
+ * Bootstrap limitation: this command only helps CLI versions at or
15
+ * after #144. Pre-#144 upgrades were silent; existing workspaces pick
16
+ * up detection on first rebuild after this PR lands.
17
+ */
18
+ import { execFileSync } from 'node:child_process';
19
+ import { existsSync } from 'node:fs';
20
+ import { join } from 'node:path';
21
+ function git(cwd, ...args) {
22
+ return execFileSync('git', args, {
23
+ cwd,
24
+ encoding: 'utf-8',
25
+ stdio: ['ignore', 'pipe', 'pipe'],
26
+ }).trim();
27
+ }
28
+ /**
29
+ * Run the self-update sequence in `sourceRepoDir` (typically the
30
+ * result of findCliPackageRoot()).
31
+ *
32
+ * Set MACF_SELF_UPDATE_SKIP_BUILD=1 to skip the `npm run build` step
33
+ * (used by unit tests that don't want to exercise npm).
34
+ */
35
+ export function selfUpdate(sourceRepoDir) {
36
+ if (!existsSync(join(sourceRepoDir, '.git'))) {
37
+ throw new Error(`${sourceRepoDir} is not a git-cloned install (no .git/ directory). ` +
38
+ `self-update only works for npm-link dev installs, not tarball extracts. ` +
39
+ `See the bootstrap-limitation note in #144.`);
40
+ }
41
+ // Refuse dirty trees — operator may have in-flight work we'd lose.
42
+ // Clamp the status output to the first 20 lines so a pathological
43
+ // dirty state (e.g. `rm -rf node_modules/` before the rebuild) doesn't
44
+ // produce a wall of text in the error. 20 lines is enough context for
45
+ // a typical few-modified-files case; beyond that the operator needs
46
+ // to run `git status` themselves anyway. Per science-agent's #144
47
+ // review non-blocker.
48
+ const status = git(sourceRepoDir, 'status', '--porcelain');
49
+ if (status !== '') {
50
+ const lines = status.split('\n');
51
+ const shown = lines.slice(0, 20).join('\n');
52
+ const truncation = lines.length > 20
53
+ ? `\n... (${lines.length - 20} more; run \`git status\` to see them all)`
54
+ : '';
55
+ throw new Error(`working tree is dirty in ${sourceRepoDir}:\n${shown}${truncation}\n` +
56
+ `self-update refuses to overwrite uncommitted changes. ` +
57
+ `Commit or stash first, then re-run.`);
58
+ }
59
+ const previousCommit = git(sourceRepoDir, 'rev-parse', 'HEAD');
60
+ // Fetch origin/main (don't merge yet — we want to decide based on
61
+ // divergence first).
62
+ git(sourceRepoDir, 'fetch', 'origin', 'main');
63
+ const remoteCommit = git(sourceRepoDir, 'rev-parse', 'origin/main');
64
+ if (previousCommit === remoteCommit) {
65
+ console.log(`Already up-to-date at ${previousCommit.slice(0, 7)} — no update needed.`);
66
+ return { updated: false, previousCommit, newCommit: previousCommit };
67
+ }
68
+ // Check for divergence: if local HEAD isn't an ancestor of remote,
69
+ // ff-only would fail — surface a clear error instead of letting git
70
+ // spit its usual "non-fast-forward" message.
71
+ try {
72
+ git(sourceRepoDir, 'merge-base', '--is-ancestor', previousCommit, remoteCommit);
73
+ }
74
+ catch {
75
+ throw new Error(`local HEAD has diverged from origin/main (non-fast-forward, ff-only would fail). ` +
76
+ `Rebase or reset manually, then re-run self-update. Local: ${previousCommit.slice(0, 7)}, ` +
77
+ `remote: ${remoteCommit.slice(0, 7)}.`);
78
+ }
79
+ console.log(`Fast-forwarding ${previousCommit.slice(0, 7)} → ${remoteCommit.slice(0, 7)}...`);
80
+ git(sourceRepoDir, 'merge', '--ff-only', 'origin/main');
81
+ // Only reinstall deps when package-lock.json actually changed between
82
+ // commits. `npm ci` is load-bearing when deps move (nukes node_modules
83
+ // and reinstalls from lockfile — correctness-critical for a rebuild)
84
+ // but on the common case where a PR touches only `src/`, it's
85
+ // minutes of wasted cold-cache time. Caught in the post-#140 audit
86
+ // pass, 2026-04-20. `git diff --quiet` exits 0 on no-diff, non-zero
87
+ // on any diff — exactly the signal we want.
88
+ let lockChanged = false;
89
+ try {
90
+ execFileSync('git', ['diff', '--quiet', previousCommit, remoteCommit, '--', 'package-lock.json'], { cwd: sourceRepoDir, stdio: 'ignore' });
91
+ }
92
+ catch {
93
+ lockChanged = true;
94
+ }
95
+ if (lockChanged) {
96
+ console.log('package-lock.json changed; will run `npm ci` to refresh node_modules.');
97
+ }
98
+ else {
99
+ console.log('package-lock.json unchanged; skipping `npm ci` (deps already up-to-date).');
100
+ }
101
+ if (process.env['MACF_SELF_UPDATE_SKIP_BUILD'] !== '1') {
102
+ if (lockChanged) {
103
+ execFileSync('npm', ['ci'], { cwd: sourceRepoDir, stdio: 'inherit' });
104
+ }
105
+ console.log('Running `npm run build` to refresh dist/...');
106
+ execFileSync('npm', ['run', 'build'], { cwd: sourceRepoDir, stdio: 'inherit' });
107
+ }
108
+ const newCommit = git(sourceRepoDir, 'rev-parse', 'HEAD');
109
+ console.log(`CLI refreshed — now at ${newCommit.slice(0, 7)}.`);
110
+ return { updated: true, previousCommit, newCommit };
111
+ }
112
+ //# sourceMappingURL=self-update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-update.js","sourceRoot":"","sources":["../../../src/cli/commands/self-update.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Show status of registered agents by pinging their /health endpoints.
3
+ *
4
+ * If projectDir is given, uses that project's config for registry access
5
+ * (scopes the view to that project's peers). Otherwise loads all agents
6
+ * from the global index and uses the first one's config.
7
+ */
8
+ export declare function showStatus(projectDir?: string): Promise<void>;
9
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkFnE"}