@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as displayPath, C as isRecord$4, D as SHARED_INSTRUCTIONS_TEMPLATE, E as AuraManifestError, S as errorMessage$1, T as resolveAuraManifestPath, _ as assertAuraManifestWritable, a as planDesiredMcpConvergence, b as parseAuraManifest, c as rememberMcpConvergence, d as sharedSkillsRoot, g as hashContent, h as stripLegacyManagedBlock, k as pluralize, m as planSharedSkillTreeUpdate, n as appendInstructionFragments, p as managedContentRevisionStatus, r as forgetMcpPlans, t as planSharedInstructionLink, u as planSkillDeployment, v as createAuraManifestWriteOperation, w as AURA_MANIFEST_PATH, x as errorCode, y as createEmptyAuraManifest } from "./shared-link-plan-
|
|
1
|
+
import { A as displayPath, C as isRecord$4, D as SHARED_INSTRUCTIONS_TEMPLATE, E as AuraManifestError, S as errorMessage$1, T as resolveAuraManifestPath, _ as assertAuraManifestWritable, a as planDesiredMcpConvergence, b as parseAuraManifest, c as rememberMcpConvergence, d as sharedSkillsRoot, g as hashContent, h as stripLegacyManagedBlock, k as pluralize, m as planSharedSkillTreeUpdate, n as appendInstructionFragments, p as managedContentRevisionStatus, r as forgetMcpPlans, t as planSharedInstructionLink, u as planSkillDeployment, v as createAuraManifestWriteOperation, w as AURA_MANIFEST_PATH, x as errorCode, y as createEmptyAuraManifest } from "./shared-link-plan-DMijyUdG.js";
|
|
2
2
|
import { COMMAND_NOT_FOUND_EXIT_CODE, DEFAULT_EXEC_TIMEOUT_MS, DEFAULT_HTTP_TIMEOUT_MS, MAX_EXEC_OUTPUT_CHARACTERS, MAX_EXEC_TIMEOUT_MS, MAX_HTTP_RESPONSE_BYTES, MAX_HTTP_TIMEOUT_MS, McpWriteError, NOT_EXECUTABLE_EXIT_CODE, OUTPUT_LIMIT_EXIT_CODE, SHARED_INSTRUCTIONS_TEMPLATE_TOKEN, TIMEOUT_EXIT_CODE, defineOwnProperty, detectExecutable, hasMcpRedaction, jsonPropertyPath, mcpEnvironmentVariableNames, mcpServerNameProblem, normalizeMcpServerDefinition, parseMcpServerDefinition, parseMcpServerManifest, parseMcpServerManifestValue, parseSkillFrontmatter, parseSkillReferences, resolveMcpSecretNameCollisions, resolveSkillDirectory, splitSourceLines } from "@tryaura/aura-sdk";
|
|
3
3
|
import { basename, delimiter, dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
|
|
4
4
|
import { Buffer as Buffer$1, isUtf8 } from "node:buffer";
|
|
@@ -931,25 +931,21 @@ function backupRoot(homeDir) {
|
|
|
931
931
|
/**
|
|
932
932
|
* Determines where a plan may write.
|
|
933
933
|
*
|
|
934
|
-
*
|
|
935
|
-
*
|
|
934
|
+
* Roots are derived only from Aura's shared home directory and what detected adapters declared as
|
|
935
|
+
* global-scope configuration, so the writable surface of the home directory is exactly the
|
|
936
936
|
* applications Aura found rather than a hardcoded guess. A declared file contributes its own
|
|
937
937
|
* directory — `~/.claude/CLAUDE.md` yields `~/.claude`, not `~` — which keeps an adapter that reads
|
|
938
938
|
* something under a shared directory such as `~/.config/<app>` from opening `~/.config` wholesale.
|
|
939
939
|
*/
|
|
940
|
-
function resolveAllowedRoots(model
|
|
940
|
+
function resolveAllowedRoots(model) {
|
|
941
941
|
const homeDir = resolve(model.homeDir);
|
|
942
942
|
const roots = /* @__PURE__ */ new Map();
|
|
943
|
-
add(roots, {
|
|
944
|
-
exact: false,
|
|
945
|
-
path: resolve(model.projectRoot ?? model.cwd)
|
|
946
|
-
});
|
|
947
943
|
const sharedDirectory = dirname(resolve(model.sharedInstructions.path));
|
|
948
944
|
if (isStrictDescendant(homeDir, sharedDirectory)) add(roots, {
|
|
949
945
|
exact: false,
|
|
950
946
|
path: sharedDirectory
|
|
951
947
|
});
|
|
952
|
-
for (const root of
|
|
948
|
+
for (const root of deriveManagedHomeRoots(model, homeDir)) add(roots, root);
|
|
953
949
|
return Object.freeze([...roots.values()]);
|
|
954
950
|
}
|
|
955
951
|
/**
|
|
@@ -1002,17 +998,6 @@ function rootsForSpec(spec, homeDir) {
|
|
|
1002
998
|
path
|
|
1003
999
|
}];
|
|
1004
1000
|
}
|
|
1005
|
-
function validateManagedHomeRoots(managedHomeRoots, homeDir) {
|
|
1006
|
-
return managedHomeRoots.map((candidate) => {
|
|
1007
|
-
if (!isAbsolute(candidate)) throw new FixPlanError("invalid-options", `managedHomeRoots must contain absolute paths: ${candidate}`, { path: candidate });
|
|
1008
|
-
const path = resolve(candidate);
|
|
1009
|
-
if (!isStrictDescendant(homeDir, path)) throw new FixPlanError("invalid-options", `managedHomeRoots must sit strictly inside ${homeDir}: ${candidate}`, { path: candidate });
|
|
1010
|
-
return {
|
|
1011
|
-
exact: false,
|
|
1012
|
-
path
|
|
1013
|
-
};
|
|
1014
|
-
});
|
|
1015
|
-
}
|
|
1016
1001
|
function add(roots, root) {
|
|
1017
1002
|
const existing = roots.get(root.path);
|
|
1018
1003
|
if (existing === void 0 || existing.exact && !root.exact) roots.set(root.path, root);
|
|
@@ -1109,12 +1094,12 @@ async function mapWithConcurrency(items, limit, run) {
|
|
|
1109
1094
|
//#endregion
|
|
1110
1095
|
//#region ../core/src/fix-plan/path-policy.ts
|
|
1111
1096
|
/** Resolves the roots and filesystem traits a plan is validated against. */
|
|
1112
|
-
async function createPathPolicy(model
|
|
1097
|
+
async function createPathPolicy(model) {
|
|
1113
1098
|
const sensitivity = await detectCaseSensitivity(resolve(model.projectRoot ?? model.cwd));
|
|
1114
1099
|
return {
|
|
1115
1100
|
caseInsensitive: sensitivity !== "sensitive",
|
|
1116
1101
|
reservedRoots: Object.freeze([backupRoot(model.homeDir)]),
|
|
1117
|
-
roots: resolveAllowedRoots(model
|
|
1102
|
+
roots: resolveAllowedRoots(model),
|
|
1118
1103
|
rootsCaseInsensitive: sensitivity === "insensitive"
|
|
1119
1104
|
};
|
|
1120
1105
|
}
|
|
@@ -2878,7 +2863,7 @@ function decodeUtf8(content) {
|
|
|
2878
2863
|
//#endregion
|
|
2879
2864
|
//#region ../core/src/fix-plan/prepare.ts
|
|
2880
2865
|
async function prepareOperations(options) {
|
|
2881
|
-
const policy = await createPathPolicy(options.model
|
|
2866
|
+
const policy = await createPathPolicy(options.model);
|
|
2882
2867
|
const validated = await validatePlanPaths(options.plan, policy);
|
|
2883
2868
|
const budget = { remaining: MAX_RETAINED_PLAN_BYTES };
|
|
2884
2869
|
const operations = [];
|
|
@@ -3397,7 +3382,7 @@ async function undoFixPlan(options) {
|
|
|
3397
3382
|
if (options.backupId !== void 0) throw new FixPlanError("backup-error", `no undo journal entry named ${options.backupId}`);
|
|
3398
3383
|
return Object.freeze({ status: "nothing-to-undo" });
|
|
3399
3384
|
}
|
|
3400
|
-
const policy = await createPathPolicy(options.model
|
|
3385
|
+
const policy = await createPathPolicy(options.model);
|
|
3401
3386
|
return withJournalLock(root, options.now, async () => {
|
|
3402
3387
|
const selection = await select(root, options);
|
|
3403
3388
|
if (selection === void 0) return Object.freeze({ status: "nothing-to-undo" });
|
|
@@ -3748,50 +3733,32 @@ function portableSkillFilePathKey(path) {
|
|
|
3748
3733
|
//#endregion
|
|
3749
3734
|
//#region ../core/src/workspace/shared-links.ts
|
|
3750
3735
|
const GLOBAL_PREFIX = "~/";
|
|
3751
|
-
const PROJECT_PREFIX = "./";
|
|
3752
3736
|
const SHARED_INSTRUCTIONS_SEGMENTS = Object.freeze(["agents", "AGENTS.md"]);
|
|
3753
3737
|
const SHARED_INSTRUCTIONS_HOME_REFERENCE = "~/agents/AGENTS.md";
|
|
3754
|
-
const PROJECT_SHARED_INSTRUCTIONS_FILE = "AGENTS.md";
|
|
3755
3738
|
const SHARED_LINK_KINDS = /* @__PURE__ */ new Set([
|
|
3756
3739
|
"import-line",
|
|
3757
3740
|
"native-copy",
|
|
3758
3741
|
"symlink"
|
|
3759
3742
|
]);
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
scope: "global"
|
|
3763
|
-
});
|
|
3764
|
-
const PROJECT_SLOT = Object.freeze({
|
|
3765
|
-
name: "projectSharedLink",
|
|
3766
|
-
scope: "project"
|
|
3767
|
-
});
|
|
3743
|
+
/** The one adapter field a shared-link declaration can come from. */
|
|
3744
|
+
const SLOT_NAME = "sharedLink";
|
|
3768
3745
|
/** Canonical shared-instruction path for one captured environment. */
|
|
3769
3746
|
function sharedInstructionsPath(environment) {
|
|
3770
3747
|
return join(environment.homeDir, ...SHARED_INSTRUCTIONS_SEGMENTS);
|
|
3771
3748
|
}
|
|
3772
3749
|
/**
|
|
3773
|
-
*
|
|
3750
|
+
* Returns every declarative problem in a shared-link contribution.
|
|
3774
3751
|
*
|
|
3775
|
-
*
|
|
3776
|
-
*
|
|
3752
|
+
* The `~/` prefix is required rather than merely accepted. A declaration naming `./something` is
|
|
3753
|
+
* what let a home-scoped check demand a file inside whichever repository the user was standing in,
|
|
3754
|
+
* written with their home directory spelled out absolutely; Aura links only the home entry now, so
|
|
3755
|
+
* the prefix is the whole statement of where the file lives.
|
|
3777
3756
|
*/
|
|
3778
|
-
function
|
|
3779
|
-
return join(projectRoot ?? cwd, PROJECT_SHARED_INSTRUCTIONS_FILE);
|
|
3780
|
-
}
|
|
3781
|
-
/**
|
|
3782
|
-
* Returns every declarative problem in a shared-link contribution to the given scope.
|
|
3783
|
-
*
|
|
3784
|
-
* The prefix is checked against the slot rather than merely being one of the two: a global
|
|
3785
|
-
* declaration naming `./something` is what let a home-scoped check demand a file inside whichever
|
|
3786
|
-
* repository the user was standing in, written with their home directory spelled out absolutely.
|
|
3787
|
-
* Which slot an adapter fills is the whole statement of where the file lives.
|
|
3788
|
-
*/
|
|
3789
|
-
function sharedLinkViolations(link, scope) {
|
|
3757
|
+
function sharedLinkViolations(link) {
|
|
3790
3758
|
const violations = [];
|
|
3791
3759
|
if (!SHARED_LINK_KINDS.has(link.kind)) violations.push(`kind "${String(link.kind)}" is not supported`);
|
|
3792
|
-
const
|
|
3793
|
-
|
|
3794
|
-
if (relative === void 0) violations.push(`entryPath must begin with "${prefix}" at ${scope} scope`);
|
|
3760
|
+
const relative = typeof link.entryPath === "string" && link.entryPath.startsWith(GLOBAL_PREFIX) ? link.entryPath.slice(2) : void 0;
|
|
3761
|
+
if (relative === void 0) violations.push(`entryPath must begin with "${GLOBAL_PREFIX}"`);
|
|
3795
3762
|
else if (relative.length === 0 || relative.includes("\\") || relative.includes("\0") || relative.split("/").some((segment) => segment.length === 0 || segment === "." || segment === "..")) violations.push("entryPath must be a normalized portable path without traversal");
|
|
3796
3763
|
if (link.kind === "symlink") {
|
|
3797
3764
|
if (link.lineTemplate !== void 0) violations.push("symlink declarations must not provide lineTemplate");
|
|
@@ -3802,51 +3769,36 @@ function sharedLinkViolations(link, scope) {
|
|
|
3802
3769
|
}
|
|
3803
3770
|
/** Resolves and verifies one adapter's shared-link declaration against its read-side files. */
|
|
3804
3771
|
function resolveAdapterSharedLink(adapter, environment, files) {
|
|
3805
|
-
return resolveSharedLink(adapter.sharedLink,
|
|
3806
|
-
}
|
|
3807
|
-
function resolveAdapterProjectSharedLink(adapter, environment, files, projectRoot) {
|
|
3808
|
-
const target = projectSharedInstructionsPath(projectRoot, environment.cwd);
|
|
3809
|
-
return resolveSharedLink(adapter.projectSharedLink, PROJECT_SLOT, environment, files, (entryPath) => portableRelativePath(entryPath, target));
|
|
3772
|
+
return resolveSharedLink(adapter.sharedLink, environment, files);
|
|
3810
3773
|
}
|
|
3811
3774
|
/**
|
|
3812
|
-
* Resolves one declaration, naming the shared source the way
|
|
3775
|
+
* Resolves one declaration, naming the shared source the way a home entry can survive.
|
|
3813
3776
|
*
|
|
3814
|
-
*
|
|
3815
|
-
* owns. A project entry names it relative to itself, so the file it produces is one the whole team
|
|
3816
|
-
* can commit. Nothing here can write a machine-specific absolute path into a repository, because
|
|
3817
|
-
* the slot decides both halves at once.
|
|
3777
|
+
* The entry names the source through `~`, which is the same string on every machine the user owns.
|
|
3818
3778
|
*/
|
|
3819
|
-
function resolveSharedLink(declaration,
|
|
3779
|
+
function resolveSharedLink(declaration, environment, files) {
|
|
3820
3780
|
if (declaration === void 0) return;
|
|
3821
|
-
const entryPath = resolveSharedEntry(declaration,
|
|
3781
|
+
const entryPath = resolveSharedEntry(declaration, environment, files);
|
|
3822
3782
|
if (declaration.kind === "symlink") return {
|
|
3823
3783
|
entryPath,
|
|
3824
3784
|
kind: declaration.kind,
|
|
3825
|
-
scope:
|
|
3785
|
+
scope: "global"
|
|
3826
3786
|
};
|
|
3827
3787
|
return {
|
|
3828
|
-
content: declaration.lineTemplate?.replace(SHARED_INSTRUCTIONS_TEMPLATE_TOKEN,
|
|
3788
|
+
content: declaration.lineTemplate?.replace(SHARED_INSTRUCTIONS_TEMPLATE_TOKEN, SHARED_INSTRUCTIONS_HOME_REFERENCE),
|
|
3829
3789
|
entryPath,
|
|
3830
3790
|
kind: declaration.kind,
|
|
3831
|
-
scope:
|
|
3791
|
+
scope: "global"
|
|
3832
3792
|
};
|
|
3833
3793
|
}
|
|
3834
|
-
function resolveSharedEntry(declaration,
|
|
3835
|
-
const violations = sharedLinkViolations(declaration
|
|
3836
|
-
if (violations.length > 0) throw new Error(`declares an invalid ${
|
|
3837
|
-
const
|
|
3838
|
-
const
|
|
3839
|
-
|
|
3840
|
-
if (![...files.values()].some((file) => resolve(file.spec.path) === resolve(entryPath) && file.spec.scope === slot.scope)) throw new Error(`declares ${slot.name} entry ${entryPath}, but its files() result did not declare that path at ${slot.scope} scope`);
|
|
3794
|
+
function resolveSharedEntry(declaration, environment, files) {
|
|
3795
|
+
const violations = sharedLinkViolations(declaration);
|
|
3796
|
+
if (violations.length > 0) throw new Error(`declares an invalid ${SLOT_NAME}: ${violations.join("; ")}`);
|
|
3797
|
+
const relative = declaration.entryPath.slice(2);
|
|
3798
|
+
const entryPath = join(environment.homeDir, ...relative.split("/"));
|
|
3799
|
+
if (![...files.values()].some((file) => resolve(file.spec.path) === resolve(entryPath) && file.spec.scope === "global")) throw new Error(`declares ${SLOT_NAME} entry ${entryPath}, but its files() result did not declare that path at global scope`);
|
|
3841
3800
|
return entryPath;
|
|
3842
3801
|
}
|
|
3843
|
-
function portableRelativePath(entryPath, targetPath) {
|
|
3844
|
-
const path = relative(dirname(entryPath), targetPath).replaceAll("\\", "/");
|
|
3845
|
-
return path.startsWith(".") ? path : `./${path}`;
|
|
3846
|
-
}
|
|
3847
|
-
function entryPrefix(scope) {
|
|
3848
|
-
return scope === "global" ? GLOBAL_PREFIX : PROJECT_PREFIX;
|
|
3849
|
-
}
|
|
3850
3802
|
function countToken(template) {
|
|
3851
3803
|
return template.split(SHARED_INSTRUCTIONS_TEMPLATE_TOKEN).length - 1;
|
|
3852
3804
|
}
|
|
@@ -3866,6 +3818,41 @@ function toSharedInstructions(path, contents) {
|
|
|
3866
3818
|
problem
|
|
3867
3819
|
};
|
|
3868
3820
|
}
|
|
3821
|
+
//#endregion
|
|
3822
|
+
//#region ../core/src/plugin-validation-links.ts
|
|
3823
|
+
/**
|
|
3824
|
+
* Validates an adapter's shared-instruction link declaration.
|
|
3825
|
+
*
|
|
3826
|
+
* Two rules, because the contract removed one field and constrained the other. A plugin built
|
|
3827
|
+
* against the previous contract still carries `projectSharedLink`; `apiVersion` already refuses
|
|
3828
|
+
* that plugin, and naming the property is what tells its author which one to delete.
|
|
3829
|
+
*/
|
|
3830
|
+
function collectSharedLinkDeclarationViolations(state, adapter, pluginLabel) {
|
|
3831
|
+
if ("projectSharedLink" in adapter) state.violations.push(`${pluginLabel} adapter "${adapter.id}" declares removed projectSharedLink; Aura manages only global shared-instruction links. Remove this property.`);
|
|
3832
|
+
if (adapter.sharedLink === void 0) return;
|
|
3833
|
+
for (const violation of sharedLinkViolations(adapter.sharedLink)) state.violations.push(`${pluginLabel} adapter "${adapter.id}" declares invalid sharedLink: ${violation}.`);
|
|
3834
|
+
}
|
|
3835
|
+
//#endregion
|
|
3836
|
+
//#region ../core/src/plugin-validation-skills.ts
|
|
3837
|
+
/** Rejects skill destinations that are empty, duplicated, or outside the user's home directory. */
|
|
3838
|
+
function collectSkillDirectoryViolations(state, adapter, pluginLabel) {
|
|
3839
|
+
const directories = adapter.capabilities?.skills?.directories;
|
|
3840
|
+
if (directories === void 0) return;
|
|
3841
|
+
if (directories.length === 0) {
|
|
3842
|
+
state.violations.push(`${pluginLabel} adapter "${adapter.id}" declares Agent Skills support without a global skills directory.`);
|
|
3843
|
+
return;
|
|
3844
|
+
}
|
|
3845
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3846
|
+
for (const directory of directories) {
|
|
3847
|
+
if (ids.has(directory.id)) state.violations.push(`${pluginLabel} adapter "${adapter.id}" declares duplicate skills directory ID "${directory.id}".`);
|
|
3848
|
+
ids.add(directory.id);
|
|
3849
|
+
if (!isGlobalSkillPath(directory.entryPath)) state.violations.push(`${pluginLabel} adapter "${adapter.id}" declares invalid global skills directory ${directory.entryPath}.`);
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
function isGlobalSkillPath(entryPath) {
|
|
3853
|
+
const relative = entryPath.startsWith("~/") ? entryPath.slice(2) : void 0;
|
|
3854
|
+
return relative !== void 0 && relative.length > 0 && !relative.includes("\\") && !relative.includes("\0") && relative.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
3855
|
+
}
|
|
3869
3856
|
/** Plugin ids become id namespaces, so `/` and anything shell- or path-surprising is refused. */
|
|
3870
3857
|
const PLUGIN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
3871
3858
|
function createRegistryState() {
|
|
@@ -3875,7 +3862,7 @@ function createRegistryState() {
|
|
|
3875
3862
|
};
|
|
3876
3863
|
}
|
|
3877
3864
|
function isSupportedPlugin(candidate) {
|
|
3878
|
-
return candidate.apiVersion ===
|
|
3865
|
+
return candidate.apiVersion === 2;
|
|
3879
3866
|
}
|
|
3880
3867
|
/** Validates each contribution's namespace and collects the ones that claimed their id. */
|
|
3881
3868
|
function collectNamespaced(state, kind, contributions, plugin, collected) {
|
|
@@ -3913,15 +3900,8 @@ function collectAdapterViolations(state, adapters, plugin) {
|
|
|
3913
3900
|
for (const adapter of adapters ?? []) {
|
|
3914
3901
|
if (!PLUGIN_ID_PATTERN.test(adapter.id)) state.violations.push(`${formatPlugin(plugin)} contributes adapter ID "${adapter.id}", which is not a usable adapter ID; expected lowercase letters, digits, ".", "-", or "_", starting with a letter or digit.`);
|
|
3915
3902
|
collectSharedLinkCapabilityViolations(state, adapter, plugin);
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
"global",
|
|
3919
|
-
adapter.sharedLink
|
|
3920
|
-
], [
|
|
3921
|
-
"projectSharedLink",
|
|
3922
|
-
"project",
|
|
3923
|
-
adapter.projectSharedLink
|
|
3924
|
-
]]) if (link !== void 0) for (const violation of sharedLinkViolations(link, scope)) state.violations.push(`${formatPlugin(plugin)} adapter "${adapter.id}" declares invalid ${name}: ${violation}.`);
|
|
3903
|
+
collectSkillDirectoryViolations(state, adapter, formatPlugin(plugin));
|
|
3904
|
+
collectSharedLinkDeclarationViolations(state, adapter, formatPlugin(plugin));
|
|
3925
3905
|
}
|
|
3926
3906
|
}
|
|
3927
3907
|
function collectSharedLinkCapabilityViolations(state, adapter, plugin) {
|
|
@@ -3954,8 +3934,8 @@ function claimId(state, kind, id, plugin) {
|
|
|
3954
3934
|
return true;
|
|
3955
3935
|
}
|
|
3956
3936
|
function formatApiVersionViolation(candidate) {
|
|
3957
|
-
const remedy = candidate.apiVersion >
|
|
3958
|
-
return `${formatPlugin(candidate)} uses unsupported apiVersion ${candidate.apiVersion}; this Aura build supports apiVersion
|
|
3937
|
+
const remedy = candidate.apiVersion > 2 ? "Upgrade Aura, or install a plugin release built against" : "Upgrade the plugin to a release built against";
|
|
3938
|
+
return `${formatPlugin(candidate)} uses unsupported apiVersion ${candidate.apiVersion}; this Aura build supports apiVersion 2. ${remedy} apiVersion 2.`;
|
|
3959
3939
|
}
|
|
3960
3940
|
function formatViolations(violations) {
|
|
3961
3941
|
return [violations.length === 1 ? "Aura cannot build the plugin registry:" : `Aura cannot build the plugin registry (${violations.length} problems):`, ...violations.map((violation) => ` - ${violation}`)].join("\n");
|
|
@@ -5164,9 +5144,8 @@ function normalizeVersion(version) {
|
|
|
5164
5144
|
/**
|
|
5165
5145
|
* Splits desired servers into the ones Aura may write and the collisions a person has to settle.
|
|
5166
5146
|
*
|
|
5167
|
-
*
|
|
5168
|
-
*
|
|
5169
|
-
* that would not have collided or skips one that never happened.
|
|
5147
|
+
* Desired servers are global-only. A server named `docs` in a project `.mcp.json` remains a
|
|
5148
|
+
* read-only observation and cannot collide with the global entry Aura manages.
|
|
5170
5149
|
*/
|
|
5171
5150
|
function classifyDesired(desired, ledgerNames, state) {
|
|
5172
5151
|
const ledger = new Set(ledgerNames);
|
|
@@ -5185,11 +5164,11 @@ function classifyDesired(desired, ledgerNames, state) {
|
|
|
5185
5164
|
/** `owned` to write it, a blocker to refuse, `undefined` when the config already satisfies it. */
|
|
5186
5165
|
function collisionBlocker(entry, ledger, state) {
|
|
5187
5166
|
if (ledger.has(entry.name)) return "owned";
|
|
5188
|
-
const sameName = (candidate) => candidate.name === entry.name && candidate.scope ===
|
|
5167
|
+
const sameName = (candidate) => candidate.name === entry.name && candidate.scope === "global";
|
|
5189
5168
|
const unusable = state.unusable.find(sameName);
|
|
5190
5169
|
if (unusable !== void 0) return {
|
|
5191
|
-
message: unusable.reason === "disabled" ? `MCP server ${entry.name} is already declared in this application's
|
|
5192
|
-
scope:
|
|
5170
|
+
message: unusable.reason === "disabled" ? `MCP server ${entry.name} is already declared in this application's global configuration but is turned off there. Remove or enable it, then run the fix again.` : `MCP server ${entry.name} is already declared in this application's global configuration in a form Aura does not recognize, so Aura left it unchanged.`,
|
|
5171
|
+
scope: "global",
|
|
5193
5172
|
sourceId: unusable.sourceId
|
|
5194
5173
|
};
|
|
5195
5174
|
const existing = state.servers.filter(sameName);
|
|
@@ -5198,7 +5177,7 @@ function collisionBlocker(entry, ledger, state) {
|
|
|
5198
5177
|
if ("message" in normalized) return normalized;
|
|
5199
5178
|
return existing.every((server) => isDeepStrictEqual(server.transport, normalized.transport)) ? void 0 : {
|
|
5200
5179
|
message: `MCP server ${entry.name} already exists outside Aura's ownership ledger and differs from the manifest.`,
|
|
5201
|
-
scope:
|
|
5180
|
+
scope: "global"
|
|
5202
5181
|
};
|
|
5203
5182
|
}
|
|
5204
5183
|
/**
|
|
@@ -5213,7 +5192,7 @@ function normalizeDesired(entry) {
|
|
|
5213
5192
|
} catch (error) {
|
|
5214
5193
|
return {
|
|
5215
5194
|
message: error instanceof McpWriteError ? `MCP server ${entry.name} cannot be written as the manifest defines it: ${error.message}` : `MCP server ${entry.name} has a manifest definition Aura cannot represent.`,
|
|
5216
|
-
scope:
|
|
5195
|
+
scope: "global"
|
|
5217
5196
|
};
|
|
5218
5197
|
}
|
|
5219
5198
|
}
|
|
@@ -5227,28 +5206,25 @@ function createAppMcpConvergence(adapter, files, state) {
|
|
|
5227
5206
|
const targets = [...files.values()].filter((file) => file.spec.kind === "mcp");
|
|
5228
5207
|
const classified = classifyDesired(desired, ledgerNames, state);
|
|
5229
5208
|
if (classified.blockers.length > 0) return blocked(classified.blockers);
|
|
5230
|
-
const
|
|
5231
|
-
|
|
5232
|
-
const operations = scopes.flatMap((result) => result.operations);
|
|
5233
|
-
return blockers.length > 0 ? blocked(blockers) : {
|
|
5209
|
+
const planned = planGlobalTarget(adapter, writer, targets, classified.owned, ledgerNames, state.secretSightings);
|
|
5210
|
+
return planned.blockers.length > 0 ? blocked(planned.blockers) : {
|
|
5234
5211
|
blockers: [],
|
|
5235
|
-
operations,
|
|
5212
|
+
operations: planned.operations,
|
|
5236
5213
|
ownedNames: [...new Set(classified.owned.map((entry) => entry.name))].sort()
|
|
5237
5214
|
};
|
|
5238
5215
|
};
|
|
5239
5216
|
}
|
|
5240
|
-
function
|
|
5241
|
-
const
|
|
5242
|
-
|
|
5243
|
-
if (
|
|
5244
|
-
|
|
5245
|
-
const target = scopedTargets[0];
|
|
5217
|
+
function planGlobalTarget(adapter, writer, targets, desired, ledgerNames, sightings) {
|
|
5218
|
+
const globalTargets = targets.filter((target) => target.spec.scope === "global");
|
|
5219
|
+
if (desired.length > 0 && globalTargets.length === 0) return blockedScope(`${adapter.displayName} has no global MCP configuration target.`, "global");
|
|
5220
|
+
if (globalTargets.length > 1) return blockedScope(`${adapter.displayName} declares more than one global MCP write target.`, "global");
|
|
5221
|
+
const target = globalTargets[0];
|
|
5246
5222
|
return target === void 0 ? {
|
|
5247
5223
|
blockers: [],
|
|
5248
5224
|
operations: []
|
|
5249
|
-
} :
|
|
5225
|
+
} : writeGlobalTarget(adapter, writer, target, desired, ledgerNames, sightings);
|
|
5250
5226
|
}
|
|
5251
|
-
function
|
|
5227
|
+
function writeGlobalTarget(adapter, writer, target, desired, ledgerNames, sightings) {
|
|
5252
5228
|
if (!needsTargetWrite(target, desired, ledgerNames)) return {
|
|
5253
5229
|
blockers: [],
|
|
5254
5230
|
operations: []
|
|
@@ -5257,7 +5233,7 @@ function writeScopeTarget(adapter, writer, target, desired, ledgerNames, scope,
|
|
|
5257
5233
|
if (unwritable !== void 0) return {
|
|
5258
5234
|
blockers: [{
|
|
5259
5235
|
...unwritable,
|
|
5260
|
-
scope,
|
|
5236
|
+
scope: "global",
|
|
5261
5237
|
sourceId: target.spec.id
|
|
5262
5238
|
}],
|
|
5263
5239
|
operations: []
|
|
@@ -5267,7 +5243,7 @@ function writeScopeTarget(adapter, writer, target, desired, ledgerNames, scope,
|
|
|
5267
5243
|
blockers: [{
|
|
5268
5244
|
message: written.refusal,
|
|
5269
5245
|
path: target.spec.path,
|
|
5270
|
-
scope,
|
|
5246
|
+
scope: "global",
|
|
5271
5247
|
sourceId: target.spec.id
|
|
5272
5248
|
}],
|
|
5273
5249
|
operations: []
|
|
@@ -5276,14 +5252,14 @@ function writeScopeTarget(adapter, writer, target, desired, ledgerNames, scope,
|
|
|
5276
5252
|
blockers: [{
|
|
5277
5253
|
message: `${adapter.displayName}'s MCP configuration at ${target.spec.path} is larger than Aura will rewrite in one operation, so Aura left it unchanged.`,
|
|
5278
5254
|
path: target.spec.path,
|
|
5279
|
-
scope,
|
|
5255
|
+
scope: "global",
|
|
5280
5256
|
sourceId: target.spec.id
|
|
5281
5257
|
}],
|
|
5282
5258
|
operations: []
|
|
5283
5259
|
};
|
|
5284
5260
|
const operation = {
|
|
5285
5261
|
content: written.content,
|
|
5286
|
-
mode:
|
|
5262
|
+
mode: 384,
|
|
5287
5263
|
path: target.spec.path,
|
|
5288
5264
|
precondition: targetPrecondition(target),
|
|
5289
5265
|
type: "write"
|
|
@@ -5526,11 +5502,9 @@ async function scanAdapter(adapter, context) {
|
|
|
5526
5502
|
return { diagnostics: [failure$2(adapter, "files", error)] };
|
|
5527
5503
|
}
|
|
5528
5504
|
diagnostics.push(...discovery.diagnostics);
|
|
5529
|
-
let projectSharedLink;
|
|
5530
5505
|
let sharedLink;
|
|
5531
5506
|
try {
|
|
5532
5507
|
sharedLink = resolveAdapterSharedLink(adapter, context.environment, discovery.files);
|
|
5533
|
-
projectSharedLink = resolveAdapterProjectSharedLink(adapter, context.environment, discovery.files, await context.projectRoot);
|
|
5534
5508
|
} catch (error) {
|
|
5535
5509
|
diagnostics.push(failure$2(adapter, "files", error));
|
|
5536
5510
|
}
|
|
@@ -5554,9 +5528,8 @@ async function scanAdapter(adapter, context) {
|
|
|
5554
5528
|
mcpServers,
|
|
5555
5529
|
metadata: snapshot.metadata,
|
|
5556
5530
|
skills: snapshot.skills,
|
|
5557
|
-
skillDirectories: (adapter.capabilities?.skills?.directories ?? []).map((directory) => resolveSkillDirectory(directory, context.environment.homeDir
|
|
5531
|
+
skillDirectories: (adapter.capabilities?.skills?.directories ?? []).map((directory) => resolveSkillDirectory(directory, context.environment.homeDir)),
|
|
5558
5532
|
sourceFiles: [...discovery.files.values()].filter((file) => file.spec.kind !== "probe").map(toStatus),
|
|
5559
|
-
...projectSharedLink === void 0 ? {} : { projectSharedLink },
|
|
5560
5533
|
...sharedLink === void 0 ? {} : { sharedLink },
|
|
5561
5534
|
support: evaluateSupport(adapter.supportedRange, detection.version),
|
|
5562
5535
|
...adapter.synthetic === void 0 ? {} : { synthetic: adapter.synthetic },
|
|
@@ -9389,7 +9362,7 @@ function applyRequiredMcpServers(model, config) {
|
|
|
9389
9362
|
model
|
|
9390
9363
|
};
|
|
9391
9364
|
const manifest = model.manifest.value;
|
|
9392
|
-
const configured = new Set(manifest.mcpServers.map((server) =>
|
|
9365
|
+
const configured = new Set(manifest.mcpServers.map((server) => server.name));
|
|
9393
9366
|
const configuredCatalogIds = new Set(manifest.mcpServers.flatMap((server) => server.catalogId === void 0 ? [] : [server.catalogId]));
|
|
9394
9367
|
const diagnostics = [];
|
|
9395
9368
|
const required = [];
|
|
@@ -9410,7 +9383,7 @@ function applyRequiredMcpServers(model, config) {
|
|
|
9410
9383
|
continue;
|
|
9411
9384
|
}
|
|
9412
9385
|
const name = catalog.manifest.serverName;
|
|
9413
|
-
if (configured.has(
|
|
9386
|
+
if (configured.has(name)) {
|
|
9414
9387
|
diagnostics.push(note(`is already configured as "${name}", so your own settings were kept`, catalog.id, selection));
|
|
9415
9388
|
continue;
|
|
9416
9389
|
}
|
|
@@ -9424,7 +9397,6 @@ function applyRequiredMcpServers(model, config) {
|
|
|
9424
9397
|
catalogId: catalog.id,
|
|
9425
9398
|
name,
|
|
9426
9399
|
requiredBy: selection.provenance.label,
|
|
9427
|
-
scope: "global",
|
|
9428
9400
|
transport: catalog.manifest.transportTemplate
|
|
9429
9401
|
}));
|
|
9430
9402
|
}
|
|
@@ -9704,10 +9676,7 @@ function isBundledSkillSelection(skill) {
|
|
|
9704
9676
|
/** Final instruction handling only; paths, sources, and duplicate choices stop here. */
|
|
9705
9677
|
function instructionActions(selections) {
|
|
9706
9678
|
if (selections === void 0) return [];
|
|
9707
|
-
return [
|
|
9708
|
-
action,
|
|
9709
|
-
scope
|
|
9710
|
-
}));
|
|
9679
|
+
return [{ action: selections.global.action }];
|
|
9711
9680
|
}
|
|
9712
9681
|
/**
|
|
9713
9682
|
* Per-check state and severity counts for every executed check, ordered by check ID.
|
|
@@ -13876,7 +13845,7 @@ function planLinks$1(context, scopeSelections, archived, ownership, manualSteps)
|
|
|
13876
13845
|
}
|
|
13877
13846
|
function planAppLinks(context, app, scopeSelections, archived, ownership, manualSteps) {
|
|
13878
13847
|
return scopeSelections.flatMap((selection) => {
|
|
13879
|
-
const link =
|
|
13848
|
+
const link = app.sharedLink;
|
|
13880
13849
|
if (link === void 0) return [];
|
|
13881
13850
|
const outcome = planSharedInstructionLink(app, context.model, {
|
|
13882
13851
|
link,
|
|
@@ -13903,12 +13872,12 @@ function planAppLinks(context, app, scopeSelections, archived, ownership, manual
|
|
|
13903
13872
|
* only once that entry is merged here and archived. The wizard does not offer an entry already
|
|
13904
13873
|
* pointing at the target, so leaving this to the selection strands the app on every run.
|
|
13905
13874
|
*/
|
|
13906
|
-
function mandatoryEntryPaths(context
|
|
13875
|
+
function mandatoryEntryPaths(context) {
|
|
13907
13876
|
const managedIds = new Set(managedAppIdList(context));
|
|
13908
13877
|
const paths = /* @__PURE__ */ new Set();
|
|
13909
13878
|
for (const app of context.model.apps) {
|
|
13910
13879
|
if (app.synthetic === true || !managedIds.has(app.adapterId)) continue;
|
|
13911
|
-
const link =
|
|
13880
|
+
const link = app.sharedLink;
|
|
13912
13881
|
if (link?.kind === "symlink") paths.add(resolve(link.entryPath));
|
|
13913
13882
|
}
|
|
13914
13883
|
return paths;
|
|
@@ -13920,7 +13889,7 @@ function mandatoryEntryPaths(context, scope) {
|
|
|
13920
13889
|
* archives them.
|
|
13921
13890
|
*/
|
|
13922
13891
|
function requiredEntries(context, selection, inventory) {
|
|
13923
|
-
const required = mandatoryEntryPaths(context
|
|
13892
|
+
const required = mandatoryEntryPaths(context);
|
|
13924
13893
|
return inventory.filter((source) => source.scope === selection.scope && required.has(resolve(source.path)));
|
|
13925
13894
|
}
|
|
13926
13895
|
/** Folds the required entries into the selection, so the merge and the archive both see them. */
|
|
@@ -13955,7 +13924,7 @@ function canonicalSourcePath(document) {
|
|
|
13955
13924
|
*/
|
|
13956
13925
|
function isAuraArtifact(document, model) {
|
|
13957
13926
|
const path = resolve(document.path);
|
|
13958
|
-
return model.apps.some((app) =>
|
|
13927
|
+
return model.apps.some((app) => app.sharedLink !== void 0 && resolve(app.sharedLink.entryPath) === path && matchesLink(document, app.sharedLink));
|
|
13959
13928
|
}
|
|
13960
13929
|
function matchesLink(document, link) {
|
|
13961
13930
|
if (link.kind === "native-copy") return document.content === link.content;
|
|
@@ -13969,7 +13938,7 @@ function matchesLink(document, link) {
|
|
|
13969
13938
|
* would otherwise re-walk every application to rediscover the same handful of links.
|
|
13970
13939
|
*/
|
|
13971
13940
|
function importLineLinks(model) {
|
|
13972
|
-
return model.apps.
|
|
13941
|
+
return model.apps.map((app) => app.sharedLink).filter((link) => link?.kind === "import-line" && link.content !== void 0);
|
|
13973
13942
|
}
|
|
13974
13943
|
/** Removes legacy marked links and a plain import Aura appended at the declared entry path. */
|
|
13975
13944
|
function stripAuraInstructionArtifacts(content, path, links) {
|
|
@@ -14105,17 +14074,14 @@ function removeRanges(content, ranges) {
|
|
|
14105
14074
|
//#endregion
|
|
14106
14075
|
//#region src/setup/instructions.ts
|
|
14107
14076
|
function instructionTargets(model) {
|
|
14108
|
-
return {
|
|
14109
|
-
global: resolve(model.sharedInstructions.path),
|
|
14110
|
-
project: resolve(projectSharedInstructionsPath(model.projectRoot, model.cwd))
|
|
14111
|
-
};
|
|
14077
|
+
return { global: resolve(model.sharedInstructions.path) };
|
|
14112
14078
|
}
|
|
14113
14079
|
function instructionInventory(model) {
|
|
14114
14080
|
const targets = instructionTargets(model);
|
|
14115
14081
|
const owned = new Set(model.manifest.status === "ready" ? Object.values(model.manifest.value.ownership).flatMap((entry) => entry.files.map((path) => resolve(path))) : []);
|
|
14116
14082
|
const documents = /* @__PURE__ */ new Map();
|
|
14117
14083
|
for (const document of model.instructionFiles) {
|
|
14118
|
-
if ([resolve(document.path), canonicalSourcePath(document)].some((candidate) => candidate === targets.global ||
|
|
14084
|
+
if ([resolve(document.path), canonicalSourcePath(document)].some((candidate) => candidate === targets.global || owned.has(candidate)) || isAuraArtifact(document, model)) continue;
|
|
14119
14085
|
const key = canonicalSourcePath(document);
|
|
14120
14086
|
const current = documents.get(key);
|
|
14121
14087
|
if (current === void 0 || document.content.length > current.content.length) documents.set(key, document);
|
|
@@ -14216,10 +14182,8 @@ function planInstructions(context) {
|
|
|
14216
14182
|
const ownership = /* @__PURE__ */ new Map();
|
|
14217
14183
|
const inventory = instructionInventory(context.model);
|
|
14218
14184
|
const clusters = duplicateClusters(context.findings ?? []);
|
|
14219
|
-
const
|
|
14220
|
-
const
|
|
14221
|
-
for (const selection of scopeSelections) if (planScope(context, selection, inventory, clusters, state)) linked.push(selection);
|
|
14222
|
-
const linkOperations = planLinks$1(context, linked, state.archived, ownership, state.manualSteps);
|
|
14185
|
+
const selection = selections.global;
|
|
14186
|
+
const linkOperations = planLinks$1(context, selection.action !== "blocked" && planScope(context, selection, inventory, clusters, state) ? [selection] : [], state.archived, ownership, state.manualSteps);
|
|
14223
14187
|
for (const [path, archive] of state.archived) {
|
|
14224
14188
|
const linkIndex = linkOperations.findIndex((operation) => resolve(primaryPath(operation)) === path);
|
|
14225
14189
|
const link = linkIndex === -1 ? void 0 : linkOperations.splice(linkIndex, 1)[0];
|
|
@@ -14261,14 +14225,10 @@ function planScope(context, requested, inventory, clusters, state) {
|
|
|
14261
14225
|
if (selection.action !== "consolidate") return true;
|
|
14262
14226
|
const content = composeConsolidatedInstructions(chosen, selection, clusters, context.model, existing);
|
|
14263
14227
|
if (content.trim().length === 0) {
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14267
|
-
|
|
14268
|
-
});
|
|
14269
|
-
return false;
|
|
14270
|
-
}
|
|
14271
|
-
state.manualSteps.push(`Configure ${selection.targetPath} on the next run: select at least one project source to consolidate, or choose the starter template. Aura wrote nothing there.`);
|
|
14228
|
+
state.blockers.push({
|
|
14229
|
+
path: selection.targetPath,
|
|
14230
|
+
reason: "No selected instruction content is available to consolidate. Select a source, or choose the starter template."
|
|
14231
|
+
});
|
|
14272
14232
|
return false;
|
|
14273
14233
|
}
|
|
14274
14234
|
planConsolidatedTarget(context, selection, existing, content, state);
|
|
@@ -14452,7 +14412,7 @@ function withRequiredOverrides(manifest, requiredIds, overriddenIds) {
|
|
|
14452
14412
|
* was available to receive it.
|
|
14453
14413
|
*/
|
|
14454
14414
|
function unconfiguredRequirement(context, id) {
|
|
14455
|
-
return context.interactive ? `Required MCP catalog entry ${id} could not be selected for a managed compatible application.` : `Required MCP catalog entry ${id} is not configured yet. Run setup interactively to choose its name
|
|
14415
|
+
return context.interactive ? `Required MCP catalog entry ${id} could not be selected for a managed compatible application.` : `Required MCP catalog entry ${id} is not configured yet. Run setup interactively to choose its name and applications; later non-interactive runs re-apply what the manifest records globally.`;
|
|
14456
14416
|
}
|
|
14457
14417
|
function withMcpOwnership(manifest, appId, names) {
|
|
14458
14418
|
const previous = manifest.ownership[appId];
|
|
@@ -16603,7 +16563,6 @@ function excerpt(sources, path, startLine, endLine) {
|
|
|
16603
16563
|
//#region src/setup/steps/instruction-stages.ts
|
|
16604
16564
|
const TEMPLATE_VALUE = "template";
|
|
16605
16565
|
const CONSOLIDATE_VALUE = "consolidate";
|
|
16606
|
-
const SKIP_VALUE = "skip";
|
|
16607
16566
|
/**
|
|
16608
16567
|
* The target's current text when this scope has nothing left to decide, otherwise undefined.
|
|
16609
16568
|
*
|
|
@@ -16634,13 +16593,13 @@ function scopeStages(input) {
|
|
|
16634
16593
|
return [
|
|
16635
16594
|
{
|
|
16636
16595
|
isApplicable: () => !input.blocked,
|
|
16637
|
-
label:
|
|
16596
|
+
label: "Personal",
|
|
16638
16597
|
apply: (state, answers) => update(state, { action: settled(answers[actionId], offered, fallback) }),
|
|
16639
16598
|
questions: (state) => input.blocked ? void 0 : [{
|
|
16640
16599
|
id: actionId,
|
|
16641
16600
|
initial: [draft(state).action ?? fallback],
|
|
16642
16601
|
kind: "select",
|
|
16643
|
-
label:
|
|
16602
|
+
label: "Personal",
|
|
16644
16603
|
options,
|
|
16645
16604
|
prompt: `How should Aura configure ${input.targetPath}?`
|
|
16646
16605
|
}]
|
|
@@ -16659,7 +16618,7 @@ function scopeStages(input) {
|
|
|
16659
16618
|
label: source.path,
|
|
16660
16619
|
value: source.path
|
|
16661
16620
|
})),
|
|
16662
|
-
prompt:
|
|
16621
|
+
prompt: "Which personal instruction files should Aura consolidate?"
|
|
16663
16622
|
}] : void 0
|
|
16664
16623
|
},
|
|
16665
16624
|
{
|
|
@@ -16696,10 +16655,8 @@ function settled(answer, offered, fallback) {
|
|
|
16696
16655
|
* advice, the mark and the cursor on row `1.` together, and leaves the rest of the menu in the
|
|
16697
16656
|
* order it was built. A scope with nothing to combine recommends nothing and keeps that order.
|
|
16698
16657
|
*
|
|
16699
|
-
*
|
|
16700
|
-
*
|
|
16701
|
-
* offers should not be one the closing checklist then fails the run over. The project tier has no
|
|
16702
|
-
* error-severity counterpart, so a declined project scope still ends green.
|
|
16658
|
+
* Personal instructions cannot be skipped because setup must leave applications connected to the
|
|
16659
|
+
* global shared source.
|
|
16703
16660
|
*/
|
|
16704
16661
|
function actionOptions(input) {
|
|
16705
16662
|
const options = [];
|
|
@@ -16719,11 +16676,6 @@ function actionOptions(input) {
|
|
|
16719
16676
|
label: "Use starter template",
|
|
16720
16677
|
value: TEMPLATE_VALUE
|
|
16721
16678
|
});
|
|
16722
|
-
if (input.scope === "project") options.push({
|
|
16723
|
-
description: hasTargetContent(input) ? `Aura writes nothing here and adds no project-level link; ${basename(input.targetPath)} and anything already linking to it stay as they are.` : "Aura writes nothing here and adds no project-level link.",
|
|
16724
|
-
label: "Skip project instructions",
|
|
16725
|
-
value: SKIP_VALUE
|
|
16726
|
-
});
|
|
16727
16679
|
return recommendedFirst(options);
|
|
16728
16680
|
}
|
|
16729
16681
|
function hasTargetContent(input) {
|
|
@@ -16732,7 +16684,7 @@ function hasTargetContent(input) {
|
|
|
16732
16684
|
//#endregion
|
|
16733
16685
|
//#region src/setup/steps/instructions.ts
|
|
16734
16686
|
/**
|
|
16735
|
-
* The instructions step: one back-navigable chain of forms
|
|
16687
|
+
* The instructions step: one back-navigable chain of forms for personal instructions.
|
|
16736
16688
|
*
|
|
16737
16689
|
* Each scope contributes action → sources → duplicate review stages; a stage whose precondition no
|
|
16738
16690
|
* longer holds (a non-consolidate action, no duplicated paragraphs left) simply disappears from
|
|
@@ -16742,9 +16694,8 @@ function hasTargetContent(input) {
|
|
|
16742
16694
|
const instructionsStep = {
|
|
16743
16695
|
gather: async (context, io) => {
|
|
16744
16696
|
const inputs = scopeInputs(context);
|
|
16745
|
-
|
|
16746
|
-
|
|
16747
|
-
const stages = scopes.flatMap(scopeStages);
|
|
16697
|
+
if (context.revisited !== true) emitScopeNotes(inputs.global, io);
|
|
16698
|
+
const stages = scopeStages(inputs.global);
|
|
16748
16699
|
const result = await runFormChain(stages, initialState(context), io, {
|
|
16749
16700
|
entry: context.enteredBackward === true ? "end" : "start",
|
|
16750
16701
|
flow: context.flow
|
|
@@ -16758,55 +16709,34 @@ const instructionsStep = {
|
|
|
16758
16709
|
telemetryCategory: "instructions",
|
|
16759
16710
|
title: "Instructions"
|
|
16760
16711
|
};
|
|
16761
|
-
/**
|
|
16762
|
-
function scopeList(inputs) {
|
|
16763
|
-
return inputs.project === void 0 ? [inputs.global] : [inputs.global, inputs.project];
|
|
16764
|
-
}
|
|
16765
|
-
/** Folds both scopes' drafts into the step's selections, unoffered when it asked nothing to do it. */
|
|
16712
|
+
/** Folds the personal draft into the step's selections, unoffered when it asked nothing to do it. */
|
|
16766
16713
|
function settle(context, inputs, result, unoffered) {
|
|
16767
|
-
const project = inputs.project === void 0 ? void 0 : scopeSelection(inputs.project, result.project);
|
|
16768
16714
|
const selections = {
|
|
16769
16715
|
...context.selections,
|
|
16770
|
-
instructions: {
|
|
16771
|
-
global: scopeSelection(inputs.global, result.global),
|
|
16772
|
-
...project === void 0 ? {} : { project }
|
|
16773
|
-
}
|
|
16716
|
+
instructions: { global: scopeSelection(inputs.global, result.global) }
|
|
16774
16717
|
};
|
|
16775
16718
|
return unoffered ? {
|
|
16776
16719
|
selections,
|
|
16777
16720
|
unoffered: true
|
|
16778
16721
|
} : selections;
|
|
16779
16722
|
}
|
|
16780
|
-
/** Everything
|
|
16723
|
+
/** Everything personal instruction setup needs from the workspace. */
|
|
16781
16724
|
function scopeInputs(context) {
|
|
16782
16725
|
const inventory = instructionInventory(context.model);
|
|
16783
16726
|
const targets = instructionTargets(context.model);
|
|
16784
16727
|
const clusters = duplicateClusters(context.findings ?? []);
|
|
16785
|
-
|
|
16728
|
+
return { global: {
|
|
16786
16729
|
blocked: context.model.sharedInstructions.problem !== void 0,
|
|
16787
16730
|
clusters,
|
|
16788
16731
|
scope: "global",
|
|
16789
16732
|
sources: inventory.filter((source) => source.scope === "global"),
|
|
16790
16733
|
targetContentValue: instructionTargetContent(context.model, "global", targets.global),
|
|
16791
16734
|
targetPath: targets.global
|
|
16792
|
-
};
|
|
16793
|
-
const projectSources = inventory.filter((source) => source.scope === "project");
|
|
16794
|
-
const projectContent = instructionTargetContent(context.model, "project", targets.project);
|
|
16795
|
-
return {
|
|
16796
|
-
global,
|
|
16797
|
-
project: projectSources.length === 0 && projectContent === void 0 ? void 0 : {
|
|
16798
|
-
blocked: false,
|
|
16799
|
-
clusters,
|
|
16800
|
-
scope: "project",
|
|
16801
|
-
sources: projectSources,
|
|
16802
|
-
targetContentValue: projectContent,
|
|
16803
|
-
targetPath: targets.project
|
|
16804
|
-
}
|
|
16805
|
-
};
|
|
16735
|
+
} };
|
|
16806
16736
|
}
|
|
16807
16737
|
function scopeSelection(input, draft) {
|
|
16808
16738
|
if (input.blocked) return inactiveSelection(input, "blocked");
|
|
16809
|
-
if (draft.action !== "consolidate") return inactiveSelection(input, inactiveAction(draft.action
|
|
16739
|
+
if (draft.action !== "consolidate") return inactiveSelection(input, inactiveAction(draft.action));
|
|
16810
16740
|
const selectedSources = draft.selectedSources ?? [];
|
|
16811
16741
|
const relevant = relevantDuplicateClusters(selectedSources, input.clusters);
|
|
16812
16742
|
return {
|
|
@@ -16824,9 +16754,9 @@ function scopeSelection(input, draft) {
|
|
|
16824
16754
|
* INS-002 firing at error severity, so the action menu never offers it there; this mapping is the
|
|
16825
16755
|
* second half of that rule, keeping an action off the menu from becoming one the planner obeys.
|
|
16826
16756
|
*/
|
|
16827
|
-
function inactiveAction(action
|
|
16757
|
+
function inactiveAction(action) {
|
|
16828
16758
|
if (action === "template") return "template";
|
|
16829
|
-
return
|
|
16759
|
+
return "keep";
|
|
16830
16760
|
}
|
|
16831
16761
|
function inactiveSelection(input, action) {
|
|
16832
16762
|
return {
|
|
@@ -16840,10 +16770,7 @@ function inactiveSelection(input, action) {
|
|
|
16840
16770
|
/** A re-entered step resumes from what this run already decided, not from cold-start defaults. */
|
|
16841
16771
|
function initialState(context) {
|
|
16842
16772
|
const existing = context.selections.instructions;
|
|
16843
|
-
return {
|
|
16844
|
-
global: toDraft(existing?.global),
|
|
16845
|
-
project: toDraft(existing?.project)
|
|
16846
|
-
};
|
|
16773
|
+
return { global: toDraft(existing?.global) };
|
|
16847
16774
|
}
|
|
16848
16775
|
function toDraft(selection) {
|
|
16849
16776
|
if (selection === void 0 || selection.action === "blocked") return {};
|
|
@@ -16865,10 +16792,10 @@ function emitScopeNotes(input, io) {
|
|
|
16865
16792
|
path: input.targetPath,
|
|
16866
16793
|
scope: input.scope
|
|
16867
16794
|
});
|
|
16868
|
-
io.note(
|
|
16795
|
+
io.note(`Personal instructions already live in ${input.targetPath} (${size}); Aura found nothing else to consolidate and leaves the file as it is.`);
|
|
16869
16796
|
return;
|
|
16870
16797
|
}
|
|
16871
|
-
if (input.sources.length > 0) io.note(`Found ${describeSources(input.sources)} for
|
|
16798
|
+
if (input.sources.length > 0) io.note(`Found ${describeSources(input.sources)} for personal consolidation.`);
|
|
16872
16799
|
}
|
|
16873
16800
|
function describeSources(sources) {
|
|
16874
16801
|
return sources.map((source) => `${basename(source.path)} (${describeInstructionSource(source)})`).join(" and ");
|
|
@@ -17057,47 +16984,27 @@ async function configureEntry(context, entry, taken, io) {
|
|
|
17057
16984
|
return SETUP_BACK;
|
|
17058
16985
|
}
|
|
17059
16986
|
const defaultName = seed?.name ?? catalog?.serverName ?? "custom";
|
|
17060
|
-
const defaultScope = seed?.scope ?? (entry.repo === true ? "project" : "global");
|
|
17061
16987
|
const apps = appChoices(context, catalog?.supportedApps, seed?.apps ?? []);
|
|
17062
16988
|
if (apps.eligible.length === 0 && (seed?.apps.length ?? 0) === 0) {
|
|
17063
16989
|
io.note("No detected, managed, compatible application can receive this MCP server.");
|
|
17064
16990
|
return;
|
|
17065
16991
|
}
|
|
17066
16992
|
let name = defaultName;
|
|
17067
|
-
let scope = defaultScope;
|
|
17068
16993
|
let selectedApps = seed === void 0 || seed.apps.length === 0 ? apps.eligible : seed.apps;
|
|
17069
16994
|
for (;;) {
|
|
17070
|
-
const result = await io.ask([
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
|
|
17078
|
-
label: "Global",
|
|
17079
|
-
value: "global"
|
|
17080
|
-
}, {
|
|
17081
|
-
label: "Project",
|
|
17082
|
-
value: "project"
|
|
17083
|
-
}],
|
|
17084
|
-
prompt: "Where should applications configure this server?"
|
|
17085
|
-
},
|
|
17086
|
-
{
|
|
17087
|
-
id: "mcp-apps",
|
|
17088
|
-
initial: selectedApps,
|
|
17089
|
-
kind: "multiselect",
|
|
17090
|
-
label: "Applications",
|
|
17091
|
-
options: apps.options,
|
|
17092
|
-
prompt: "Which applications should enable this server?"
|
|
17093
|
-
}
|
|
17094
|
-
]);
|
|
16995
|
+
const result = await io.ask([textQuestion("mcp-name", "Name", "Configuration name", name), {
|
|
16996
|
+
id: "mcp-apps",
|
|
16997
|
+
initial: selectedApps,
|
|
16998
|
+
kind: "multiselect",
|
|
16999
|
+
label: "Applications",
|
|
17000
|
+
options: apps.options,
|
|
17001
|
+
prompt: "Which applications should enable this server?"
|
|
17002
|
+
}]);
|
|
17095
17003
|
if (result === "aborted") return SETUP_ABORTED;
|
|
17096
17004
|
if (result === "back") return SETUP_BACK;
|
|
17097
17005
|
name = answerText(result["mcp-name"]);
|
|
17098
|
-
scope = selectedValues(result["mcp-scope"])[0] === "project" ? "project" : "global";
|
|
17099
17006
|
selectedApps = selectedValues(result["mcp-apps"]);
|
|
17100
|
-
const problem = entryProblem(name,
|
|
17007
|
+
const problem = entryProblem(name, selectedApps, taken);
|
|
17101
17008
|
if (problem !== void 0) {
|
|
17102
17009
|
io.note(problem);
|
|
17103
17010
|
if (!context.interactive) return SETUP_ABORTED;
|
|
@@ -17107,7 +17014,6 @@ async function configureEntry(context, entry, taken, io) {
|
|
|
17107
17014
|
apps: Object.freeze([...new Set(selectedApps)]),
|
|
17108
17015
|
...catalog === void 0 ? {} : { catalogId: catalog.id },
|
|
17109
17016
|
name,
|
|
17110
|
-
scope,
|
|
17111
17017
|
transport
|
|
17112
17018
|
});
|
|
17113
17019
|
}
|
|
@@ -17120,12 +17026,12 @@ async function configureEntry(context, entry, taken, io) {
|
|
|
17120
17026
|
* the form that is one line to re-type; caught at serialization it would end the run after every
|
|
17121
17027
|
* question had been answered.
|
|
17122
17028
|
*/
|
|
17123
|
-
function entryProblem(name,
|
|
17029
|
+
function entryProblem(name, apps, taken) {
|
|
17124
17030
|
const nameProblem = mcpServerNameProblem(name);
|
|
17125
17031
|
if (nameProblem !== void 0) return `MCP server $.name ${nameProblem}.`;
|
|
17126
17032
|
if (apps.length === 0) return "MCP server $.apps must select at least one application.";
|
|
17127
17033
|
const selected = new Set(apps);
|
|
17128
|
-
return taken.some((server) => server.name === name && server.
|
|
17034
|
+
return taken.some((server) => server.name === name && server.apps.some((app) => selected.has(app))) ? `MCP server $.name ${name} is already configured globally for an application selected here. Choose another name.` : void 0;
|
|
17129
17035
|
}
|
|
17130
17036
|
function appChoices(context, supportedApps, existingApps) {
|
|
17131
17037
|
const managed = new Set(managedAppIdList(context));
|
|
@@ -17186,7 +17092,7 @@ function workingEntries(context) {
|
|
|
17186
17092
|
}
|
|
17187
17093
|
function matchesEntry(server, entry) {
|
|
17188
17094
|
const existing = entry.existing;
|
|
17189
|
-
if (existing !== void 0) return existing.catalogId === server.catalogId && existing.name === server.name
|
|
17095
|
+
if (existing !== void 0) return existing.catalogId === server.catalogId && existing.name === server.name;
|
|
17190
17096
|
return entry.catalog?.id === server.catalogId;
|
|
17191
17097
|
}
|
|
17192
17098
|
function nextCustomKey(entries) {
|
|
@@ -17197,7 +17103,7 @@ function nextCustomKey(entries) {
|
|
|
17197
17103
|
/**
|
|
17198
17104
|
* A default name no other row has taken.
|
|
17199
17105
|
*
|
|
17200
|
-
* Two servers cannot share one name in one application
|
|
17106
|
+
* Two servers cannot share one name in one application — the manifest refuses to record
|
|
17201
17107
|
* it — so seeding every custom server with `custom` would turn "add two, accept the defaults" into
|
|
17202
17108
|
* a plan the manifest declines to hold.
|
|
17203
17109
|
*/
|
|
@@ -17259,7 +17165,6 @@ async function gatherMcp(context, io) {
|
|
|
17259
17165
|
const server = {
|
|
17260
17166
|
apps: [],
|
|
17261
17167
|
name: nextCustomName(entries),
|
|
17262
|
-
scope: "global",
|
|
17263
17168
|
transport
|
|
17264
17169
|
};
|
|
17265
17170
|
entries.push({
|