@clearlydev/nx-archweave 0.3.1 → 0.3.3

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 (46) hide show
  1. package/dist/bin/review-verdict.js +5 -248
  2. package/dist/eslint/index.js +28 -321
  3. package/dist/generators/_chunks/chunk-2WSX5YNB.js +274 -0
  4. package/dist/generators/_chunks/chunk-34V6HAJT.js +55 -0
  5. package/dist/generators/_chunks/chunk-3A6RZUCZ.js +114 -0
  6. package/dist/generators/_chunks/chunk-47T7NOPP.js +30 -0
  7. package/dist/generators/_chunks/chunk-5MI3O4VZ.js +99 -0
  8. package/dist/generators/_chunks/chunk-67INEVWL.js +1068 -0
  9. package/dist/generators/_chunks/chunk-6ZTIUBB2.js +139 -0
  10. package/dist/generators/_chunks/chunk-AIJCJ7JK.js +203 -0
  11. package/dist/generators/_chunks/chunk-AQHV7DJS.js +656 -0
  12. package/dist/generators/_chunks/chunk-C2VVUWBA.js +386 -0
  13. package/dist/generators/_chunks/chunk-EXYZLL7A.js +234 -0
  14. package/dist/generators/_chunks/chunk-FQHFT5IH.js +50 -0
  15. package/dist/generators/_chunks/chunk-H222FPLK.js +80 -0
  16. package/dist/generators/_chunks/chunk-IIDOBI42.js +616 -0
  17. package/dist/generators/_chunks/chunk-JHRTVURZ.js +189 -0
  18. package/dist/generators/_chunks/chunk-L2RZ2LBO.js +132 -0
  19. package/dist/generators/_chunks/chunk-MQNQJOWA.js +217 -0
  20. package/dist/generators/_chunks/chunk-QQNDYJTR.js +243 -0
  21. package/dist/generators/_chunks/chunk-SFXU6IUS.js +0 -0
  22. package/dist/generators/_chunks/chunk-SNBIVIMI.js +89 -0
  23. package/dist/generators/_chunks/chunk-TUN34LAQ.js +8 -0
  24. package/dist/generators/_chunks/chunk-TXAQ2GJC.js +20 -0
  25. package/dist/generators/_chunks/chunk-U6LAFHSF.js +49 -0
  26. package/dist/generators/_chunks/chunk-URV6FLUH.js +73 -0
  27. package/dist/generators/_chunks/chunk-YNMLB4DL.js +25 -0
  28. package/dist/generators/_chunks/chunk-YY2UESLM.js +282 -0
  29. package/dist/generators/add-agent-rules/generator.js +11 -842
  30. package/dist/generators/add-backend/generator.js +11 -906
  31. package/dist/generators/add-fsd-lite/generator.js +12 -801
  32. package/dist/generators/add-github-templates/generator.js +13 -116
  33. package/dist/generators/add-integration/generator.js +15 -743
  34. package/dist/generators/add-review/generator.js +12 -1231
  35. package/dist/generators/allow-builds/generator.js +7 -71
  36. package/dist/generators/doctor/generator.js +74 -2042
  37. package/dist/generators/entity/generator.js +15 -604
  38. package/dist/generators/feature/generator.js +15 -604
  39. package/dist/generators/init/generator.js +68 -3376
  40. package/dist/generators/install-integration/generator.js +35 -871
  41. package/dist/generators/page/generator.js +15 -604
  42. package/dist/generators/record-integration/generator.js +12 -778
  43. package/dist/generators/shared/generator.js +15 -604
  44. package/dist/generators/widget/generator.js +15 -604
  45. package/dist/index.js +5 -663
  46. package/package.json +2 -2
@@ -1,3 +1,15 @@
1
+ import {
2
+ runRecordPipeline
3
+ } from "../_chunks/chunk-URV6FLUH.js";
4
+ import "../_chunks/chunk-U6LAFHSF.js";
5
+ import {
6
+ PLUGIN_IDS,
7
+ createIntegrationsCompositionRoot,
8
+ getCatalogEntry,
9
+ recordMetadataFor
10
+ } from "../_chunks/chunk-AQHV7DJS.js";
11
+ import "../_chunks/chunk-34V6HAJT.js";
12
+
1
13
  // src/generators/record-integration/render.ts
2
14
  function renderRecordIntegrationView(view) {
3
15
  const lines = [
@@ -35,784 +47,6 @@ function renderRecordIntegrationView(view) {
35
47
  return lines.join("\n");
36
48
  }
37
49
 
38
- // ../integrations/dist/registry/integration-registry.js
39
- function createIntegrationRegistry(integrations) {
40
- const seenIds = /* @__PURE__ */ new Set();
41
- for (const integration of integrations) {
42
- if (seenIds.has(integration.id)) {
43
- throw new Error(`Duplicate integration id: "${integration.id}"`);
44
- }
45
- seenIds.add(integration.id);
46
- }
47
- return integrations;
48
- }
49
- function listIntegrations(registry) {
50
- return registry;
51
- }
52
- function getIntegration(registry, id) {
53
- return registry.find((integration) => integration.id === id);
54
- }
55
-
56
- // ../integrations/dist/manifest/manifest.js
57
- import { existsSync, lstatSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
58
- import { join } from "node:path";
59
- import { randomBytes } from "node:crypto";
60
- var MANIFEST_FILENAME = "archweave-integrations.json";
61
- function createEmptyManifest() {
62
- return { integrations: {} };
63
- }
64
- function loadManifest(root) {
65
- const manifestPath = join(root, MANIFEST_FILENAME);
66
- if (!existsSync(manifestPath)) {
67
- return createEmptyManifest();
68
- }
69
- try {
70
- const parsed = JSON.parse(readFileSync(manifestPath, "utf8"));
71
- if (parsed && typeof parsed === "object" && typeof parsed.integrations === "object") {
72
- return { integrations: parsed.integrations ?? {} };
73
- }
74
- return createEmptyManifest();
75
- } catch {
76
- return createEmptyManifest();
77
- }
78
- }
79
- function serializeManifest(manifest) {
80
- return `${JSON.stringify(manifest, null, 2)}
81
- `;
82
- }
83
- function writeManifestAtomic(root, manifest) {
84
- const finalPath = join(root, MANIFEST_FILENAME);
85
- if (existsSync(finalPath) && lstatSync(finalPath).isSymbolicLink()) {
86
- throw new Error(`Refusing to write ${MANIFEST_FILENAME}: the path is a symlink. Remove or replace it with a regular file first.`);
87
- }
88
- const suffix = randomBytes(6).toString("hex");
89
- const tempPath = join(root, `.${MANIFEST_FILENAME}.tmp-${suffix}`);
90
- try {
91
- writeFileSync(tempPath, serializeManifest(manifest));
92
- renameSync(tempPath, finalPath);
93
- } catch (error) {
94
- if (existsSync(tempPath)) {
95
- unlinkSync(tempPath);
96
- }
97
- throw error;
98
- }
99
- }
100
-
101
- // ../integrations/dist/manifest/record-plan.js
102
- function entriesEqual(a, b) {
103
- return JSON.stringify(a) === JSON.stringify(b);
104
- }
105
- function buildRecordPlan(input) {
106
- const isRecordable = input.verification.verified && input.verificationSource === "claude-cli";
107
- const proposedEntry = isRecordable ? {
108
- source: input.metadata.source,
109
- repository: input.metadata.repository,
110
- version: input.verification.resolvedVersion,
111
- method: input.metadata.method,
112
- managedBy: "nx-archweave",
113
- verification: "passed",
114
- target: input.target,
115
- evidenceKind: "claude-cli"
116
- } : void 0;
117
- const hasConflict = input.existingEntry !== void 0 && proposedEntry !== void 0 && !entriesEqual(input.existingEntry, proposedEntry);
118
- return {
119
- id: input.id,
120
- target: input.target,
121
- verificationSource: input.verificationSource,
122
- currentVerification: input.verification,
123
- existingEntry: input.existingEntry,
124
- proposedEntry,
125
- hasConflict
126
- };
127
- }
128
-
129
- // ../shared/dist/lib/spawnable-command.js
130
- import { statSync } from "node:fs";
131
- import { win32 } from "node:path";
132
- function isFile(path) {
133
- try {
134
- return statSync(path).isFile();
135
- } catch {
136
- return false;
137
- }
138
- }
139
- var DEFAULT_ENV = { platform: process.platform, env: process.env, isFile };
140
- function envValue(env, name) {
141
- const key = Object.keys(env).find((candidate) => candidate.toUpperCase() === name);
142
- return key === void 0 ? void 0 : env[key];
143
- }
144
- function resolveWindowsCommand(command, environment) {
145
- const exts = (envValue(environment.env, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
146
- const candidates = (base) => win32.extname(base) !== "" ? [base] : exts.map((ext) => base + ext);
147
- if (win32.isAbsolute(command) || /[\\/]/.test(command)) {
148
- return candidates(command).find(environment.isFile);
149
- }
150
- const dirs = (envValue(environment.env, "PATH") ?? "").split(";").filter(Boolean);
151
- for (const dir of dirs) {
152
- const found = candidates(win32.join(dir, command)).find(environment.isFile);
153
- if (found !== void 0)
154
- return found;
155
- }
156
- return void 0;
157
- }
158
- var META_CHARS = /([()\][%!^"`<>&|;, *?])/g;
159
- var BIN_SHIM = /node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/i;
160
- function escapeCmdArgument(arg, doubleEscape = false) {
161
- let quoted = arg.replace(/(\\*)"/g, '$1$1\\"');
162
- quoted = quoted.replace(/(\\*)$/, "$1$1");
163
- const escaped = `"${quoted}"`.replace(META_CHARS, "^$1");
164
- return doubleEscape ? escaped.replace(META_CHARS, "^$1") : escaped;
165
- }
166
- function spawnableCommand(command, args, environment = DEFAULT_ENV) {
167
- if (environment.platform !== "win32")
168
- return { command, args: [...args] };
169
- const resolved = resolveWindowsCommand(command, environment);
170
- if (resolved === void 0 || !/\.(cmd|bat)$/i.test(resolved))
171
- return { command, args: [...args] };
172
- const doubleEscape = BIN_SHIM.test(resolved);
173
- const line = [resolved.replace(META_CHARS, "^$1"), ...args.map((arg) => escapeCmdArgument(arg, doubleEscape))].join(" ");
174
- return {
175
- command: envValue(environment.env, "COMSPEC") ?? "cmd.exe",
176
- args: ["/d", "/s", "/c", `"${line}"`],
177
- windowsVerbatimArguments: true
178
- };
179
- }
180
-
181
- // ../integrations/dist/process/read-only-command-runner.js
182
- var CLAUDE_CLI_TIMEOUT_MS = 5e3;
183
- var CLAUDE_CLI_STDOUT_LIMIT_BYTES = 1048576;
184
- var CLAUDE_CLI_STDERR_LIMIT_BYTES = 65536;
185
-
186
- // ../integrations/dist/catalog/plugin-catalog.js
187
- var PLUGIN_HOSTS = ["claude-code"];
188
- var PLUGIN_CATALOG = [
189
- {
190
- // Researched 2026-07-15. The claude-code values are the ones the manifest and verifier
191
- // were measured with. ponytail documents official installs for ~20 hosts; only claude-code
192
- // has an adapter.
193
- id: "ponytail",
194
- displayName: "ponytail",
195
- // The project's own tagline.
196
- description: 'over-engineering review rules and skills \u2014 "makes your AI agent think like the laziest senior dev in the room"',
197
- homepage: "https://ponytail.dev",
198
- repository: "https://github.com/DietrichGebert/ponytail",
199
- license: "MIT",
200
- npmPackage: "@dietrichgebert/ponytail",
201
- installDocs: "https://github.com/DietrichGebert/ponytail#install",
202
- uninstallSteps: [
203
- "Run this from a clone of https://github.com/DietrichGebert/ponytail (or before removing the plugin below \u2014 the script is itself a plugin file, so removing the plugin first deletes it): node scripts/uninstall.js",
204
- "Then remove the plugin via your host's own command, e.g. in Claude Code: /plugin uninstall ponytail@ponytail (or use the Claude Code Desktop app: Customize > manage marketplaces > remove plugin).",
205
- "See https://github.com/DietrichGebert/ponytail#uninstall for the full, current official instructions."
206
- ],
207
- hosts: {
208
- "claude-code": { name: "ponytail", marketplace: "DietrichGebert/ponytail", pluginId: "ponytail@ponytail" }
209
- }
210
- }
211
- ];
212
- var PLUGIN_IDS = PLUGIN_CATALOG.map((entry) => entry.id);
213
- function getCatalogEntry(id) {
214
- return PLUGIN_CATALOG.find((entry) => entry.id === id);
215
- }
216
- var CLAUDE_MARKETPLACE_METHOD = "claude-code-plugin-marketplace";
217
- function recordMetadataFor(entry) {
218
- const claude = entry.hosts["claude-code"];
219
- if (!claude)
220
- return void 0;
221
- return { source: claude.marketplace, repository: entry.repository, method: CLAUDE_MARKETPLACE_METHOD };
222
- }
223
-
224
- // ../integrations/dist/hosts/claude-code/detect.js
225
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
226
- import { join as join2 } from "node:path";
227
- async function detectWorkspacePackage(entry, context) {
228
- const npmPackage = entry.npmPackage;
229
- if (npmPackage === void 0) {
230
- return { installed: false, managedBy: "unknown", evidence: [] };
231
- }
232
- const packageJsonPath = join2(context.workspaceRoot, "package.json");
233
- if (!existsSync2(packageJsonPath)) {
234
- return { installed: false, managedBy: "unknown", evidence: [] };
235
- }
236
- let pkg;
237
- try {
238
- pkg = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
239
- } catch {
240
- return { installed: false, managedBy: "unknown", evidence: [] };
241
- }
242
- const deps = pkg["dependencies"] ?? {};
243
- const devDeps = pkg["devDependencies"] ?? {};
244
- const version = deps[npmPackage] ?? devDeps[npmPackage];
245
- if (!version) {
246
- return { installed: false, managedBy: "unknown", evidence: [] };
247
- }
248
- return {
249
- installed: true,
250
- installedVersion: version,
251
- managedBy: "external-tool",
252
- evidence: [`package.json: ${npmPackage}@${version}`]
253
- };
254
- }
255
-
256
- // ../integrations/dist/hosts/claude-code/plan.js
257
- var VERSION_DECIDED_BY_MARKETPLACE = "latest (decided by the marketplace)";
258
- var WORKSPACE_FILES_BY_SCOPE = {
259
- user: [],
260
- project: [".claude/settings.json"],
261
- local: [".claude/settings.local.json"]
262
- };
263
- var DEFAULT_PLUGIN_SCOPE = "project";
264
- function createClaudeMarketplacePlan(entry) {
265
- return async (options) => {
266
- const distribution = entry.hosts["claude-code"];
267
- if (options.targetEnvironment !== "claude-code" || distribution === void 0) {
268
- const supported = Object.entries(entry.hosts).filter(([, value]) => value !== void 0).map(([host]) => host).join(", ");
269
- throw new Error(`Target environment "${options.targetEnvironment}" is not supported by this integration. Supported: ${supported}.`);
270
- }
271
- const resolvedVersion = options.versionRange ?? VERSION_DECIDED_BY_MARKETPLACE;
272
- const scope = options.scope ?? DEFAULT_PLUGIN_SCOPE;
273
- return {
274
- officialCommand: {
275
- executable: "claude",
276
- args: [
277
- "plugin",
278
- "install",
279
- distribution.name,
280
- "--marketplace",
281
- distribution.marketplace,
282
- "--scope",
283
- scope,
284
- "--json"
285
- ]
286
- },
287
- resolvedVersion,
288
- filesCreated: [],
289
- // project/local scope: Claude Code records the plugin in the workspace's own
290
- // .claude settings — shown before approval as the files that will change.
291
- filesModified: [...WORKSPACE_FILES_BY_SCOPE[scope]],
292
- conflicts: [],
293
- sourceRepository: entry.repository,
294
- officialHomepage: entry.homepage,
295
- license: entry.license,
296
- requiresManualExecution: false
297
- };
298
- };
299
- }
300
-
301
- // ../integrations/dist/hosts/claude-code/install.js
302
- import { homedir } from "node:os";
303
- function manualInstallSteps(entry) {
304
- const distribution = entry.hosts["claude-code"];
305
- if (distribution === void 0)
306
- return [`See ${entry.installDocs} for the official instructions.`];
307
- return [
308
- `In Claude Code, send this as its own prompt: /plugin marketplace add ${distribution.marketplace}`,
309
- `Then, as a second separate prompt: /plugin install ${distribution.pluginId}`,
310
- `See ${entry.installDocs} for the official instructions.`
311
- ];
312
- }
313
- var CLAUDE_CLI_INSTALL_TIMEOUT_MS = 12e4;
314
- var CLAUDE_CLI_INSTALL_STDOUT_LIMIT_BYTES = 1048576;
315
- var CLAUDE_CLI_INSTALL_STDERR_LIMIT_BYTES = 262144;
316
- function defaultRerunCommand(entry, runNx = "pnpm nx") {
317
- return `${runNx} g @clearlydev/nx-archweave:install-integration --tool=${entry.id} --apply`;
318
- }
319
- function chatAlternative(entry) {
320
- return ["Alternatively, inside a Claude Code session (the chat path):", ...manualInstallSteps(entry)];
321
- }
322
- var MARKETPLACE_NOTE = "If the marketplace was registered before the failure, it stays in your user settings \u2014 see `claude plugin marketplace list`.";
323
- function claudeCliNotFoundSteps(entry, rerunCommand) {
324
- return [
325
- "The claude CLI was not found on PATH. Install the Claude Code CLI from your terminal: npm install -g @anthropic-ai/claude-code (setup guide: https://code.claude.com/docs/en/setup)",
326
- `Then rerun: ${rerunCommand}`,
327
- ...chatAlternative(entry)
328
- ];
329
- }
330
- function claudeCliUnsupportedSteps(entry, rerunCommand, cliSaid) {
331
- return [
332
- `Your claude CLI does not know this command (it said: ${cliSaid}). Scriptable plugin install needs Claude Code 2.1.285 or newer: run \`claude update\`, or reinstall it with npm install -g @anthropic-ai/claude-code@latest`,
333
- `Then rerun: ${rerunCommand}`,
334
- ...chatAlternative(entry)
335
- ];
336
- }
337
- var TAIL_CHARS = 500;
338
- var UNSUPPORTED_CLI_PATTERN = /error: unknown (command|option) '(plugin|install|marketplace|--json|--marketplace|--scope)'/;
339
- function tail(text) {
340
- const trimmed = text.trim();
341
- return trimmed.length > TAIL_CHARS ? trimmed.slice(-TAIL_CHARS) : trimmed;
342
- }
343
- var ESCAPED_HOME = homedir().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
344
- var HOME_PATTERN = new RegExp(`${ESCAPED_HOME}(?=[/\\\\\\s'"\`]|$)`, "g");
345
- function redactHomePaths(text) {
346
- let out = ESCAPED_HOME.length > 1 ? text.replace(HOME_PATTERN, "~") : text;
347
- out = out.replace(/\/(?:Users|home)\/[^/\s'"`]+/g, "~");
348
- out = out.replace(/[A-Za-z]:\\Users\\[^\\\s'"`]+/g, "~");
349
- out = out.replace(/\/root(?=[/\s'"`]|$)/g, "~");
350
- return out;
351
- }
352
- function parseLastJsonObject(stdout) {
353
- const lines = stdout.split("\n");
354
- for (let i = lines.length - 1; i >= 0; i--) {
355
- const line = lines[i].trim();
356
- if (!line.startsWith("{"))
357
- continue;
358
- try {
359
- const parsed = JSON.parse(line);
360
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed))
361
- return parsed;
362
- } catch {
363
- }
364
- }
365
- return null;
366
- }
367
- function renderUserCommand(command) {
368
- return [command.executable, ...command.args.filter((arg) => arg !== "--json")].join(" ");
369
- }
370
- async function classifyRun(runner, command, cwd) {
371
- let raw;
372
- try {
373
- raw = await runner.run({
374
- executable: command.executable,
375
- argv: command.args,
376
- ...cwd === void 0 ? {} : { cwd },
377
- timeoutMs: CLAUDE_CLI_INSTALL_TIMEOUT_MS,
378
- stdoutLimitBytes: CLAUDE_CLI_INSTALL_STDOUT_LIMIT_BYTES,
379
- stderrLimitBytes: CLAUDE_CLI_INSTALL_STDERR_LIMIT_BYTES
380
- });
381
- } catch (error) {
382
- const code = typeof error?.code === "string" ? error.code : "unknown";
383
- return code === "ENOENT" ? { kind: "cli-not-found" } : { kind: "failed-to-start", code };
384
- }
385
- if (raw.timedOut)
386
- return { kind: "timeout" };
387
- if (raw.outputExceeded)
388
- return { kind: "output-too-large" };
389
- const json = parseLastJsonObject(raw.stdout);
390
- const jsonSaysFailed = json !== null && json["outcome"] === "failed";
391
- if (raw.exitCode === 0 && !jsonSaysFailed)
392
- return { kind: "executed" };
393
- if (json !== null) {
394
- const message = redactHomePaths(typeof json["message"] === "string" ? json["message"] : `the claude CLI exited with code ${raw.exitCode}`);
395
- const failureCode = typeof json["failureCode"] === "string" ? json["failureCode"] : void 0;
396
- const shown = json["shownCommand"];
397
- if (typeof shown === "object" && shown !== null)
398
- return { kind: "confirmation-required", message };
399
- return failureCode === void 0 ? { kind: "failed", message } : { kind: "failed", failureCode, message };
400
- }
401
- if (UNSUPPORTED_CLI_PATTERN.test(raw.stderr)) {
402
- const line = raw.stderr.split("\n").find((l) => UNSUPPORTED_CLI_PATTERN.test(l)) ?? "";
403
- return { kind: "cli-unsupported", stderrTail: redactHomePaths(line.trim()) };
404
- }
405
- const said = redactHomePaths(tail(raw.stderr.length > 0 ? raw.stderr : raw.stdout));
406
- return { kind: "failed", message: said.length > 0 ? said : `the claude CLI exited with code ${raw.exitCode}` };
407
- }
408
- function manualStepsFor(entry, detail, command, rerunCommand) {
409
- const chat = chatAlternative(entry);
410
- const userCommand = command === null ? "" : renderUserCommand(command);
411
- const rerun = `Once resolved, rerun to install, verify and record it: ${rerunCommand}`;
412
- switch (detail.kind) {
413
- case "executed":
414
- return [];
415
- case "manual-only":
416
- return manualInstallSteps(entry);
417
- case "cli-not-found":
418
- return claudeCliNotFoundSteps(entry, rerunCommand);
419
- case "failed-to-start":
420
- return [
421
- `The claude CLI was found but could not be started (${detail.code}) \u2014 check its permissions and your system limits.`,
422
- rerun,
423
- ...chat
424
- ];
425
- case "cli-unsupported":
426
- return claudeCliUnsupportedSteps(entry, rerunCommand, detail.stderrTail);
427
- case "confirmation-required":
428
- return [
429
- `The marketplace declares a command that must be confirmed by a person; nx-archweave never accepts it for you (${detail.message})`,
430
- `Review it and run the install yourself: ${userCommand}`,
431
- rerun,
432
- ...chat
433
- ];
434
- case "failed":
435
- return [`The install command did not succeed: ${detail.message}`, MARKETPLACE_NOTE, rerun, ...chat];
436
- case "timeout":
437
- return [
438
- `The install command did not finish within ${CLAUDE_CLI_INSTALL_TIMEOUT_MS / 1e3} s and was stopped.`,
439
- MARKETPLACE_NOTE,
440
- rerun,
441
- ...chat
442
- ];
443
- case "output-too-large":
444
- return [
445
- "The install command produced more output than nx-archweave is willing to buffer and was stopped.",
446
- `Run it yourself to see the full output: ${userCommand}`,
447
- rerun,
448
- ...chat
449
- ];
450
- }
451
- }
452
- async function installClaudeMarketplacePlugin(entry, deps, plan, options = {}) {
453
- const rerunCommand = options.rerunCommand ?? defaultRerunCommand(entry);
454
- const detail = plan.officialCommand === null ? { kind: "manual-only" } : await classifyRun(deps.runner, plan.officialCommand, options.cwd);
455
- const executed = detail.kind === "executed";
456
- const result = {
457
- executed,
458
- // One command: it either reached exit 0 or it did not. What a failed run may
459
- // have left behind is re-checked live by install-integration, not guessed here.
460
- partialFailure: false,
461
- filesCreated: [],
462
- // The workspace files the plan said the host would change (project/local scope);
463
- // Claude's own plugin store is outside the workspace and never listed.
464
- filesModified: executed ? [...plan.filesModified] : [],
465
- manualStepsRemaining: manualStepsFor(entry, detail, plan.officialCommand, rerunCommand)
466
- };
467
- return { result, detail };
468
- }
469
- function createClaudeMarketplaceInstall(entry, deps) {
470
- return async (plan) => (await installClaudeMarketplacePlugin(entry, deps, plan)).result;
471
- }
472
-
473
- // ../integrations/dist/hosts/claude-code/verify-via-claude-cli.js
474
- import { realpathSync } from "node:fs";
475
- import { resolve } from "node:path";
476
-
477
- // ../integrations/dist/hosts/claude-code/parse-claude-plugin-list.js
478
- var KNOWN_SCOPES = /* @__PURE__ */ new Set(["user", "project", "local", "managed"]);
479
- function isKnownScope(scope) {
480
- return KNOWN_SCOPES.has(scope);
481
- }
482
- function isValidEntry(value) {
483
- if (typeof value !== "object" || value === null) {
484
- return false;
485
- }
486
- const record = value;
487
- if (typeof record["id"] !== "string") {
488
- return false;
489
- }
490
- if (typeof record["enabled"] !== "boolean") {
491
- return false;
492
- }
493
- if (typeof record["scope"] !== "string" || record["scope"].length === 0) {
494
- return false;
495
- }
496
- if (record["version"] !== void 0 && typeof record["version"] !== "string") {
497
- return false;
498
- }
499
- return true;
500
- }
501
- function parseClaudePluginList(rawStdout, options = {}) {
502
- let parsed;
503
- try {
504
- parsed = JSON.parse(rawStdout);
505
- } catch {
506
- return { ok: false, reason: "invalid-json" };
507
- }
508
- if (!Array.isArray(parsed)) {
509
- return { ok: false, reason: "non-array" };
510
- }
511
- const entries = [];
512
- const otherScopeIds = [];
513
- for (const item of parsed) {
514
- if (!isValidEntry(item)) {
515
- return { ok: false, reason: "missing-fields" };
516
- }
517
- if (typeof item.projectPath === "string" && options.isThisWorkspace && !options.isThisWorkspace(item.projectPath)) {
518
- continue;
519
- }
520
- const scope = item.scope;
521
- if (!isKnownScope(scope)) {
522
- otherScopeIds.push(item.id);
523
- continue;
524
- }
525
- entries.push({
526
- id: item.id,
527
- version: item.version,
528
- scope,
529
- enabled: item.enabled
530
- });
531
- }
532
- return { ok: true, entries, otherScopeIds };
533
- }
534
-
535
- // ../integrations/dist/hosts/claude-code/match-claude-plugin.js
536
- function matchClaudePlugin(entries, pluginId) {
537
- const matches = entries.filter((candidate) => candidate.id === pluginId);
538
- if (matches.length === 0) {
539
- return { kind: "absent" };
540
- }
541
- if (matches.length > 1) {
542
- return { kind: "ambiguous", entries: matches };
543
- }
544
- return { kind: "matched", entry: matches[0] };
545
- }
546
-
547
- // ../integrations/dist/hosts/claude-code/claude-cli-verification-result.js
548
- function evaluateClaudeCliMatch(match) {
549
- switch (match.kind) {
550
- case "matched":
551
- return match.entry.enabled ? { status: "verified", version: match.entry.version, scope: match.entry.scope } : { status: "installed-but-disabled", scope: match.entry.scope };
552
- case "absent":
553
- return { status: "absent" };
554
- case "ambiguous":
555
- return { status: "ambiguous" };
556
- }
557
- }
558
-
559
- // ../integrations/dist/hosts/claude-code/verify-via-claude-cli.js
560
- async function verifyViaClaudeCli(runner, pluginId, cwd) {
561
- let raw;
562
- try {
563
- raw = await runner.run({
564
- executable: "claude",
565
- argv: ["plugin", "list", "--json"],
566
- ...cwd === void 0 ? {} : { cwd },
567
- timeoutMs: CLAUDE_CLI_TIMEOUT_MS,
568
- stdoutLimitBytes: CLAUDE_CLI_STDOUT_LIMIT_BYTES,
569
- stderrLimitBytes: CLAUDE_CLI_STDERR_LIMIT_BYTES
570
- });
571
- } catch {
572
- return { source: "claude-cli-unavailable", reason: "not-found" };
573
- }
574
- if (raw.timedOut) {
575
- return { source: "claude-cli-unavailable", reason: "timeout" };
576
- }
577
- if (raw.outputExceeded) {
578
- return { source: "claude-cli-unavailable", reason: "output-too-large" };
579
- }
580
- if (raw.exitCode !== 0) {
581
- return { source: "claude-cli-unavailable", reason: "non-zero-exit" };
582
- }
583
- const parsed = parseClaudePluginList(raw.stdout, cwd === void 0 ? {} : { isThisWorkspace: samePathAs(cwd) });
584
- if (!parsed.ok) {
585
- return {
586
- source: "claude-cli-unavailable",
587
- reason: parsed.reason === "invalid-json" ? "invalid-json" : "unexpected-schema"
588
- };
589
- }
590
- const match = matchClaudePlugin(parsed.entries, pluginId);
591
- if (parsed.otherScopeIds.includes(pluginId)) {
592
- return match.kind === "absent" ? { source: "claude-cli-unavailable", reason: "unexpected-schema" } : { source: "claude-cli", outcome: { status: "ambiguous" } };
593
- }
594
- return { source: "claude-cli", outcome: evaluateClaudeCliMatch(match) };
595
- }
596
- function canonical(path) {
597
- try {
598
- return realpathSync(path);
599
- } catch {
600
- return resolve(path);
601
- }
602
- }
603
- function samePathAs(workspaceRoot) {
604
- const root = canonical(workspaceRoot);
605
- return (projectPath) => canonical(projectPath) === root;
606
- }
607
-
608
- // ../integrations/dist/hosts/claude-code/verify.js
609
- async function verifyClaudeMarketplacePlugin(entry, deps, context) {
610
- const detect = deps.detect ?? detectWorkspacePackage;
611
- const pluginId = entry.hosts["claude-code"]?.pluginId;
612
- if (pluginId === void 0) {
613
- throw new Error(`${entry.displayName} has no Claude Code distribution \u2014 it cannot be verified through the claude CLI.`);
614
- }
615
- const cliResult = await verifyViaClaudeCli(deps.runner, pluginId, context.workspaceRoot);
616
- if (cliResult.source === "claude-cli") {
617
- switch (cliResult.outcome.status) {
618
- case "verified":
619
- return {
620
- source: "claude-cli",
621
- result: { verified: true, resolvedVersion: cliResult.outcome.version, problems: [] }
622
- };
623
- case "installed-but-disabled":
624
- return {
625
- source: "claude-cli",
626
- result: {
627
- verified: false,
628
- problems: [
629
- `${entry.displayName} is installed (scope: ${cliResult.outcome.scope}) but currently disabled in Claude Code.`,
630
- "Enable it before recording this integration \u2014 a disabled plugin is never recorded as verified."
631
- ]
632
- }
633
- };
634
- case "absent":
635
- return {
636
- source: "claude-cli",
637
- result: {
638
- verified: false,
639
- problems: [
640
- `${pluginId} was not found in the installed Claude Code plugin list.`,
641
- `Install it (${defaultRerunCommand(entry)}) or complete the manual Claude Code steps, then try again.`
642
- ]
643
- }
644
- };
645
- case "ambiguous":
646
- return {
647
- source: "claude-cli",
648
- result: {
649
- verified: false,
650
- problems: [
651
- `Verification error: more than one installed plugin matches ${pluginId} exactly. This is ambiguous and cannot be safely resolved automatically.`
652
- ]
653
- }
654
- };
655
- }
656
- }
657
- if (cliResult.reason === "not-found") {
658
- const detected = await detect(entry, context);
659
- if (detected.installed) {
660
- return {
661
- source: "package-json",
662
- result: { verified: true, resolvedVersion: detected.installedVersion, problems: [] }
663
- };
664
- }
665
- return {
666
- source: "package-json",
667
- result: {
668
- verified: false,
669
- problems: [
670
- `The claude CLI was not found, and ${entry.displayName} was not found as a workspace dependency either.`,
671
- `Install the Claude Code CLI to use the authoritative verification signal, or add ${entry.displayName} as a workspace dependency for the weaker fallback signal.`
672
- ]
673
- }
674
- };
675
- }
676
- return {
677
- source: "claude-cli",
678
- result: {
679
- verified: false,
680
- problems: [`Verification error: the claude CLI returned an unexpected result (${cliResult.reason}).`]
681
- }
682
- };
683
- }
684
- function createClaudeMarketplaceVerifier(entry, deps) {
685
- return async (context) => (await verifyClaudeMarketplacePlugin(entry, deps, context)).result;
686
- }
687
-
688
- // ../integrations/dist/catalog/create-catalog-integration.js
689
- function createCatalogIntegration(entry, deps) {
690
- return {
691
- id: entry.id,
692
- displayName: entry.displayName,
693
- homepage: entry.homepage,
694
- repository: entry.repository,
695
- license: entry.license,
696
- supportedEnvironments: PLUGIN_HOSTS.filter((host) => entry.hosts[host] !== void 0),
697
- detect: (context) => detectWorkspacePackage(entry, context),
698
- plan: createClaudeMarketplacePlan(entry),
699
- install: createClaudeMarketplaceInstall(entry, { runner: deps.runner }),
700
- verify: createClaudeMarketplaceVerifier(entry, { runner: deps.runner }),
701
- uninstallInstructions: async () => [...entry.uninstallSteps]
702
- };
703
- }
704
- function createCatalogIntegrations(deps) {
705
- return PLUGIN_CATALOG.map((entry) => createCatalogIntegration(entry, deps));
706
- }
707
-
708
- // src/runtime/node-command-runner.ts
709
- import { spawn } from "node:child_process";
710
- var KILL_GRACE_MS = 500;
711
- var NodeCommandRunner = class {
712
- run(input) {
713
- return new Promise((resolve2, reject) => {
714
- let stdout = "";
715
- let stderr = "";
716
- let timedOut = false;
717
- let outputExceeded = false;
718
- let settled = false;
719
- let killTimer;
720
- const spawnable = spawnableCommand(input.executable, input.argv);
721
- const child = spawn(spawnable.command, spawnable.args, {
722
- cwd: input.cwd,
723
- shell: false,
724
- windowsVerbatimArguments: spawnable.windowsVerbatimArguments,
725
- windowsHide: true,
726
- // No stdin: nothing here ever answers a prompt (a command that wants
727
- // confirmation must fail fast instead of waiting on an open pipe).
728
- stdio: ["ignore", "pipe", "pipe"]
729
- });
730
- const terminate = () => {
731
- child.kill("SIGTERM");
732
- killTimer = setTimeout(() => {
733
- child.kill("SIGKILL");
734
- }, KILL_GRACE_MS);
735
- };
736
- const timer = setTimeout(() => {
737
- timedOut = true;
738
- terminate();
739
- }, input.timeoutMs);
740
- child.stdout.on("data", (chunk) => {
741
- stdout += chunk.toString("utf8");
742
- if (Buffer.byteLength(stdout, "utf8") > input.stdoutLimitBytes) {
743
- outputExceeded = true;
744
- terminate();
745
- }
746
- });
747
- child.stderr.on("data", (chunk) => {
748
- stderr += chunk.toString("utf8");
749
- if (Buffer.byteLength(stderr, "utf8") > input.stderrLimitBytes) {
750
- outputExceeded = true;
751
- terminate();
752
- }
753
- });
754
- child.on("error", (error) => {
755
- if (settled) return;
756
- settled = true;
757
- clearTimeout(timer);
758
- clearTimeout(killTimer);
759
- reject(error);
760
- });
761
- child.on("close", (code) => {
762
- if (settled) return;
763
- settled = true;
764
- clearTimeout(timer);
765
- clearTimeout(killTimer);
766
- resolve2({ stdout, stderr, exitCode: code, timedOut, outputExceeded });
767
- });
768
- });
769
- }
770
- };
771
-
772
- // src/runtime/integrations-composition-root.ts
773
- function createIntegrationsCompositionRoot(deps = {}) {
774
- const runner = deps.runner ?? new NodeCommandRunner();
775
- const registry = createIntegrationRegistry(createCatalogIntegrations({ runner }));
776
- return {
777
- registry,
778
- runner,
779
- getIntegration: (id) => getIntegration(registry, id),
780
- listIntegrations: () => listIntegrations(registry)
781
- };
782
- }
783
-
784
- // src/generators/shared/record-pipeline.ts
785
- async function runRecordPipeline(input) {
786
- const entry = getCatalogEntry(input.id);
787
- const metadata = entry === void 0 ? void 0 : recordMetadataFor(entry);
788
- if (!entry || !metadata) {
789
- throw new Error(`No manifest metadata is defined for "${input.id}".`);
790
- }
791
- const { result: verification, source: verificationSource } = await verifyClaudeMarketplacePlugin(
792
- entry,
793
- { runner: input.runner },
794
- { workspaceRoot: input.workspaceRoot, targetEnvironment: input.target }
795
- );
796
- const manifest = loadManifest(input.workspaceRoot);
797
- const existingEntry = manifest.integrations[input.id];
798
- const plan = buildRecordPlan({ id: input.id, target: input.target, verificationSource, verification, existingEntry, metadata });
799
- let wasWritten = false;
800
- if (input.write && plan.proposedEntry) {
801
- writeManifestAtomic(input.workspaceRoot, {
802
- integrations: { ...manifest.integrations, [input.id]: plan.proposedEntry }
803
- });
804
- wasWritten = true;
805
- }
806
- return {
807
- verificationSource,
808
- verification,
809
- existingEntry,
810
- proposedEntry: plan.proposedEntry,
811
- hasConflict: plan.hasConflict,
812
- wasWritten
813
- };
814
- }
815
-
816
50
  // src/generators/record-integration/generator.ts
817
51
  async function recordIntegrationGenerator(tree, options, deps = {}) {
818
52
  const root = deps.integrationsRoot ?? createIntegrationsCompositionRoot();