agemon 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2388 @@
1
+ // src/cli/index.ts
2
+ import { fileURLToPath } from "url";
3
+ import { Command, CommanderError } from "commander";
4
+
5
+ // src/platform/detect.ts
6
+ import { constants } from "fs";
7
+ import { access, readFile } from "fs/promises";
8
+ import { delimiter, join } from "path";
9
+ var REQUIRED_BINARIES = [
10
+ "python3",
11
+ "pip",
12
+ "pipx",
13
+ "uv",
14
+ "crg"
15
+ ];
16
+ var UnsupportedPlatformError = class extends Error {
17
+ constructor(osId, osReleasePath) {
18
+ super(
19
+ `agemon v1 supports Ubuntu only. Detected '${osId}' in ${osReleasePath}. Windows and macOS support are planned for v2.`
20
+ );
21
+ }
22
+ };
23
+ function normalizeOsReleaseValue(value) {
24
+ const trimmedValue = value.trim();
25
+ if (trimmedValue.startsWith('"') && trimmedValue.endsWith('"') || trimmedValue.startsWith("'") && trimmedValue.endsWith("'")) {
26
+ return trimmedValue.slice(1, -1);
27
+ }
28
+ return trimmedValue;
29
+ }
30
+ function parseOsReleaseId(osReleaseContents) {
31
+ const lines = osReleaseContents.split(/\r?\n/);
32
+ for (const line of lines) {
33
+ const trimmedLine = line.trim();
34
+ if (trimmedLine.length === 0 || trimmedLine.startsWith("#")) {
35
+ continue;
36
+ }
37
+ const separatorIndex = trimmedLine.indexOf("=");
38
+ if (separatorIndex === -1) {
39
+ continue;
40
+ }
41
+ const key = trimmedLine.slice(0, separatorIndex).trim();
42
+ if (key !== "ID") {
43
+ continue;
44
+ }
45
+ return normalizeOsReleaseValue(
46
+ trimmedLine.slice(separatorIndex + 1)
47
+ ).toLowerCase();
48
+ }
49
+ return "unknown";
50
+ }
51
+ async function findBinaryPath(binaryName, pathEnv) {
52
+ for (const pathEntry of pathEnv.split(delimiter)) {
53
+ if (!pathEntry) {
54
+ continue;
55
+ }
56
+ const binaryPath = join(pathEntry, binaryName);
57
+ try {
58
+ await access(binaryPath, constants.X_OK);
59
+ return binaryPath;
60
+ } catch {
61
+ }
62
+ }
63
+ return void 0;
64
+ }
65
+ async function detectBinaries(pathEnv) {
66
+ const binaryResults = await Promise.all(
67
+ REQUIRED_BINARIES.map(async (binaryName) => {
68
+ const binaryPath = await findBinaryPath(binaryName, pathEnv);
69
+ return {
70
+ name: binaryName,
71
+ present: binaryPath !== void 0,
72
+ path: binaryPath
73
+ };
74
+ })
75
+ );
76
+ return Object.fromEntries(
77
+ binaryResults.map((binaryResult) => [binaryResult.name, binaryResult])
78
+ );
79
+ }
80
+ function resolveOsReleasePath(input) {
81
+ return input.osReleasePath ?? process.env.AGEMON_OS_RELEASE_PATH ?? "/etc/os-release";
82
+ }
83
+ async function detectPlatform(input = {}) {
84
+ const osReleasePath = resolveOsReleasePath(input);
85
+ const osReleaseContents = await readFile(osReleasePath, "utf8");
86
+ const osId = parseOsReleaseId(osReleaseContents);
87
+ if (osId !== "ubuntu") {
88
+ throw new UnsupportedPlatformError(osId, osReleasePath);
89
+ }
90
+ return {
91
+ os: "ubuntu",
92
+ osReleasePath,
93
+ binaries: await detectBinaries(input.pathEnv ?? process.env.PATH ?? "")
94
+ };
95
+ }
96
+
97
+ // src/platform/service-manager/fake.ts
98
+ import { mkdir, readFile as readFile2, rm, writeFile } from "fs/promises";
99
+ import { homedir } from "os";
100
+ import { join as join2 } from "path";
101
+ function resolveHomeDir(homeDir) {
102
+ return homeDir ?? process.env.HOME ?? homedir();
103
+ }
104
+ function resolveUser(user) {
105
+ return user ?? process.env.USER ?? "unknown-user";
106
+ }
107
+ function markerPath(homeDir, unitName) {
108
+ return join2(homeDir, ".agemon", "fake-services", `${unitName}.json`);
109
+ }
110
+ async function readMarker(homeDir, unitName) {
111
+ try {
112
+ const markerContents = await readFile2(
113
+ markerPath(homeDir, unitName),
114
+ "utf8"
115
+ );
116
+ return JSON.parse(markerContents);
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+ function createFakeServiceManager(input) {
122
+ const homeDir = resolveHomeDir(input.homeDir);
123
+ const user = resolveUser(input.user);
124
+ const isActive = async (unitName) => {
125
+ const marker = await readMarker(homeDir, unitName);
126
+ return { active: marker?.active === true };
127
+ };
128
+ const registerAutostart = async (registration) => {
129
+ const destinationPath = markerPath(homeDir, registration.unitName);
130
+ await mkdir(join2(homeDir, ".agemon", "fake-services"), { recursive: true });
131
+ const state = {
132
+ unitName: registration.unitName,
133
+ unitContents: registration.unitContents,
134
+ user,
135
+ active: true,
136
+ lingerEnabledByAgemon: true
137
+ };
138
+ await writeFile(
139
+ destinationPath,
140
+ `${JSON.stringify(state, null, 2)}
141
+ `,
142
+ "utf8"
143
+ );
144
+ return {
145
+ unitPath: destinationPath,
146
+ lingerEnabledByAgemon: state.lingerEnabledByAgemon
147
+ };
148
+ };
149
+ const unregisterAutostart = async (registration) => {
150
+ await rm(markerPath(homeDir, registration.unitName), { force: true });
151
+ };
152
+ return {
153
+ isActive,
154
+ registerAutostart,
155
+ unregisterAutostart
156
+ };
157
+ }
158
+
159
+ // src/platform/service-manager/linux.ts
160
+ import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
161
+ import { homedir as homedir2 } from "os";
162
+ import { dirname, join as join3 } from "path";
163
+ var SYSTEM_COMMAND_TIMEOUT_MS = 2e4;
164
+ function resolveHomeDir2(homeDir) {
165
+ return homeDir ?? process.env.HOME ?? homedir2();
166
+ }
167
+ function resolveUser2(user) {
168
+ const resolvedUser = user ?? process.env.USER;
169
+ if (!resolvedUser) {
170
+ try {
171
+ const fallbackUser = process.env.LOGNAME ?? process.env.USERNAME;
172
+ if (fallbackUser) {
173
+ return fallbackUser;
174
+ }
175
+ } catch {
176
+ }
177
+ throw new Error(
178
+ "Unable to resolve current user for loginctl commands. Set USER, LOGNAME, or pass an explicit user."
179
+ );
180
+ }
181
+ return resolvedUser;
182
+ }
183
+ function resolveUnitPath(homeDir, unitName) {
184
+ return join3(homeDir, ".config", "systemd", "user", unitName);
185
+ }
186
+ function isMissingUnitError(stderr) {
187
+ const normalized = stderr.toLowerCase();
188
+ return normalized.includes("not loaded") || normalized.includes("no such file") || normalized.includes("not found");
189
+ }
190
+ async function assertSucceeded(run, command, args, failureMessage) {
191
+ const result = await run(command, args, {
192
+ timeoutMs: SYSTEM_COMMAND_TIMEOUT_MS
193
+ });
194
+ if (result.code !== 0) {
195
+ throw new Error(result.stderr || failureMessage);
196
+ }
197
+ return { stdout: result.stdout, stderr: result.stderr };
198
+ }
199
+ function createLinuxServiceManager(input) {
200
+ const homeDir = resolveHomeDir2(input.homeDir);
201
+ const user = resolveUser2(input.user);
202
+ const isActive = async (unitName) => {
203
+ const result = await input.run(
204
+ "systemctl",
205
+ ["--user", "is-active", unitName],
206
+ {
207
+ timeoutMs: SYSTEM_COMMAND_TIMEOUT_MS
208
+ }
209
+ );
210
+ return { active: result.code === 0 };
211
+ };
212
+ const registerAutostart = async (registration) => {
213
+ const unitPath = resolveUnitPath(homeDir, registration.unitName);
214
+ await mkdir2(dirname(unitPath), { recursive: true });
215
+ await writeFile2(unitPath, registration.unitContents, "utf8");
216
+ const lingerStatus = await assertSucceeded(
217
+ input.run,
218
+ "loginctl",
219
+ ["show-user", user, "--property=Linger", "--value"],
220
+ "Unable to read linger status."
221
+ );
222
+ const lingerWasOn = lingerStatus.stdout.trim() === "yes";
223
+ if (!lingerWasOn) {
224
+ await assertSucceeded(
225
+ input.run,
226
+ "loginctl",
227
+ ["enable-linger", user],
228
+ "Unable to enable linger for current user."
229
+ );
230
+ }
231
+ await assertSucceeded(
232
+ input.run,
233
+ "systemctl",
234
+ ["--user", "daemon-reload"],
235
+ "Unable to reload systemd user units."
236
+ );
237
+ await assertSucceeded(
238
+ input.run,
239
+ "systemctl",
240
+ ["--user", "enable", "--now", registration.unitName],
241
+ "Unable to enable/start the daemon unit."
242
+ );
243
+ return {
244
+ unitPath,
245
+ lingerEnabledByAgemon: !lingerWasOn
246
+ };
247
+ };
248
+ const unregisterAutostart = async (registration) => {
249
+ const disableResult = await input.run(
250
+ "systemctl",
251
+ ["--user", "disable", "--now", registration.unitName],
252
+ {
253
+ timeoutMs: SYSTEM_COMMAND_TIMEOUT_MS
254
+ }
255
+ );
256
+ if (disableResult.code !== 0 && !isMissingUnitError(disableResult.stderr || "")) {
257
+ throw new Error(
258
+ disableResult.stderr || "Unable to disable/stop the daemon unit during uninstall."
259
+ );
260
+ }
261
+ const unitPath = resolveUnitPath(homeDir, registration.unitName);
262
+ await rm2(unitPath, { force: true });
263
+ await assertSucceeded(
264
+ input.run,
265
+ "systemctl",
266
+ ["--user", "daemon-reload"],
267
+ "Unable to reload systemd user units."
268
+ );
269
+ if (registration.disableLinger) {
270
+ await assertSucceeded(
271
+ input.run,
272
+ "loginctl",
273
+ ["disable-linger", user],
274
+ "Unable to disable linger for current user."
275
+ );
276
+ }
277
+ };
278
+ return {
279
+ isActive,
280
+ registerAutostart,
281
+ unregisterAutostart
282
+ };
283
+ }
284
+
285
+ // src/platform/service-manager/index.ts
286
+ function createServiceManager(input) {
287
+ if (process.env.AGEMON_FAKE_SERVICES === "1") {
288
+ return createFakeServiceManager({
289
+ homeDir: input.homeDir,
290
+ user: input.user
291
+ });
292
+ }
293
+ return createLinuxServiceManager({
294
+ run: input.run,
295
+ homeDir: input.homeDir,
296
+ user: input.user
297
+ });
298
+ }
299
+
300
+ // src/core/state-manifest.ts
301
+ import { randomUUID } from "crypto";
302
+ import { mkdir as mkdir3, readFile as readFile3, rename, rm as rm3, writeFile as writeFile3 } from "fs/promises";
303
+ import { dirname as dirname2, join as join4 } from "path";
304
+ var MANIFEST_RELATIVE_PATH = ".agemon/state.json";
305
+ function createDefaultState(now) {
306
+ return {
307
+ version: 1,
308
+ createdAt: now,
309
+ updatedAt: now,
310
+ os: "linux",
311
+ actions: []
312
+ };
313
+ }
314
+ function isManifestAction(value) {
315
+ if (!value || typeof value !== "object") {
316
+ return false;
317
+ }
318
+ const typed = value;
319
+ return typeof typed.id === "string" && typeof typed.plugin === "string" && typeof typed.type === "string" && typeof typed.target === "string" && typeof typed.preExisting === "boolean" && typeof typed.createdAt === "string";
320
+ }
321
+ function isStateManifestData(value) {
322
+ if (!value || typeof value !== "object") {
323
+ return false;
324
+ }
325
+ const typed = value;
326
+ return typed.version === 1 && typed.os === "linux" && typeof typed.createdAt === "string" && typeof typed.updatedAt === "string" && Array.isArray(typed.actions) && typed.actions.every((action) => isManifestAction(action));
327
+ }
328
+ var StateManifest = class _StateManifest {
329
+ constructor(manifestPath, state) {
330
+ this.manifestPath = manifestPath;
331
+ this.state = state;
332
+ }
333
+ manifestPath;
334
+ state;
335
+ static async load(cwd) {
336
+ const manifestPath = join4(cwd, MANIFEST_RELATIVE_PATH);
337
+ const now = (/* @__PURE__ */ new Date()).toISOString();
338
+ try {
339
+ const contents = await readFile3(manifestPath, "utf8");
340
+ const parsed = JSON.parse(contents);
341
+ if (!isStateManifestData(parsed)) {
342
+ throw new Error("State manifest has an invalid schema");
343
+ }
344
+ return new _StateManifest(manifestPath, parsed);
345
+ } catch (error) {
346
+ const errno = error;
347
+ if (errno.code === "ENOENT") {
348
+ return new _StateManifest(manifestPath, createDefaultState(now));
349
+ }
350
+ throw new Error(
351
+ `Unable to load manifest at ${manifestPath}: ${String(error)}`
352
+ );
353
+ }
354
+ }
355
+ getActions() {
356
+ return [...this.state.actions];
357
+ }
358
+ hasActions() {
359
+ return this.state.actions.length > 0;
360
+ }
361
+ hasActionForPlugin(plugin) {
362
+ return this.state.actions.some((action) => action.plugin === plugin);
363
+ }
364
+ async recordAction(input) {
365
+ const action = {
366
+ id: randomUUID(),
367
+ plugin: input.plugin,
368
+ type: input.type,
369
+ target: input.target,
370
+ preExisting: input.preExisting,
371
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
372
+ };
373
+ this.state.actions.push(action);
374
+ await this.persist();
375
+ return action;
376
+ }
377
+ async removeActionsForPlugin(plugin) {
378
+ const nextActions = this.state.actions.filter(
379
+ (action) => action.plugin !== plugin
380
+ );
381
+ if (nextActions.length === this.state.actions.length) {
382
+ return;
383
+ }
384
+ this.state.actions = nextActions;
385
+ await this.persist();
386
+ }
387
+ async pruneIfEmpty() {
388
+ if (this.state.actions.length > 0) {
389
+ return;
390
+ }
391
+ const manifestDir = dirname2(this.manifestPath);
392
+ await rm3(this.manifestPath, { force: true });
393
+ try {
394
+ await rm3(manifestDir, { recursive: false });
395
+ } catch {
396
+ return;
397
+ }
398
+ }
399
+ async persist() {
400
+ const now = (/* @__PURE__ */ new Date()).toISOString();
401
+ this.state.updatedAt = now;
402
+ const payload = `${JSON.stringify(this.state, null, 2)}
403
+ `;
404
+ const manifestDir = dirname2(this.manifestPath);
405
+ const tempPath = `${this.manifestPath}.tmp-${process.pid}-${Date.now()}`;
406
+ await mkdir3(manifestDir, { recursive: true });
407
+ await writeFile3(tempPath, payload, "utf8");
408
+ await rename(tempPath, this.manifestPath);
409
+ }
410
+ };
411
+
412
+ // src/core/subprocess-runner.ts
413
+ import { spawn } from "child_process";
414
+ function formatCommand(command, args) {
415
+ return [command, ...args].join(" ");
416
+ }
417
+ var fakeCrgInstalledState = null;
418
+ var fakeInstalledSkillNames = null;
419
+ var fakeInstalledGlobalNpmPackageNames = null;
420
+ var fakeStateSignature = null;
421
+ function buildFakeStateSignature() {
422
+ return [
423
+ process.env.AGEMON_FAKE_PREINSTALLED_CRG ?? "",
424
+ process.env.AGEMON_FAKE_PREINSTALLED_SKILLS ?? "",
425
+ process.env.AGEMON_FAKE_PREINSTALLED_NPM_PACKAGES ?? ""
426
+ ].join("||");
427
+ }
428
+ function initializeFakeStateIfNeeded() {
429
+ const currentStateSignature = buildFakeStateSignature();
430
+ if (fakeCrgInstalledState !== null && fakeStateSignature === currentStateSignature) {
431
+ return;
432
+ }
433
+ fakeStateSignature = currentStateSignature;
434
+ fakeCrgInstalledState = process.env.AGEMON_FAKE_PREINSTALLED_CRG === "1";
435
+ const preinstalledSkills = process.env.AGEMON_FAKE_PREINSTALLED_SKILLS?.split(
436
+ ","
437
+ ).map((skillName) => skillName.trim()).filter((skillName) => skillName.length > 0);
438
+ fakeInstalledSkillNames = new Set(preinstalledSkills ?? []);
439
+ const preinstalledGlobalNpmPackages = process.env.AGEMON_FAKE_PREINSTALLED_NPM_PACKAGES?.split(",").map((packageName) => packageName.trim()).filter((packageName) => packageName.length > 0);
440
+ fakeInstalledGlobalNpmPackageNames = new Set(
441
+ preinstalledGlobalNpmPackages ?? []
442
+ );
443
+ }
444
+ function readOptionValue(args, optionName) {
445
+ const optionIndex = args.indexOf(optionName);
446
+ if (optionIndex < 0) {
447
+ return void 0;
448
+ }
449
+ return args[optionIndex + 1];
450
+ }
451
+ function buildFakeSkillsList() {
452
+ const skills = Array.from(fakeInstalledSkillNames ?? []).map((name) => ({
453
+ name,
454
+ path: `/fake/.claude/skills/${name}`,
455
+ scope: "project",
456
+ agents: ["Claude Code"]
457
+ }));
458
+ return {
459
+ code: 0,
460
+ stdout: `${JSON.stringify(skills, null, 2)}
461
+ `,
462
+ stderr: ""
463
+ };
464
+ }
465
+ function buildFakeSkillsAdd(args) {
466
+ const packageSource = args[2];
467
+ if (!packageSource) {
468
+ return {
469
+ code: 1,
470
+ stdout: "",
471
+ stderr: "skills add requires a package source"
472
+ };
473
+ }
474
+ const skillName = readOptionValue(args, "--skill") ?? packageSource;
475
+ fakeInstalledSkillNames?.add(skillName);
476
+ return {
477
+ code: 0,
478
+ stdout: `installed skill ${skillName} from ${packageSource}
479
+ `,
480
+ stderr: ""
481
+ };
482
+ }
483
+ function buildFakeSkillsRemove(args) {
484
+ const skillNames = args.slice(2).filter((arg) => arg.length > 0 && !arg.startsWith("-"));
485
+ for (const skillName of skillNames) {
486
+ fakeInstalledSkillNames?.delete(skillName);
487
+ }
488
+ return {
489
+ code: 0,
490
+ stdout: `removed ${skillNames.length} skill(s)
491
+ `,
492
+ stderr: ""
493
+ };
494
+ }
495
+ function buildFakeNpxResponse(args) {
496
+ if (args[0] !== "skills") {
497
+ return void 0;
498
+ }
499
+ const subcommand = args[1];
500
+ if (subcommand === "list" || subcommand === "ls") {
501
+ return buildFakeSkillsList();
502
+ }
503
+ if (subcommand === "add") {
504
+ return buildFakeSkillsAdd(args);
505
+ }
506
+ if (subcommand === "remove" || subcommand === "rm") {
507
+ return buildFakeSkillsRemove(args);
508
+ }
509
+ return {
510
+ code: 0,
511
+ stdout: `[fake subprocess] npx ${args.join(" ")}
512
+ `,
513
+ stderr: ""
514
+ };
515
+ }
516
+ function buildFakeCrgResponse(args) {
517
+ if (!fakeCrgInstalledState) {
518
+ return {
519
+ code: 1,
520
+ stdout: "",
521
+ stderr: "code-review-graph: command not found"
522
+ };
523
+ }
524
+ if (args[0] === "status") {
525
+ return {
526
+ code: 0,
527
+ stdout: "Files: 1\nNodes: 1\nEdges: 1\n",
528
+ stderr: ""
529
+ };
530
+ }
531
+ if (args[0] === "build") {
532
+ return {
533
+ code: 0,
534
+ stdout: "Graph build complete\n",
535
+ stderr: ""
536
+ };
537
+ }
538
+ return {
539
+ code: 0,
540
+ stdout: "code-review-graph 0.0.0-fake\n",
541
+ stderr: ""
542
+ };
543
+ }
544
+ function buildFakePipxResponse(args) {
545
+ if (args[0] === "run" && args[1] === "--spec" && args[2] === "code-review-graph" && args[3] === "code-review-graph") {
546
+ return buildFakeCrgResponse(args.slice(4));
547
+ }
548
+ const [operation, packageName] = args;
549
+ if (packageName !== "code-review-graph") {
550
+ return void 0;
551
+ }
552
+ if (operation === "install") {
553
+ fakeCrgInstalledState = true;
554
+ return {
555
+ code: 0,
556
+ stdout: "installed package code-review-graph\n",
557
+ stderr: ""
558
+ };
559
+ }
560
+ if (operation === "uninstall") {
561
+ fakeCrgInstalledState = false;
562
+ return {
563
+ code: 0,
564
+ stdout: "uninstalled package code-review-graph\n",
565
+ stderr: ""
566
+ };
567
+ }
568
+ return void 0;
569
+ }
570
+ function parseGlobalNpmPackageNames(args) {
571
+ const packageNames = [];
572
+ for (let index = 1; index < args.length; index += 1) {
573
+ const arg = args[index];
574
+ if (!arg || arg.startsWith("-")) {
575
+ continue;
576
+ }
577
+ packageNames.push(arg);
578
+ }
579
+ return packageNames;
580
+ }
581
+ function buildFakeNpmResponse(args) {
582
+ const operation = args[0];
583
+ if (!operation) {
584
+ return void 0;
585
+ }
586
+ const hasGlobalFlag = args.includes("-g") || args.includes("--global");
587
+ if (!hasGlobalFlag) {
588
+ return {
589
+ code: 0,
590
+ stdout: `[fake subprocess] npm ${args.join(" ")}
591
+ `,
592
+ stderr: ""
593
+ };
594
+ }
595
+ const packageNames = parseGlobalNpmPackageNames(args);
596
+ if (operation === "install") {
597
+ for (const packageName of packageNames) {
598
+ fakeInstalledGlobalNpmPackageNames?.add(packageName);
599
+ }
600
+ return {
601
+ code: 0,
602
+ stdout: `installed ${packageNames.length} package(s) globally
603
+ `,
604
+ stderr: ""
605
+ };
606
+ }
607
+ if (operation === "uninstall") {
608
+ for (const packageName of packageNames) {
609
+ fakeInstalledGlobalNpmPackageNames?.delete(packageName);
610
+ }
611
+ return {
612
+ code: 0,
613
+ stdout: `removed ${packageNames.length} package(s) globally
614
+ `,
615
+ stderr: ""
616
+ };
617
+ }
618
+ return {
619
+ code: 0,
620
+ stdout: `[fake subprocess] npm ${args.join(" ")}
621
+ `,
622
+ stderr: ""
623
+ };
624
+ }
625
+ function buildFakeAgnixResponse(args) {
626
+ if (!fakeInstalledGlobalNpmPackageNames?.has("agnix")) {
627
+ return {
628
+ code: 1,
629
+ stdout: "",
630
+ stderr: "agnix: command not found"
631
+ };
632
+ }
633
+ if (args[0] === "--version") {
634
+ return {
635
+ code: 0,
636
+ stdout: "agnix 0.0.0-fake\n",
637
+ stderr: ""
638
+ };
639
+ }
640
+ if (args[0] === "lint") {
641
+ return {
642
+ code: 0,
643
+ stdout: "agnix lint passed\n",
644
+ stderr: ""
645
+ };
646
+ }
647
+ return {
648
+ code: 0,
649
+ stdout: `[fake subprocess] agnix ${args.join(" ")}
650
+ `,
651
+ stderr: ""
652
+ };
653
+ }
654
+ async function runSubprocess(command, args, options = {}) {
655
+ if (process.env.AGEMON_FAKE_SUBPROCESS === "1") {
656
+ initializeFakeStateIfNeeded();
657
+ if (command === "pipx") {
658
+ const pipxResponse = buildFakePipxResponse(args);
659
+ if (pipxResponse) {
660
+ return pipxResponse;
661
+ }
662
+ }
663
+ if (command === "npx") {
664
+ const npxResponse = buildFakeNpxResponse(args);
665
+ if (npxResponse) {
666
+ return npxResponse;
667
+ }
668
+ }
669
+ if (command === "npm") {
670
+ const npmResponse = buildFakeNpmResponse(args);
671
+ if (npmResponse) {
672
+ return npmResponse;
673
+ }
674
+ }
675
+ if (command === "code-review-graph") {
676
+ return buildFakeCrgResponse(args);
677
+ }
678
+ if (command === "agnix") {
679
+ return buildFakeAgnixResponse(args);
680
+ }
681
+ return {
682
+ code: 0,
683
+ stdout: `[fake subprocess] ${formatCommand(command, args)}
684
+ `,
685
+ stderr: ""
686
+ };
687
+ }
688
+ return await new Promise((resolve) => {
689
+ let settled = false;
690
+ let timeoutHandle;
691
+ let hardKillHandle;
692
+ let timedOut = false;
693
+ const finalize = (result) => {
694
+ if (settled) {
695
+ return;
696
+ }
697
+ settled = true;
698
+ if (timeoutHandle) {
699
+ clearTimeout(timeoutHandle);
700
+ }
701
+ if (hardKillHandle) {
702
+ clearTimeout(hardKillHandle);
703
+ }
704
+ resolve(result);
705
+ };
706
+ const childProcess = spawn(command, args, {
707
+ cwd: process.cwd(),
708
+ env: process.env,
709
+ stdio: ["ignore", "pipe", "pipe"]
710
+ });
711
+ if (options.timeoutMs && options.timeoutMs > 0) {
712
+ timeoutHandle = setTimeout(() => {
713
+ timedOut = true;
714
+ const signal = options.killSignal ?? "SIGTERM";
715
+ const terminated = childProcess.kill(signal);
716
+ if (!terminated) {
717
+ finalize({
718
+ code: 124,
719
+ stdout,
720
+ stderr: `${stderr}
721
+ Command timed out after ${options.timeoutMs}ms and could not be signaled with ${signal}.`
722
+ });
723
+ return;
724
+ }
725
+ hardKillHandle = setTimeout(() => {
726
+ if (!settled) {
727
+ childProcess.kill("SIGKILL");
728
+ }
729
+ }, 2e3);
730
+ }, options.timeoutMs);
731
+ }
732
+ let stdout = "";
733
+ let stderr = "";
734
+ childProcess.stdout.on("data", (chunk) => {
735
+ stdout += Buffer.from(chunk).toString("utf8");
736
+ });
737
+ childProcess.stderr.on("data", (chunk) => {
738
+ stderr += Buffer.from(chunk).toString("utf8");
739
+ });
740
+ childProcess.on("error", (error) => {
741
+ const errorMessage = stderr ? `${stderr}
742
+ process spawn error: ${error.message}` : `process spawn error: ${error.message}`;
743
+ finalize({
744
+ code: 1,
745
+ stdout,
746
+ stderr: errorMessage
747
+ });
748
+ });
749
+ childProcess.on(
750
+ "close",
751
+ (code, signal) => {
752
+ if (timedOut) {
753
+ const timeoutMessage = `Command timed out after ${options.timeoutMs}ms.`;
754
+ const timeoutStderr = stderr ? `${stderr}
755
+ ${timeoutMessage}` : timeoutMessage;
756
+ finalize({
757
+ code: 124,
758
+ stdout,
759
+ stderr: timeoutStderr
760
+ });
761
+ return;
762
+ }
763
+ if (signal) {
764
+ finalize({
765
+ code: 1,
766
+ stdout,
767
+ stderr: `${stderr}Process terminated by signal: ${signal}`
768
+ });
769
+ return;
770
+ }
771
+ finalize({
772
+ code: code ?? 1,
773
+ stdout,
774
+ stderr
775
+ });
776
+ }
777
+ );
778
+ });
779
+ }
780
+
781
+ // src/core/context.ts
782
+ async function createContext(input) {
783
+ const platform = await detectPlatform({
784
+ osReleasePath: process.env.AGEMON_OS_RELEASE_PATH
785
+ });
786
+ for (const binaryName of REQUIRED_BINARIES) {
787
+ const binary = platform.binaries[binaryName];
788
+ const status = binary.present ? `present (${binary.path ?? "resolved via PATH"})` : "missing";
789
+ input.ui.info(`Binary check: ${binaryName} ${status}`);
790
+ }
791
+ return {
792
+ cwd: process.cwd(),
793
+ os: platform.os,
794
+ binaries: REQUIRED_BINARIES.map((name) => platform.binaries[name]),
795
+ dryRun: input.dryRun,
796
+ yes: input.yes,
797
+ log: console,
798
+ ui: input.ui,
799
+ run: runSubprocess,
800
+ manifest: await StateManifest.load(process.cwd()),
801
+ serviceManager: createServiceManager({
802
+ os: platform.os,
803
+ run: runSubprocess,
804
+ homeDir: process.env.HOME,
805
+ user: process.env.USER
806
+ })
807
+ };
808
+ }
809
+
810
+ // src/core/dev-mode.ts
811
+ var FAKE_BACKEND_ENV_VARS = [
812
+ "AGEMON_FAKE_SUBPROCESS",
813
+ "AGEMON_FAKE_SERVICES"
814
+ ];
815
+ function assertFakeBackendsAreDevOnly() {
816
+ const activeFakeVars = FAKE_BACKEND_ENV_VARS.filter(
817
+ (name) => process.env[name] === "1"
818
+ );
819
+ if (activeFakeVars.length > 0 && process.env.AGEMON_DEV !== "1") {
820
+ throw new Error(
821
+ `${activeFakeVars.join(", ")} must never be set outside the sandbox harness \u2014 refusing to start.`
822
+ );
823
+ }
824
+ }
825
+
826
+ // src/core/orchestrator.ts
827
+ function parseOnlyPluginIds(only) {
828
+ if (!only) return [];
829
+ return only.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
830
+ }
831
+ function resolvePluginOrder(allPlugins, onlyIds) {
832
+ const pluginById = new Map(allPlugins.map((plugin) => [plugin.id, plugin]));
833
+ const unresolvedOnlyIds = onlyIds.filter((id) => !pluginById.has(id));
834
+ if (unresolvedOnlyIds.length > 0) {
835
+ throw new Error(`Unknown plugin id(s): ${unresolvedOnlyIds.join(", ")}`);
836
+ }
837
+ const selected = onlyIds.length === 0 ? [...allPlugins] : allPlugins.filter((plugin) => onlyIds.includes(plugin.id));
838
+ const temporary = /* @__PURE__ */ new Set();
839
+ const permanent = /* @__PURE__ */ new Set();
840
+ const ordered = [];
841
+ const visit = (plugin) => {
842
+ if (permanent.has(plugin.id)) {
843
+ return;
844
+ }
845
+ if (temporary.has(plugin.id)) {
846
+ throw new Error(`Circular dependency detected at plugin '${plugin.id}'.`);
847
+ }
848
+ temporary.add(plugin.id);
849
+ for (const dependencyId of plugin.dependsOn ?? []) {
850
+ const dependencyPlugin = pluginById.get(dependencyId);
851
+ if (!dependencyPlugin) {
852
+ throw new Error(
853
+ `Plugin '${plugin.id}' depends on missing plugin '${dependencyId}'.`
854
+ );
855
+ }
856
+ visit(dependencyPlugin);
857
+ }
858
+ temporary.delete(plugin.id);
859
+ permanent.add(plugin.id);
860
+ ordered.push(plugin);
861
+ };
862
+ for (const plugin of selected) {
863
+ visit(plugin);
864
+ }
865
+ return ordered;
866
+ }
867
+ function buildVerificationMessage(pluginId, detail) {
868
+ if (!detail) return `Installed ${pluginId}`;
869
+ return `Installed ${pluginId} (${detail})`;
870
+ }
871
+ async function installPlugins(ctx, allPlugins, options) {
872
+ const onlyIds = parseOnlyPluginIds(options.only);
873
+ const plugins = resolvePluginOrder(allPlugins, onlyIds);
874
+ if (plugins.length === 0) {
875
+ ctx.ui.info("Nothing to do \u2014 no plugins selected.");
876
+ return;
877
+ }
878
+ for (const plugin of plugins) {
879
+ ctx.ui.start(`Installing ${plugin.id}`);
880
+ const presence = await plugin.detect(ctx);
881
+ if (presence.present) {
882
+ ctx.ui.succeed(`Already installed ${plugin.id}`);
883
+ continue;
884
+ }
885
+ await plugin.install(ctx);
886
+ const verification = await plugin.verify(ctx);
887
+ if (!verification.ok) {
888
+ ctx.ui.fail(`Verification failed for ${plugin.id}`);
889
+ throw new Error(
890
+ verification.detail ?? `Verification failed for plugin '${plugin.id}'.`
891
+ );
892
+ }
893
+ ctx.ui.succeed(buildVerificationMessage(plugin.id, verification.detail));
894
+ }
895
+ }
896
+ async function uninstallPlugins(ctx, allPlugins, options) {
897
+ const onlyIds = parseOnlyPluginIds(options.only);
898
+ const plugins = resolvePluginOrder(allPlugins, onlyIds).reverse();
899
+ if (plugins.length === 0) {
900
+ ctx.ui.info("Nothing to do \u2014 no plugins selected.");
901
+ return;
902
+ }
903
+ for (const plugin of plugins) {
904
+ ctx.ui.start(`Uninstalling ${plugin.id}`);
905
+ await plugin.uninstall(ctx);
906
+ ctx.ui.succeed(`Uninstalled ${plugin.id}`);
907
+ }
908
+ await ctx.manifest.pruneIfEmpty();
909
+ }
910
+
911
+ // src/plugins/cli-tool/catalog.ts
912
+ var CLI_TOOL_BUNDLE = [
913
+ {
914
+ id: "agnix",
915
+ packageName: "agnix",
916
+ binaryName: "agnix"
917
+ }
918
+ ];
919
+
920
+ // src/plugins/cli-tool/index.ts
921
+ var PLUGIN_ID = "cli-tool";
922
+ var ACTION_TYPE_INSTALLED_CLI_TOOL = "installed-cli-tool";
923
+ var ACTION_TYPE_PREEXISTING_CLI_TOOL = "preexisting-cli-tool";
924
+ var VERSION_CHECK_TIMEOUT_MS = 2e4;
925
+ var INSTALL_TIMEOUT_MS = 18e4;
926
+ var UNINSTALL_TIMEOUT_MS = 6e4;
927
+ function getPluginActions(ctx) {
928
+ return ctx.manifest.getActions().filter((action) => action.plugin === PLUGIN_ID);
929
+ }
930
+ function hasManagedInstallRecord(ctx, toolId) {
931
+ return getPluginActions(ctx).some(
932
+ (action) => action.type === ACTION_TYPE_INSTALLED_CLI_TOOL && action.target === toolId && action.preExisting === false
933
+ );
934
+ }
935
+ function hasPreexistingRecord(ctx, toolId) {
936
+ return getPluginActions(ctx).some(
937
+ (action) => action.type === ACTION_TYPE_PREEXISTING_CLI_TOOL && action.target === toolId && action.preExisting === true
938
+ );
939
+ }
940
+ function hasAnyRecordForTool(ctx, toolId) {
941
+ return hasManagedInstallRecord(ctx, toolId) || hasPreexistingRecord(ctx, toolId);
942
+ }
943
+ async function isToolBinaryAvailable(ctx, entry) {
944
+ const result = await ctx.run(entry.binaryName, ["--version"], {
945
+ timeoutMs: VERSION_CHECK_TIMEOUT_MS
946
+ });
947
+ return result.code === 0;
948
+ }
949
+ async function detectCliTools(ctx) {
950
+ const bundleAvailability = await Promise.all(
951
+ CLI_TOOL_BUNDLE.map(async (entry) => ({
952
+ entry,
953
+ available: await isToolBinaryAvailable(ctx, entry)
954
+ }))
955
+ );
956
+ if (bundleAvailability.some((tool) => !tool.available)) {
957
+ return { present: false, preExisting: false };
958
+ }
959
+ const allManaged = CLI_TOOL_BUNDLE.every(
960
+ (entry) => hasManagedInstallRecord(ctx, entry.id)
961
+ );
962
+ if (allManaged) {
963
+ return { present: true, preExisting: false };
964
+ }
965
+ if (!ctx.dryRun) {
966
+ for (const { entry } of bundleAvailability) {
967
+ if (hasAnyRecordForTool(ctx, entry.id)) {
968
+ continue;
969
+ }
970
+ await ctx.manifest.recordAction({
971
+ plugin: PLUGIN_ID,
972
+ type: ACTION_TYPE_PREEXISTING_CLI_TOOL,
973
+ target: entry.id,
974
+ preExisting: true
975
+ });
976
+ }
977
+ }
978
+ return { present: true, preExisting: true };
979
+ }
980
+ async function installCliTools(ctx) {
981
+ if (ctx.dryRun) {
982
+ for (const entry of CLI_TOOL_BUNDLE) {
983
+ ctx.ui.info(`Would install ${entry.packageName} globally via npm`);
984
+ }
985
+ return;
986
+ }
987
+ for (const entry of CLI_TOOL_BUNDLE) {
988
+ const toolIsAvailable = await isToolBinaryAvailable(ctx, entry);
989
+ if (toolIsAvailable) {
990
+ if (!hasAnyRecordForTool(ctx, entry.id)) {
991
+ await ctx.manifest.recordAction({
992
+ plugin: PLUGIN_ID,
993
+ type: ACTION_TYPE_PREEXISTING_CLI_TOOL,
994
+ target: entry.id,
995
+ preExisting: true
996
+ });
997
+ }
998
+ continue;
999
+ }
1000
+ const installResult = await ctx.run(
1001
+ "npm",
1002
+ ["install", "--global", entry.packageName],
1003
+ {
1004
+ timeoutMs: INSTALL_TIMEOUT_MS
1005
+ }
1006
+ );
1007
+ if (installResult.code !== 0) {
1008
+ throw new Error(
1009
+ installResult.stderr || `npm global install failed for ${entry.packageName}`
1010
+ );
1011
+ }
1012
+ await ctx.manifest.recordAction({
1013
+ plugin: PLUGIN_ID,
1014
+ type: ACTION_TYPE_INSTALLED_CLI_TOOL,
1015
+ target: entry.id,
1016
+ preExisting: false
1017
+ });
1018
+ }
1019
+ }
1020
+ async function verifyCliTools(ctx) {
1021
+ if (ctx.dryRun) {
1022
+ return { ok: true, detail: "dry-run" };
1023
+ }
1024
+ const missingToolIds = [];
1025
+ for (const entry of CLI_TOOL_BUNDLE) {
1026
+ const toolIsAvailable = await isToolBinaryAvailable(ctx, entry);
1027
+ if (!toolIsAvailable) {
1028
+ missingToolIds.push(entry.id);
1029
+ }
1030
+ }
1031
+ if (missingToolIds.length > 0) {
1032
+ return {
1033
+ ok: false,
1034
+ detail: `Missing CLI tool(s): ${missingToolIds.join(", ")}`
1035
+ };
1036
+ }
1037
+ return {
1038
+ ok: true,
1039
+ detail: `${CLI_TOOL_BUNDLE.length} CLI tool bundle entries present`
1040
+ };
1041
+ }
1042
+ async function uninstallCliTools(ctx) {
1043
+ const managedActions = getPluginActions(ctx).filter(
1044
+ (action) => action.type === ACTION_TYPE_INSTALLED_CLI_TOOL && action.preExisting === false
1045
+ );
1046
+ if (ctx.dryRun) {
1047
+ if (managedActions.length === 0) {
1048
+ ctx.ui.info("Would keep pre-existing CLI tools untouched");
1049
+ return;
1050
+ }
1051
+ for (const action of managedActions) {
1052
+ const bundleEntry = CLI_TOOL_BUNDLE.find(
1053
+ (entry) => entry.id === action.target
1054
+ );
1055
+ const packageName = bundleEntry?.packageName ?? action.target;
1056
+ ctx.ui.info(`Would uninstall global npm package ${packageName}`);
1057
+ }
1058
+ return;
1059
+ }
1060
+ for (const action of managedActions) {
1061
+ const bundleEntry = CLI_TOOL_BUNDLE.find(
1062
+ (entry) => entry.id === action.target
1063
+ );
1064
+ const packageName = bundleEntry?.packageName ?? action.target;
1065
+ const uninstallResult = await ctx.run(
1066
+ "npm",
1067
+ ["uninstall", "--global", packageName],
1068
+ {
1069
+ timeoutMs: UNINSTALL_TIMEOUT_MS
1070
+ }
1071
+ );
1072
+ if (uninstallResult.code !== 0) {
1073
+ throw new Error(
1074
+ uninstallResult.stderr || `npm global uninstall failed for ${packageName}`
1075
+ );
1076
+ }
1077
+ }
1078
+ await ctx.manifest.removeActionsForPlugin(PLUGIN_ID);
1079
+ }
1080
+ var cliToolPlugin = {
1081
+ id: PLUGIN_ID,
1082
+ detect: detectCliTools,
1083
+ install: installCliTools,
1084
+ verify: verifyCliTools,
1085
+ uninstall: uninstallCliTools
1086
+ };
1087
+
1088
+ // src/plugins/crg/index.ts
1089
+ var PLUGIN_ID2 = "crg";
1090
+ var PACKAGE_NAME = "code-review-graph";
1091
+ var PIPX_RUN_PREFIX = ["run", "--spec", PACKAGE_NAME, "code-review-graph"];
1092
+ var VERSION_CHECK_TIMEOUT_MS2 = 2e4;
1093
+ var INSTALL_TIMEOUT_MS2 = 18e4;
1094
+ var BUILD_TIMEOUT_MS = 3e5;
1095
+ var STATUS_TIMEOUT_MS = 3e4;
1096
+ var UNINSTALL_TIMEOUT_MS2 = 12e4;
1097
+ var ACTION_TYPE_INSTALLED_BINARY = "installed-binary";
1098
+ var ACTION_TYPE_PREEXISTING_BINARY = "preexisting-binary";
1099
+ var ACTION_TYPE_GRAPH_BUILT = "graph-built";
1100
+ function getPluginActions2(ctx) {
1101
+ return ctx.manifest.getActions().filter((action) => action.plugin === PLUGIN_ID2);
1102
+ }
1103
+ function hasManagedInstall(ctx) {
1104
+ return getPluginActions2(ctx).some(
1105
+ (action) => action.type === ACTION_TYPE_INSTALLED_BINARY && action.preExisting === false
1106
+ );
1107
+ }
1108
+ function hasPreexistingRecord2(ctx) {
1109
+ return getPluginActions2(ctx).some(
1110
+ (action) => action.type === ACTION_TYPE_PREEXISTING_BINARY && action.preExisting === true
1111
+ );
1112
+ }
1113
+ async function detectCrgPresence(ctx) {
1114
+ const versionCheck = await ctx.run("code-review-graph", ["--version"], {
1115
+ timeoutMs: VERSION_CHECK_TIMEOUT_MS2
1116
+ });
1117
+ const commandIsAvailable = versionCheck.code === 0;
1118
+ if (!commandIsAvailable) {
1119
+ return { present: false, preExisting: false };
1120
+ }
1121
+ if (hasManagedInstall(ctx)) {
1122
+ return { present: true, preExisting: false };
1123
+ }
1124
+ if (!hasPreexistingRecord2(ctx) && !ctx.dryRun) {
1125
+ await ctx.manifest.recordAction({
1126
+ plugin: PLUGIN_ID2,
1127
+ type: ACTION_TYPE_PREEXISTING_BINARY,
1128
+ target: PACKAGE_NAME,
1129
+ preExisting: true
1130
+ });
1131
+ }
1132
+ return { present: true, preExisting: true };
1133
+ }
1134
+ async function installCrg(ctx) {
1135
+ if (ctx.dryRun) {
1136
+ ctx.ui.info(`Would install ${PACKAGE_NAME} via pipx`);
1137
+ ctx.ui.info("Would build graph for current repository");
1138
+ return;
1139
+ }
1140
+ const installResult = await ctx.run("pipx", ["install", PACKAGE_NAME], {
1141
+ timeoutMs: INSTALL_TIMEOUT_MS2
1142
+ });
1143
+ if (installResult.code !== 0) {
1144
+ throw new Error(
1145
+ installResult.stderr || `pipx install failed for ${PACKAGE_NAME}`
1146
+ );
1147
+ }
1148
+ await ctx.manifest.recordAction({
1149
+ plugin: PLUGIN_ID2,
1150
+ type: ACTION_TYPE_INSTALLED_BINARY,
1151
+ target: `${PACKAGE_NAME} (pipx)`,
1152
+ preExisting: false
1153
+ });
1154
+ const buildResult = await ctx.run("pipx", [...PIPX_RUN_PREFIX, "build"], {
1155
+ timeoutMs: BUILD_TIMEOUT_MS
1156
+ });
1157
+ if (buildResult.code !== 0) {
1158
+ throw new Error(buildResult.stderr || "code-review-graph build failed");
1159
+ }
1160
+ await ctx.manifest.recordAction({
1161
+ plugin: PLUGIN_ID2,
1162
+ type: ACTION_TYPE_GRAPH_BUILT,
1163
+ target: ".code-review-graph",
1164
+ preExisting: false
1165
+ });
1166
+ }
1167
+ async function verifyCrg(ctx) {
1168
+ if (ctx.dryRun) {
1169
+ return { ok: true, detail: "dry-run" };
1170
+ }
1171
+ const statusResult = await ctx.run("pipx", [...PIPX_RUN_PREFIX, "status"], {
1172
+ timeoutMs: STATUS_TIMEOUT_MS
1173
+ });
1174
+ if (statusResult.code !== 0) {
1175
+ return {
1176
+ ok: false,
1177
+ detail: statusResult.stderr || "code-review-graph status failed"
1178
+ };
1179
+ }
1180
+ return { ok: true, detail: "status command passed" };
1181
+ }
1182
+ async function uninstallCrg(ctx) {
1183
+ const managedInstall = hasManagedInstall(ctx);
1184
+ if (ctx.dryRun) {
1185
+ if (managedInstall) {
1186
+ ctx.ui.info(`Would uninstall ${PACKAGE_NAME} from pipx`);
1187
+ } else {
1188
+ ctx.ui.info(`Would keep pre-existing ${PACKAGE_NAME} install`);
1189
+ }
1190
+ return;
1191
+ }
1192
+ if (managedInstall) {
1193
+ const uninstallResult = await ctx.run("pipx", ["uninstall", PACKAGE_NAME], {
1194
+ timeoutMs: UNINSTALL_TIMEOUT_MS2
1195
+ });
1196
+ if (uninstallResult.code !== 0) {
1197
+ throw new Error(
1198
+ uninstallResult.stderr || `pipx uninstall failed for ${PACKAGE_NAME}`
1199
+ );
1200
+ }
1201
+ }
1202
+ await ctx.manifest.removeActionsForPlugin(PLUGIN_ID2);
1203
+ }
1204
+ var crgPlugin = {
1205
+ id: PLUGIN_ID2,
1206
+ detect: detectCrgPresence,
1207
+ install: installCrg,
1208
+ verify: verifyCrg,
1209
+ uninstall: uninstallCrg
1210
+ };
1211
+
1212
+ // src/plugins/daemon/index.ts
1213
+ var PLUGIN_ID3 = "daemon";
1214
+ var UNIT_NAME = "agemon-crg-daemon.service";
1215
+ var ACTION_TYPE_REGISTERED_SERVICE = "registered-service";
1216
+ var ACTION_TYPE_PREEXISTING_SERVICE = "preexisting-service";
1217
+ var ACTION_TYPE_ENABLED_LINGER = "enabled-linger";
1218
+ function readHardeningProfileFromEnv() {
1219
+ return process.env.AGEMON_DAEMON_HARDENING === "compat" ? "compat" : "strict";
1220
+ }
1221
+ function getHardeningDirectives(profile) {
1222
+ if (profile === "compat") {
1223
+ return ["NoNewPrivileges=yes", "RestrictSUIDSGID=yes"];
1224
+ }
1225
+ return [
1226
+ "NoNewPrivileges=yes",
1227
+ "RestrictSUIDSGID=yes",
1228
+ "ProtectSystem=full",
1229
+ "ProtectHome=read-only",
1230
+ "PrivateTmp=yes"
1231
+ ];
1232
+ }
1233
+ function buildUnitContents(cwd, profile) {
1234
+ return [
1235
+ "[Unit]",
1236
+ "Description=agemon code-review-graph daemon",
1237
+ "After=network-online.target",
1238
+ "",
1239
+ "[Service]",
1240
+ "Type=simple",
1241
+ `WorkingDirectory=${cwd}`,
1242
+ "ExecStart=crg-daemon start",
1243
+ ...getHardeningDirectives(profile),
1244
+ "Restart=always",
1245
+ "RestartSec=3",
1246
+ "",
1247
+ "[Install]",
1248
+ "WantedBy=default.target",
1249
+ ""
1250
+ ].join("\n");
1251
+ }
1252
+ function getPluginActions3(ctx) {
1253
+ return ctx.manifest.getActions().filter((action) => action.plugin === PLUGIN_ID3);
1254
+ }
1255
+ function hasManagedServiceRegistration(ctx) {
1256
+ return getPluginActions3(ctx).some(
1257
+ (action) => action.type === ACTION_TYPE_REGISTERED_SERVICE && action.preExisting === false
1258
+ );
1259
+ }
1260
+ function hasPreexistingServiceRecord(ctx) {
1261
+ return getPluginActions3(ctx).some(
1262
+ (action) => action.type === ACTION_TYPE_PREEXISTING_SERVICE && action.preExisting === true
1263
+ );
1264
+ }
1265
+ function enabledLingerByAgemon(ctx) {
1266
+ return getPluginActions3(ctx).some(
1267
+ (action) => action.type === ACTION_TYPE_ENABLED_LINGER && action.preExisting === false
1268
+ );
1269
+ }
1270
+ async function detectDaemon(ctx) {
1271
+ const status = await ctx.serviceManager.isActive(UNIT_NAME);
1272
+ if (!status.active) {
1273
+ return { present: false, preExisting: false };
1274
+ }
1275
+ if (hasManagedServiceRegistration(ctx)) {
1276
+ return { present: true, preExisting: false };
1277
+ }
1278
+ if (!hasPreexistingServiceRecord(ctx) && !ctx.dryRun) {
1279
+ await ctx.manifest.recordAction({
1280
+ plugin: PLUGIN_ID3,
1281
+ type: ACTION_TYPE_PREEXISTING_SERVICE,
1282
+ target: UNIT_NAME,
1283
+ preExisting: true
1284
+ });
1285
+ }
1286
+ return { present: true, preExisting: true };
1287
+ }
1288
+ async function installDaemon(ctx) {
1289
+ if (ctx.dryRun) {
1290
+ ctx.ui.info(`Would register user service ${UNIT_NAME}`);
1291
+ ctx.ui.info("Would enable user linger when not already enabled");
1292
+ return;
1293
+ }
1294
+ const configuredProfile = readHardeningProfileFromEnv();
1295
+ const registration = await (async () => {
1296
+ try {
1297
+ return await ctx.serviceManager.registerAutostart({
1298
+ unitName: UNIT_NAME,
1299
+ unitContents: buildUnitContents(ctx.cwd, configuredProfile)
1300
+ });
1301
+ } catch (error) {
1302
+ if (configuredProfile !== "strict") {
1303
+ throw error;
1304
+ }
1305
+ ctx.ui.info(
1306
+ "Strict daemon hardening failed; retrying with compatibility hardening."
1307
+ );
1308
+ return await ctx.serviceManager.registerAutostart({
1309
+ unitName: UNIT_NAME,
1310
+ unitContents: buildUnitContents(ctx.cwd, "compat")
1311
+ });
1312
+ }
1313
+ })();
1314
+ await ctx.manifest.recordAction({
1315
+ plugin: PLUGIN_ID3,
1316
+ type: ACTION_TYPE_REGISTERED_SERVICE,
1317
+ target: registration.unitPath,
1318
+ preExisting: false
1319
+ });
1320
+ if (registration.lingerEnabledByAgemon) {
1321
+ await ctx.manifest.recordAction({
1322
+ plugin: PLUGIN_ID3,
1323
+ type: ACTION_TYPE_ENABLED_LINGER,
1324
+ target: process.env.USER ?? "current-user",
1325
+ preExisting: false
1326
+ });
1327
+ }
1328
+ }
1329
+ async function verifyDaemon(ctx) {
1330
+ if (ctx.dryRun) {
1331
+ return { ok: true, detail: "dry-run" };
1332
+ }
1333
+ const status = await ctx.serviceManager.isActive(UNIT_NAME);
1334
+ if (!status.active) {
1335
+ return { ok: false, detail: "user service is not active" };
1336
+ }
1337
+ return { ok: true, detail: "service active" };
1338
+ }
1339
+ async function uninstallDaemon(ctx) {
1340
+ const managedService = hasManagedServiceRegistration(ctx);
1341
+ if (ctx.dryRun) {
1342
+ if (managedService) {
1343
+ ctx.ui.info(`Would unregister user service ${UNIT_NAME}`);
1344
+ if (enabledLingerByAgemon(ctx)) {
1345
+ ctx.ui.info("Would disable user linger because agemon enabled it");
1346
+ }
1347
+ } else {
1348
+ ctx.ui.info(`Would keep pre-existing user service ${UNIT_NAME}`);
1349
+ }
1350
+ return;
1351
+ }
1352
+ if (managedService) {
1353
+ await ctx.serviceManager.unregisterAutostart({
1354
+ unitName: UNIT_NAME,
1355
+ disableLinger: enabledLingerByAgemon(ctx)
1356
+ });
1357
+ }
1358
+ await ctx.manifest.removeActionsForPlugin(PLUGIN_ID3);
1359
+ }
1360
+ var daemonPlugin = {
1361
+ id: PLUGIN_ID3,
1362
+ dependsOn: ["crg"],
1363
+ detect: detectDaemon,
1364
+ install: installDaemon,
1365
+ verify: verifyDaemon,
1366
+ uninstall: uninstallDaemon
1367
+ };
1368
+
1369
+ // src/plugins/master-prompt/index.ts
1370
+ import { existsSync } from "fs";
1371
+ import { mkdir as mkdir4, readFile as readFile4, rm as rm4, writeFile as writeFile4 } from "fs/promises";
1372
+ import { dirname as dirname3, join as join5 } from "path";
1373
+ var PLUGIN_ID4 = "master-prompt";
1374
+ var ACTION_TYPE_MANAGED_RULE_FILE = "managed-rule-file";
1375
+ var ACTION_TYPE_PREEXISTING_RULE_FILE = "preexisting-rule-file";
1376
+ var BACKUP_DIRECTORY = ".agemon/backups/master-prompt";
1377
+ function buildPointerFileContents(toolName, targetFileName) {
1378
+ return [
1379
+ "<!-- AI agent rules pointer -->",
1380
+ "# AI Agent Rules",
1381
+ "",
1382
+ "The canonical rules for this repo live in AGENTS.md \u2014 read that file in full before",
1383
+ `making any changes here. This file exists only because ${toolName} looks for \`${targetFileName}\` specifically; it intentionally does not restate the rules.`,
1384
+ ""
1385
+ ].join("\n");
1386
+ }
1387
+ function buildAgentsFileContents() {
1388
+ return [
1389
+ "# AI Agent Rules",
1390
+ "",
1391
+ "This is the canonical, repo-wide rule set for every AI coding agent in this repository.",
1392
+ "`CLAUDE.md`, `.cursorrules`, `.windsurfrules`, and `GEMINI.md` are pointer files that",
1393
+ "exist only because their tools look for those exact filenames.",
1394
+ "",
1395
+ "## Standard Task Lifecycle",
1396
+ "",
1397
+ "1. Discover existing tools, skills, and patterns before writing new code.",
1398
+ "2. Plan the smallest complete change before implementation.",
1399
+ "3. Verify assumptions with tests or reproducible checks.",
1400
+ "4. Implement with clear naming, modular boundaries, and zero dead code.",
1401
+ "5. Re-verify after changes and record out-of-scope findings in improvements.md.",
1402
+ "",
1403
+ "## Core Directives",
1404
+ "",
1405
+ "- Keep code simple and task-scoped: no speculative abstractions.",
1406
+ "- Reuse existing project utilities instead of duplicating logic.",
1407
+ "- Preserve user-authored content outside agemon-managed files.",
1408
+ "- Prioritize safe and reversible changes.",
1409
+ ""
1410
+ ].join("\n");
1411
+ }
1412
+ function getRuleFileDefinitions() {
1413
+ return [
1414
+ {
1415
+ target: "AGENTS.md",
1416
+ contents: buildAgentsFileContents()
1417
+ },
1418
+ {
1419
+ target: "CLAUDE.md",
1420
+ contents: buildPointerFileContents("Claude Code", "CLAUDE.md")
1421
+ },
1422
+ {
1423
+ target: "GEMINI.md",
1424
+ contents: buildPointerFileContents("Gemini CLI", "GEMINI.md")
1425
+ },
1426
+ {
1427
+ target: ".cursorrules",
1428
+ contents: buildPointerFileContents("Cursor", ".cursorrules")
1429
+ },
1430
+ {
1431
+ target: ".windsurfrules",
1432
+ contents: buildPointerFileContents("Windsurf", ".windsurfrules")
1433
+ }
1434
+ ];
1435
+ }
1436
+ function getPluginActions4(ctx) {
1437
+ return ctx.manifest.getActions().filter((action) => action.plugin === PLUGIN_ID4);
1438
+ }
1439
+ function hasActionForTarget(ctx, target) {
1440
+ return getPluginActions4(ctx).some((action) => action.target === target);
1441
+ }
1442
+ function getManagedActions(ctx) {
1443
+ return getPluginActions4(ctx).filter(
1444
+ (action) => action.type === ACTION_TYPE_MANAGED_RULE_FILE
1445
+ );
1446
+ }
1447
+ function encodeTargetAsBackupFileName(target) {
1448
+ return target.replace(/[^a-zA-Z0-9._-]/gu, "_");
1449
+ }
1450
+ function buildBackupPath(cwd, target) {
1451
+ return join5(
1452
+ cwd,
1453
+ BACKUP_DIRECTORY,
1454
+ `${encodeTargetAsBackupFileName(target)}.bak`
1455
+ );
1456
+ }
1457
+ async function readFileIfExists(filePath) {
1458
+ try {
1459
+ return await readFile4(filePath, "utf8");
1460
+ } catch {
1461
+ return null;
1462
+ }
1463
+ }
1464
+ function allRuleFilesMatch(ctx) {
1465
+ return getRuleFileDefinitions().map(async (definition) => {
1466
+ const targetPath = join5(ctx.cwd, definition.target);
1467
+ const currentContents = await readFileIfExists(targetPath);
1468
+ return currentContents === definition.contents;
1469
+ });
1470
+ }
1471
+ async function detectMasterPrompt(ctx) {
1472
+ const matchResults = await Promise.all(allRuleFilesMatch(ctx));
1473
+ const allMatch = matchResults.every((matches) => matches);
1474
+ if (!allMatch) {
1475
+ return { present: false, preExisting: false };
1476
+ }
1477
+ if (!ctx.dryRun) {
1478
+ for (const definition of getRuleFileDefinitions()) {
1479
+ if (hasActionForTarget(ctx, definition.target)) {
1480
+ continue;
1481
+ }
1482
+ await ctx.manifest.recordAction({
1483
+ plugin: PLUGIN_ID4,
1484
+ type: ACTION_TYPE_PREEXISTING_RULE_FILE,
1485
+ target: definition.target,
1486
+ preExisting: true
1487
+ });
1488
+ }
1489
+ }
1490
+ const allManaged = getRuleFileDefinitions().every(
1491
+ (definition) => getManagedActions(ctx).some(
1492
+ (action) => action.target === definition.target
1493
+ )
1494
+ );
1495
+ return { present: true, preExisting: !allManaged };
1496
+ }
1497
+ async function backupOriginalFileIfNeeded(cwd, target, existingContents) {
1498
+ const backupPath = buildBackupPath(cwd, target);
1499
+ await mkdir4(dirname3(backupPath), { recursive: true });
1500
+ await writeFile4(backupPath, existingContents, "utf8");
1501
+ }
1502
+ async function installMasterPrompt(ctx) {
1503
+ if (ctx.dryRun) {
1504
+ for (const definition of getRuleFileDefinitions()) {
1505
+ ctx.ui.info(`Would consolidate rule file ${definition.target}`);
1506
+ }
1507
+ return;
1508
+ }
1509
+ for (const definition of getRuleFileDefinitions()) {
1510
+ const targetPath = join5(ctx.cwd, definition.target);
1511
+ const existingContents = await readFileIfExists(targetPath);
1512
+ if (existingContents === definition.contents) {
1513
+ if (!hasActionForTarget(ctx, definition.target)) {
1514
+ await ctx.manifest.recordAction({
1515
+ plugin: PLUGIN_ID4,
1516
+ type: ACTION_TYPE_PREEXISTING_RULE_FILE,
1517
+ target: definition.target,
1518
+ preExisting: true
1519
+ });
1520
+ }
1521
+ continue;
1522
+ }
1523
+ if (existingContents !== null) {
1524
+ await backupOriginalFileIfNeeded(
1525
+ ctx.cwd,
1526
+ definition.target,
1527
+ existingContents
1528
+ );
1529
+ }
1530
+ await mkdir4(dirname3(targetPath), { recursive: true });
1531
+ await writeFile4(targetPath, definition.contents, "utf8");
1532
+ if (!hasActionForTarget(ctx, definition.target)) {
1533
+ await ctx.manifest.recordAction({
1534
+ plugin: PLUGIN_ID4,
1535
+ type: ACTION_TYPE_MANAGED_RULE_FILE,
1536
+ target: definition.target,
1537
+ preExisting: existingContents !== null
1538
+ });
1539
+ }
1540
+ }
1541
+ }
1542
+ async function verifyMasterPrompt(ctx) {
1543
+ if (ctx.dryRun) {
1544
+ return { ok: true, detail: "dry-run" };
1545
+ }
1546
+ const missingOrMismatchedTargets = [];
1547
+ for (const definition of getRuleFileDefinitions()) {
1548
+ const targetPath = join5(ctx.cwd, definition.target);
1549
+ const currentContents = await readFileIfExists(targetPath);
1550
+ if (currentContents !== definition.contents) {
1551
+ missingOrMismatchedTargets.push(definition.target);
1552
+ }
1553
+ }
1554
+ if (missingOrMismatchedTargets.length > 0) {
1555
+ return {
1556
+ ok: false,
1557
+ detail: `Missing or mismatched rule file(s): ${missingOrMismatchedTargets.join(", ")}`
1558
+ };
1559
+ }
1560
+ return {
1561
+ ok: true,
1562
+ detail: `${getRuleFileDefinitions().length} consolidated rule files present`
1563
+ };
1564
+ }
1565
+ async function uninstallMasterPrompt(ctx) {
1566
+ const managedActions = getManagedActions(ctx);
1567
+ if (ctx.dryRun) {
1568
+ if (managedActions.length === 0) {
1569
+ ctx.ui.info("Would keep pre-existing rule files untouched");
1570
+ return;
1571
+ }
1572
+ for (const action of managedActions) {
1573
+ if (action.preExisting) {
1574
+ ctx.ui.info(`Would restore original rule file ${action.target}`);
1575
+ } else {
1576
+ ctx.ui.info(`Would remove generated rule file ${action.target}`);
1577
+ }
1578
+ }
1579
+ return;
1580
+ }
1581
+ for (const action of managedActions) {
1582
+ const targetPath = join5(ctx.cwd, action.target);
1583
+ const backupPath = buildBackupPath(ctx.cwd, action.target);
1584
+ if (action.preExisting) {
1585
+ if (!existsSync(backupPath)) {
1586
+ throw new Error(
1587
+ `Missing backup for pre-existing file ${action.target}`
1588
+ );
1589
+ }
1590
+ const backupContents = await readFile4(backupPath, "utf8");
1591
+ await writeFile4(targetPath, backupContents, "utf8");
1592
+ await rm4(backupPath, { force: true });
1593
+ continue;
1594
+ }
1595
+ await rm4(targetPath, { force: true });
1596
+ }
1597
+ await ctx.manifest.removeActionsForPlugin(PLUGIN_ID4);
1598
+ }
1599
+ var masterPromptPlugin = {
1600
+ id: PLUGIN_ID4,
1601
+ detect: detectMasterPrompt,
1602
+ install: installMasterPrompt,
1603
+ verify: verifyMasterPrompt,
1604
+ uninstall: uninstallMasterPrompt
1605
+ };
1606
+
1607
+ // src/plugins/patcher-checkpoint.ts
1608
+ import { existsSync as existsSync2, readFileSync } from "fs";
1609
+ import { join as join6 } from "path";
1610
+
1611
+ // src/patchers/markdown-block.ts
1612
+ import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
1613
+ function startMarker(blockId) {
1614
+ return `<!-- agemon:start:${blockId} -->`;
1615
+ }
1616
+ function endMarker(blockId) {
1617
+ return `<!-- agemon:end:${blockId} -->`;
1618
+ }
1619
+ function normalizeBlockBody(blockBody) {
1620
+ return blockBody.replace(/\s+$/u, "");
1621
+ }
1622
+ function buildManagedBlock(blockId, blockBody) {
1623
+ const body = normalizeBlockBody(blockBody);
1624
+ if (body.length === 0) {
1625
+ return `${startMarker(blockId)}
1626
+ ${endMarker(blockId)}`;
1627
+ }
1628
+ return `${startMarker(blockId)}
1629
+ ${body}
1630
+ ${endMarker(blockId)}`;
1631
+ }
1632
+ function findManagedBlockRange(content, blockId) {
1633
+ const start = content.indexOf(startMarker(blockId));
1634
+ if (start < 0) {
1635
+ return null;
1636
+ }
1637
+ const endTag = endMarker(blockId);
1638
+ const endTagIndex = content.indexOf(endTag, start);
1639
+ if (endTagIndex < 0) {
1640
+ throw new Error(
1641
+ `Found start marker without matching end marker for '${blockId}'.`
1642
+ );
1643
+ }
1644
+ return { start, end: endTagIndex + endTag.length };
1645
+ }
1646
+ function upsertManagedMarkdownBlock(content, blockId, blockBody) {
1647
+ const managedBlock = buildManagedBlock(blockId, blockBody);
1648
+ const range = findManagedBlockRange(content, blockId);
1649
+ if (range) {
1650
+ const current = content.slice(range.start, range.end);
1651
+ if (current === managedBlock) {
1652
+ return { nextContent: content, changed: false };
1653
+ }
1654
+ const next = content.slice(0, range.start) + managedBlock + content.slice(range.end);
1655
+ return { nextContent: next, changed: true };
1656
+ }
1657
+ if (content.length === 0) {
1658
+ return { nextContent: `${managedBlock}
1659
+ `, changed: true };
1660
+ }
1661
+ const separator = content.endsWith("\n\n") ? "" : content.endsWith("\n") ? "\n" : "\n\n";
1662
+ return {
1663
+ nextContent: `${content}${separator}${managedBlock}
1664
+ `,
1665
+ changed: true
1666
+ };
1667
+ }
1668
+ function removeManagedMarkdownBlock(content, blockId) {
1669
+ const range = findManagedBlockRange(content, blockId);
1670
+ if (!range) {
1671
+ return { nextContent: content, changed: false };
1672
+ }
1673
+ const before = content.slice(0, range.start);
1674
+ const after = content.slice(range.end);
1675
+ let next = `${before}${after}`;
1676
+ next = next.replace(/\n{3,}/gu, "\n\n");
1677
+ if (next === "\n") {
1678
+ next = "";
1679
+ }
1680
+ return { nextContent: next, changed: true };
1681
+ }
1682
+ async function readFileOrEmpty(filePath) {
1683
+ try {
1684
+ return await readFile5(filePath, "utf8");
1685
+ } catch {
1686
+ return "";
1687
+ }
1688
+ }
1689
+ async function upsertManagedMarkdownBlockFile(filePath, blockId, blockBody) {
1690
+ const content = await readFileOrEmpty(filePath);
1691
+ const updated = upsertManagedMarkdownBlock(content, blockId, blockBody);
1692
+ if (!updated.changed) {
1693
+ return false;
1694
+ }
1695
+ await writeFile5(filePath, updated.nextContent, "utf8");
1696
+ return true;
1697
+ }
1698
+ async function removeManagedMarkdownBlockFile(filePath, blockId) {
1699
+ const content = await readFileOrEmpty(filePath);
1700
+ const updated = removeManagedMarkdownBlock(content, blockId);
1701
+ if (!updated.changed) {
1702
+ return false;
1703
+ }
1704
+ await writeFile5(filePath, updated.nextContent, "utf8");
1705
+ return true;
1706
+ }
1707
+
1708
+ // src/plugins/patcher-checkpoint.ts
1709
+ var PLUGIN_ID5 = "patcher-checkpoint";
1710
+ var BLOCK_ID = "phase-5-checkpoint";
1711
+ var TARGET_FILE = "CLAUDE.md";
1712
+ function getTargetPath(cwd) {
1713
+ return join6(cwd, TARGET_FILE);
1714
+ }
1715
+ function hasManagedBlock(cwd) {
1716
+ const filePath = getTargetPath(cwd);
1717
+ if (!existsSync2(filePath)) {
1718
+ return false;
1719
+ }
1720
+ const content = readFileSync(filePath, "utf8");
1721
+ return content.includes(`<!-- agemon:start:${BLOCK_ID} -->`);
1722
+ }
1723
+ var patcherCheckpointPlugin = {
1724
+ id: PLUGIN_ID5,
1725
+ async detect(ctx) {
1726
+ return {
1727
+ present: hasManagedBlock(ctx.cwd),
1728
+ preExisting: false
1729
+ };
1730
+ },
1731
+ async install(ctx) {
1732
+ if (ctx.dryRun) {
1733
+ ctx.ui.info(`Would patch ${TARGET_FILE} with one managed markdown block`);
1734
+ return;
1735
+ }
1736
+ const targetPath = getTargetPath(ctx.cwd);
1737
+ const filePreviouslyExisted = existsSync2(targetPath);
1738
+ const changed = await upsertManagedMarkdownBlockFile(
1739
+ targetPath,
1740
+ BLOCK_ID,
1741
+ "This block is owned by agemon and safe to remove during nuke."
1742
+ );
1743
+ if (!changed) {
1744
+ return;
1745
+ }
1746
+ await ctx.manifest.recordAction({
1747
+ plugin: PLUGIN_ID5,
1748
+ type: "patched-block",
1749
+ target: TARGET_FILE,
1750
+ preExisting: filePreviouslyExisted
1751
+ });
1752
+ },
1753
+ async verify(ctx) {
1754
+ if (ctx.dryRun) {
1755
+ return { ok: true, detail: "dry-run" };
1756
+ }
1757
+ return {
1758
+ ok: hasManagedBlock(ctx.cwd),
1759
+ detail: `managed block present in ${TARGET_FILE}`
1760
+ };
1761
+ },
1762
+ async uninstall(ctx) {
1763
+ if (ctx.dryRun) {
1764
+ ctx.ui.info(`Would remove managed markdown block from ${TARGET_FILE}`);
1765
+ return;
1766
+ }
1767
+ const targetPath = getTargetPath(ctx.cwd);
1768
+ await removeManagedMarkdownBlockFile(targetPath, BLOCK_ID);
1769
+ await ctx.manifest.removeActionsForPlugin(PLUGIN_ID5);
1770
+ }
1771
+ };
1772
+
1773
+ // src/plugins/skills/index.ts
1774
+ import { existsSync as existsSync3 } from "fs";
1775
+ import { rm as rm5 } from "fs/promises";
1776
+ import { join as join7 } from "path";
1777
+
1778
+ // src/plugins/skills/catalog.ts
1779
+ var SKILL_BUNDLE = [
1780
+ {
1781
+ id: "web-design-guidelines",
1782
+ source: "vercel-labs/agent-skills",
1783
+ skillName: "web-design-guidelines",
1784
+ agent: "claude-code"
1785
+ },
1786
+ {
1787
+ id: "writing-guidelines",
1788
+ source: "vercel-labs/agent-skills",
1789
+ skillName: "writing-guidelines",
1790
+ agent: "claude-code"
1791
+ }
1792
+ ];
1793
+
1794
+ // src/plugins/skills/index.ts
1795
+ var PLUGIN_ID6 = "skills";
1796
+ var ACTION_TYPE_INSTALLED_SKILL = "installed-skill";
1797
+ var ACTION_TYPE_PREEXISTING_SKILL = "preexisting-skill";
1798
+ var ACTION_TYPE_GENERATED_SKILLS_LOCK = "generated-skills-lock";
1799
+ var SKILLS_LOCK_FILE = "skills-lock.json";
1800
+ var SKILLS_LIST_TIMEOUT_MS = 3e4;
1801
+ var SKILLS_ADD_TIMEOUT_MS = 18e4;
1802
+ var SKILLS_REMOVE_TIMEOUT_MS = 12e4;
1803
+ function getPluginActions5(ctx) {
1804
+ return ctx.manifest.getActions().filter((action) => action.plugin === PLUGIN_ID6);
1805
+ }
1806
+ function hasManagedInstallRecord2(ctx, skillName) {
1807
+ return getPluginActions5(ctx).some(
1808
+ (action) => action.type === ACTION_TYPE_INSTALLED_SKILL && action.target === skillName && action.preExisting === false
1809
+ );
1810
+ }
1811
+ function hasPreexistingRecord3(ctx, skillName) {
1812
+ return getPluginActions5(ctx).some(
1813
+ (action) => action.type === ACTION_TYPE_PREEXISTING_SKILL && action.target === skillName && action.preExisting === true
1814
+ );
1815
+ }
1816
+ function hasAnyRecordForSkill(ctx, skillName) {
1817
+ return hasManagedInstallRecord2(ctx, skillName) || hasPreexistingRecord3(ctx, skillName);
1818
+ }
1819
+ function getSkillsLockPath(cwd) {
1820
+ return join7(cwd, SKILLS_LOCK_FILE);
1821
+ }
1822
+ function hasManagedSkillsLockRecord(ctx) {
1823
+ return getPluginActions5(ctx).some(
1824
+ (action) => action.type === ACTION_TYPE_GENERATED_SKILLS_LOCK && action.target === SKILLS_LOCK_FILE && action.preExisting === false
1825
+ );
1826
+ }
1827
+ async function listInstalledSkillNames(ctx) {
1828
+ const listResult = await ctx.run("npx", ["skills", "list", "--json"], {
1829
+ timeoutMs: SKILLS_LIST_TIMEOUT_MS
1830
+ });
1831
+ if (listResult.code !== 0) {
1832
+ throw new Error(listResult.stderr || "npx skills list failed");
1833
+ }
1834
+ let parsed;
1835
+ try {
1836
+ parsed = JSON.parse(listResult.stdout || "[]");
1837
+ } catch {
1838
+ const stderr = listResult.stderr.trim();
1839
+ const stdoutPreview = listResult.stdout.trim().slice(0, 200);
1840
+ const detail = stderr || stdoutPreview || "no command output";
1841
+ throw new Error(`npx skills list returned invalid JSON: ${detail}`);
1842
+ }
1843
+ if (!Array.isArray(parsed)) {
1844
+ throw new Error("npx skills list returned non-array JSON");
1845
+ }
1846
+ const installedSkillNames = /* @__PURE__ */ new Set();
1847
+ for (const item of parsed) {
1848
+ if (!item || typeof item !== "object") {
1849
+ continue;
1850
+ }
1851
+ const typedItem = item;
1852
+ if (typeof typedItem.name === "string" && typedItem.name.length > 0) {
1853
+ installedSkillNames.add(typedItem.name);
1854
+ }
1855
+ }
1856
+ return installedSkillNames;
1857
+ }
1858
+ function allBundleSkillsPresent(installedSkillNames) {
1859
+ return SKILL_BUNDLE.every(
1860
+ (entry) => installedSkillNames.has(entry.skillName)
1861
+ );
1862
+ }
1863
+ async function detectSkills(ctx) {
1864
+ const installedSkillNames = await listInstalledSkillNames(ctx);
1865
+ if (!allBundleSkillsPresent(installedSkillNames)) {
1866
+ return { present: false, preExisting: false };
1867
+ }
1868
+ const allManaged = SKILL_BUNDLE.every(
1869
+ (entry) => hasManagedInstallRecord2(ctx, entry.skillName)
1870
+ );
1871
+ if (allManaged) {
1872
+ return { present: true, preExisting: false };
1873
+ }
1874
+ if (!ctx.dryRun) {
1875
+ for (const entry of SKILL_BUNDLE) {
1876
+ if (hasAnyRecordForSkill(ctx, entry.skillName)) {
1877
+ continue;
1878
+ }
1879
+ await ctx.manifest.recordAction({
1880
+ plugin: PLUGIN_ID6,
1881
+ type: ACTION_TYPE_PREEXISTING_SKILL,
1882
+ target: entry.skillName,
1883
+ preExisting: true
1884
+ });
1885
+ }
1886
+ }
1887
+ return { present: true, preExisting: true };
1888
+ }
1889
+ async function installSkills(ctx) {
1890
+ if (ctx.dryRun) {
1891
+ for (const entry of SKILL_BUNDLE) {
1892
+ ctx.ui.info(`Would add skill ${entry.skillName} from ${entry.source}`);
1893
+ }
1894
+ return;
1895
+ }
1896
+ const skillsLockPreviouslyExisted = existsSync3(getSkillsLockPath(ctx.cwd));
1897
+ const installedSkillNames = await listInstalledSkillNames(ctx);
1898
+ for (const entry of SKILL_BUNDLE) {
1899
+ if (installedSkillNames.has(entry.skillName)) {
1900
+ if (!hasAnyRecordForSkill(ctx, entry.skillName)) {
1901
+ await ctx.manifest.recordAction({
1902
+ plugin: PLUGIN_ID6,
1903
+ type: ACTION_TYPE_PREEXISTING_SKILL,
1904
+ target: entry.skillName,
1905
+ preExisting: true
1906
+ });
1907
+ }
1908
+ continue;
1909
+ }
1910
+ const addResult = await ctx.run(
1911
+ "npx",
1912
+ [
1913
+ "skills",
1914
+ "add",
1915
+ entry.source,
1916
+ "--skill",
1917
+ entry.skillName,
1918
+ "--agent",
1919
+ entry.agent,
1920
+ "--yes"
1921
+ ],
1922
+ {
1923
+ timeoutMs: SKILLS_ADD_TIMEOUT_MS
1924
+ }
1925
+ );
1926
+ if (addResult.code !== 0) {
1927
+ throw new Error(
1928
+ addResult.stderr || `npx skills add failed for ${entry.skillName}`
1929
+ );
1930
+ }
1931
+ await ctx.manifest.recordAction({
1932
+ plugin: PLUGIN_ID6,
1933
+ type: ACTION_TYPE_INSTALLED_SKILL,
1934
+ target: entry.skillName,
1935
+ preExisting: false
1936
+ });
1937
+ }
1938
+ const skillsLockExistsAfterInstall = existsSync3(getSkillsLockPath(ctx.cwd));
1939
+ if (!skillsLockPreviouslyExisted && skillsLockExistsAfterInstall && !hasManagedSkillsLockRecord(ctx)) {
1940
+ await ctx.manifest.recordAction({
1941
+ plugin: PLUGIN_ID6,
1942
+ type: ACTION_TYPE_GENERATED_SKILLS_LOCK,
1943
+ target: SKILLS_LOCK_FILE,
1944
+ preExisting: false
1945
+ });
1946
+ }
1947
+ }
1948
+ async function verifySkills(ctx) {
1949
+ if (ctx.dryRun) {
1950
+ return { ok: true, detail: "dry-run" };
1951
+ }
1952
+ const installedSkillNames = await listInstalledSkillNames(ctx);
1953
+ const missingSkillNames = SKILL_BUNDLE.map((entry) => entry.skillName).filter(
1954
+ (skillName) => !installedSkillNames.has(skillName)
1955
+ );
1956
+ if (missingSkillNames.length > 0) {
1957
+ return {
1958
+ ok: false,
1959
+ detail: `Missing skill(s): ${missingSkillNames.join(", ")}`
1960
+ };
1961
+ }
1962
+ return {
1963
+ ok: true,
1964
+ detail: `${SKILL_BUNDLE.length} skill bundle entries present`
1965
+ };
1966
+ }
1967
+ async function uninstallSkills(ctx) {
1968
+ const managedActions = getPluginActions5(ctx).filter(
1969
+ (action) => action.type === ACTION_TYPE_INSTALLED_SKILL && action.preExisting === false
1970
+ );
1971
+ const shouldRemoveManagedSkillsLock = hasManagedSkillsLockRecord(ctx);
1972
+ if (ctx.dryRun) {
1973
+ if (managedActions.length === 0) {
1974
+ if (shouldRemoveManagedSkillsLock) {
1975
+ ctx.ui.info(`Would remove ${SKILLS_LOCK_FILE}`);
1976
+ } else {
1977
+ ctx.ui.info("Would keep pre-existing skills untouched");
1978
+ }
1979
+ return;
1980
+ }
1981
+ for (const action of managedActions) {
1982
+ ctx.ui.info(`Would remove skill ${action.target}`);
1983
+ }
1984
+ if (shouldRemoveManagedSkillsLock) {
1985
+ ctx.ui.info(`Would remove ${SKILLS_LOCK_FILE}`);
1986
+ }
1987
+ return;
1988
+ }
1989
+ const installedSkillNames = await listInstalledSkillNames(ctx);
1990
+ for (const action of managedActions) {
1991
+ if (!installedSkillNames.has(action.target)) {
1992
+ continue;
1993
+ }
1994
+ const removeResult = await ctx.run(
1995
+ "npx",
1996
+ ["skills", "remove", action.target, "--yes"],
1997
+ {
1998
+ timeoutMs: SKILLS_REMOVE_TIMEOUT_MS
1999
+ }
2000
+ );
2001
+ if (removeResult.code !== 0) {
2002
+ throw new Error(
2003
+ removeResult.stderr || `npx skills remove failed for ${action.target}`
2004
+ );
2005
+ }
2006
+ }
2007
+ if (shouldRemoveManagedSkillsLock) {
2008
+ await rm5(getSkillsLockPath(ctx.cwd), { force: true });
2009
+ }
2010
+ await ctx.manifest.removeActionsForPlugin(PLUGIN_ID6);
2011
+ }
2012
+ var skillsPlugin = {
2013
+ id: PLUGIN_ID6,
2014
+ detect: detectSkills,
2015
+ install: installSkills,
2016
+ verify: verifySkills,
2017
+ uninstall: uninstallSkills
2018
+ };
2019
+
2020
+ // src/plugins/test-plugin.ts
2021
+ var testPlugin = {
2022
+ id: "test-plugin",
2023
+ async detect(ctx) {
2024
+ return {
2025
+ present: ctx.manifest.hasActionForPlugin("test-plugin"),
2026
+ preExisting: false
2027
+ };
2028
+ },
2029
+ async install(ctx) {
2030
+ if (ctx.dryRun) {
2031
+ return;
2032
+ }
2033
+ await ctx.manifest.recordAction({
2034
+ plugin: "test-plugin",
2035
+ type: "simulated-install",
2036
+ target: "phase-1 orchestrator checkpoint",
2037
+ preExisting: false
2038
+ });
2039
+ },
2040
+ async verify(ctx) {
2041
+ if (ctx.dryRun) {
2042
+ return { ok: true, detail: "dry-run" };
2043
+ }
2044
+ return {
2045
+ ok: ctx.manifest.hasActionForPlugin("test-plugin"),
2046
+ detail: "action logged in manifest"
2047
+ };
2048
+ },
2049
+ async uninstall(ctx) {
2050
+ if (ctx.dryRun) {
2051
+ return;
2052
+ }
2053
+ await ctx.manifest.removeActionsForPlugin("test-plugin");
2054
+ }
2055
+ };
2056
+
2057
+ // src/plugins/workflow-scaffolder/index.ts
2058
+ import { existsSync as existsSync4 } from "fs";
2059
+ import { mkdir as mkdir5, rm as rm6, writeFile as writeFile6 } from "fs/promises";
2060
+ import { join as join8 } from "path";
2061
+
2062
+ // src/plugins/workflow-scaffolder/catalog.ts
2063
+ var WORKFLOW_BUNDLE = [
2064
+ {
2065
+ id: "claude-code-security-review",
2066
+ filename: "claude-code-security-review.yml",
2067
+ contents: [
2068
+ "name: Claude Code Security Review",
2069
+ "",
2070
+ "on:",
2071
+ " pull_request:",
2072
+ "",
2073
+ "permissions:",
2074
+ " contents: read",
2075
+ " security-events: write",
2076
+ "",
2077
+ "jobs:",
2078
+ " security-review:",
2079
+ " runs-on: ubuntu-latest",
2080
+ " steps:",
2081
+ " - name: Checkout repository",
2082
+ " uses: actions/checkout@v4",
2083
+ "",
2084
+ " - name: Run Claude Code Security Review",
2085
+ " uses: anthropics/claude-code-security-review@main",
2086
+ ""
2087
+ ].join("\n")
2088
+ },
2089
+ {
2090
+ id: "code-review-graph-action",
2091
+ filename: "code-review-graph-action.yml",
2092
+ contents: [
2093
+ "name: Code Review Graph Action",
2094
+ "",
2095
+ "on:",
2096
+ " pull_request:",
2097
+ "",
2098
+ "permissions:",
2099
+ " contents: read",
2100
+ "",
2101
+ "jobs:",
2102
+ " code-review-graph:",
2103
+ " runs-on: ubuntu-latest",
2104
+ " steps:",
2105
+ " - name: Checkout repository",
2106
+ " uses: actions/checkout@v4",
2107
+ "",
2108
+ " - name: Run Code Review Graph",
2109
+ " uses: tirth8205/code-review-graph@main",
2110
+ ""
2111
+ ].join("\n")
2112
+ }
2113
+ ];
2114
+
2115
+ // src/plugins/workflow-scaffolder/index.ts
2116
+ var PLUGIN_ID7 = "workflow-scaffolder";
2117
+ var WORKFLOW_DIRECTORY = ".github/workflows";
2118
+ var ACTION_TYPE_SCAFFOLDED_WORKFLOW = "scaffolded-workflow";
2119
+ function getPluginActions6(ctx) {
2120
+ return ctx.manifest.getActions().filter((action) => action.plugin === PLUGIN_ID7);
2121
+ }
2122
+ function buildWorkflowPath(cwd, entry) {
2123
+ return join8(cwd, WORKFLOW_DIRECTORY, entry.filename);
2124
+ }
2125
+ function buildWorkflowTarget(entry) {
2126
+ return `${WORKFLOW_DIRECTORY}/${entry.filename}`;
2127
+ }
2128
+ function hasManagedWorkflowRecord(ctx, workflowTarget) {
2129
+ return getPluginActions6(ctx).some(
2130
+ (action) => action.type === ACTION_TYPE_SCAFFOLDED_WORKFLOW && action.target === workflowTarget && action.preExisting === false
2131
+ );
2132
+ }
2133
+ function getUnmanagedCollisions(ctx) {
2134
+ const collisions = [];
2135
+ for (const entry of WORKFLOW_BUNDLE) {
2136
+ const workflowPath = buildWorkflowPath(ctx.cwd, entry);
2137
+ const workflowTarget = buildWorkflowTarget(entry);
2138
+ if (!existsSync4(workflowPath)) {
2139
+ continue;
2140
+ }
2141
+ if (hasManagedWorkflowRecord(ctx, workflowTarget)) {
2142
+ continue;
2143
+ }
2144
+ collisions.push(workflowTarget);
2145
+ }
2146
+ return collisions;
2147
+ }
2148
+ function assertNoUnmanagedCollisions(ctx) {
2149
+ const collisions = getUnmanagedCollisions(ctx);
2150
+ if (collisions.length === 0) {
2151
+ return;
2152
+ }
2153
+ throw new Error(
2154
+ `Refusing to overwrite pre-existing workflow file(s): ${collisions.join(", ")}`
2155
+ );
2156
+ }
2157
+ function allBundleWorkflowsExist(ctx) {
2158
+ return WORKFLOW_BUNDLE.every(
2159
+ (entry) => existsSync4(buildWorkflowPath(ctx.cwd, entry))
2160
+ );
2161
+ }
2162
+ async function detectWorkflows(ctx) {
2163
+ assertNoUnmanagedCollisions(ctx);
2164
+ if (!allBundleWorkflowsExist(ctx)) {
2165
+ return { present: false, preExisting: false };
2166
+ }
2167
+ return { present: true, preExisting: false };
2168
+ }
2169
+ async function installWorkflows(ctx) {
2170
+ assertNoUnmanagedCollisions(ctx);
2171
+ if (ctx.dryRun) {
2172
+ for (const entry of WORKFLOW_BUNDLE) {
2173
+ ctx.ui.info(`Would scaffold workflow ${buildWorkflowTarget(entry)}`);
2174
+ }
2175
+ return;
2176
+ }
2177
+ const workflowDirectoryPath = join8(ctx.cwd, WORKFLOW_DIRECTORY);
2178
+ await mkdir5(workflowDirectoryPath, { recursive: true });
2179
+ for (const entry of WORKFLOW_BUNDLE) {
2180
+ const workflowPath = buildWorkflowPath(ctx.cwd, entry);
2181
+ const workflowTarget = buildWorkflowTarget(entry);
2182
+ await writeFile6(workflowPath, entry.contents, "utf8");
2183
+ if (!hasManagedWorkflowRecord(ctx, workflowTarget)) {
2184
+ await ctx.manifest.recordAction({
2185
+ plugin: PLUGIN_ID7,
2186
+ type: ACTION_TYPE_SCAFFOLDED_WORKFLOW,
2187
+ target: workflowTarget,
2188
+ preExisting: false
2189
+ });
2190
+ }
2191
+ }
2192
+ }
2193
+ async function verifyWorkflows(ctx) {
2194
+ if (ctx.dryRun) {
2195
+ return { ok: true, detail: "dry-run" };
2196
+ }
2197
+ const missingWorkflows = WORKFLOW_BUNDLE.map(
2198
+ (entry) => buildWorkflowTarget(entry)
2199
+ ).filter((workflowTarget) => !existsSync4(join8(ctx.cwd, workflowTarget)));
2200
+ if (missingWorkflows.length > 0) {
2201
+ return {
2202
+ ok: false,
2203
+ detail: `Missing workflow file(s): ${missingWorkflows.join(", ")}`
2204
+ };
2205
+ }
2206
+ return {
2207
+ ok: true,
2208
+ detail: `${WORKFLOW_BUNDLE.length} workflow bundle entries present`
2209
+ };
2210
+ }
2211
+ async function uninstallWorkflows(ctx) {
2212
+ const managedActions = getPluginActions6(ctx).filter(
2213
+ (action) => action.type === ACTION_TYPE_SCAFFOLDED_WORKFLOW && action.preExisting === false
2214
+ );
2215
+ if (ctx.dryRun) {
2216
+ if (managedActions.length === 0) {
2217
+ ctx.ui.info("Would keep pre-existing workflow files untouched");
2218
+ return;
2219
+ }
2220
+ for (const action of managedActions) {
2221
+ ctx.ui.info(`Would remove workflow ${action.target}`);
2222
+ }
2223
+ return;
2224
+ }
2225
+ for (const action of managedActions) {
2226
+ await rm6(join8(ctx.cwd, action.target), { force: true });
2227
+ }
2228
+ await ctx.manifest.removeActionsForPlugin(PLUGIN_ID7);
2229
+ }
2230
+ var workflowScaffolderPlugin = {
2231
+ id: PLUGIN_ID7,
2232
+ detect: detectWorkflows,
2233
+ install: installWorkflows,
2234
+ verify: verifyWorkflows,
2235
+ uninstall: uninstallWorkflows
2236
+ };
2237
+
2238
+ // src/plugins/index.ts
2239
+ var corePluginOrder = [
2240
+ crgPlugin,
2241
+ daemonPlugin,
2242
+ skillsPlugin,
2243
+ workflowScaffolderPlugin,
2244
+ cliToolPlugin,
2245
+ masterPromptPlugin
2246
+ ];
2247
+ function getRegisteredPlugins() {
2248
+ const devOnlyPlugins = process.env.AGEMON_DEV === "1" ? [testPlugin, patcherCheckpointPlugin] : [];
2249
+ return [...corePluginOrder, ...devOnlyPlugins];
2250
+ }
2251
+
2252
+ // src/ui/banner.ts
2253
+ import boxen from "boxen";
2254
+
2255
+ // src/ui/theme.ts
2256
+ import pc from "picocolors";
2257
+ function colorsEnabled() {
2258
+ if (process.env.NO_COLOR) return false;
2259
+ return Boolean(process.stdout.isTTY);
2260
+ }
2261
+ function colors() {
2262
+ return pc.createColors(colorsEnabled());
2263
+ }
2264
+ var theme = {
2265
+ success: (text) => colors().green(text),
2266
+ warn: (text) => colors().yellow(text),
2267
+ error: (text) => colors().red(text),
2268
+ info: (text) => colors().cyan(text),
2269
+ muted: (text) => colors().gray(text),
2270
+ bold: (text) => colors().bold(text)
2271
+ };
2272
+
2273
+ // src/ui/banner.ts
2274
+ function renderBanner(title, message) {
2275
+ return boxen(`${theme.bold(title)}
2276
+
2277
+ ${message}`, {
2278
+ padding: 1,
2279
+ borderColor: "cyan",
2280
+ borderStyle: "round"
2281
+ });
2282
+ }
2283
+
2284
+ // src/ui/spinner.ts
2285
+ import ora from "ora";
2286
+ function isInteractive() {
2287
+ return Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
2288
+ }
2289
+ function createStepSpinner() {
2290
+ let step = 0;
2291
+ let current;
2292
+ return {
2293
+ start(label) {
2294
+ step += 1;
2295
+ const prefixed = `[${step}] ${label}`;
2296
+ if (isInteractive()) {
2297
+ current = ora(prefixed).start();
2298
+ } else {
2299
+ console.log(prefixed);
2300
+ }
2301
+ },
2302
+ succeed(label) {
2303
+ const text = label ?? current?.text ?? "";
2304
+ if (isInteractive() && current) {
2305
+ current.succeed(text);
2306
+ } else {
2307
+ console.log(theme.success(`\u2714 ${text}`));
2308
+ }
2309
+ },
2310
+ fail(label) {
2311
+ const text = label ?? current?.text ?? "";
2312
+ if (isInteractive() && current) {
2313
+ current.fail(text);
2314
+ } else {
2315
+ console.log(theme.error(`\u2718 ${text}`));
2316
+ }
2317
+ },
2318
+ info(label) {
2319
+ if (isInteractive() && current) {
2320
+ current.info(label);
2321
+ } else {
2322
+ console.log(theme.info(label));
2323
+ }
2324
+ }
2325
+ };
2326
+ }
2327
+
2328
+ // src/cli/index.ts
2329
+ var VERSION = "0.1.0";
2330
+ var DESCRIPTION = "Bootstraps and reverses an AI coding agent's working environment in a repo.";
2331
+ async function runInstall(options) {
2332
+ const spinner = createStepSpinner();
2333
+ const plugins = getRegisteredPlugins().filter(
2334
+ (plugin) => !(options.skipDaemon && plugin.id === "daemon")
2335
+ );
2336
+ const context = await createContext({
2337
+ dryRun: Boolean(options.dryRun),
2338
+ yes: Boolean(options.yes),
2339
+ ui: spinner
2340
+ });
2341
+ await installPlugins(context, plugins, { only: options.only });
2342
+ }
2343
+ async function runNuke(options) {
2344
+ const spinner = createStepSpinner();
2345
+ const plugins = getRegisteredPlugins();
2346
+ const context = await createContext({
2347
+ dryRun: Boolean(options.dryRun),
2348
+ yes: Boolean(options.yes),
2349
+ ui: spinner
2350
+ });
2351
+ await uninstallPlugins(context, plugins, { only: options.only });
2352
+ }
2353
+ function createProgram() {
2354
+ const program = new Command();
2355
+ program.name("agemon").description(DESCRIPTION).version(VERSION).option("--dry-run", "narrate actions without making changes").option("--yes", "skip confirmation prompts").option("--skip-daemon", "skip daemon registration").option("--only <plugins>", "comma-separated list of plugin ids to run").option("--no-color", "disable colored output").option("-v, --verbose", "show raw subprocess output beneath each step").option("-q, --quiet", "print only the final summary").addHelpText("beforeAll", () => `${renderBanner("agemon", DESCRIPTION)}
2356
+ `).hook("preAction", (thisCommand) => {
2357
+ if (thisCommand.opts().color === false) {
2358
+ process.env.NO_COLOR = "1";
2359
+ }
2360
+ }).action((options) => runInstall(options));
2361
+ program.command("nuke").description("Reverse agemon-managed changes").option("--dry-run", "narrate actions without making changes").option("--yes", "skip confirmation prompts").option("--only <plugins>", "comma-separated list of plugin ids to run").action((options) => runNuke(options));
2362
+ program.exitOverride();
2363
+ return program;
2364
+ }
2365
+ async function runCli(argv) {
2366
+ const program = createProgram();
2367
+ try {
2368
+ assertFakeBackendsAreDevOnly();
2369
+ await program.parseAsync(argv, { from: "user" });
2370
+ return 0;
2371
+ } catch (error) {
2372
+ if (error instanceof CommanderError) {
2373
+ return error.exitCode;
2374
+ }
2375
+ console.error(
2376
+ theme.error(error instanceof Error ? error.message : String(error))
2377
+ );
2378
+ return 1;
2379
+ }
2380
+ }
2381
+ var isDirectlyExecuted = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
2382
+ if (isDirectlyExecuted) {
2383
+ process.exitCode = await runCli(process.argv.slice(2));
2384
+ }
2385
+ export {
2386
+ runCli
2387
+ };
2388
+ //# sourceMappingURL=index.js.map