@shrkcrft/cli 0.1.0-alpha.1 → 0.1.0-alpha.11

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 (120) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/api-diff.command.d.ts +11 -0
  3. package/dist/commands/api-diff.command.d.ts.map +1 -0
  4. package/dist/commands/api-diff.command.js +116 -0
  5. package/dist/commands/arch.command.d.ts +9 -0
  6. package/dist/commands/arch.command.d.ts.map +1 -0
  7. package/dist/commands/arch.command.js +186 -0
  8. package/dist/commands/boundaries.command.d.ts.map +1 -1
  9. package/dist/commands/boundaries.command.js +0 -12
  10. package/dist/commands/check.command.d.ts.map +1 -1
  11. package/dist/commands/check.command.js +20 -30
  12. package/dist/commands/code-intel.command.d.ts +18 -0
  13. package/dist/commands/code-intel.command.d.ts.map +1 -0
  14. package/dist/commands/code-intel.command.js +146 -0
  15. package/dist/commands/command-catalog.d.ts +7 -3
  16. package/dist/commands/command-catalog.d.ts.map +1 -1
  17. package/dist/commands/command-catalog.js +201 -47
  18. package/dist/commands/commands.command.d.ts.map +1 -1
  19. package/dist/commands/commands.command.js +4 -4
  20. package/dist/commands/completion.command.d.ts +10 -0
  21. package/dist/commands/completion.command.d.ts.map +1 -0
  22. package/dist/commands/completion.command.js +121 -0
  23. package/dist/commands/constructs.command.d.ts.map +1 -1
  24. package/dist/commands/constructs.command.js +5 -22
  25. package/dist/commands/context.command.d.ts.map +1 -1
  26. package/dist/commands/context.command.js +89 -0
  27. package/dist/commands/diff-check.command.d.ts +30 -0
  28. package/dist/commands/diff-check.command.d.ts.map +1 -0
  29. package/dist/commands/diff-check.command.js +210 -0
  30. package/dist/commands/doctor.command.d.ts.map +1 -1
  31. package/dist/commands/doctor.command.js +42 -9
  32. package/dist/commands/export.command.d.ts.map +1 -1
  33. package/dist/commands/export.command.js +76 -3
  34. package/dist/commands/framework.command.d.ts +12 -0
  35. package/dist/commands/framework.command.d.ts.map +1 -0
  36. package/dist/commands/framework.command.js +180 -0
  37. package/dist/commands/gate.command.d.ts +15 -0
  38. package/dist/commands/gate.command.d.ts.map +1 -0
  39. package/dist/commands/gate.command.js +296 -0
  40. package/dist/commands/graph-code-subverbs.d.ts +11 -0
  41. package/dist/commands/graph-code-subverbs.d.ts.map +1 -0
  42. package/dist/commands/graph-code-subverbs.js +818 -0
  43. package/dist/commands/graph.command.d.ts.map +1 -1
  44. package/dist/commands/graph.command.js +22 -0
  45. package/dist/commands/help.command.d.ts +4 -3
  46. package/dist/commands/help.command.d.ts.map +1 -1
  47. package/dist/commands/help.command.js +77 -21
  48. package/dist/commands/helper.command.js +1 -1
  49. package/dist/commands/impact.command.d.ts.map +1 -1
  50. package/dist/commands/impact.command.js +170 -1
  51. package/dist/commands/import.command.d.ts.map +1 -1
  52. package/dist/commands/import.command.js +121 -5
  53. package/dist/commands/init.command.d.ts.map +1 -1
  54. package/dist/commands/init.command.js +184 -16
  55. package/dist/commands/mcp.command.d.ts.map +1 -1
  56. package/dist/commands/mcp.command.js +2 -131
  57. package/dist/commands/migrate.command.d.ts +13 -0
  58. package/dist/commands/migrate.command.d.ts.map +1 -0
  59. package/dist/commands/migrate.command.js +152 -0
  60. package/dist/commands/onboard.command.d.ts.map +1 -1
  61. package/dist/commands/onboard.command.js +3 -15
  62. package/dist/commands/packs-new.d.ts +1 -1
  63. package/dist/commands/packs-new.d.ts.map +1 -1
  64. package/dist/commands/packs-new.js +5 -36
  65. package/dist/commands/packs.command.d.ts.map +1 -1
  66. package/dist/commands/packs.command.js +3 -17
  67. package/dist/commands/plan-context.command.d.ts +11 -0
  68. package/dist/commands/plan-context.command.d.ts.map +1 -0
  69. package/dist/commands/plan-context.command.js +77 -0
  70. package/dist/commands/profiles.command.js +4 -4
  71. package/dist/commands/release.command.js +13 -13
  72. package/dist/commands/review.command.d.ts.map +1 -1
  73. package/dist/commands/review.command.js +2 -28
  74. package/dist/commands/rule-graph-subverbs.d.ts +3 -0
  75. package/dist/commands/rule-graph-subverbs.d.ts.map +1 -0
  76. package/dist/commands/rule-graph-subverbs.js +132 -0
  77. package/dist/commands/search-structural.command.d.ts +18 -0
  78. package/dist/commands/search-structural.command.d.ts.map +1 -0
  79. package/dist/commands/search-structural.command.js +376 -0
  80. package/dist/commands/search.command.js +1 -1
  81. package/dist/commands/task-context.command.js +0 -16
  82. package/dist/commands/task.command.d.ts.map +1 -1
  83. package/dist/commands/task.command.js +8 -2
  84. package/dist/dashboard/code-intelligence-data.d.ts +33 -0
  85. package/dist/dashboard/code-intelligence-data.d.ts.map +1 -0
  86. package/dist/dashboard/code-intelligence-data.js +307 -0
  87. package/dist/dashboard/dashboard-api-server.d.ts.map +1 -1
  88. package/dist/dashboard/dashboard-api-server.js +162 -1
  89. package/dist/export/claude-commands-export.d.ts +60 -0
  90. package/dist/export/claude-commands-export.d.ts.map +1 -0
  91. package/dist/export/claude-commands-export.js +276 -0
  92. package/dist/export/export-formats.d.ts +1 -1
  93. package/dist/export/export-formats.d.ts.map +1 -1
  94. package/dist/export/export-formats.js +139 -12
  95. package/dist/init/init-templates.d.ts.map +1 -1
  96. package/dist/init/init-templates.js +133 -113
  97. package/dist/init/paths-advisory.d.ts +20 -0
  98. package/dist/init/paths-advisory.d.ts.map +1 -0
  99. package/dist/init/paths-advisory.js +88 -0
  100. package/dist/main.d.ts +1 -1
  101. package/dist/main.d.ts.map +1 -1
  102. package/dist/main.js +137 -46
  103. package/dist/output/failure-hints.d.ts +1 -9
  104. package/dist/output/failure-hints.d.ts.map +1 -1
  105. package/dist/output/failure-hints.js +2 -8
  106. package/dist/output/watch-loop.d.ts +9 -1
  107. package/dist/output/watch-loop.d.ts.map +1 -1
  108. package/dist/output/watch-loop.js +13 -3
  109. package/dist/schemas/json-schemas.d.ts +36 -36
  110. package/dist/schemas/json-schemas.js +36 -36
  111. package/dist/surface/about.d.ts.map +1 -1
  112. package/dist/surface/about.js +37 -15
  113. package/dist/surface/no-args-landing.d.ts.map +1 -1
  114. package/dist/surface/no-args-landing.js +9 -13
  115. package/dist/surface/surface-config-writer.d.ts.map +1 -1
  116. package/dist/surface/surface-config-writer.js +23 -11
  117. package/package.json +37 -25
  118. package/dist/commands/plugin.command.d.ts +0 -11
  119. package/dist/commands/plugin.command.d.ts.map +0 -1
  120. package/dist/commands/plugin.command.js +0 -394
@@ -6,6 +6,7 @@ import { listBuiltInSurfaceProfiles, suggestSurfaceProfile } from '@shrkcrft/ins
6
6
  import { INIT_FILES } from "../init/init-templates.js";
7
7
  import { buildDetectedBlock, renderDetectedBlockText } from "../init/detected-block.js";
8
8
  import { ensureSharkcraftGitignore, renderGitignorePatch } from "../init/gitignore.js";
9
+ import { annotatePathsAgainstDisk } from "../init/paths-advisory.js";
9
10
  import { applySurfaceTextEdit } from "../surface/surface-config-writer.js";
10
11
  import { flagBool, flagString, resolveCwd } from "../command-registry.js";
11
12
  import { bullet, header } from "../output/format-output.js";
@@ -108,8 +109,13 @@ async function applyPresetInit(cwd, mode) {
108
109
  process.stdout.write(bullet(`(warning) ${warn}`) + '\n');
109
110
  }
110
111
  process.stdout.write('\nNext:\n');
111
- process.stdout.write(bullet('$ shrk init --zero-config --write') + '\n');
112
- process.stdout.write(bullet('Then prove it: $ shrk check boundaries --changed-only --since main') + '\n');
112
+ for (const cmd of preset.recommendedNextCommands ?? [
113
+ 'shrk init --zero-config --write',
114
+ 'shrk doctor',
115
+ 'shrk context --task "<task>"',
116
+ ]) {
117
+ process.stdout.write(bullet(`$ ${cmd}`) + '\n');
118
+ }
113
119
  if (!mode.skipGitignore) {
114
120
  const patch = ensureSharkcraftGitignore({ cwd, dryRun: true });
115
121
  process.stdout.write('\n' + renderGitignorePatch(patch, true));
@@ -128,6 +134,11 @@ async function applyPresetInit(cwd, mode) {
128
134
  source: 'override',
129
135
  });
130
136
  }
137
+ // Workspace-shape advisory: when the preset emits path conventions
138
+ // that don't exist in this repo (common with generic presets in
139
+ // framework-specific repos), annotate paths.ts so the user knows
140
+ // which defaults to fix.
141
+ const pathsAdvisory = annotatePathsAgainstDisk(cwd, plan.sharkcraftDir);
131
142
  process.stdout.write(header('SharkCraft initialized'));
132
143
  process.stdout.write(`Preset: ${preset.id} — ${preset.title}\n`);
133
144
  process.stdout.write(`Folder: ${plan.sharkcraftDir}\n`);
@@ -164,20 +175,29 @@ async function applyPresetInit(cwd, mode) {
164
175
  process.stdout.write('\n' + renderGitignorePatch(patch, false));
165
176
  }
166
177
  }
167
- process.stdout.write('\nProve it works (60 seconds):\n');
168
- process.stdout.write(bullet('$ shrk check boundaries --changed-only --since main') + '\n');
169
- process.stdout.write(bullet('$ shrk review packet --v3 --since main') + '\n');
170
- process.stdout.write('\nOptional:\n');
171
- process.stdout.write(bullet('$ shrk doctor') + '\n');
172
- process.stdout.write(bullet('$ shrk mcp serve # plug into Claude Code / Cursor / Cline') + '\n');
173
- if (preset.recommendedNextCommands && preset.recommendedNextCommands.length > 0) {
174
- process.stdout.write('\nPreset-specific next steps:\n');
175
- for (const cmd of preset.recommendedNextCommands) {
176
- process.stdout.write(bullet(`$ ${cmd}`) + '\n');
177
- }
178
+ if (pathsAdvisory.annotated) {
179
+ renderPathsAdvisory(pathsAdvisory);
178
180
  }
181
+ process.stdout.write('\nNext:\n');
182
+ process.stdout.write(bullet('$ shrk doctor — verify the setup (shape-aware verdict)') + '\n');
183
+ process.stdout.write(bullet('$ shrk brief — single-page brief Claude reads first') + '\n');
184
+ process.stdout.write(bullet('$ shrk export claude-skill --write — inline the rules into Claude\'s prompt via .claude/skills/') + '\n');
185
+ for (const cmd of preset.recommendedNextCommands ?? []) {
186
+ process.stdout.write(bullet(`$ ${cmd}`) + '\n');
187
+ }
188
+ process.stdout.write(bullet('Start the MCP server: `shrk mcp serve` (or run it via Claude Code)') + '\n');
179
189
  return 0;
180
190
  }
191
+ function renderPathsAdvisory(advisory) {
192
+ process.stdout.write('\n');
193
+ process.stdout.write(header('Paths advisory'));
194
+ process.stdout.write('These preset-default paths do not exist in this repo:\n');
195
+ for (const p of advisory.missingPaths) {
196
+ process.stdout.write(bullet(p) + '\n');
197
+ }
198
+ process.stdout.write('\nEdit sharkcraft/paths.ts to match your real layout. ' +
199
+ 'Run `shrk onboard --dry-run` to see what the engine infers.\n');
200
+ }
181
201
  function applyLegacyInit(cwd, force) {
182
202
  const { root } = detectProjectRoot(cwd);
183
203
  const sharkcraftDir = nodePath.join(root, 'sharkcraft');
@@ -211,6 +231,10 @@ function applyLegacyInit(cwd, force) {
211
231
  for (const s of skipped)
212
232
  process.stdout.write(bullet(s) + '\n');
213
233
  }
234
+ const legacyAdvisory = annotatePathsAgainstDisk(cwd, sharkcraftDir);
235
+ if (legacyAdvisory.annotated) {
236
+ renderPathsAdvisory(legacyAdvisory);
237
+ }
214
238
  process.stdout.write('\nNext:\n');
215
239
  process.stdout.write(bullet('Run `shrk inspect` to see your project summary.') + '\n');
216
240
  process.stdout.write(bullet('Run `shrk knowledge list` to see what was seeded.') + '\n');
@@ -219,13 +243,23 @@ function applyLegacyInit(cwd, force) {
219
243
  }
220
244
  export const initCommand = {
221
245
  name: 'init',
222
- description: 'Initialize a sharkcraft/ folder in the current repository. Pass --preset <id> to apply a built-in preset (default: generic). Use --zero-config or --preset auto to detect the workspace and pick a preset automatically. Use --legacy for the full pre-preset seed.',
223
- usage: 'shrk init [--preset <id|auto>] [--zero-config] [--dry-run] [--write] [--legacy] [--force] [--merge] [--suggest-only] [--no-gitignore] [--surface-profile <id>]',
246
+ description: 'Initialize a sharkcraft/ folder in the current repository. Pass --preset <id> to apply a built-in preset (default: generic). Use --zero-config or --preset auto to detect the workspace and pick a preset automatically. Use --legacy for the full pre-preset seed. Pass --infer to scan the repo and populate sharkcraft/* with the real signals it finds (paths from disk, rules from tsconfig/package.json, boundaries from layer structure) — combines with --preset to add inferred entries on top of the preset baseline. Pass --with-claude-skill to also generate .claude/skills/<name>/SKILL.md so Claude Code picks up the rules automatically.',
247
+ usage: 'shrk init [--preset <id|auto>] [--zero-config] [--infer] [--dry-run] [--write] [--with-claude-skill] [--legacy] [--force] [--merge] [--suggest-only] [--no-gitignore] [--surface-profile <id>]',
224
248
  async run(args) {
225
249
  const force = flagBool(args, 'force');
226
250
  const merge = flagBool(args, 'merge');
227
251
  const cwd = resolveCwd(args);
252
+ const wantInfer = flagBool(args, 'infer');
228
253
  const skipGitignore = flagBool(args, 'no-gitignore');
254
+ // `--infer` mode short-circuits the preset path entirely. The
255
+ // inferred output IS the populated sharkcraft/*, no preset
256
+ // baseline to merge against. This is the cleaner contract: the
257
+ // user opted into "scan my repo and tell me what's there", and
258
+ // adding a preset's static defaults on top would muddy the
259
+ // confidence reporting in .inferred-report.md.
260
+ if (wantInfer) {
261
+ return runInferInit(cwd, args, force);
262
+ }
229
263
  if (flagBool(args, 'legacy')) {
230
264
  const code = applyLegacyInit(cwd, force);
231
265
  if (code === 0 && !skipGitignore) {
@@ -286,7 +320,7 @@ export const initCommand = {
286
320
  if (dryRun) {
287
321
  process.stdout.write(`Surface profile: ${surfaceDecision.profile} (${surfaceDecision.source}) — ${surfaceDecision.reason}\n`);
288
322
  }
289
- return applyPresetInit(cwd, {
323
+ const presetCode = await applyPresetInit(cwd, {
290
324
  presetId,
291
325
  dryRun,
292
326
  force,
@@ -296,5 +330,139 @@ export const initCommand = {
296
330
  surfaceProfile: surfaceDecision.profile,
297
331
  surfaceProfileReason: surfaceDecision.reason,
298
332
  });
333
+ // `--with-claude-skill` chains the claude-skill export onto the init
334
+ // so a fresh user goes from `npx shrk init --with-claude-skill --write`
335
+ // to a working .claude/skills/<name>/SKILL.md in one step. Skipped in
336
+ // dry-run + when init itself didn't succeed.
337
+ if (presetCode === 0 &&
338
+ !dryRun &&
339
+ (flagBool(args, 'with-claude-skill') || flagBool(args, 'with-skill'))) {
340
+ const skillCode = await runClaudeSkillExportAfterInit(cwd, force);
341
+ // Init success is what we report; skill failure is logged but
342
+ // doesn't fail the init exit code (otherwise users would see a
343
+ // half-applied repo with a non-zero exit and panic).
344
+ if (skillCode !== 0) {
345
+ process.stderr.write('\n(claude-skill export failed — re-run `shrk export claude-skill --write` to retry.)\n');
346
+ }
347
+ }
348
+ return presetCode;
299
349
  },
300
350
  };
351
+ /**
352
+ * Chain the claude-skill export onto a successful init. Loads the
353
+ * exporter lazily (it depends on inspectSharkcraft which has its own
354
+ * caching) and writes the skill file using the same `--force` semantics
355
+ * as init itself.
356
+ */
357
+ /**
358
+ * `shrk init --infer` — scan the repo and populate `sharkcraft/*`
359
+ * with the real signals it finds, instead of writing preset defaults.
360
+ *
361
+ * Pipeline:
362
+ * 1. `inspectSharkcraft({ cwd })` builds the full inspection.
363
+ * 2. `buildOnboardingPlan(inspection)` extracts inferred paths,
364
+ * rules, boundaries, pipelines, verification commands.
365
+ * 3. `synthesizeFromOnboarding(plan)` triages each entry by
366
+ * confidence and emits populated `sharkcraft/*.ts` files plus
367
+ * a companion `.inferred-report.md` + `.inferred-report.json`.
368
+ * 4. Writer respects `--dry-run` / `--write` / `--force` /
369
+ * `--with-claude-skill` semantics consistently with the
370
+ * preset-init path.
371
+ *
372
+ * Honest-by-default: every adopted entry is tagged with its source
373
+ * signal; medium-confidence rows get a `// TODO: review` marker; low-
374
+ * confidence rows are dropped from the populated files and listed in
375
+ * the report so the user knows exactly what was inferred vs needs
376
+ * authoring.
377
+ */
378
+ async function runInferInit(cwd, args, force) {
379
+ const { inspectSharkcraft, buildOnboardingPlan, synthesizeFromOnboarding } = await import('@shrkcrft/inspector');
380
+ const dryRun = !flagBool(args, 'write');
381
+ const sharkcraftDir = nodePath.join(cwd, 'sharkcraft');
382
+ process.stdout.write(header('SharkCraft init — infer'));
383
+ process.stdout.write(`Folder: ${sharkcraftDir}\n`);
384
+ const inspection = await inspectSharkcraft({ cwd });
385
+ const plan = buildOnboardingPlan(inspection);
386
+ const result = synthesizeFromOnboarding(plan);
387
+ // Summary block — print BEFORE writing so the user can ctrl-C if the
388
+ // confidence triage doesn't look right.
389
+ process.stdout.write(`\nDetected profiles: ${plan.projectSummary.profiles.join(', ') || '(none)'}\n`);
390
+ process.stdout.write(`Inference triage: ${result.report.adoptedHigh.length} adopted directly · ` +
391
+ `${result.report.adoptedMedium.length} marked for review · ` +
392
+ `${result.report.dropped.length} dropped (in report).\n\n`);
393
+ const written = [];
394
+ const skipped = [];
395
+ const wouldWrite = [];
396
+ for (const file of result.files) {
397
+ const fullPath = nodePath.join(sharkcraftDir, file.path);
398
+ if (dryRun) {
399
+ wouldWrite.push(file.path);
400
+ continue;
401
+ }
402
+ mkdirSync(nodePath.dirname(fullPath), { recursive: true });
403
+ if (existsSync(fullPath) && !force) {
404
+ skipped.push(file.path);
405
+ continue;
406
+ }
407
+ writeFileSync(fullPath, file.content, 'utf8');
408
+ written.push(file.path);
409
+ }
410
+ if (dryRun) {
411
+ process.stdout.write('Would write:\n');
412
+ for (const p of wouldWrite)
413
+ process.stdout.write(bullet(p) + '\n');
414
+ process.stdout.write('\nRe-run with --write to persist.\n');
415
+ return 0;
416
+ }
417
+ if (written.length) {
418
+ process.stdout.write('Wrote:\n');
419
+ for (const p of written)
420
+ process.stdout.write(bullet(p) + '\n');
421
+ }
422
+ if (skipped.length) {
423
+ process.stdout.write('\nSkipped (already exist; use --force to overwrite):\n');
424
+ for (const p of skipped)
425
+ process.stdout.write(bullet(p) + '\n');
426
+ }
427
+ process.stdout.write(`\nRead the confidence report: \`${nodePath.join('sharkcraft', '.inferred-report.md')}\`\n` +
428
+ `It lists what was adopted high-confidence, what's marked for your review, ` +
429
+ `and what shrk can't infer (project-specific decisions you should author by hand).\n`);
430
+ process.stdout.write('\nNext:\n');
431
+ process.stdout.write(bullet('$ shrk doctor — verify the setup (shape-aware verdict)') + '\n');
432
+ process.stdout.write(bullet('$ shrk brief — single-page brief Claude reads first') + '\n');
433
+ process.stdout.write(bullet('$ shrk export claude-skill --write — inline the rules into Claude\'s prompt') + '\n');
434
+ // Chain claude-skill if requested.
435
+ if (flagBool(args, 'with-claude-skill') || flagBool(args, 'with-skill')) {
436
+ const skillCode = await runClaudeSkillExportAfterInit(cwd, force);
437
+ if (skillCode !== 0) {
438
+ process.stderr.write('\n(claude-skill export failed — re-run `shrk export claude-skill --write` to retry.)\n');
439
+ }
440
+ }
441
+ // Gitignore + paths advisory same as preset path.
442
+ const skipGitignore = flagBool(args, 'no-gitignore');
443
+ if (!skipGitignore) {
444
+ const patch = ensureSharkcraftGitignore({ cwd, dryRun: false });
445
+ if (patch.added.length > 0) {
446
+ process.stdout.write('\n' + renderGitignorePatch(patch, false));
447
+ }
448
+ }
449
+ return 0;
450
+ }
451
+ async function runClaudeSkillExportAfterInit(cwd, force) {
452
+ const { inspectSharkcraft } = await import('@shrkcrft/inspector');
453
+ const { renderExport } = await import("../export/export-formats.js");
454
+ const inspection = await inspectSharkcraft({ cwd });
455
+ const result = renderExport(inspection, { format: 'claude-skill' });
456
+ const outputPath = nodePath.join(cwd, result.suggestedPath);
457
+ mkdirSync(nodePath.dirname(outputPath), { recursive: true });
458
+ if (existsSync(outputPath) && !force) {
459
+ process.stdout.write(`\nSkipped claude-skill export — ${result.suggestedPath} already exists. Pass --force to overwrite, or run \`shrk export claude-skill --write --force\`.\n`);
460
+ return 0;
461
+ }
462
+ writeFileSync(outputPath, result.content, 'utf8');
463
+ process.stdout.write('\n');
464
+ process.stdout.write(header('Claude-skill exported'));
465
+ process.stdout.write(`Wrote ${result.suggestedPath}\n` +
466
+ ` → Claude Code will auto-load this skill in any session opened against this repo.\n`);
467
+ return 0;
468
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.command.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.command.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,UAAU,EAAE,eAiDxB,CAAC"}
1
+ {"version":3,"file":"mcp.command.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.command.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,UAAU,EAAE,eAiCxB,CAAC"}
@@ -6,7 +6,7 @@ import { buildSurfaceSummary, findCommandInSummary } from "../surface/surface-su
6
6
  export const mcpCommand = {
7
7
  name: 'mcp',
8
8
  description: 'MCP server operations (subcommand required).',
9
- usage: 'shrk [--cwd <dir>] mcp serve [--verbose] [--watch] [--http] [--port <n>] [--host <h>] | shrk mcp install [<harness>]',
9
+ usage: 'shrk [--cwd <dir>] mcp serve [--verbose] [--watch] [--http] [--port <n>] [--host <h>]',
10
10
  async run(args) {
11
11
  const sub = args.positional[0];
12
12
  if (sub === 'serve') {
@@ -35,139 +35,10 @@ export const mcpCommand = {
35
35
  return 1;
36
36
  }
37
37
  }
38
- if (sub === 'install') {
39
- const harness = args.positional[1];
40
- const showAll = flagBool(args, 'all-paths');
41
- if (harness !== undefined && !HARNESSES.some((h) => h.id === harness)) {
42
- process.stderr.write(`Unknown harness: ${harness}\nKnown: ${HARNESSES.map((h) => h.id).join(', ')}\n`);
43
- return 2;
44
- }
45
- process.stdout.write(renderMcpInstallInstructions(harness, showAll));
46
- return 0;
47
- }
48
- process.stderr.write('Usage:\n' +
49
- ' shrk mcp serve [--http] [--port N] [--watch]\n' +
50
- ' shrk mcp install [claude-code|claude-desktop|cursor|cline]\n');
38
+ process.stderr.write('Usage: shrk mcp serve [--http] [--port N] [--watch]\n');
51
39
  return 2;
52
40
  },
53
41
  };
54
- const HARNESSES = [
55
- {
56
- id: 'claude-code',
57
- label: 'Claude Code (Anthropic CLI)',
58
- configPaths: [
59
- { os: '*', path: '<repo>/.mcp.json # project-scoped (preferred)' },
60
- { os: '*', path: '~/.claude.json # user-scoped (fallback)' },
61
- ],
62
- note: 'Claude Code reads both; project-scoped wins when present.',
63
- },
64
- {
65
- id: 'claude-desktop',
66
- label: 'Claude Desktop',
67
- configPaths: [
68
- { os: 'darwin', path: '~/Library/Application Support/Claude/claude_desktop_config.json' },
69
- { os: 'win32', path: '%APPDATA%\\Claude\\claude_desktop_config.json' },
70
- { os: 'linux', path: '~/.config/Claude/claude_desktop_config.json' },
71
- ],
72
- note: 'Restart Claude Desktop after editing.',
73
- },
74
- {
75
- id: 'cursor',
76
- label: 'Cursor IDE',
77
- configPaths: [
78
- { os: '*', path: '~/.cursor/mcp.json # user-scoped' },
79
- { os: '*', path: '<repo>/.cursor/mcp.json # project-scoped' },
80
- ],
81
- note: 'Cursor merges both. Reload the IDE after editing.',
82
- },
83
- {
84
- id: 'cline',
85
- label: 'Cline (VS Code extension)',
86
- configPaths: [
87
- {
88
- os: 'darwin',
89
- path: '~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json',
90
- },
91
- {
92
- os: 'win32',
93
- path: '%APPDATA%\\Code\\User\\globalStorage\\saoudrizwan.claude-dev\\settings\\cline_mcp_settings.json',
94
- },
95
- {
96
- os: 'linux',
97
- path: '~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json',
98
- },
99
- ],
100
- note: 'Reload the Cline panel after editing.',
101
- },
102
- ];
103
- function currentOs() {
104
- const p = process.platform;
105
- if (p === 'darwin' || p === 'win32' || p === 'linux')
106
- return p;
107
- return 'other';
108
- }
109
- function pathsForPlatform(target, showAll) {
110
- if (showAll)
111
- return target.configPaths.map((p) => p.path);
112
- const os = currentOs();
113
- const matched = target.configPaths.filter((p) => p.os === '*' || p.os === os);
114
- // If the current OS has no specific entry, fall back to all to avoid an
115
- // empty list. (`other` platforms like FreeBSD land here.)
116
- if (matched.length === 0)
117
- return target.configPaths.map((p) => p.path);
118
- return matched.map((p) => p.path);
119
- }
120
- const SHARKCRAFT_MCP_SNIPPET = `{
121
- "mcpServers": {
122
- "sharkcraft": {
123
- "command": "shrk",
124
- "args": ["mcp", "serve"]
125
- }
126
- }
127
- }`;
128
- function renderMcpInstallInstructions(harness, showAll) {
129
- const lines = [];
130
- if (!harness) {
131
- lines.push('SharkCraft MCP server entry (universal, stdio transport):');
132
- lines.push('');
133
- lines.push(SHARKCRAFT_MCP_SNIPPET);
134
- lines.push('');
135
- lines.push('Paste it into one of these harnesses (`shrk mcp install <id>` for exact paths):');
136
- lines.push('');
137
- for (const h of HARNESSES) {
138
- lines.push(` ${h.id.padEnd(16)} ${h.label}`);
139
- }
140
- lines.push('');
141
- lines.push('After pasting, restart/reload the harness. The server is read-only — agents cannot write.');
142
- return lines.join('\n') + '\n';
143
- }
144
- const target = HARNESSES.find((h) => h.id === harness);
145
- if (!target) {
146
- return (`Unknown harness: ${harness}\n` +
147
- `Known: ${HARNESSES.map((h) => h.id).join(', ')}\n`);
148
- }
149
- const paths = pathsForPlatform(target, showAll);
150
- const os = currentOs();
151
- lines.push(`${target.label}`);
152
- lines.push('');
153
- lines.push(showAll || os === 'other'
154
- ? 'Config file (all platforms):'
155
- : `Config file (${os}; pass --all-paths to see every platform):`);
156
- for (const p of paths)
157
- lines.push(` ${p}`);
158
- lines.push('');
159
- lines.push('Add this entry (merge with any existing `mcpServers` block):');
160
- lines.push('');
161
- lines.push(SHARKCRAFT_MCP_SNIPPET);
162
- if (target.note) {
163
- lines.push('');
164
- lines.push(target.note);
165
- }
166
- lines.push('');
167
- lines.push('Verify by asking the agent: "List MCP tools available." You should see ~250 sharkcraft tools.');
168
- lines.push('The server is read-only — agents cannot write. Use `shrk apply --verify-signature` for writes.');
169
- return lines.join('\n') + '\n';
170
- }
171
42
  /**
172
43
  * Build the MCP tier-gate resolver from the surface summary.
173
44
  * Returns a function that, given a tool, decides whether to refuse the
@@ -0,0 +1,13 @@
1
+ import { type ICommandHandler } from '../command-registry.js';
2
+ /**
3
+ * `shrk migrate` — run a multi-step migration definition.
4
+ *
5
+ * - shrk migrate plan <id> preview what each step would do
6
+ * - shrk migrate apply <id> execute the migration (writes!)
7
+ *
8
+ * Migrations live in `sharkcraft/migrations/<id>.ts` by default; pass
9
+ * `--from <path>` to point at a specific file. Each migration file
10
+ * default-exports an `IMigration` (use `defineMigration({ ... })`).
11
+ */
12
+ export declare const migrateCommand: ICommandHandler;
13
+ //# sourceMappingURL=migrate.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.command.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.command.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,eAsI5B,CAAC"}
@@ -0,0 +1,152 @@
1
+ import { existsSync } from 'node:fs';
2
+ import * as nodePath from 'node:path';
3
+ import { safeImport } from '@shrkcrft/core';
4
+ import { applyMigration, planMigration, pruneMigrations, resumeMigration, } from '@shrkcrft/migrate';
5
+ import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
6
+ import { asJson, header, kv } from "../output/format-output.js";
7
+ /**
8
+ * `shrk migrate` — run a multi-step migration definition.
9
+ *
10
+ * - shrk migrate plan <id> preview what each step would do
11
+ * - shrk migrate apply <id> execute the migration (writes!)
12
+ *
13
+ * Migrations live in `sharkcraft/migrations/<id>.ts` by default; pass
14
+ * `--from <path>` to point at a specific file. Each migration file
15
+ * default-exports an `IMigration` (use `defineMigration({ ... })`).
16
+ */
17
+ export const migrateCommand = {
18
+ name: 'migrate',
19
+ description: 'Orchestrate multi-step refactors: structural rewrites + shell + checks in one named, replayable migration.',
20
+ usage: 'shrk migrate plan <id> [--from <path>] [--json] | shrk migrate apply <id> [--from <path>] [--dry-run] [--no-stop-on-failure] [--json] | shrk migrate resume <id> [--from <path>] [--json] | shrk migrate prune [--older-than <days>] [--include-failed] [--dry-run] [--json]',
21
+ async run(args) {
22
+ const sub = args.positional[0];
23
+ if (sub !== 'plan' && sub !== 'apply' && sub !== 'resume' && sub !== 'prune') {
24
+ process.stderr.write(this.usage + '\n');
25
+ return 2;
26
+ }
27
+ const cwd = resolveCwd(args);
28
+ const wantJson = flagBool(args, 'json');
29
+ if (sub === 'prune') {
30
+ const olderThanRaw = flagString(args, 'older-than');
31
+ const olderThanDays = olderThanRaw !== undefined ? Number(olderThanRaw) : undefined;
32
+ if (olderThanDays !== undefined && (!Number.isFinite(olderThanDays) || olderThanDays < 0)) {
33
+ process.stderr.write(`Invalid --older-than: ${olderThanRaw}\n`);
34
+ return 2;
35
+ }
36
+ const result = pruneMigrations({
37
+ projectRoot: cwd,
38
+ ...(olderThanDays !== undefined ? { olderThanDays } : {}),
39
+ includeFailed: flagBool(args, 'include-failed'),
40
+ dryRun: flagBool(args, 'dry-run'),
41
+ });
42
+ if (wantJson) {
43
+ process.stdout.write(asJson(result) + '\n');
44
+ return 0;
45
+ }
46
+ process.stdout.write(header(`Migrate prune (${result.dryRun ? 'dry-run' : 'applied'})`));
47
+ process.stdout.write(kv('scanned', String(result.scanned)) + '\n');
48
+ process.stdout.write(kv('eligible', String(result.eligible)) + '\n');
49
+ process.stdout.write(kv('removed', String(result.removed)) + '\n');
50
+ for (const e of result.entries.slice(0, 50)) {
51
+ process.stdout.write(` ${e.id} (${e.overall}, ${e.ageDays}d, reason=${e.reason})\n`);
52
+ }
53
+ return 0;
54
+ }
55
+ const id = args.positional[1];
56
+ if (!id) {
57
+ process.stderr.write('Usage: shrk migrate ' + sub + ' <id>\n');
58
+ return 2;
59
+ }
60
+ const fromFlag = flagString(args, 'from');
61
+ const candidate = fromFlag
62
+ ? (nodePath.isAbsolute(fromFlag) ? fromFlag : nodePath.resolve(cwd, fromFlag))
63
+ : nodePath.resolve(cwd, 'sharkcraft', 'migrations', `${id}.ts`);
64
+ if (!existsSync(candidate)) {
65
+ process.stderr.write(`Migration file not found: ${candidate}\n`);
66
+ return 1;
67
+ }
68
+ const result = await safeImport(candidate);
69
+ if (!result.ok) {
70
+ process.stderr.write(`Migration load failed: ${result.error.message}\n`);
71
+ return 1;
72
+ }
73
+ const migration = result.module.default ?? result.module.migration;
74
+ if (!migration) {
75
+ process.stderr.write(`Migration module ${candidate} does not export a default migration.\n`);
76
+ return 1;
77
+ }
78
+ if (migration.id !== id) {
79
+ process.stderr.write(`Migration id mismatch: requested "${id}", file contains "${migration.id}".\n`);
80
+ return 1;
81
+ }
82
+ if (sub === 'plan') {
83
+ const plan = planMigration(migration, cwd);
84
+ if (wantJson) {
85
+ process.stdout.write(asJson(plan) + '\n');
86
+ return 0;
87
+ }
88
+ process.stdout.write(header(`Migration plan: ${plan.migration.title}`));
89
+ process.stdout.write(kv('id', plan.migration.id) + '\n');
90
+ process.stdout.write(kv('total steps', String(plan.plannedSteps.length)) + '\n');
91
+ process.stdout.write(kv('total edits', String(plan.totalEdits)) + '\n');
92
+ process.stdout.write(kv('files affected', String(plan.totalFiles)) + '\n');
93
+ for (const step of plan.plannedSteps) {
94
+ process.stdout.write(`\n [${step.index + 1}/${plan.plannedSteps.length}] ${step.id} (${step.step.kind})\n`);
95
+ if (step.description)
96
+ process.stdout.write(` ${step.description}\n`);
97
+ if (step.rewritePlan) {
98
+ process.stdout.write(` files=${step.rewritePlan.files.length} edits=${step.rewritePlan.totalEdits}\n`);
99
+ }
100
+ else if (step.step.kind === 'shell' || step.step.kind === 'check') {
101
+ process.stdout.write(` $ ${step.step.command}\n`);
102
+ }
103
+ }
104
+ return 0;
105
+ }
106
+ // apply / resume
107
+ const dryRun = flagBool(args, 'dry-run');
108
+ const noStop = flagBool(args, 'no-stop-on-failure');
109
+ let report;
110
+ let resumedFromIndex = 0;
111
+ const resumeDiagnostics = [];
112
+ if (sub === 'resume') {
113
+ const result = resumeMigration(migration, {
114
+ projectRoot: cwd,
115
+ dryRun,
116
+ stopOnFailure: !noStop,
117
+ });
118
+ report = result.report;
119
+ resumedFromIndex = result.resumedFromIndex;
120
+ for (const d of result.diagnostics)
121
+ resumeDiagnostics.push(d);
122
+ }
123
+ else {
124
+ report = applyMigration(migration, {
125
+ projectRoot: cwd,
126
+ dryRun,
127
+ stopOnFailure: !noStop,
128
+ });
129
+ }
130
+ if (wantJson) {
131
+ process.stdout.write(asJson({ ...report, resumedFromIndex, resumeDiagnostics }) + '\n');
132
+ return report.overall === 'fail' ? 1 : 0;
133
+ }
134
+ if (sub === 'resume') {
135
+ for (const d of resumeDiagnostics)
136
+ process.stdout.write(`! ${d}\n`);
137
+ }
138
+ process.stdout.write(header(`Migration ${dryRun ? '(dry-run)' : '(applied)'}: ${report.overall.toUpperCase()}`));
139
+ process.stdout.write(kv('id', report.migration.id) + '\n');
140
+ process.stdout.write(kv('total duration', `${report.totalDurationMs}ms`) + '\n');
141
+ for (const s of report.steps) {
142
+ process.stdout.write(` [${s.status.padEnd(8)}] ${s.id} (${s.kind}) (${s.durationMs}ms)\n`);
143
+ process.stdout.write(` ${s.message}\n`);
144
+ if (s.rewriteStats && s.rewriteStats.conflicts.length > 0) {
145
+ for (const c of s.rewriteStats.conflicts.slice(0, 5)) {
146
+ process.stdout.write(` conflict: ${c}\n`);
147
+ }
148
+ }
149
+ }
150
+ return report.overall === 'fail' ? 1 : 0;
151
+ },
152
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"onboard.command.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.command.ts"],"names":[],"mappings":"AA6CA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,cAAc,EAAE,eA2O5B,CAAC"}
1
+ {"version":3,"file":"onboard.command.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.command.ts"],"names":[],"mappings":"AA6CA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,cAAc,EAAE,eA4N5B,CAAC"}
@@ -5,8 +5,8 @@ import { flagBool, flagNumber, flagString, flagList, resolveCwd, } from "../comm
5
5
  import { asJson, header, kv } from "../output/format-output.js";
6
6
  export const onboardCommand = {
7
7
  name: 'onboard',
8
- description: 'Analyze an existing repository and produce a SharkCraft onboarding plan (rules / paths / templates / boundaries / pipelines + readiness estimate). Default is dry-run; `--write-drafts` writes advisory drafts under sharkcraft/onboarding/ (never overwrites rules.ts / paths.ts / templates.ts). Re-running `--write-drafts` against hand-edited drafts performs a three-way merge against the previous snapshot; conflicts emit conflict markers inside the draft (use `--abort-on-conflict` for CI to leave the file untouched and exit non-zero). `--scaffold-templates` drafts runnable template bodies. `--import-agents` parses AGENTS.md / CLAUDE.md / .cursor/rules into a draft. `--diff` compares the plan against the live config.',
9
- usage: 'shrk [--cwd <dir>] onboard [--dry-run] [--write-drafts] [--abort-on-conflict] [--scaffold-templates] [--import-agents] [--diff] [--preset <id>] [--json]',
8
+ description: 'Analyze an existing repository and produce a SharkCraft onboarding plan (rules / paths / templates / boundaries / pipelines + readiness estimate). Default is dry-run; `--write-drafts` writes advisory drafts under sharkcraft/onboarding/ (never overwrites rules.ts / paths.ts / templates.ts). `--scaffold-templates` drafts runnable template bodies. `--import-agents` parses AGENTS.md / CLAUDE.md / .cursor/rules into a draft. `--diff` compares the plan against the live config.',
9
+ usage: 'shrk [--cwd <dir>] onboard [--dry-run] [--write-drafts] [--scaffold-templates] [--import-agents] [--diff] [--preset <id>] [--json]',
10
10
  async run(args) {
11
11
  // Dispatch sub-verb: `shrk onboard adopt [...]`.
12
12
  if (args.positional[0] === 'adopt') {
@@ -15,7 +15,6 @@ export const onboardCommand = {
15
15
  }
16
16
  const cwd = resolveCwd(args);
17
17
  const writeDrafts = flagBool(args, 'write-drafts');
18
- const abortOnConflict = flagBool(args, 'abort-on-conflict');
19
18
  const scaffoldTemplates = flagBool(args, 'scaffold-templates');
20
19
  const importAgents = flagBool(args, 'import-agents');
21
20
  const diffMode = flagBool(args, 'diff');
@@ -36,7 +35,6 @@ export const onboardCommand = {
36
35
  written = writeOnboardingDrafts(plan, {
37
36
  projectRoot: cwd,
38
37
  ...(importedAgentRules ? { importedAgentRules } : {}),
39
- abortOnConflict,
40
38
  });
41
39
  }
42
40
  const diff = diffMode ? buildOnboardingDiff(inspection, plan) : undefined;
@@ -168,18 +166,8 @@ export const onboardCommand = {
168
166
  if (writeDrafts && written) {
169
167
  process.stdout.write(`Wrote ${written.files.length} draft file(s) to:\n ${written.outDir}\n`);
170
168
  for (const f of written.files) {
171
- process.stdout.write(` + ${f.path} (${f.bytes} bytes) [${f.mergeStatus}]\n`);
169
+ process.stdout.write(` + ${f.path} (${f.bytes} bytes)\n`);
172
170
  }
173
- if (written.conflicts > 0) {
174
- process.stderr.write(`\nConflict regions emitted in ${written.conflicts} draft file(s). ` +
175
- 'Resolve <<<<<<< / ======= / >>>>>>> markers before adopting.\n');
176
- }
177
- if (written.aborted > 0) {
178
- process.stderr.write(`\n--abort-on-conflict: ${written.aborted} draft file(s) left unchanged due to conflicts.\n`);
179
- return 1;
180
- }
181
- if (written.conflicts > 0)
182
- return 2;
183
171
  }
184
172
  else if (dryRun) {
185
173
  process.stdout.write('Dry-run only. Re-run with `--write-drafts` to write advisory drafts.\n');
@@ -1,5 +1,5 @@
1
1
  import { type ICommandHandler } from '../command-registry.js';
2
- export type PackKind = 'generic' | 'framework' | 'architecture' | 'enterprise' | 'platform-adopter';
2
+ export type PackKind = 'generic' | 'framework' | 'architecture' | 'enterprise';
3
3
  interface IScaffoldFile {
4
4
  /** Path relative to the new pack root. */
5
5
  relativePath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"packs-new.d.ts","sourceRoot":"","sources":["../../src/commands/packs-new.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,GAAG,YAAY,GAAG,kBAAkB,CAAC;AAUpG,UAAU,aAAa;IACrB,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,CA+G/E;AA4TD,eAAO,MAAM,eAAe,EAAE,eAoG7B,CAAC;AAUF,eAAO,MAAM,gBAAgB,EAAE,eAqI9B,CAAC"}
1
+ {"version":3,"file":"packs-new.d.ts","sourceRoot":"","sources":["../../src/commands/packs-new.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAIhC,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,GAAG,YAAY,CAAC;AAS/E,UAAU,aAAa;IACrB,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,CAyG/E;AA0SD,eAAO,MAAM,eAAe,EAAE,eAoG7B,CAAC;AAUF,eAAO,MAAM,gBAAgB,EAAE,eAqI9B,CAAC"}