@tryaura/aura-cli 0.2.1 → 0.3.1

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.
@@ -1,4 +1,4 @@
1
- import { G as src_default$4, I as createAuraManifestWriteOperation, K as pluralize, S as readManagedBlock, W as SHARED_INSTRUCTIONS_TEMPLATE, _ as planSharedSkillTreeUpdate, b as reconcileManagedSnippet, c as planManifestMcpConvergence, f as isAuraOwnedSkillTarget, g as managedContentRevisionStatus, h as skillDeploymentStatus, i as planMcpSecretRemediation, l as planMcpServerRemoval, m as sharedSkillsRoot, n as canPlanMcpSecretRemediation, o as mcpConvergenceBlockers, p as planSkillDeployment, q as displayPath, t as planSharedInstructionLink, v as reconcileParsedManagedBlock, y as diffManagedSnippet } from "./shared-link-plan-D8fxdFYv.js";
1
+ import { A as displayPath, D as SHARED_INSTRUCTIONS_TEMPLATE, O as src_default$4, d as sharedSkillsRoot, f as skillDeploymentStatus, i as mcpConvergenceBlockers, k as pluralize, l as isAuraOwnedSkillTarget, m as planSharedSkillTreeUpdate, o as planManifestMcpConvergence, p as managedContentRevisionStatus, s as planMcpServerRemoval, t as planSharedInstructionLink, u as planSkillDeployment, v as createAuraManifestWriteOperation } from "./shared-link-plan-AcSkw9bO.js";
2
2
  import { EMPTY_COLLECTION, McpWriteError, SHARED_INSTRUCTIONS_TEMPLATE_TOKEN, TIMEOUT_EXIT_CODE, collectJsonMcpServers, collectMcpServers, configStringArray, configStringRecord, defineAdapter, defineCheck, definePlugin, detectExecutable, detectLineEnding, inspectJsonMcpSecrets, isConfigRecord, isFileReference, jsonMcpEntry, jsonMcpSecretTransform, maskMarkdownCode, mcpCommandEntry, mcpEnvironmentVariableNames, mcpWriteResult, normalizeMcpServerDefinition, parseAtImports, parseConfigObject, parseInstalledSkills, parseJsonMcpConfig, redactMcpArguments, referenceTargetPath, sanitizeMcpUrl, skillDirectorySpecs, splitSourceLines, stdioTransport, writeJsonMcpServers } from "@tryaura/aura-sdk";
3
3
  import { dirname, extname, isAbsolute, join, resolve, sep } from "node:path";
4
4
  import { parse, stringify } from "smol-toml";
@@ -243,8 +243,7 @@ const claudeCodeAdapter = defineAdapter({
243
243
  },
244
244
  sharedLink: {
245
245
  entryPath: "~/.claude/CLAUDE.md",
246
- kind: "import-line",
247
- lineTemplate: `@${SHARED_INSTRUCTIONS_TEMPLATE_TOKEN}`
246
+ kind: "symlink"
248
247
  },
249
248
  supportedRange: ">=2.1.0 <3.0.0"
250
249
  });
@@ -1521,12 +1520,12 @@ var src_default$2 = definePlugin({
1521
1520
  * silently stop finding anything, so the contract is exported and imported rather than retyped.
1522
1521
  *
1523
1522
  * `rulesProject` names the `.cursor/rules` directory slot; each discovered child file gets a
1524
- * derived id of the form `cursor.rules.project/<encoded-name>`, and `aura` is the derived id
1525
- * reserved for the rule Aura itself maintains there.
1523
+ * derived id of the form `cursor.rules.project/<encoded-name>`. No id is reserved for a rule Aura
1524
+ * writes, because Aura writes none: a rule left behind by an older version is read as the ordinary
1525
+ * project rule it is.
1526
1526
  */
1527
1527
  const CURSOR_SOURCE_IDS = Object.freeze({
1528
1528
  agents: "cursor.rules.project.agents",
1529
- aura: "cursor.rules.project/aura-owned",
1530
1529
  legacyRules: "cursor.rules.project.legacy",
1531
1530
  mcpGlobal: "cursor.mcp.global",
1532
1531
  mcpProject: "cursor.mcp.project",
@@ -1812,6 +1811,7 @@ function normalizeReference(reference) {
1812
1811
  //#region ../../plugins/adapter-cursor/src/adapter.ts
1813
1812
  const cursorAdapter = defineAdapter({
1814
1813
  capabilities: { instructions: {
1814
+ globalSharedLink: "not-applicable",
1815
1815
  importStyle: "at-import",
1816
1816
  loading: "import-graph"
1817
1817
  } },
@@ -1840,18 +1840,6 @@ const cursorAdapter = defineAdapter({
1840
1840
  unusableMcpServers: mcpFiles.flatMap(({ config }) => config.unusable)
1841
1841
  };
1842
1842
  },
1843
- sharedLink: {
1844
- entryPath: "./.cursor/rules/aura.mdc",
1845
- kind: "native-copy",
1846
- lineTemplate: [
1847
- "---",
1848
- "alwaysApply: true",
1849
- "---",
1850
- "",
1851
- `@file ${SHARED_INSTRUCTIONS_TEMPLATE_TOKEN}`,
1852
- ""
1853
- ].join("\n")
1854
- },
1855
1843
  supportedRange: ">=0.45.0 <4.0.0"
1856
1844
  });
1857
1845
  /**
@@ -1903,13 +1891,6 @@ function cursorFiles({ environment, files }) {
1903
1891
  path: join(environment.cwd, "AGENTS.md"),
1904
1892
  scope: "project"
1905
1893
  },
1906
- {
1907
- id: CURSOR_SOURCE_IDS.aura,
1908
- kind: "instructions",
1909
- optional: true,
1910
- path: join(environment.cwd, ".cursor", "rules", "aura.mdc"),
1911
- scope: "project"
1912
- },
1913
1894
  {
1914
1895
  id: CURSOR_SOURCE_IDS.mcpGlobal,
1915
1896
  kind: "mcp",
@@ -1931,16 +1912,13 @@ function discoveredRuleSpecs(files) {
1931
1912
  const specs = [];
1932
1913
  for (const file of files.values()) {
1933
1914
  if (!isRuleSourceId(file.spec.id) || file.entries === void 0) continue;
1934
- for (const entry of file.entries) {
1935
- if (file.spec.id === CURSOR_SOURCE_IDS.rulesProject && entry === "aura.mdc") continue;
1936
- specs.push({
1937
- id: `${file.spec.id}/${encodeURIComponent(entry)}`,
1938
- kind: "instructions",
1939
- optional: true,
1940
- path: join(file.spec.path, entry),
1941
- scope: "project"
1942
- });
1943
- }
1915
+ for (const entry of file.entries) specs.push({
1916
+ id: `${file.spec.id}/${encodeURIComponent(entry)}`,
1917
+ kind: "instructions",
1918
+ optional: true,
1919
+ path: join(file.spec.path, entry),
1920
+ scope: "project"
1921
+ });
1944
1922
  }
1945
1923
  return specs;
1946
1924
  }
@@ -2183,7 +2161,7 @@ function detectConflicts(model) {
2183
2161
  function claudeFindings(app) {
2184
2162
  const effective = readClaudePermissionMode(app);
2185
2163
  if (effective === void 0 || !RESTRICTIVE_MODES.has(effective.mode)) return [];
2186
- const path = sourcePath$2(app, effective.sourceId);
2164
+ const path = sourcePath$1(app, effective.sourceId);
2187
2165
  return [{
2188
2166
  details: effective.mode === "plan" ? "Plan mode prevents source edits and most commands. Choose a normal working mode if this was not intentional." : "dontAsk denies tools that were not pre-approved, which can make requests stop without a permission prompt.",
2189
2167
  id: `claude-permission-mode:${effective.mode}`,
@@ -2199,7 +2177,7 @@ function claudeFindings(app) {
2199
2177
  function codexFindings(app) {
2200
2178
  const trust = readCodexProjectTrust(app) ?? "unknown";
2201
2179
  if (trust === "trusted" || trust === "unreadable") return [];
2202
- const path = sourcePath$2(app, CODEX_SOURCE_IDS.mcp);
2180
+ const path = sourcePath$1(app, CODEX_SOURCE_IDS.mcp);
2203
2181
  return [{
2204
2182
  details: trust === "untrusted" ? "Codex explicitly marks this project untrusted and skips its project-scoped configuration." : "Codex has no trusted entry for this working directory or its primary Git checkout, so project-scoped configuration may be unavailable until trust is confirmed.",
2205
2183
  id: `codex-project-trust:${trust}`,
@@ -2212,7 +2190,7 @@ function codexFindings(app) {
2212
2190
  }
2213
2191
  }];
2214
2192
  }
2215
- function sourcePath$2(app, sourceId) {
2193
+ function sourcePath$1(app, sourceId) {
2216
2194
  return app.sourceFiles.find((file) => file.spec.id === sourceId)?.spec.path;
2217
2195
  }
2218
2196
  function guidedFix$2(finding, model) {
@@ -4072,9 +4050,9 @@ const sharedInstructionLinksCheck = defineCheck({
4072
4050
  detect(model) {
4073
4051
  return model.apps.flatMap((app) => detectMissingLink(app, model));
4074
4052
  },
4075
- explain: `Each detected agent application must load the canonical shared instructions through a mechanism its adapter understands. A missing import, native copy, or symlink leaves that application working from different rules even when the shared source is correct.
4053
+ explain: `Each detected agent application must load the canonical shared instructions through a mechanism its adapter understands. A missing import, native copy, or symlink leaves that application working from different rules even when the shared source is correct. Applications with no instruction file of their own in the home directory are out of scope: Cursor, for one, keeps its global User Rules inside its own settings, where Aura cannot write.
4076
4054
 
4077
- Re-run the check with \`--fix\` to add the adapter-supported link without replacing unrelated instructions. Inspect the preview first, apply it, then restart the affected application if it caches instruction files.`,
4055
+ Re-run the check with \`--fix\` to add the adapter-supported link without replacing unrelated instructions. Inspect the preview first, apply it, then restart the affected application if it caches instruction files. An application whose instruction file already holds guidance of its own is a manual finding instead, because \`--fix\` never overwrites one: run \`aura setup\` and choose instruction consolidation to merge that content into the shared source, which archives the original before the link takes its place.`,
4078
4056
  findingGroup: SHARED_INSTRUCTION_GROUP,
4079
4057
  fix(finding, model) {
4080
4058
  const app = model.apps.find((candidate) => candidate.adapterId === findingAppId(finding));
@@ -4089,6 +4067,10 @@ Re-run the check with \`--fix\` to add the adapter-supported link without replac
4089
4067
  });
4090
4068
  function detectMissingLink(app, model) {
4091
4069
  if (app.synthetic === true) return [];
4070
+ if (app.capabilities?.instructions?.globalSharedLink === "not-applicable") return [];
4071
+ return detectRequiredLink(app, model);
4072
+ }
4073
+ function detectRequiredLink(app, model) {
4092
4074
  const shared = app.sharedLink;
4093
4075
  const sharedPath = resolve(model.sharedInstructions.path);
4094
4076
  if (shared !== void 0 && linksToShared(app, shared, sharedPath)) return [];
@@ -4247,431 +4229,47 @@ function isScope$1(value) {
4247
4229
  return SCOPES.some((scope) => scope === value);
4248
4230
  }
4249
4231
  //#endregion
4250
- //#region ../../plugins/checks-core/src/mgd-001-documents.ts
4251
- const SHARED_SOURCE_ID = "aura.shared-instructions";
4252
- /**
4253
- * Every document whose managed block this check owns, parsed.
4254
- *
4255
- * Memoized per model because detection and remediation ask the same question: `detect` walks the
4256
- * set once, then each finding's guided fixes look its document back up. Rebuilding the set per
4257
- * finding re-resolves every instruction path and re-parses every block to reach an answer that
4258
- * cannot have changed — the model is frozen for the length of a run.
4259
- */
4260
- const CACHE = /* @__PURE__ */ new WeakMap();
4261
- function managedDocuments(model) {
4262
- const cached = CACHE.get(model);
4263
- if (cached !== void 0) return cached;
4264
- const documents = /* @__PURE__ */ new Map();
4265
- if (model.sharedInstructions.content !== void 0) documents.set(resolve(model.sharedInstructions.path), managedDocument(model.sharedInstructions.content, model.sharedInstructions.path, SHARED_SOURCE_ID));
4266
- for (const document of canonicalInstructionDocuments(model.instructionFiles)) {
4267
- const path = resolve(document.path);
4268
- if (!documents.has(path) && !isSymlinkDocument(document, model)) documents.set(path, managedDocument(document.content, document.path, document.sourceId));
4269
- }
4270
- const resolved = Object.freeze([...documents.values()]);
4271
- CACHE.set(model, resolved);
4272
- return resolved;
4273
- }
4274
- /** Finds the document one finding was raised against, without rebuilding the set. */
4275
- function findManagedDocument(model, sourceId, path) {
4276
- const target = resolve(path);
4277
- return managedDocuments(model).find((document) => document.sourceId === sourceId && resolve(document.path) === target);
4278
- }
4279
- function managedDocument(content, path, sourceId) {
4280
- return Object.freeze({
4281
- content,
4282
- parsed: readManagedBlock(content),
4283
- path,
4284
- sourceId
4285
- });
4286
- }
4287
- function isSymlinkDocument(document, model) {
4288
- const path = resolve(document.path);
4289
- return model.apps.some((app) => app.sourceFiles.some((file) => file.spec.id === document.sourceId && resolve(file.spec.path) === path && file.pathKind === "symlink"));
4290
- }
4291
- //#endregion
4292
- //#region ../../plugins/checks-core/src/mgd-001-fixes.ts
4293
- const CHECK_ID$4 = "MGD-001";
4294
- function guidedFixes$2(finding, model) {
4295
- if (finding.checkId !== CHECK_ID$4) return [];
4296
- const kind = finding.metadata?.["kind"];
4297
- if (kind === "malformed") return [malformedMergeChoice(finding)];
4298
- if (kind === "unterminated-fence") return [unterminatedFenceChoice(finding)];
4299
- return kind === "hash-mismatch" ? mismatchChoices(finding, model) : [];
4300
- }
4301
- function mismatchChoices(finding, model) {
4302
- const context = mismatchContext(finding, model);
4303
- if (context === void 0) throw new Error(`The managed snippet for finding "${finding.id}" is no longer available.`);
4304
- const choices = [];
4305
- const keep = keepChoice(context, model);
4306
- choices.push(keep);
4307
- const canonical = model.availableSnippets.find((candidate) => candidate.id === context.snippet.id);
4308
- const restore = restoreChoice(context, canonical, model);
4309
- if (restore !== void 0) choices.push(restore);
4310
- choices.push(mergeChoice(context.document, context.snippet, canonical?.content));
4311
- return Object.freeze(choices);
4312
- }
4313
- function mismatchContext(finding, model) {
4314
- const document = findingDocument(finding, model);
4315
- const snippetId = finding.metadata?.["snippetId"];
4316
- if (document === void 0 || typeof snippetId !== "string") return;
4317
- if (document.parsed.status !== "present") return;
4318
- const snippet = document.parsed.block.snippets.find((candidate) => candidate.id === snippetId);
4319
- return snippet === void 0 ? void 0 : {
4320
- document,
4321
- snippet
4322
- };
4323
- }
4324
- function keepChoice(context, model) {
4325
- const { document, snippet } = context;
4326
- const keep = reconcileManagedSnippet(document.content, snippet.id, { kind: "keep" });
4327
- if (keep.status === "invalid") throw new Error(`Keep yours could not reconcile ${snippet.id}: ${keep.problems.map((problem) => problem.message).join(" ")}`);
4328
- const applied = operationsWithManifest({
4329
- content: keep.content,
4330
- hash: snippet.computedHash,
4331
- model,
4332
- path: document.path,
4333
- pinned: true,
4334
- snippetId: snippet.id
4335
- });
4336
- return {
4337
- id: "keep",
4338
- label: "Keep yours",
4339
- plan: {
4340
- ...applied.manualSteps.length === 0 ? {} : { manualSteps: [...applied.manualSteps] },
4341
- operations: applied.operations,
4342
- summary: `Adopt the edited ${snippet.id} snippet.`
4343
- }
4344
- };
4345
- }
4346
- function restoreChoice(context, canonical, model) {
4347
- if (canonical === void 0) return;
4348
- const restored = reconcileManagedSnippet(context.document.content, context.snippet.id, {
4349
- content: canonical.content,
4350
- kind: "restore"
4351
- });
4352
- if (restored.status === "invalid") throw new Error(`Restore could not reconcile ${context.snippet.id}: ${restored.problems.map((problem) => problem.message).join(" ")}`);
4353
- const applied = operationsWithManifest({
4354
- content: restored.content,
4355
- hash: canonical.hash,
4356
- model,
4357
- path: context.document.path,
4358
- pinned: false,
4359
- snippetId: context.snippet.id,
4360
- version: canonical.version
4361
- });
4362
- return {
4363
- id: "restore",
4364
- label: "Restore",
4365
- plan: {
4366
- ...applied.manualSteps.length === 0 ? {} : { manualSteps: [...applied.manualSteps] },
4367
- operations: applied.operations,
4368
- summary: `Restore ${context.snippet.id} from the registered snippet.`
4369
- }
4370
- };
4371
- }
4372
- /**
4373
- * Pairs the instruction-file write with the manifest update that records it.
4374
- *
4375
- * The write can succeed where the manifest update cannot: an unreadable manifest, or an id that
4376
- * several selections claim, leaves nothing unambiguous to edit. Applying only the file half is
4377
- * still the right outcome — the marker hash is what stops the next run overwriting the user — but
4378
- * it leaves the manifest disagreeing with disk, which is the drift the manifest exists to catch.
4379
- * So the shortfall becomes a manual step rather than a silence.
4380
- */
4381
- function operationsWithManifest(options) {
4382
- const operations = [{
4383
- content: options.content,
4384
- path: options.path,
4385
- type: "write"
4386
- }];
4387
- if (options.model.manifest.status === "missing") return {
4388
- manualSteps: [`Create ${options.model.manifest.path} and record ${options.snippetId} at hash ${options.hash}; the manifest does not exist, so this fix updates only ${options.path}.`],
4389
- operations
4390
- };
4391
- if (options.model.manifest.status === "read-only") return {
4392
- manualSteps: [`Record ${options.snippetId} at hash ${options.hash} after repairing the Aura manifest: ${options.model.manifest.problem.message}`],
4393
- operations
4394
- };
4395
- const matches = options.model.manifest.value.snippets.filter((snippet) => snippet.id === options.snippetId);
4396
- const selected = matches[0];
4397
- if (matches.length !== 1 || selected === void 0) return {
4398
- manualSteps: [`Record ${options.snippetId} at hash ${options.hash} in ${options.model.manifest.path} by hand; ${String(matches.length)} manifest entries claim that id, so this fix updates only ${options.path}.`],
4399
- operations
4400
- };
4401
- const manifest = {
4402
- ...options.model.manifest.value,
4403
- snippets: options.model.manifest.value.snippets.map((snippet) => snippet === selected ? updatedSelection(snippet, options) : snippet)
4404
- };
4405
- operations.push(createAuraManifestWriteOperation(options.model.manifest, manifest));
4406
- return {
4407
- manualSteps: [],
4408
- operations
4409
- };
4410
- }
4411
- function updatedSelection(snippet, options) {
4412
- return {
4413
- ...snippet,
4414
- hash: options.hash,
4415
- pinned: options.pinned,
4416
- version: options.version ?? snippet.version
4417
- };
4418
- }
4419
- function mergeChoice(document, snippet, canonicalContent) {
4420
- const lines = `${String(snippet.startLine)}-${String(snippet.endLine)}`;
4421
- return {
4422
- ...canonicalContent === void 0 ? {} : { details: () => diffManagedSnippet(document.path, snippet.content, canonicalContent) },
4423
- id: "merge",
4424
- label: "Merge",
4425
- plan: {
4426
- manualSteps: [canonicalContent === void 0 ? `Edit ${document.path} at lines ${lines}; the registry no longer provides ${snippet.id}, so no canonical diff is available.` : `Review the diff and edit ${document.path} at lines ${lines} to the combined content you want.`, "Run `aura check` again and choose Keep yours to adopt the result."],
4427
- operations: [],
4428
- summary: `Manually merge the edited ${snippet.id} snippet.`
4429
- }
4430
- };
4431
- }
4432
- function malformedMergeChoice(finding) {
4433
- const path = finding.locations?.[0]?.path;
4434
- return {
4435
- id: "merge",
4436
- label: "Repair manually",
4437
- plan: {
4438
- manualSteps: [path === void 0 ? "Repair the malformed Aura marker structure without changing surrounding content." : `Repair the malformed Aura marker structure in ${path} without changing surrounding content.`, "Run `aura check` again after the block parses cleanly."],
4439
- operations: [],
4440
- summary: "Repair the malformed Aura-managed block manually."
4441
- }
4442
- };
4443
- }
4444
- function unterminatedFenceChoice(finding) {
4445
- const path = finding.locations?.[0]?.path;
4446
- return {
4447
- id: "close-fence",
4448
- label: "Close the fence",
4449
- plan: {
4450
- manualSteps: [path === void 0 ? "Close the Markdown fence that hides the Aura-managed markers." : `Close the Markdown fence that hides the Aura-managed markers in ${path}.`, "Run `aura check` again after the markers are visible."],
4451
- operations: [],
4452
- summary: "Close the unterminated Markdown fence manually."
4453
- }
4454
- };
4455
- }
4456
- function findingDocument(finding, model) {
4457
- const sourceId = finding.metadata?.["sourceId"];
4458
- const path = finding.locations?.[0]?.path;
4459
- return typeof sourceId === "string" && path !== void 0 ? findManagedDocument(model, sourceId, path) : void 0;
4460
- }
4461
- const managedBlockHashCheck = defineCheck({
4462
- defaultSeverity: "warn",
4463
- detect: detectManagedBlockDrift,
4464
- explain: `Aura hashes each managed snippet so hand edits cannot be overwritten silently. A mismatch means the installed text no longer matches the registered version, so a later setup run must not guess which side should win.
4465
-
4466
- Re-run the check with \`--fix\` and choose Keep yours to adopt the edit, Restore to return to registered content, or Merge to review both versions and reconcile them manually. Every executable choice is previewed and backed up before Aura writes.`,
4467
- fix: () => void 0,
4468
- fixability: "guided",
4469
- guidedFixes: guidedFixes$2,
4470
- id: "MGD-001",
4471
- scope: "global",
4472
- title: "Aura-managed instruction blocks have not changed by hand"
4473
- });
4474
- function detectManagedBlockDrift(model) {
4475
- return managedDocuments(model).flatMap((document) => findingsForDocument(document, model));
4476
- }
4477
- function findingsForDocument(document, model) {
4478
- const parsed = document.parsed;
4479
- const hiddenMarker = parsed.notes.find((note) => note.code === "unterminated-fence");
4480
- if (hiddenMarker !== void 0) return [unterminatedFenceFinding(document, hiddenMarker.line, model)];
4481
- if (parsed.status === "invalid") return [malformedFinding(document, parsed.problems, model)];
4482
- if (parsed.status === "absent") return [];
4483
- const manifestHashes = manifestSnippetHashes(model);
4484
- return parsed.block.snippets.filter((snippet) => {
4485
- const manifestHash = manifestHashes.get(snippet.id);
4486
- return !snippet.hashMatches || manifestHash !== void 0 && manifestHash !== snippet.computedHash;
4487
- }).map((snippet) => ({
4488
- details: "Run `aura check --fix` and choose Keep yours, Restore, or Merge before running setup again.",
4489
- id: `${document.sourceId}:${snippet.id}`,
4490
- locations: [{
4491
- line: snippet.startLine,
4492
- path: document.path
4493
- }],
4494
- message: `Managed snippet ${snippet.id} in ${displayInstructionPath(document.path, model)} was edited by hand.`,
4495
- metadata: {
4496
- kind: "hash-mismatch",
4497
- snippetId: snippet.id,
4498
- sourceId: document.sourceId
4499
- }
4500
- }));
4501
- }
4502
- function unterminatedFenceFinding(document, line, model) {
4503
- return {
4504
- details: "Close the Markdown fence, then run `aura check --fix` again. Aura will not reconcile this document while its markers are hidden.",
4505
- id: `${document.sourceId}:unterminated-fence`,
4506
- locations: [{
4507
- ...line === void 0 ? {} : { line },
4508
- path: document.path
4509
- }],
4510
- message: `An unclosed Markdown fence hides Aura-managed markers in ${displayInstructionPath(document.path, model)}.`,
4511
- metadata: {
4512
- kind: "unterminated-fence",
4513
- sourceId: document.sourceId
4514
- }
4515
- };
4516
- }
4517
- function malformedFinding(document, problems, model) {
4518
- return {
4519
- details: `${problems.map((problem) => problem.message).join(" ")} Aura will not write to a block it cannot parse; repair the markers by hand.`,
4520
- id: `${document.sourceId}:malformed-managed-block`,
4521
- locations: problems.map((problem) => ({
4522
- ...problem.line === void 0 ? {} : { line: problem.line },
4523
- path: document.path
4524
- })),
4525
- message: `The Aura-managed block in ${displayInstructionPath(document.path, model)} is malformed.`,
4526
- metadata: {
4527
- kind: "malformed",
4528
- problems: problems.map((problem) => ({
4529
- code: problem.code,
4530
- ...problem.line === void 0 ? {} : { line: problem.line }
4531
- })),
4532
- sourceId: document.sourceId
4533
- }
4534
- };
4535
- }
4536
- function manifestSnippetHashes(model) {
4537
- if (model.manifest.status !== "ready") return /* @__PURE__ */ new Map();
4538
- const hashes = /* @__PURE__ */ new Map();
4539
- const ambiguous = /* @__PURE__ */ new Set();
4540
- for (const snippet of model.manifest.value.snippets) if (hashes.has(snippet.id)) ambiguous.add(snippet.id);
4541
- else hashes.set(snippet.id, snippet.hash);
4542
- for (const id of ambiguous) hashes.delete(id);
4543
- return hashes;
4544
- }
4545
- //#endregion
4546
4232
  //#region ../../plugins/checks-core/src/mgd-002-fixes.ts
4547
- function managedContentUpdateChoices(finding, model) {
4548
- if (finding.checkId !== "MGD-002") return [];
4233
+ function managedSkillUpdateChoices(finding, model) {
4549
4234
  const kind = finding.metadata?.["kind"];
4550
- if (kind === "snippet-update" || kind === "snippet-diverged") return snippetUpdateChoices(finding, model);
4551
- if (kind === "snippet-missing") return missingSnippetChoices(finding, model);
4552
- return kind === "skill-update" || kind === "skill-diverged" ? skillUpdateChoices(finding, model) : [];
4235
+ return finding.checkId === "MGD-002" && (kind === "skill-update" || kind === "skill-diverged") ? skillUpdateChoices(finding, model) : [];
4553
4236
  }
4554
- /**
4555
- * How a choice names the revision it would apply.
4556
- *
4557
- * A diverged revision is a deliberate move to content that is not newer, so calling it an update
4558
- * would misdescribe what the user is accepting.
4559
- */
4560
- function applyLabel(finding, version) {
4561
- return `${finding.metadata?.["kind"] === "snippet-update" || finding.metadata?.["kind"] === "skill-update" ? "Update to" : "Switch to"} ${version}`;
4562
- }
4563
- function snippetUpdateChoices(finding, model) {
4564
- const context = readyFindingContext(finding, model);
4565
- if (context === void 0) return [];
4566
- const { contentId, manifest: manifestState } = context;
4567
- const installed = manifestState.value.snippets.find((snippet) => snippet.id === contentId);
4568
- const available = model.availableSnippets.find((snippet) => snippet.id === contentId);
4569
- const located = locateManagedSnippet(model, contentId);
4570
- if (installed === void 0 || available === void 0 || located === void 0) return [];
4571
- if (!located.snippet.hashMatches || located.snippet.computedHash !== installed.hash) return [resolveDriftFirst(contentId)];
4572
- const reconciled = reconcileManagedSnippet(located.document.content, contentId, {
4573
- content: available.content,
4574
- kind: "restore"
4575
- });
4576
- if (reconciled.status === "invalid") return [resolveDriftFirst(contentId)];
4577
- const manifest = {
4578
- ...manifestState.value,
4579
- snippets: manifestState.value.snippets.map((snippet) => snippet === installed ? {
4580
- ...snippet,
4581
- hash: available.hash,
4582
- pinned: false,
4583
- version: available.version
4584
- } : snippet)
4585
- };
4586
- return [{
4587
- details: () => diffManagedSnippet(located.document.path, located.snippet.content, available.content),
4588
- id: "update",
4589
- label: applyLabel(finding, available.version),
4590
- plan: {
4591
- operations: [{
4592
- content: reconciled.content,
4593
- path: located.document.path,
4594
- type: "write"
4595
- }, createAuraManifestWriteOperation(manifestState, manifest)],
4596
- summary: `Set managed snippet ${contentId} to ${available.version}.`
4597
- }
4598
- }];
4599
- }
4600
- function missingSnippetChoices(finding, model) {
4601
- const context = readyFindingContext(finding, model);
4237
+ function skillUpdateChoices(finding, model) {
4238
+ const context = skillUpdateContext(finding, model);
4602
4239
  if (context === void 0) return [];
4603
- const { contentId, manifest } = context;
4604
- const installed = manifest.value.snippets.find((snippet) => snippet.id === contentId);
4605
- if (installed === void 0) return [];
4606
- const pinnedManifest = {
4607
- ...manifest.value,
4608
- snippets: manifest.value.snippets.map((snippet) => snippet === installed ? {
4609
- ...snippet,
4610
- pinned: true
4611
- } : snippet)
4612
- };
4613
- const choices = [{
4614
- id: "pin",
4615
- label: "Keep as pinned",
4616
- plan: {
4617
- operations: [createAuraManifestWriteOperation(manifest, pinnedManifest)],
4618
- summary: `Keep unavailable snippet ${contentId} at its installed revision.`
4619
- }
4620
- }];
4621
- const removal = removeSnippetPlan(model, contentId, installed);
4622
- if (removal !== void 0) choices.push({
4623
- id: "remove",
4624
- label: "Remove",
4625
- plan: removal
4626
- });
4627
- return choices;
4628
- }
4629
- function removeSnippetPlan(model, contentId, installed) {
4630
- if (model.manifest.status !== "ready") return;
4631
- const located = locateManagedSnippet(model, contentId);
4632
- if (located === void 0) return;
4633
- const preserve = located.document.parsed.status === "present" ? located.document.parsed.block.snippets.map((snippet) => snippet.id).filter((id) => id !== contentId) : [];
4634
- const reconciled = reconcileParsedManagedBlock(located.document.content, located.document.parsed, [], {
4635
- ownedSnippetIds: model.manifest.value.snippets.map((snippet) => snippet.id),
4636
- preserveSnippetIds: preserve,
4637
- previousSnippetHashes: new Map(model.manifest.value.snippets.map((snippet) => [snippet.id, snippet.hash]))
4638
- });
4639
- if (reconciled.status === "invalid") return;
4640
- const manifest = {
4641
- ...model.manifest.value,
4642
- snippets: model.manifest.value.snippets.filter((snippet) => snippet !== installed)
4643
- };
4644
- return {
4645
- operations: [{
4646
- content: reconciled.content,
4647
- path: located.document.path,
4648
- type: "write"
4649
- }, createAuraManifestWriteOperation(model.manifest, manifest)],
4650
- summary: `Remove unavailable managed snippet ${contentId}.`
4651
- };
4240
+ if (context.shared.problem !== void 0 || context.shared.treeHash !== context.installed.treeHash) return [resolveSkillDriftFirst(context.contentId)];
4241
+ return [skillUpdateChoice(finding, context)];
4652
4242
  }
4653
- function skillUpdateChoices(finding, model) {
4243
+ function skillUpdateContext(finding, model) {
4654
4244
  const context = readyFindingContext(finding, model);
4655
4245
  const sourceId = finding.metadata?.["sourceId"];
4656
- if (context === void 0 || typeof sourceId !== "string") return [];
4657
- const { contentId, manifest: manifestState } = context;
4658
- const installed = manifestState.value.skills.find((skill) => skill.id === contentId && skill.source === sourceId);
4246
+ if (context === void 0 || typeof sourceId !== "string") return;
4247
+ const { contentId, manifest } = context;
4248
+ const installed = manifest.value.skills.find((skill) => skill.id === contentId && skill.source === sourceId);
4659
4249
  const available = model.availableSkills?.find((skill) => skill.id === contentId && skill.source.id === sourceId);
4660
4250
  const shared = model.sharedSkills?.find((skill) => skill.id === contentId);
4661
- if (installed === void 0 || available === void 0 || shared === void 0) return [];
4662
- if (shared.problem !== void 0 || shared.treeHash !== installed.treeHash) return [resolveSkillDriftFirst(contentId)];
4251
+ return installed === void 0 || available === void 0 || shared === void 0 ? void 0 : {
4252
+ available,
4253
+ contentId,
4254
+ installed,
4255
+ manifest,
4256
+ shared
4257
+ };
4258
+ }
4259
+ function skillUpdateChoice(finding, context) {
4260
+ const { available, contentId, installed, manifest: manifestState, shared } = context;
4663
4261
  const manifest = {
4664
4262
  ...manifestState.value,
4665
4263
  skills: manifestState.value.skills.map((skill) => skill === installed ? updatedSkill(skill, available) : skill)
4666
4264
  };
4667
- return [{
4265
+ return {
4668
4266
  id: "update",
4669
- label: applyLabel(finding, available.version),
4267
+ label: `${finding.metadata?.["kind"] === "skill-update" ? "Update to" : "Switch to"} ${available.version}`,
4670
4268
  plan: {
4671
4269
  operations: [...planSharedSkillTreeUpdate(shared.path, shared.entries, available), createAuraManifestWriteOperation(manifestState, manifest)],
4672
4270
  summary: `Set managed skill ${contentId} to ${available.version}.`
4673
4271
  }
4674
- }];
4272
+ };
4675
4273
  }
4676
4274
  function readyFindingContext(finding, model) {
4677
4275
  const contentId = finding.metadata?.["contentId"];
@@ -4688,27 +4286,6 @@ function updatedSkill(installed, available) {
4688
4286
  version: available.version
4689
4287
  };
4690
4288
  }
4691
- function locateManagedSnippet(model, contentId) {
4692
- for (const document of managedDocuments(model)) {
4693
- if (document.parsed.status !== "present") continue;
4694
- const snippet = document.parsed.block.snippets.find((candidate) => candidate.id === contentId);
4695
- if (snippet !== void 0) return {
4696
- document,
4697
- snippet
4698
- };
4699
- }
4700
- }
4701
- function resolveDriftFirst(contentId) {
4702
- return {
4703
- id: "resolve-drift",
4704
- label: "Resolve local edits first",
4705
- plan: {
4706
- manualSteps: [`Run \`aura check --only MGD-001 --fix\` and resolve edits to ${contentId}, then review this update again.`],
4707
- operations: [],
4708
- summary: `Resolve local edits to ${contentId} before updating it.`
4709
- }
4710
- };
4711
- }
4712
4289
  function resolveSkillDriftFirst(contentId) {
4713
4290
  return {
4714
4291
  id: "resolve-drift",
@@ -4720,55 +4297,21 @@ function resolveSkillDriftFirst(contentId) {
4720
4297
  }
4721
4298
  };
4722
4299
  }
4300
+ //#endregion
4301
+ //#region ../../plugins/checks-core/src/mgd-002.ts
4723
4302
  const managedContentUpdateCheck = defineCheck({
4724
4303
  defaultSeverity: "info",
4725
- detect: detectManagedContentUpdates,
4726
- explain: "Aura records the exact version and content hash of each managed snippet and skill. When an installed plugin offers a different revision, Aura reports it and waits for a reviewed change instead of silently replacing managed content. A revision that is not newer — a rollback, or one this build cannot order — is reported too, because Aura keeps the recorded revision either way and an unreported hold would look like nothing to do. Pinned selections remain quiet until they are unpinned.\n\nRun `aura check --fix` to review bundled revisions and vanished snippets. Locally edited content must be resolved first; directory-sourced skill updates stay in `aura setup`, where network and credential approval are explicit.",
4304
+ detect: detectManagedSkillUpdates,
4305
+ explain: "Aura records the exact source revision of each managed skill. When an installed plugin offers a different revision, Aura reports it and waits for a reviewed change instead of silently replacing the skill. Pinned skills remain quiet until they are unpinned.\n\nRun `aura check --fix` to review bundled skill revisions. Directory-sourced skill updates stay in `aura setup`, where network and credential approval are explicit.",
4727
4306
  fix: () => void 0,
4728
4307
  fixability: "guided",
4729
- guidedFixes: managedContentUpdateChoices,
4308
+ guidedFixes: managedSkillUpdateChoices,
4730
4309
  id: "MGD-002",
4731
4310
  scope: "global",
4732
- title: "Managed snippets and skills are at their reviewed source revisions"
4311
+ title: "Managed skills are at their reviewed source revisions"
4733
4312
  });
4734
- function detectManagedContentUpdates(model) {
4735
- if (model.manifest.status !== "ready") return [];
4736
- return [...model.manifest.value.snippets.flatMap((snippet) => snippetFinding(snippet, model)), ...model.manifest.value.skills.flatMap((skill) => skillFinding(skill, model))];
4737
- }
4738
- function snippetFinding(installed, model) {
4739
- if (installed.pinned) return [];
4740
- const available = model.availableSnippets.find((snippet) => snippet.id === installed.id);
4741
- if (available === void 0) return [{
4742
- id: `snippet:${installed.id}:missing`,
4743
- message: `Managed snippet ${installed.id} at version ${installed.version} is no longer provided by an installed plugin.`,
4744
- metadata: {
4745
- contentId: installed.id,
4746
- kind: "snippet-missing"
4747
- }
4748
- }];
4749
- const status = managedContentRevisionStatus(installed.version, installed.hash, available.version, available.hash);
4750
- if (status === "current") return [];
4751
- if (status === "diverged") return [{
4752
- details: "Aura keeps the recorded revision because the offered one is not newer. Re-run `aura setup` interactively to switch to it deliberately, or pin this snippet to stop the comparison.",
4753
- id: `snippet:${installed.id}:diverged`,
4754
- message: `Managed snippet ${installed.id} is recorded at version ${installed.version}, but the installed plugin offers ${available.version}, which is not newer.`,
4755
- metadata: {
4756
- availableVersion: available.version,
4757
- contentId: installed.id,
4758
- installedVersion: installed.version,
4759
- kind: "snippet-diverged"
4760
- }
4761
- }];
4762
- return [{
4763
- id: `snippet:${installed.id}:update`,
4764
- message: `Managed snippet ${installed.id} can update from version ${installed.version} to ${available.version}.`,
4765
- metadata: {
4766
- availableVersion: available.version,
4767
- contentId: installed.id,
4768
- installedVersion: installed.version,
4769
- kind: "snippet-update"
4770
- }
4771
- }];
4313
+ function detectManagedSkillUpdates(model) {
4314
+ return model.manifest.status === "ready" ? model.manifest.value.skills.flatMap((skill) => skillFinding(skill, model)) : [];
4772
4315
  }
4773
4316
  function skillFinding(installed, model) {
4774
4317
  if (installed.pinned || !installed.source.startsWith("plugin:")) return [];
@@ -5188,7 +4731,7 @@ const mcp003 = defineCheck({
5188
4731
  Remote URLs are silent and generate no network traffic unless \`check --online\` is supplied. Online probes use short, bounded requests without configured authentication headers, and they skip an endpoint whose credentials Aura stripped, because the remaining URL no longer addresses the configured server. A redirect to a private address is reported rather than followed. A timeout or a server-side fault is a warning, not an error: it may not repeat. Re-run with \`--fix\` to choose whether to repair or remove a server that definitely failed its probe. Cursor separately requires its MCP servers to be approved and enabled in its own settings, which no file records; Aura reads those states by running \`cursor-agent mcp list\` when that CLI is installed and identifies itself. The command is Cursor's, and it connects to servers to answer, so Aura runs it from your home directory: only your own global configuration takes part, never a \`.cursor/mcp.json\` committed to the repository you are in. Aura never approves, enables, or retries a server itself, and every state it reports this way is a warning. Aura also reports an informational reminder when a desired MCP transport references an unset environment variable. It retains only the variable name and whether it is set; credential values never enter the workspace model or report.`,
5189
4732
  fix: () => void 0,
5190
4733
  fixability: "guided",
5191
- guidedFixes: guidedFixes$1,
4734
+ guidedFixes,
5192
4735
  id: CHECK_ID,
5193
4736
  scope: "global",
5194
4737
  title: "Configured MCP servers can be reached"
@@ -5258,14 +4801,14 @@ function findingFor(server, probe, model) {
5258
4801
  ...probe.transient === true ? { severity: "warn" } : {}
5259
4802
  };
5260
4803
  }
5261
- function guidedFixes$1(finding, model) {
4804
+ function guidedFixes(finding, model) {
5262
4805
  const server = findingServer(finding, model);
5263
4806
  const kind = finding.metadata?.["kind"];
5264
4807
  if (server === void 0 || kind !== "command" && kind !== "url") return [];
5265
4808
  return [repairChoice(server, kind, model), removeChoice(server, model)];
5266
4809
  }
5267
4810
  function repairChoice(server, kind, model) {
5268
- const location = sourcePath$1(server, model) ?? "the application's MCP configuration";
4811
+ const location = sourcePath(server, model) ?? "the application's MCP configuration";
5269
4812
  return {
5270
4813
  id: "repair",
5271
4814
  label: "Repair server",
@@ -5293,7 +4836,7 @@ function removeChoice(server, model) {
5293
4836
  };
5294
4837
  }
5295
4838
  function manualRemoval(server, model, blockers) {
5296
- const path = sourcePath$1(server, model) ?? "the application's MCP configuration file";
4839
+ const path = sourcePath(server, model) ?? "the application's MCP configuration file";
5297
4840
  const selected = model.manifest.status === "ready" && model.manifest.value.mcpServers.some((entry) => entry.name === server.name && entry.scope === server.scope && entry.apps.includes(server.appId));
5298
4841
  return {
5299
4842
  manualSteps: [
@@ -5315,72 +4858,9 @@ function findingServer(finding, model) {
5315
4858
  if (typeof appId !== "string" || typeof name !== "string" || scope !== "global" && scope !== "project" || typeof sourceId !== "string") return;
5316
4859
  return model.mcpServers.find((server) => server.appId === appId && server.name === name && server.scope === scope && server.sourceId === sourceId);
5317
4860
  }
5318
- function sourcePath$1(server, model) {
4861
+ function sourcePath(server, model) {
5319
4862
  return model.apps.find((app) => app.adapterId === server.appId)?.sourceFiles.find((file) => file.spec.id === server.sourceId)?.spec.path;
5320
4863
  }
5321
- const mcp004 = defineCheck({
5322
- defaultSeverity: "error",
5323
- detect: detectInlineSecrets,
5324
- explain: `Inline credentials in MCP configuration can leak through source control, logs, diagnostics, and copied configuration. Aura identifies credential-bearing fields without retaining their values, prefixes, or lengths.
5325
-
5326
- Re-run with \`--fix\` to replace every behavior-preserving sighting in the same source file with an environment reference. Copy the current values directly from the named file before applying the rewrite; Aura never displays or stores those values outside the protected undo payload, which stays owner-only under \`~/agents/.backups\` until twenty later fixes have pushed it out of the journal. Then rotate each credential at its issuer: moving a value behind an environment reference stops it leaking again, not from wherever it has already reached.`,
5327
- findingGroup: {
5328
- description: "Replace inline credentials with environment-variable references.",
5329
- id: "checks-core/mcp-credentials",
5330
- title: "Protect credentials stored in MCP configuration"
5331
- },
5332
- fix: remediation,
5333
- fixability: "guided",
5334
- guidedFixes,
5335
- id: "MCP-004",
5336
- scope: "global",
5337
- title: "MCP credentials use environment references"
5338
- });
5339
- function detectInlineSecrets(model) {
5340
- return model.mcpSecretSightings.map((sighting) => {
5341
- const supported = canPlanMcpSecretRemediation(model, sighting);
5342
- const path = sourcePath(model, sighting);
5343
- return {
5344
- details: supported ? `Aura can replace this field with ${sighting.suggestedEnvName} as part of a whole-file guided rewrite.` : manualDetails(sighting),
5345
- ...supported ? {} : { fixability: "manual" },
5346
- id: `${sighting.appId}:${sighting.sourceId}:${sighting.serverName}:${sighting.field}`,
5347
- ...path === void 0 ? {} : { locations: [{ path }] },
5348
- message: `MCP server ${sighting.serverName} in ${sighting.appId} stores a credential inline at ${sighting.field}.`,
5349
- metadata: {
5350
- appId: sighting.appId,
5351
- field: sighting.field,
5352
- serverName: sighting.serverName,
5353
- sourceId: sighting.sourceId,
5354
- suggestedEnvName: sighting.suggestedEnvName
5355
- }
5356
- };
5357
- });
5358
- }
5359
- function remediation(finding, model) {
5360
- const sighting = findingSighting(finding, model);
5361
- return sighting === void 0 ? void 0 : planMcpSecretRemediation(model, sighting);
5362
- }
5363
- function guidedFixes(finding, model) {
5364
- const plan = remediation(finding, model);
5365
- return plan === void 0 ? [] : [{
5366
- id: "replace-file-secrets",
5367
- label: "Replace inline credentials in this file",
5368
- plan
5369
- }];
5370
- }
5371
- function findingSighting(finding, model) {
5372
- const appId = finding.metadata?.["appId"];
5373
- const sourceId = finding.metadata?.["sourceId"];
5374
- const serverName = finding.metadata?.["serverName"];
5375
- const field = finding.metadata?.["field"];
5376
- return model.mcpSecretSightings.find((sighting) => sighting.appId === appId && sighting.sourceId === sourceId && sighting.serverName === serverName && sighting.field === field);
5377
- }
5378
- function sourcePath(model, sighting) {
5379
- return model.apps.find((app) => app.adapterId === sighting.appId)?.sourceFiles.find((file) => file.spec.id === sighting.sourceId)?.spec.path;
5380
- }
5381
- function manualDetails(sighting) {
5382
- return sighting.appId === "codex" ? `Aura cannot rewrite ${sighting.field} without changing how Codex reads it — either the field has no environment-reference form, or the entry is not written as a \`[mcp_servers.${sighting.serverName}]\` table. Move the credential manually to an environment-aware field, then run \`aura check\` again.` : `This adapter cannot safely rewrite ${sighting.field}. Replace it manually with ${sighting.suggestedEnvName}, then run \`aura check\` again.`;
5383
- }
5384
4864
  //#endregion
5385
4865
  //#region ../../plugins/checks-core/src/mcp-005-placement.ts
5386
4866
  /** Identifier every MCP-005 module stamps and matches on. */
@@ -5901,13 +5381,11 @@ const checksCorePlugin = definePlugin({
5901
5381
  instructionLinkIntegrityCheck,
5902
5382
  instructionContextBudgetCheck,
5903
5383
  instructionPrecedenceCheck,
5904
- managedBlockHashCheck,
5905
5384
  managedContentUpdateCheck,
5906
5385
  unmanagedDetectedAppCheck,
5907
5386
  mcp001,
5908
5387
  mcp002,
5909
5388
  mcp003,
5910
- mcp004,
5911
5389
  mcp005,
5912
5390
  skl001,
5913
5391
  skl002,