@tryaura/aura-cli 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/aura.js +3 -3
- package/dist/index.js +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/{plugins-BEuoEUo_.js → plugins-C-b2qvpr.js} +94 -258
- package/dist/{run.boundary-NVWIwGgg.js → run.boundary-fZhCH0bP.js} +145 -240
- package/dist/{shared-link-plan-AcSkw9bO.js → shared-link-plan-DMijyUdG.js} +25 -16
- package/package.json +5 -5
package/dist/bin/aura.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as OFFICIAL_REGISTRY_OPTIONS, t as OFFICIAL_PLUGINS } from "../plugins-
|
|
3
|
-
import { t as runCli } from "../run.boundary-
|
|
2
|
+
import { n as OFFICIAL_REGISTRY_OPTIONS, t as OFFICIAL_PLUGINS } from "../plugins-C-b2qvpr.js";
|
|
3
|
+
import { t as runCli } from "../run.boundary-fZhCH0bP.js";
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region src/bin.ts
|
|
6
6
|
await runCli({
|
|
@@ -9,7 +9,7 @@ await runCli({
|
|
|
9
9
|
description: "Agent Unification & Repair Assistant",
|
|
10
10
|
displayName: "Aura",
|
|
11
11
|
docsUrl: "https://tryaura.sh/docs/introduction",
|
|
12
|
-
version: "0.
|
|
12
|
+
version: "0.4.0"
|
|
13
13
|
},
|
|
14
14
|
plugins: OFFICIAL_PLUGINS,
|
|
15
15
|
registry: OFFICIAL_REGISTRY_OPTIONS
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as vetHttpUrl, n as clampHttpTimeout, r as failureReason, t as runCli } from "./run.boundary-
|
|
1
|
+
import { i as vetHttpUrl, n as clampHttpTimeout, r as failureReason, t as runCli } from "./run.boundary-fZhCH0bP.js";
|
|
2
2
|
//#region ../core/src/http-post.boundary.ts
|
|
3
3
|
/**
|
|
4
4
|
* Creates a bounded, TLS-only JSON POST that never rejects.
|
package/dist/plugins/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as codexPlugin, i as claudeCodePlugin, n as OFFICIAL_REGISTRY_OPTIONS, o as cursorPlugin, r as checksCorePlugin, s as officialContentPlugin, t as OFFICIAL_PLUGINS } from "../plugins-
|
|
1
|
+
import { a as codexPlugin, i as claudeCodePlugin, n as OFFICIAL_REGISTRY_OPTIONS, o as cursorPlugin, r as checksCorePlugin, s as officialContentPlugin, t as OFFICIAL_PLUGINS } from "../plugins-C-b2qvpr.js";
|
|
2
2
|
export { OFFICIAL_PLUGINS, OFFICIAL_REGISTRY_OPTIONS, checksCorePlugin, claudeCodePlugin, codexPlugin, cursorPlugin, officialContentPlugin };
|
|
@@ -1,5 +1,5 @@
|
|
|
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-
|
|
2
|
-
import { EMPTY_COLLECTION, McpWriteError,
|
|
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-DMijyUdG.js";
|
|
2
|
+
import { EMPTY_COLLECTION, McpWriteError, TIMEOUT_EXIT_CODE, collectJsonMcpServers, collectMcpServers, configStringArray, configStringRecord, defineAdapter, defineCheck, definePlugin, detectExecutable, 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";
|
|
5
5
|
import { Buffer } from "node:buffer";
|
|
@@ -27,8 +27,7 @@ const CLAUDE_CODE_SOURCE_IDS = Object.freeze({
|
|
|
27
27
|
settingsGlobal: "claude-code.settings.global",
|
|
28
28
|
settingsLocal: "claude-code.settings.local",
|
|
29
29
|
settingsProject: "claude-code.settings.project",
|
|
30
|
-
skillsGlobal: "claude-code.skills.global"
|
|
31
|
-
skillsProject: "claude-code.skills.project"
|
|
30
|
+
skillsGlobal: "claude-code.skills.global"
|
|
32
31
|
});
|
|
33
32
|
const CLAUDE_CODE_ADAPTER_ID = "claude-code";
|
|
34
33
|
/** Metadata key holding {@link ClaudePermissions}. */
|
|
@@ -185,9 +184,6 @@ const GLOBAL_INSTRUCTIONS_SEGMENTS = Object.freeze([".claude", "CLAUDE.md"]);
|
|
|
185
184
|
const SKILL_DIRECTORIES$1 = Object.freeze([{
|
|
186
185
|
entryPath: "~/.claude/skills",
|
|
187
186
|
id: CLAUDE_CODE_SOURCE_IDS.skillsGlobal
|
|
188
|
-
}, {
|
|
189
|
-
entryPath: "./.claude/skills",
|
|
190
|
-
id: CLAUDE_CODE_SOURCE_IDS.skillsProject
|
|
191
187
|
}]);
|
|
192
188
|
const claudeCodeAdapter = defineAdapter({
|
|
193
189
|
capabilities: {
|
|
@@ -236,11 +232,6 @@ const claudeCodeAdapter = defineAdapter({
|
|
|
236
232
|
skills: parseInstalledSkills(CLAUDE_CODE_ADAPTER_ID, input, SKILL_DIRECTORIES$1)
|
|
237
233
|
};
|
|
238
234
|
},
|
|
239
|
-
projectSharedLink: {
|
|
240
|
-
entryPath: "./CLAUDE.md",
|
|
241
|
-
kind: "import-line",
|
|
242
|
-
lineTemplate: `@${SHARED_INSTRUCTIONS_TEMPLATE_TOKEN}`
|
|
243
|
-
},
|
|
244
235
|
sharedLink: {
|
|
245
236
|
entryPath: "~/.claude/CLAUDE.md",
|
|
246
237
|
kind: "symlink"
|
|
@@ -357,7 +348,7 @@ function claudeFiles(input) {
|
|
|
357
348
|
//#region ../../plugins/adapter-claude-code/src/index.ts
|
|
358
349
|
var src_default$3 = definePlugin({
|
|
359
350
|
adapters: [claudeCodeAdapter],
|
|
360
|
-
apiVersion:
|
|
351
|
+
apiVersion: 2,
|
|
361
352
|
id: "adapter-claude-code",
|
|
362
353
|
name: "Claude Code Adapter",
|
|
363
354
|
version: "0.0.0"
|
|
@@ -1506,7 +1497,7 @@ function instructionLevel(directory, id, scope) {
|
|
|
1506
1497
|
//#region ../../plugins/adapter-codex/src/index.ts
|
|
1507
1498
|
var src_default$2 = definePlugin({
|
|
1508
1499
|
adapters: [codexAdapter],
|
|
1509
|
-
apiVersion:
|
|
1500
|
+
apiVersion: 2,
|
|
1510
1501
|
id: "adapter-codex",
|
|
1511
1502
|
name: "Codex Adapter",
|
|
1512
1503
|
version: "0.0.0"
|
|
@@ -1934,7 +1925,7 @@ function isRuleSourceId(id) {
|
|
|
1934
1925
|
//#region ../../plugins/adapter-cursor/src/index.ts
|
|
1935
1926
|
var src_default$1 = definePlugin({
|
|
1936
1927
|
adapters: [cursorAdapter],
|
|
1937
|
-
apiVersion:
|
|
1928
|
+
apiVersion: 2,
|
|
1938
1929
|
id: "adapter-cursor",
|
|
1939
1930
|
name: "Cursor Adapter",
|
|
1940
1931
|
version: "0.0.0"
|
|
@@ -1990,40 +1981,28 @@ function authenticationHint(appId, displayName) {
|
|
|
1990
1981
|
}
|
|
1991
1982
|
}
|
|
1992
1983
|
//#endregion
|
|
1993
|
-
//#region ../../plugins/checks-core/src/
|
|
1984
|
+
//#region ../../plugins/checks-core/src/env-003-legacy.ts
|
|
1994
1985
|
const BEGIN = "# aura:begin ENV-003";
|
|
1995
1986
|
const END = "# aura:end ENV-003";
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
function
|
|
2008
|
-
const
|
|
2009
|
-
if (
|
|
2010
|
-
|
|
2011
|
-
const ends = lines.filter((line) => line.text === END);
|
|
2012
|
-
if (begins.length > 1 || ends.length > 1 || begins.length !== ends.length) return;
|
|
2013
|
-
let unmanaged = source;
|
|
2014
|
-
if (begins.length === 1 && ends.length === 1) {
|
|
2015
|
-
const begin = begins[0];
|
|
2016
|
-
const end = ends[0];
|
|
2017
|
-
if (begin === void 0 || end === void 0 || end.start < begin.start) return;
|
|
2018
|
-
unmanaged = source.slice(0, begin.start) + source.slice(end.end);
|
|
2019
|
-
}
|
|
2020
|
-
const lineEnding = detectLineEnding(unmanaged);
|
|
2021
|
-
const separator = unmanaged.length === 0 || unmanaged.endsWith("\n") ? "" : lineEnding;
|
|
2022
|
-
return `${unmanaged}${separator}${MANAGED_LINES.join(lineEnding)}${lineEnding}`;
|
|
1987
|
+
/**
|
|
1988
|
+
* Finds the managed block earlier Aura releases wrote into a repository's `.gitignore`.
|
|
1989
|
+
*
|
|
1990
|
+
* Aura used to maintain this block itself and no longer writes to repository files at all. Left
|
|
1991
|
+
* unmentioned, the markers are the worst of both: they carry Aura's name, so a reader assumes Aura
|
|
1992
|
+
* still keeps them current, and nothing ever will. Naming them once is what lets someone decide to
|
|
1993
|
+
* keep the rules as their own or delete them.
|
|
1994
|
+
*
|
|
1995
|
+
* A lone marker counts. A half-removed block is still a block somebody has to finish removing, and
|
|
1996
|
+
* the reconciler that used to repair one is gone.
|
|
1997
|
+
*/
|
|
1998
|
+
function abandonedBlock(gitignore) {
|
|
1999
|
+
const markers = new Set([...splitSourceLines(gitignore.content ?? "")].map((line) => line.text.trim()).filter((text) => text === BEGIN || text === END));
|
|
2000
|
+
if (markers.size === 0) return;
|
|
2001
|
+
return markers.has(BEGIN) && markers.has(END) ? "complete" : "partial";
|
|
2023
2002
|
}
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
return
|
|
2003
|
+
/** How the block is described to the person who now owns it. */
|
|
2004
|
+
function abandonedBlockDetail(gitignorePath, block) {
|
|
2005
|
+
return `${block === "complete" ? `The block runs from \`${BEGIN}\` to \`${END}\`.` : `One of the \`${BEGIN}\` / \`${END}\` markers is present without the other, so the block was already partly removed.`} Aura wrote it in an earlier release and no longer maintains it. Its rules are still valid Git rules, so keeping them is fine — delete the two marker comments so nothing implies Aura owns them, or delete the whole block. One of its rules, \`/.claude/skills/\`, ignored the per-project skill links Aura also no longer creates; if ${join(dirname(gitignorePath), ".claude", "skills")} still holds symlinks into your home directory, they are leftovers you can remove.`;
|
|
2027
2006
|
}
|
|
2028
2007
|
//#endregion
|
|
2029
2008
|
//#region ../../plugins/checks-core/src/env-003.ts
|
|
@@ -2040,9 +2019,8 @@ const env003 = defineCheck({
|
|
|
2040
2019
|
detect: (model) => detectGitignoreFindings(model),
|
|
2041
2020
|
explain: `Personal agent overrides should stay out of version control, while shared instruction and MCP configuration should remain commit-ready. Aura evaluates the repository's root .gitignore together with .git/info/exclude, using Git-style pattern semantics including negations and rule order. Nested .gitignore files and a global core.excludesFile are not read, so \`git check-ignore -v <path>\` is the authority whenever the two disagree.
|
|
2042
2021
|
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
fixability: "auto",
|
|
2022
|
+
Aura never edits repository files. Update the root .gitignore manually if this policy does not match, and use \`git check-ignore -v <path>\` to verify the result. If a personal file was already tracked, ignore rules cannot remove it from the index; review it, then run \`git rm --cached -- <path>\` manually. Earlier releases maintained an \`# aura:begin ENV-003\` block in this file; Aura no longer writes or repairs it, and reports it once so the rules become yours rather than something carrying Aura's name that nothing updates.`,
|
|
2023
|
+
fixability: "manual",
|
|
2046
2024
|
id: "ENV-003",
|
|
2047
2025
|
scope: "project",
|
|
2048
2026
|
title: "Repository ignore rules separate personal and shared agent state"
|
|
@@ -2059,10 +2037,21 @@ function detectGitignoreFindings(model) {
|
|
|
2059
2037
|
}];
|
|
2060
2038
|
return [
|
|
2061
2039
|
...policyFindings(repository),
|
|
2062
|
-
...
|
|
2040
|
+
...abandonedBlockFindings(repository.gitignore),
|
|
2063
2041
|
...trackedPersonalFindings(repository.trackedAgentPaths ?? [])
|
|
2064
2042
|
];
|
|
2065
2043
|
}
|
|
2044
|
+
function abandonedBlockFindings(gitignore) {
|
|
2045
|
+
const block = abandonedBlock(gitignore);
|
|
2046
|
+
return block === void 0 ? [] : [{
|
|
2047
|
+
details: abandonedBlockDetail(gitignore.path, block),
|
|
2048
|
+
fixability: "manual",
|
|
2049
|
+
id: "managed-block-abandoned",
|
|
2050
|
+
locations: [{ path: gitignore.path }],
|
|
2051
|
+
message: "An earlier Aura release left a managed block in this repository's .gitignore.",
|
|
2052
|
+
severity: "info"
|
|
2053
|
+
}];
|
|
2054
|
+
}
|
|
2066
2055
|
function policyFindings(repository) {
|
|
2067
2056
|
const infoExclude = repository.infoExclude;
|
|
2068
2057
|
if (infoExclude?.problem !== void 0) return [{
|
|
@@ -2086,15 +2075,6 @@ function policyFindings(repository) {
|
|
|
2086
2075
|
}
|
|
2087
2076
|
}];
|
|
2088
2077
|
}
|
|
2089
|
-
function malformedBlockFindings(gitignore) {
|
|
2090
|
-
return reconcileGitignoreBlock(gitignore.content ?? "") === void 0 ? [{
|
|
2091
|
-
details: "Repair the duplicate, incomplete, reversed, suffixed, or indented ENV-003 markers before running the automatic fix again.",
|
|
2092
|
-
fixability: "manual",
|
|
2093
|
-
id: "managed-block-malformed",
|
|
2094
|
-
locations: [{ path: gitignore.path }],
|
|
2095
|
-
message: "Aura's managed ENV-003 block has malformed markers."
|
|
2096
|
-
}] : [];
|
|
2097
|
-
}
|
|
2098
2078
|
function trackedPersonalFindings(paths) {
|
|
2099
2079
|
return paths.flatMap((path) => PERSONAL_PATHS.includes(path) ? [{
|
|
2100
2080
|
details: `Review the file, then run \`git rm --cached -- ${shellArgument(path)}\` if it should remain local.`,
|
|
@@ -2120,21 +2100,6 @@ function shellArgument(value) {
|
|
|
2120
2100
|
function policyDetail(personalNotIgnored, shareableIgnored) {
|
|
2121
2101
|
return [...personalNotIgnored.length === 0 ? [] : [`Personal paths not ignored: ${personalNotIgnored.join(", ")}.`], ...shareableIgnored.length === 0 ? [] : [`Shareable paths currently ignored: ${shareableIgnored.join(", ")}.`]].join(" ");
|
|
2122
2102
|
}
|
|
2123
|
-
function fixGitignore(finding, model) {
|
|
2124
|
-
if (finding.id !== "gitignore-policy") return;
|
|
2125
|
-
const gitignore = model.repository?.gitignore;
|
|
2126
|
-
if (gitignore === void 0 || gitignore.problem !== void 0) return;
|
|
2127
|
-
const content = reconcileGitignoreBlock(gitignore.content ?? "");
|
|
2128
|
-
if (content === void 0) return;
|
|
2129
|
-
return {
|
|
2130
|
-
operations: [{
|
|
2131
|
-
content,
|
|
2132
|
-
path: gitignore.path,
|
|
2133
|
-
type: "write"
|
|
2134
|
-
}],
|
|
2135
|
-
summary: "Update Aura's managed agent-file rules in .gitignore."
|
|
2136
|
-
};
|
|
2137
|
-
}
|
|
2138
2103
|
//#endregion
|
|
2139
2104
|
//#region ../../plugins/checks-core/src/env-004.ts
|
|
2140
2105
|
const RESTRICTIVE_MODES = /* @__PURE__ */ new Set(["plan", "dontAsk"]);
|
|
@@ -2161,7 +2126,7 @@ function detectConflicts(model) {
|
|
|
2161
2126
|
function claudeFindings(app) {
|
|
2162
2127
|
const effective = readClaudePermissionMode(app);
|
|
2163
2128
|
if (effective === void 0 || !RESTRICTIVE_MODES.has(effective.mode)) return [];
|
|
2164
|
-
const path = sourcePath$
|
|
2129
|
+
const path = sourcePath$2(app, effective.sourceId);
|
|
2165
2130
|
return [{
|
|
2166
2131
|
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.",
|
|
2167
2132
|
id: `claude-permission-mode:${effective.mode}`,
|
|
@@ -2177,7 +2142,7 @@ function claudeFindings(app) {
|
|
|
2177
2142
|
function codexFindings(app) {
|
|
2178
2143
|
const trust = readCodexProjectTrust(app) ?? "unknown";
|
|
2179
2144
|
if (trust === "trusted" || trust === "unreadable") return [];
|
|
2180
|
-
const path = sourcePath$
|
|
2145
|
+
const path = sourcePath$2(app, CODEX_SOURCE_IDS.mcp);
|
|
2181
2146
|
return [{
|
|
2182
2147
|
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.",
|
|
2183
2148
|
id: `codex-project-trust:${trust}`,
|
|
@@ -2190,7 +2155,7 @@ function codexFindings(app) {
|
|
|
2190
2155
|
}
|
|
2191
2156
|
}];
|
|
2192
2157
|
}
|
|
2193
|
-
function sourcePath$
|
|
2158
|
+
function sourcePath$2(app, sourceId) {
|
|
2194
2159
|
return app.sourceFiles.find((file) => file.spec.id === sourceId)?.spec.path;
|
|
2195
2160
|
}
|
|
2196
2161
|
function guidedFix$2(finding, model) {
|
|
@@ -4222,10 +4187,10 @@ function sourceId(scope, tool) {
|
|
|
4222
4187
|
/** Reads back {@link sourceId}, ignoring the index {@link legacyFiles} appends per project base. */
|
|
4223
4188
|
function entryForSourceId(id) {
|
|
4224
4189
|
const [prefix, scope, tool] = id.split(".");
|
|
4225
|
-
if (prefix !== "legacy-instructions" || !isScope
|
|
4190
|
+
if (prefix !== "legacy-instructions" || !isScope(scope)) return;
|
|
4226
4191
|
return INVENTORY_FILES.find((file) => file.tool === tool);
|
|
4227
4192
|
}
|
|
4228
|
-
function isScope
|
|
4193
|
+
function isScope(value) {
|
|
4229
4194
|
return SCOPES.some((scope) => scope === value);
|
|
4230
4195
|
}
|
|
4231
4196
|
//#endregion
|
|
@@ -4397,11 +4362,11 @@ function desiredMcpTargets(model) {
|
|
|
4397
4362
|
const result = /* @__PURE__ */ new Map();
|
|
4398
4363
|
for (const server of [...manifest.value.mcpServers, ...model.requiredMcpServers ?? []]) for (const appId of server.apps) {
|
|
4399
4364
|
if (!detected.has(appId) || manifest.value.apps[appId]?.managed !== true) continue;
|
|
4400
|
-
result.set(`${appId}\0${server.
|
|
4365
|
+
result.set(`${appId}\0${server.name}`, {
|
|
4401
4366
|
appId,
|
|
4402
4367
|
name: server.name,
|
|
4403
4368
|
..."requiredBy" in server && typeof server.requiredBy === "string" ? { requiredBy: server.requiredBy } : {},
|
|
4404
|
-
scope:
|
|
4369
|
+
scope: "global",
|
|
4405
4370
|
transport: server.transport
|
|
4406
4371
|
});
|
|
4407
4372
|
}
|
|
@@ -4423,15 +4388,6 @@ function unusableFor(model, target) {
|
|
|
4423
4388
|
function configuredFor(model, target) {
|
|
4424
4389
|
return model.mcpServers.filter((server) => server.appId === target.appId && server.name === target.name && server.scope === target.scope);
|
|
4425
4390
|
}
|
|
4426
|
-
/**
|
|
4427
|
-
* Whether MCP-005, rather than MCP-001, owns this target's absent expected-scope entry.
|
|
4428
|
-
*
|
|
4429
|
-
* Takes the desired list rather than deriving it: the caller evaluates this once per target, and
|
|
4430
|
-
* recomputing the manifest's selection inside that loop rebuilt the whole list every time.
|
|
4431
|
-
*/
|
|
4432
|
-
function hasMisplacedServerFor(model, desired, target) {
|
|
4433
|
-
return model.mcpServers.some((server) => server.appId === target.appId && server.name === target.name && server.scope !== target.scope && !desired.some((candidate) => candidate.appId === server.appId && candidate.name === server.name && candidate.scope === server.scope));
|
|
4434
|
-
}
|
|
4435
4391
|
/** Turns one application's convergence blockers into findings a user can act on. */
|
|
4436
4392
|
function blockerFindings(appId, blockers) {
|
|
4437
4393
|
return blockers.map((blocker, index) => ({
|
|
@@ -4451,7 +4407,9 @@ function blockerFindings(appId, blockers) {
|
|
|
4451
4407
|
* Downgrades findings whose application cannot converge, optionally reporting why.
|
|
4452
4408
|
*
|
|
4453
4409
|
* Only one check reports the blockers themselves. Both would describe the same obstruction in the
|
|
4454
|
-
* same words, and a user reading two copies has to work out that it is one problem.
|
|
4410
|
+
* same words, and a user reading two copies has to work out that it is one problem. Where MCP-001
|
|
4411
|
+
* and MCP-005 do both fire for one server, they describe two different remedies and each names the
|
|
4412
|
+
* other, which is the distinction this rule turns on.
|
|
4455
4413
|
*/
|
|
4456
4414
|
function withBlockers(findings, affected, model, options) {
|
|
4457
4415
|
const blockers = /* @__PURE__ */ new Map();
|
|
@@ -4503,7 +4461,7 @@ function detectParity(model) {
|
|
|
4503
4461
|
const names = desiredByApp.get(target.appId) ?? /* @__PURE__ */ new Set();
|
|
4504
4462
|
names.add(target.name);
|
|
4505
4463
|
desiredByApp.set(target.appId, names);
|
|
4506
|
-
const finding = parityFinding(model,
|
|
4464
|
+
const finding = parityFinding(model, target);
|
|
4507
4465
|
if (finding !== void 0) {
|
|
4508
4466
|
affected.add(target.appId);
|
|
4509
4467
|
findings.push(finding);
|
|
@@ -4524,7 +4482,7 @@ function managedApps(model) {
|
|
|
4524
4482
|
* conflating them produces the contradiction of Aura offering to add a name that is already in the
|
|
4525
4483
|
* file — where the write then refuses because it does not own it.
|
|
4526
4484
|
*/
|
|
4527
|
-
function parityFinding(model,
|
|
4485
|
+
function parityFinding(model, target) {
|
|
4528
4486
|
const unusable = unusableFor(model, target);
|
|
4529
4487
|
if (unusable !== void 0) return {
|
|
4530
4488
|
details: unusable.reason === "disabled" ? "The entry is present but turned off, so the application does not start it. Aura will not enable a server someone switched off." : "The entry is present in a form Aura does not recognize, so it cannot tell whether the application runs it.",
|
|
@@ -4538,8 +4496,10 @@ function parityFinding(model, desired, target) {
|
|
|
4538
4496
|
sourceId: unusable.sourceId
|
|
4539
4497
|
}
|
|
4540
4498
|
};
|
|
4541
|
-
if (
|
|
4542
|
-
|
|
4499
|
+
if (configuredFor(model, target).length > 0) return;
|
|
4500
|
+
const elsewhere = elsewhereDetail(model, target);
|
|
4501
|
+
return {
|
|
4502
|
+
...elsewhere === void 0 ? {} : { details: elsewhere },
|
|
4543
4503
|
id: `missing:${target.appId}:${target.name}`,
|
|
4544
4504
|
message: `MCP server ${target.name} is missing from ${target.appId}.`,
|
|
4545
4505
|
metadata: {
|
|
@@ -4551,6 +4511,21 @@ function parityFinding(model, desired, target) {
|
|
|
4551
4511
|
};
|
|
4552
4512
|
}
|
|
4553
4513
|
/**
|
|
4514
|
+
* Says where else the name already appears, so this finding and MCP-005 read as one problem.
|
|
4515
|
+
*
|
|
4516
|
+
* Aura converges only the global target, so an entry the application keeps somewhere else leaves
|
|
4517
|
+
* both checks with something true to say: this one that the global entry is absent, MCP-005 that
|
|
4518
|
+
* the entry it found is not the one Aura manages. Read side by side without this line, "is missing
|
|
4519
|
+
* from claude-code" and "is configured in /workspace/.mcp.json" look like a contradiction.
|
|
4520
|
+
*/
|
|
4521
|
+
function elsewhereDetail(model, target) {
|
|
4522
|
+
const paths = [...new Set(model.mcpServers.filter((server) => server.appId === target.appId && server.name === target.name).map((server) => sourcePath$1(model, server) ?? `${server.scope} configuration`))];
|
|
4523
|
+
return paths.length === 0 ? void 0 : `The name is already configured in ${paths.join(", ")}, which is not the file Aura converges. Adding the global entry here does not remove that one; MCP-005 reports it separately.`;
|
|
4524
|
+
}
|
|
4525
|
+
function sourcePath$1(model, server) {
|
|
4526
|
+
return model.apps.find((app) => app.adapterId === server.appId)?.sourceFiles.find((file) => file.spec.id === server.sourceId)?.spec.path;
|
|
4527
|
+
}
|
|
4528
|
+
/**
|
|
4554
4529
|
* Names Aura still owns that desired state no longer selects.
|
|
4555
4530
|
*
|
|
4556
4531
|
* Ownership for an application this scan did not detect is skipped: Aura cannot converge what is
|
|
@@ -4767,7 +4742,7 @@ function unsetEnvironmentFindings(model) {
|
|
|
4767
4742
|
metadata: {
|
|
4768
4743
|
kind: "environment",
|
|
4769
4744
|
serverName: server.name,
|
|
4770
|
-
serverScope:
|
|
4745
|
+
serverScope: "global",
|
|
4771
4746
|
variableName
|
|
4772
4747
|
},
|
|
4773
4748
|
severity: "info"
|
|
@@ -4837,7 +4812,7 @@ function removeChoice(server, model) {
|
|
|
4837
4812
|
}
|
|
4838
4813
|
function manualRemoval(server, model, blockers) {
|
|
4839
4814
|
const path = sourcePath(server, model) ?? "the application's MCP configuration file";
|
|
4840
|
-
const selected = model.manifest.status === "ready" && model.manifest.value.mcpServers.some((entry) => entry.name === server.name && entry.
|
|
4815
|
+
const selected = model.manifest.status === "ready" && model.manifest.value.mcpServers.some((entry) => entry.name === server.name && entry.apps.includes(server.appId));
|
|
4841
4816
|
return {
|
|
4842
4817
|
manualSteps: [
|
|
4843
4818
|
...blockers,
|
|
@@ -4908,156 +4883,24 @@ function isPlacedAt(app, server, target) {
|
|
|
4908
4883
|
const canonical = scopeTarget(app, target.scope);
|
|
4909
4884
|
return canonical === void 0 || canonical.spec.id === server.sourceId;
|
|
4910
4885
|
}
|
|
4911
|
-
function isScope(value) {
|
|
4912
|
-
return value === "global" || value === "project";
|
|
4913
|
-
}
|
|
4914
|
-
//#endregion
|
|
4915
|
-
//#region ../../plugins/checks-core/src/mcp-005-fix.ts
|
|
4916
|
-
const RERUN = "Run `aura check --only MCP-005` again.";
|
|
4917
|
-
function placementFix(finding, model) {
|
|
4918
|
-
const resolved = resolveFinding(finding, model);
|
|
4919
|
-
if (resolved === void 0) return;
|
|
4920
|
-
const { app, server, target } = resolved;
|
|
4921
|
-
const blockers = mcpConvergenceBlockers(model, server.appId);
|
|
4922
|
-
const obstruction = moveObstruction(model, app, server, target, blockers.length > 0);
|
|
4923
|
-
return obstruction === void 0 ? movePlan(model, app, server, target) : manualMove(model, resolved, obstruction, blockers.map((blocker) => blocker.message));
|
|
4924
|
-
}
|
|
4925
|
-
/**
|
|
4926
|
-
* The application-wide convergence write, re-summarized as the move this finding asked for.
|
|
4927
|
-
*
|
|
4928
|
-
* Converging writes every managed server for the application, not only this one. The summary is
|
|
4929
|
-
* what the interactive chooser shows, so it names the move the user selected rather than repeating
|
|
4930
|
-
* the planner's own description for every finding on the same application.
|
|
4931
|
-
*/
|
|
4932
|
-
function movePlan(model, app, server, target) {
|
|
4933
|
-
const plan = planManifestMcpConvergence(model, server.appId).plan;
|
|
4934
|
-
return plan === void 0 ? void 0 : {
|
|
4935
|
-
...plan,
|
|
4936
|
-
summary: `Move MCP server ${server.name} into ${app.displayName}'s ${target.scope} MCP configuration.`
|
|
4937
|
-
};
|
|
4938
|
-
}
|
|
4939
|
-
function moveObstruction(model, app, server, target, blocked) {
|
|
4940
|
-
if (scopeTarget(app, target.scope) === void 0) return "no-destination";
|
|
4941
|
-
if (model.manifest.status !== "ready" || model.manifest.value.ownership[server.appId]?.mcpServerNames.includes(server.name) !== true) return "unowned";
|
|
4942
|
-
if (scopeTarget(app, server.scope)?.spec.id !== server.sourceId) return "non-canonical-source";
|
|
4943
|
-
return blocked ? "blocked" : void 0;
|
|
4944
|
-
}
|
|
4945
|
-
function manualMove(model, resolved, obstruction, blockers) {
|
|
4946
|
-
const { app, server, target } = resolved;
|
|
4947
|
-
const source = sourceFile(app, server.sourceId)?.spec.path;
|
|
4948
|
-
if (obstruction === "no-destination" && target.scope === "project") return unsupportedProjectScope(model, resolved, source, blockers);
|
|
4949
|
-
return {
|
|
4950
|
-
manualSteps: [
|
|
4951
|
-
...blockers,
|
|
4952
|
-
obstructionStep(app, server, obstruction),
|
|
4953
|
-
...moveSteps(model, resolved, source, scopeTarget(app, target.scope)?.spec.path),
|
|
4954
|
-
RERUN
|
|
4955
|
-
],
|
|
4956
|
-
operations: [],
|
|
4957
|
-
summary: `Move MCP server ${server.name} into ${app.displayName}'s ${target.scope} MCP configuration manually.`
|
|
4958
|
-
};
|
|
4959
|
-
}
|
|
4960
|
-
function obstructionStep(app, server, obstruction) {
|
|
4961
|
-
switch (obstruction) {
|
|
4962
|
-
case "blocked": return `Aura did not rewrite ${app.displayName}'s MCP configuration, because it cannot converge that configuration safely.`;
|
|
4963
|
-
case "no-destination": return `Aura cannot tell which file holds ${app.displayName}'s global MCP configuration, so it did not move server ${server.name} itself.`;
|
|
4964
|
-
case "non-canonical-source": {
|
|
4965
|
-
const canonical = scopeTarget(app, server.scope)?.spec.path;
|
|
4966
|
-
return `Aura rewrites only ${canonical === void 0 ? `the ${server.scope}-scope entries it manages for ${app.displayName}` : `the ${server.scope}-scope entries it manages for ${app.displayName} in ${canonical}`}, and server ${server.name} is not one of them.`;
|
|
4967
|
-
}
|
|
4968
|
-
case "unowned": return `MCP server ${server.name} is outside Aura's ownership ledger, so Aura did not change any configuration file.`;
|
|
4969
|
-
}
|
|
4970
|
-
}
|
|
4971
|
-
/**
|
|
4972
|
-
* How to complete the move by hand.
|
|
4973
|
-
*
|
|
4974
|
-
* One step when both ends are the same file: an application can separate scopes inside a single
|
|
4975
|
-
* document — `~/.claude.json` holds Claude Code's global servers and its per-directory ones — and
|
|
4976
|
-
* "remove it from this file, then add it to this file" is not an instruction anyone can follow.
|
|
4977
|
-
*/
|
|
4978
|
-
function moveSteps(model, resolved, source, destination) {
|
|
4979
|
-
const { app, server, target } = resolved;
|
|
4980
|
-
if (source !== void 0 && source === destination) return [`Move MCP server ${server.name} within ${source} into the entries ${app.displayName} applies at ${target.scope} scope, using its transport definition in ${model.manifest.path}.`];
|
|
4981
|
-
return [`Remove MCP server ${server.name} from ${source ?? scopeLabel(app, server.scope)}.`, `Add MCP server ${server.name} to ${destination ?? scopeLabel(app, target.scope)} using its transport definition in ${model.manifest.path}.`];
|
|
4982
|
-
}
|
|
4983
|
-
function unsupportedProjectScope(model, resolved, source, blockers) {
|
|
4984
|
-
const { app, server } = resolved;
|
|
4985
|
-
return {
|
|
4986
|
-
manualSteps: [
|
|
4987
|
-
...blockers,
|
|
4988
|
-
`${app.displayName} has no project-scope MCP configuration target, so Aura cannot move server ${server.name} out of ${source ?? scopeLabel(app, server.scope)}.`,
|
|
4989
|
-
`Either change server ${server.name} to global scope in ${model.manifest.path}, or remove ${server.appId} from that server's apps and configure a supported team distribution manually.`,
|
|
4990
|
-
RERUN
|
|
4991
|
-
],
|
|
4992
|
-
operations: [],
|
|
4993
|
-
summary: `Resolve MCP server ${server.name}'s unsupported project scope manually.`
|
|
4994
|
-
};
|
|
4995
|
-
}
|
|
4996
|
-
function scopeLabel(app, scope) {
|
|
4997
|
-
return `${app.displayName}'s ${scope} MCP configuration`;
|
|
4998
|
-
}
|
|
4999
|
-
function resolveFinding(finding, model) {
|
|
5000
|
-
if (finding.checkId !== "MCP-005") return;
|
|
5001
|
-
const identity = placementIdentity(finding);
|
|
5002
|
-
if (identity === void 0) return;
|
|
5003
|
-
const app = appFor(model, identity.appId);
|
|
5004
|
-
const server = model.mcpServers.find((candidate) => matchesServer(candidate, identity));
|
|
5005
|
-
const target = desiredMcpTargets(model).find((candidate) => matchesTarget(candidate, identity));
|
|
5006
|
-
return app === void 0 || server === void 0 || target === void 0 ? void 0 : {
|
|
5007
|
-
app,
|
|
5008
|
-
server,
|
|
5009
|
-
target
|
|
5010
|
-
};
|
|
5011
|
-
}
|
|
5012
|
-
function placementIdentity(finding) {
|
|
5013
|
-
const appId = stringMetadata(finding, "appId");
|
|
5014
|
-
const name = stringMetadata(finding, "serverName");
|
|
5015
|
-
const sourceId = stringMetadata(finding, "sourceId");
|
|
5016
|
-
const actualScope = scopeMetadata(finding, "actualScope");
|
|
5017
|
-
const expectedScope = scopeMetadata(finding, "expectedScope");
|
|
5018
|
-
if (appId === void 0 || name === void 0 || sourceId === void 0 || actualScope === void 0 || expectedScope === void 0) return;
|
|
5019
|
-
return {
|
|
5020
|
-
actualScope,
|
|
5021
|
-
appId,
|
|
5022
|
-
expectedScope,
|
|
5023
|
-
name,
|
|
5024
|
-
sourceId
|
|
5025
|
-
};
|
|
5026
|
-
}
|
|
5027
|
-
function matchesServer(server, identity) {
|
|
5028
|
-
return server.appId === identity.appId && server.name === identity.name && server.sourceId === identity.sourceId && server.scope === identity.actualScope;
|
|
5029
|
-
}
|
|
5030
|
-
function matchesTarget(target, identity) {
|
|
5031
|
-
return target.appId === identity.appId && target.name === identity.name && target.scope === identity.expectedScope;
|
|
5032
|
-
}
|
|
5033
|
-
function stringMetadata(finding, key) {
|
|
5034
|
-
const value = finding.metadata?.[key];
|
|
5035
|
-
return typeof value === "string" ? value : void 0;
|
|
5036
|
-
}
|
|
5037
|
-
function scopeMetadata(finding, key) {
|
|
5038
|
-
const value = finding.metadata?.[key];
|
|
5039
|
-
return isScope(value) ? value : void 0;
|
|
5040
|
-
}
|
|
5041
4886
|
const mcp005 = defineCheck({
|
|
5042
4887
|
defaultSeverity: "warn",
|
|
5043
4888
|
detect: detectScopePlacement,
|
|
5044
|
-
explain: `
|
|
4889
|
+
explain: `Aura-managed MCP servers are personal and belong in global application configuration. Repository MCP files remain visible to diagnostics, but Aura never edits or removes their entries.
|
|
5045
4890
|
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
fixability: "guided",
|
|
4891
|
+
Every finding here is yours to resolve, because the only remaining move is in a file Aura will not write. MCP-001 reports the global entry that is missing and can add it for you; this check reports the entry left behind, which you remove by hand once you no longer want it there.`,
|
|
4892
|
+
fixability: "manual",
|
|
5049
4893
|
id: MCP_005_ID,
|
|
5050
4894
|
scope: "global",
|
|
5051
|
-
title: "Managed MCP servers use
|
|
4895
|
+
title: "Managed MCP servers use global configuration"
|
|
5052
4896
|
});
|
|
5053
4897
|
function detectScopePlacement(model) {
|
|
5054
4898
|
const desired = desiredMcpTargets(model);
|
|
5055
4899
|
return model.mcpServers.flatMap((server) => {
|
|
5056
4900
|
const app = appFor(model, server.appId);
|
|
5057
|
-
const
|
|
5058
|
-
if (app === void 0 ||
|
|
5059
|
-
|
|
5060
|
-
return target === void 0 ? [] : [placementFinding(app, server, target)];
|
|
4901
|
+
const target = desired.find((candidate) => candidate.appId === server.appId && candidate.name === server.name);
|
|
4902
|
+
if (app === void 0 || target === void 0 || isPlacedAt(app, server, target)) return [];
|
|
4903
|
+
return [placementFinding(app, server, target)];
|
|
5061
4904
|
});
|
|
5062
4905
|
}
|
|
5063
4906
|
function placementFinding(app, server, target) {
|
|
@@ -5065,14 +4908,14 @@ function placementFinding(app, server, target) {
|
|
|
5065
4908
|
const destination = scopeTarget(app, target.scope)?.spec.path;
|
|
5066
4909
|
const locations = [...new Set([source?.path, destination].filter((path) => path !== void 0))];
|
|
5067
4910
|
return {
|
|
5068
|
-
details: placementDetails(app, source?.scope ?? server.scope,
|
|
5069
|
-
|
|
4911
|
+
details: placementDetails(app, source?.scope ?? server.scope, destination),
|
|
4912
|
+
fixability: "manual",
|
|
4913
|
+
id: `${server.appId}:${server.sourceId}:${server.name}:${server.scope}-to-global`,
|
|
5070
4914
|
...locations.length === 0 ? {} : { locations: locations.map((path) => ({ path })) },
|
|
5071
|
-
message:
|
|
4915
|
+
message: `Personal MCP server ${server.name} for ${app.displayName} is configured in ${source?.path ?? `${app.displayName}'s ${server.scope} MCP configuration`}.`,
|
|
5072
4916
|
metadata: {
|
|
5073
4917
|
actualScope: server.scope,
|
|
5074
4918
|
appId: server.appId,
|
|
5075
|
-
expectedScope: target.scope,
|
|
5076
4919
|
serverName: server.name,
|
|
5077
4920
|
sourceId: server.sourceId
|
|
5078
4921
|
}
|
|
@@ -5086,10 +4929,9 @@ function placementFinding(app, server, target) {
|
|
|
5086
4929
|
* and saying it could is the one thing that would send someone looking through their repository
|
|
5087
4930
|
* for a leak that is not there.
|
|
5088
4931
|
*/
|
|
5089
|
-
function placementDetails(app, sourceScope,
|
|
5090
|
-
const belongs = destination === void 0 ? `It belongs in ${app.displayName}'s
|
|
5091
|
-
|
|
5092
|
-
return sourceScope === "project" ? `The manifest marks this as a team server, but that is not the repository file ${app.displayName} reads for shared servers, so teammates do not receive it. ${belongs}` : `The manifest marks this as a team server, and that file is personal to you, so teammates do not receive it and their setup can drift. ${belongs}`;
|
|
4932
|
+
function placementDetails(app, sourceScope, destination) {
|
|
4933
|
+
const belongs = destination === void 0 ? `It belongs in ${app.displayName}'s global MCP configuration.` : `It belongs in ${destination}.`;
|
|
4934
|
+
return sourceScope === "project" ? `This server is declared in repository configuration. Aura will not edit that file. ${belongs} MCP-001 reports the missing global entry and can add it with \`aura check --fix\`; remove the repository entry by hand once you no longer want it there.` : `This personal server is not in the global configuration target Aura manages. ${belongs} MCP-001 reports the missing global entry and can add it with \`aura check --fix\`; remove this entry by hand once you no longer want it there.`;
|
|
5093
4935
|
}
|
|
5094
4936
|
//#endregion
|
|
5095
4937
|
//#region ../../plugins/checks-core/src/skl-001.ts
|
|
@@ -5311,25 +5153,20 @@ function brokenLinkFix(finding, model) {
|
|
|
5311
5153
|
const skl004 = defineCheck({
|
|
5312
5154
|
defaultSeverity: "error",
|
|
5313
5155
|
detect: detectInvocationCollisions,
|
|
5314
|
-
explain: "Two skill entries in one
|
|
5156
|
+
explain: "Two skill entries in one application must not expose the same invocation name. Otherwise the application cannot offer a stable, unambiguous invocation target.\n\nRename a skill and its directory together, or remove the entry the application should not load.",
|
|
5315
5157
|
fix: guidedCollisionRepair,
|
|
5316
5158
|
fixability: "guided",
|
|
5317
5159
|
id: "SKL-004",
|
|
5318
5160
|
scope: "global",
|
|
5319
|
-
title: "Skill invocation names are unique per application
|
|
5161
|
+
title: "Skill invocation names are unique per application"
|
|
5320
5162
|
});
|
|
5321
5163
|
/**
|
|
5322
|
-
* Groups by application
|
|
5323
|
-
*
|
|
5324
|
-
* Deploying one shared skill to every directory an application declares is the intended outcome of
|
|
5325
|
-
* SKL-002, and Claude Code declares both a global and a project directory. Grouping across scopes
|
|
5326
|
-
* would make SKL-002's own fix raise an error here and tell the user to delete one of the two links
|
|
5327
|
-
* Aura had just created.
|
|
5164
|
+
* Groups by application because every managed skill directory is global.
|
|
5328
5165
|
*/
|
|
5329
5166
|
function detectInvocationCollisions(model) {
|
|
5330
5167
|
const groups = /* @__PURE__ */ new Map();
|
|
5331
5168
|
for (const skill of model.skills) {
|
|
5332
|
-
const key = `${skill.appId}\0${skill.
|
|
5169
|
+
const key = `${skill.appId}\0${skill.invocationName ?? skill.name}`;
|
|
5333
5170
|
const group = groups.get(key);
|
|
5334
5171
|
if (group === void 0) groups.set(key, [skill]);
|
|
5335
5172
|
else group.push(skill);
|
|
@@ -5342,13 +5179,12 @@ function detectInvocationCollisions(model) {
|
|
|
5342
5179
|
const invocationName = first.invocationName ?? first.name;
|
|
5343
5180
|
findings.push({
|
|
5344
5181
|
details: group.map((skill) => `${skill.path} (${skill.sourceId ?? "unknown skill source"})`).join("\n"),
|
|
5345
|
-
id: `${first.appId}:${
|
|
5182
|
+
id: `${first.appId}:${invocationName}`,
|
|
5346
5183
|
locations: group.map((skill) => ({ path: skill.path })),
|
|
5347
|
-
message: `${first.appId} has ${String(group.length)}
|
|
5184
|
+
message: `${first.appId} has ${String(group.length)} skills named "${invocationName}".`,
|
|
5348
5185
|
metadata: {
|
|
5349
5186
|
appId: first.appId,
|
|
5350
|
-
invocationName
|
|
5351
|
-
scope: first.scope
|
|
5187
|
+
invocationName
|
|
5352
5188
|
}
|
|
5353
5189
|
});
|
|
5354
5190
|
}
|
|
@@ -5367,7 +5203,7 @@ function guidedCollisionRepair(finding) {
|
|
|
5367
5203
|
//#region src/plugins.ts
|
|
5368
5204
|
const checksCorePlugin = definePlugin({
|
|
5369
5205
|
adapters: [legacyInstructionsAdapter],
|
|
5370
|
-
apiVersion:
|
|
5206
|
+
apiVersion: 2,
|
|
5371
5207
|
checks: [
|
|
5372
5208
|
env001,
|
|
5373
5209
|
env002,
|