@tryaura/aura-cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/bin/aura.js +3 -3
- package/dist/index.js +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/{plugins-n3DS8XXi.js → plugins-DDfc7XT9.js} +307 -58
- package/dist/{run-pMPxJqFQ.js → run-ZDkvqvZb.js} +694 -283
- package/dist/{skill-deployment-plan-C4GTrVSy.js → shared-link-plan-BigeLxDX.js} +227 -90
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as renderRemoveDiff, B as errorMessage$1, C as canonicalizeManagedSnippet, D as renderArchiveDiff, E as renderRedactedWriteDiff, F as assertAuraManifestWritable, H as resolveAuraManifestPath, I as createAuraManifestWriteOperation, K as pluralize, L as createEmptyAuraManifest, M as FILE_MODES, N as MAX_MUTABLE_FILE_BYTES, O as renderConflict, P as MAX_RETAINED_PLAN_BYTES, R as parseAuraManifest, S as readManagedBlock, T as hashManagedSnippet, U as AuraManifestError, V as isRecord$4, W as SHARED_INSTRUCTIONS_TEMPLATE, _ as planSharedSkillTreeUpdate, a as rememberMcpSecretPlanner, d as createAppMcpConvergence, g as managedContentRevisionStatus, j as renderSymlinkDiff, k as renderMoveDiff, m as sharedSkillsRoot, p as planSkillDeployment, r as createAppMcpSecretPlanner, s as planDesiredMcpConvergence, t as planSharedInstructionLink, u as rememberMcpConvergence, v as reconcileParsedManagedBlock, w as hashCanonicalManagedSnippet, x as managedSnippetContentProblems, y as diffManagedSnippet, z as errorCode } from "./shared-link-plan-BigeLxDX.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, NOT_EXECUTABLE_EXIT_CODE, OUTPUT_LIMIT_EXIT_CODE, SHARED_INSTRUCTIONS_TEMPLATE_TOKEN, TIMEOUT_EXIT_CODE, defineOwnProperty, detectExecutable, hasMcpRedaction, jsonPropertyPath, mcpEnvironmentVariableNames, mcpServerNameProblem, parseMcpServerDefinition, parseMcpServerManifest, 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";
|
|
@@ -823,7 +823,7 @@ function resolveAllowedRoots(model, managedHomeRoots) {
|
|
|
823
823
|
path: resolve(model.projectRoot ?? model.cwd)
|
|
824
824
|
});
|
|
825
825
|
const sharedDirectory = dirname(resolve(model.sharedInstructions.path));
|
|
826
|
-
if (isStrictDescendant
|
|
826
|
+
if (isStrictDescendant(homeDir, sharedDirectory)) add(roots, {
|
|
827
827
|
exact: false,
|
|
828
828
|
path: sharedDirectory
|
|
829
829
|
});
|
|
@@ -838,7 +838,7 @@ function resolveAllowedRoots(model, managedHomeRoots) {
|
|
|
838
838
|
*/
|
|
839
839
|
function matchRoot(path, roots, caseInsensitive) {
|
|
840
840
|
const resolvedPath = resolve(path);
|
|
841
|
-
const directoryRoot = roots.filter((root) => !root.exact && isStrictDescendant
|
|
841
|
+
const directoryRoot = roots.filter((root) => !root.exact && isStrictDescendant(root.path, resolvedPath)).sort((left, right) => right.path.length - left.path.length)[0];
|
|
842
842
|
if (directoryRoot !== void 0) return directoryRoot;
|
|
843
843
|
return roots.find((root) => root.exact && comparablePath(root.path, caseInsensitive) === comparablePath(resolvedPath, caseInsensitive));
|
|
844
844
|
}
|
|
@@ -847,7 +847,7 @@ function describeRoots(roots) {
|
|
|
847
847
|
return roots.map((root) => root.exact ? root.path : `${root.path}${sep}`).sort().join(", ");
|
|
848
848
|
}
|
|
849
849
|
/** Returns true when `candidate` sits strictly inside `root`. */
|
|
850
|
-
function isStrictDescendant
|
|
850
|
+
function isStrictDescendant(root, candidate) {
|
|
851
851
|
const difference = relative(root, candidate);
|
|
852
852
|
return difference.length > 0 && difference !== ".." && !difference.startsWith(`..${sep}`) && !isAbsolute(difference);
|
|
853
853
|
}
|
|
@@ -858,13 +858,13 @@ function deriveManagedHomeRoots(model, homeDir) {
|
|
|
858
858
|
function rootsForSpec(spec, homeDir) {
|
|
859
859
|
if (spec.scope !== "global" || !isAbsolute(spec.path)) return [];
|
|
860
860
|
const path = resolve(spec.path);
|
|
861
|
-
if (!isStrictDescendant
|
|
861
|
+
if (!isStrictDescendant(homeDir, path)) return [];
|
|
862
862
|
if (spec.kind === "skills") return [{
|
|
863
863
|
exact: false,
|
|
864
864
|
path
|
|
865
865
|
}];
|
|
866
866
|
const directory = dirname(path);
|
|
867
|
-
return isStrictDescendant
|
|
867
|
+
return isStrictDescendant(homeDir, directory) ? [{
|
|
868
868
|
exact: true,
|
|
869
869
|
path
|
|
870
870
|
}, {
|
|
@@ -879,7 +879,7 @@ function validateManagedHomeRoots(managedHomeRoots, homeDir) {
|
|
|
879
879
|
return managedHomeRoots.map((candidate) => {
|
|
880
880
|
if (!isAbsolute(candidate)) throw new FixPlanError("invalid-options", `managedHomeRoots must contain absolute paths: ${candidate}`, { path: candidate });
|
|
881
881
|
const path = resolve(candidate);
|
|
882
|
-
if (!isStrictDescendant
|
|
882
|
+
if (!isStrictDescendant(homeDir, path)) throw new FixPlanError("invalid-options", `managedHomeRoots must sit strictly inside ${homeDir}: ${candidate}`, { path: candidate });
|
|
883
883
|
return {
|
|
884
884
|
exact: false,
|
|
885
885
|
path
|
|
@@ -3198,6 +3198,35 @@ function directorySourceProblem(source) {
|
|
|
3198
3198
|
if (source.kind === "private-directory" && !TOKEN_ENV_PATTERN.test(source.tokenEnv)) return `declares token variable "${source.tokenEnv}"; expected an environment variable name of uppercase letters, digits, and underscores — a name, never a value`;
|
|
3199
3199
|
}
|
|
3200
3200
|
//#endregion
|
|
3201
|
+
//#region ../core/src/plugin-validation-version.ts
|
|
3202
|
+
/**
|
|
3203
|
+
* A version's canonical rendering, or `undefined` when it is not semver at all.
|
|
3204
|
+
*
|
|
3205
|
+
* Parsing alone is too weak for an identity field: `semver` also accepts a leading `v` and
|
|
3206
|
+
* surrounding whitespace, so a version declared as "v1.0.0" or "1.0.0\n" would pass and then be
|
|
3207
|
+
* carried around raw. Requiring the declaration to equal its canonical form refuses those while
|
|
3208
|
+
* still accepting "1.2.3-rc.1+build.5" as written — build metadata has to be rebuilt by hand
|
|
3209
|
+
* because `valid()` drops it.
|
|
3210
|
+
*/
|
|
3211
|
+
function canonicalSemver(version) {
|
|
3212
|
+
const parsed = parse(version);
|
|
3213
|
+
if (parsed === null) return;
|
|
3214
|
+
return parsed.build.length === 0 ? parsed.version : `${parsed.version}+${parsed.build.join(".")}`;
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* Why a content revision could never be compared to a recorded one, or `undefined` when it can.
|
|
3218
|
+
*
|
|
3219
|
+
* Managed snippets and skills are held at their recorded revision until a newer one is reviewed,
|
|
3220
|
+
* and "newer" is a semver comparison. A version semver cannot order therefore has no upgrade path
|
|
3221
|
+
* at all: setup would never offer it and MGD-002 would never report it, so the content would sit
|
|
3222
|
+
* frozen with nothing on screen explaining why. Refusing it at registry build is what keeps that
|
|
3223
|
+
* dead end out of a user's manifest, enforcing the SDK's documented "semver version of the content
|
|
3224
|
+
* itself" rather than trusting it.
|
|
3225
|
+
*/
|
|
3226
|
+
function contentVersionProblem(version) {
|
|
3227
|
+
return canonicalSemver(version) === version ? void 0 : `declares version "${version}"; expected a canonical semver version such as "1.0.0", because managed content is held at its recorded revision until a newer one is reviewed`;
|
|
3228
|
+
}
|
|
3229
|
+
//#endregion
|
|
3201
3230
|
//#region ../core/src/skills/path-guards.ts
|
|
3202
3231
|
/**
|
|
3203
3232
|
* Pure guards over server-supplied names and paths, run before anything derived from a fetched
|
|
@@ -3444,20 +3473,6 @@ function formatApiVersionViolation(candidate) {
|
|
|
3444
3473
|
function formatViolations(violations) {
|
|
3445
3474
|
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");
|
|
3446
3475
|
}
|
|
3447
|
-
/**
|
|
3448
|
-
* A version's canonical rendering, or `undefined` when it is not semver at all.
|
|
3449
|
-
*
|
|
3450
|
-
* Parsing alone is too weak for an identity field: `semver` also accepts a leading `v` and
|
|
3451
|
-
* surrounding whitespace, so a version declared as "v1.0.0" or "1.0.0\n" would pass and then be
|
|
3452
|
-
* carried around raw. Requiring the declaration to equal its canonical form refuses those while
|
|
3453
|
-
* still accepting "1.2.3-rc.1+build.5" as written — build metadata has to be rebuilt by hand
|
|
3454
|
-
* because `valid()` drops it.
|
|
3455
|
-
*/
|
|
3456
|
-
function canonicalSemver(version) {
|
|
3457
|
-
const parsed = parse(version);
|
|
3458
|
-
if (parsed === null) return;
|
|
3459
|
-
return parsed.build.length === 0 ? parsed.version : `${parsed.version}+${parsed.build.join(".")}`;
|
|
3460
|
-
}
|
|
3461
3476
|
function formatPlugin(plugin) {
|
|
3462
3477
|
return `Plugin "${plugin.name}" (${plugin.id})`;
|
|
3463
3478
|
}
|
|
@@ -3517,7 +3532,17 @@ function collectCandidate(state, candidate, bareCheckIdPlugins, collected) {
|
|
|
3517
3532
|
collectSkillDirectories(state, plugin, collected.skillDirectories);
|
|
3518
3533
|
collectSkills$1(state, plugin, collected.skills);
|
|
3519
3534
|
collectNamespaced(state, "skill-source", plugin.skillSources, plugin, collected.skillSources);
|
|
3520
|
-
|
|
3535
|
+
collectSnippets(state, plugin, collected.snippets);
|
|
3536
|
+
}
|
|
3537
|
+
/** Snippets are managed content, so their revision has to be comparable before one is installed. */
|
|
3538
|
+
function collectSnippets(state, plugin, collected) {
|
|
3539
|
+
const claimed = [];
|
|
3540
|
+
collectNamespaced(state, "snippet", plugin.snippets, plugin, claimed);
|
|
3541
|
+
for (const snippet of claimed) {
|
|
3542
|
+
const problem = contentVersionProblem(snippet.version);
|
|
3543
|
+
if (problem === void 0) collected.push(snippet);
|
|
3544
|
+
else state.violations.push(`Plugin "${plugin.name}" (${plugin.id}) snippet "${snippet.id}" ${problem}.`);
|
|
3545
|
+
}
|
|
3521
3546
|
}
|
|
3522
3547
|
/**
|
|
3523
3548
|
* Directory ids are global, like adapter ids: `directory:agenticskills` names a place, so two
|
|
@@ -3544,6 +3569,11 @@ function collectSkills$1(state, plugin, collected) {
|
|
|
3544
3569
|
state.violations.push(`Plugin "${plugin.name}" (${plugin.id}) contributes skill-pack ID "${skill.id}"; expected a kebab-case local skill ID of at most 64 characters.`);
|
|
3545
3570
|
continue;
|
|
3546
3571
|
}
|
|
3572
|
+
const versionProblem = contentVersionProblem(skill.version);
|
|
3573
|
+
if (versionProblem !== void 0) {
|
|
3574
|
+
state.violations.push(`Plugin "${plugin.name}" (${plugin.id}) skill "${skill.id}" ${versionProblem}.`);
|
|
3575
|
+
continue;
|
|
3576
|
+
}
|
|
3547
3577
|
if (claimId(state, "skill-pack", `${source.id}/${skill.id}`, plugin)) collected.push({
|
|
3548
3578
|
skill,
|
|
3549
3579
|
source
|
|
@@ -4198,7 +4228,7 @@ async function discoverAdapterFiles(adapter, options) {
|
|
|
4198
4228
|
adapter,
|
|
4199
4229
|
projectBoundary,
|
|
4200
4230
|
reader,
|
|
4201
|
-
sharedSkillsRoot:
|
|
4231
|
+
sharedSkillsRoot: sharedSkillsRoot(environment.homeDir)
|
|
4202
4232
|
})));
|
|
4203
4233
|
for (const read of reads) {
|
|
4204
4234
|
files.set(read.file.spec.id, read.file);
|
|
@@ -5033,7 +5063,7 @@ const SKILLS_DIAGNOSTIC_ID = "core/skills";
|
|
|
5033
5063
|
const SKILL_FILE$1 = "SKILL.md";
|
|
5034
5064
|
/** Canonical shared skill root for one captured environment. */
|
|
5035
5065
|
function sharedSkillsPath(environment) {
|
|
5036
|
-
return
|
|
5066
|
+
return sharedSkillsRoot(environment.homeDir);
|
|
5037
5067
|
}
|
|
5038
5068
|
/** Resolves every bundled skill pack without letting one broken pack hide the others. */
|
|
5039
5069
|
async function resolveBundledSkills(registrations, reader) {
|
|
@@ -6723,15 +6753,26 @@ function applyRequiredMcpServers(model, config) {
|
|
|
6723
6753
|
};
|
|
6724
6754
|
const manifest = model.manifest.value;
|
|
6725
6755
|
const configured = new Set(manifest.mcpServers.map((server) => `${server.scope}\0${server.name}`));
|
|
6756
|
+
const configuredCatalogIds = new Set(manifest.mcpServers.flatMap((server) => server.catalogId === void 0 ? [] : [server.catalogId]));
|
|
6726
6757
|
const diagnostics = [];
|
|
6727
6758
|
const required = [];
|
|
6759
|
+
const overridden = [];
|
|
6760
|
+
const overrideIds = new Set(manifest.overrides?.requiredMcpServers ?? []);
|
|
6728
6761
|
for (const selection of config.requiredMcpServers) {
|
|
6762
|
+
if (overrideIds.has(selection.value)) {
|
|
6763
|
+
overridden.push({
|
|
6764
|
+
catalogId: selection.value,
|
|
6765
|
+
requiredBy: selection.provenance.label
|
|
6766
|
+
});
|
|
6767
|
+
continue;
|
|
6768
|
+
}
|
|
6769
|
+
if (configuredCatalogIds.has(selection.value)) continue;
|
|
6729
6770
|
const catalog = model.availableMcpServers.find((candidate) => candidate.id === selection.value);
|
|
6730
6771
|
if (catalog === void 0) {
|
|
6731
6772
|
diagnostics.push(note(`is not in this run's MCP catalog, so it was skipped`, selection.value, selection));
|
|
6732
6773
|
continue;
|
|
6733
6774
|
}
|
|
6734
|
-
const name = catalog.
|
|
6775
|
+
const name = catalog.manifest.serverName;
|
|
6735
6776
|
if (configured.has(`global\0${name}`)) {
|
|
6736
6777
|
diagnostics.push(note(`is already configured as "${name}", so your own settings were kept`, catalog.id, selection));
|
|
6737
6778
|
continue;
|
|
@@ -6752,9 +6793,10 @@ function applyRequiredMcpServers(model, config) {
|
|
|
6752
6793
|
}
|
|
6753
6794
|
return {
|
|
6754
6795
|
diagnostics: Object.freeze(diagnostics),
|
|
6755
|
-
model: required.length === 0 ? model : Object.freeze({
|
|
6796
|
+
model: required.length === 0 && overridden.length === 0 ? model : Object.freeze({
|
|
6756
6797
|
...model,
|
|
6757
|
-
|
|
6798
|
+
...overridden.length === 0 ? {} : { overriddenRequiredMcpServers: Object.freeze(overridden) },
|
|
6799
|
+
...required.length === 0 ? {} : { requiredMcpServers: Object.freeze(required) }
|
|
6758
6800
|
})
|
|
6759
6801
|
};
|
|
6760
6802
|
}
|
|
@@ -8583,6 +8625,18 @@ function defaultAnswer(question) {
|
|
|
8583
8625
|
function selectedValues(answer) {
|
|
8584
8626
|
return answer === void 0 || answer.kind !== "options" ? [] : answer.values;
|
|
8585
8627
|
}
|
|
8628
|
+
/**
|
|
8629
|
+
* Folds one review form's answers back into the decisions carried by a chain's state.
|
|
8630
|
+
*
|
|
8631
|
+
* Review forms are keyed `<prefix><identity>` so a single form can carry one question per item.
|
|
8632
|
+
* Answers already given for items this form did not ask about are kept: a stage re-entered through
|
|
8633
|
+
* ← re-asks a subset, and dropping the rest would silently reset decisions the user already made.
|
|
8634
|
+
*/
|
|
8635
|
+
function foldDecisions(previous, answers, prefix, fallback) {
|
|
8636
|
+
const decisions = { ...previous };
|
|
8637
|
+
for (const [id, answer] of Object.entries(answers)) if (id.startsWith(prefix)) decisions[id.slice(prefix.length)] = selectedValues(answer)[0] ?? fallback;
|
|
8638
|
+
return decisions;
|
|
8639
|
+
}
|
|
8586
8640
|
//#endregion
|
|
8587
8641
|
//#region src/guided-fix.ts
|
|
8588
8642
|
async function gatherGuidedFixes(request, wizard, diagnostics) {
|
|
@@ -9808,6 +9862,51 @@ function buildAppCatalog(adapters, model, skipped) {
|
|
|
9808
9862
|
}));
|
|
9809
9863
|
}
|
|
9810
9864
|
//#endregion
|
|
9865
|
+
//#region src/setup/planner-app-steps.ts
|
|
9866
|
+
/**
|
|
9867
|
+
* Summary lines the plan cannot express as file operations: installing an app Aura does not
|
|
9868
|
+
* install itself, and what unchecking a previously-managed app does — the manifest flips to
|
|
9869
|
+
* `managed: false` while the app's own configuration stays in place (ownership cleanup is a later
|
|
9870
|
+
* milestone).
|
|
9871
|
+
*/
|
|
9872
|
+
function appManualSteps(context) {
|
|
9873
|
+
const apps = context.selections.apps;
|
|
9874
|
+
if (apps === void 0) return [];
|
|
9875
|
+
return [...installSteps(context, apps.managed), ...stopManagingSteps(context, apps.managed)];
|
|
9876
|
+
}
|
|
9877
|
+
function installSteps(context, managed) {
|
|
9878
|
+
const steps = [];
|
|
9879
|
+
for (const id of managed) {
|
|
9880
|
+
const entry = context.appCatalog.find((candidate) => catalogEntryId(candidate) === id);
|
|
9881
|
+
if (entry?.kind === "undetected") steps.push(entry.installHint === void 0 ? `Install ${entry.displayName} — its adapter provides no install instructions.` : `Install ${entry.displayName}: ${entry.installHint}`);
|
|
9882
|
+
}
|
|
9883
|
+
return steps;
|
|
9884
|
+
}
|
|
9885
|
+
function stopManagingSteps(context, managed) {
|
|
9886
|
+
const previous = context.manifest.status === "ready" ? context.manifest.value.apps : {};
|
|
9887
|
+
const managedIds = new Set(managed);
|
|
9888
|
+
const steps = [];
|
|
9889
|
+
for (const [id, app] of Object.entries(previous)) {
|
|
9890
|
+
const entry = context.appCatalog.find((candidate) => catalogEntryId(candidate) === id);
|
|
9891
|
+
if (entry !== void 0 && app.managed && !managedIds.has(id)) steps.push(`Aura stops managing ${catalogEntryName(entry)}; its existing configuration is left in place.`);
|
|
9892
|
+
}
|
|
9893
|
+
return steps;
|
|
9894
|
+
}
|
|
9895
|
+
//#endregion
|
|
9896
|
+
//#region src/setup/app-ignore.ts
|
|
9897
|
+
/** Applies this setup pass's explicit ignored-app decisions while preserving unknown adapters. */
|
|
9898
|
+
function withIgnoredAppSelections(manifest, previous, apps, appCatalog) {
|
|
9899
|
+
const available = new Set(appCatalog.map(catalogEntryId));
|
|
9900
|
+
const managed = new Set(apps.managed);
|
|
9901
|
+
const selectedIgnored = new Set(apps.ignored ?? []);
|
|
9902
|
+
const ignoredApps = [.../* @__PURE__ */ new Set([...(previous.ignoredApps ?? []).filter((id) => !available.has(id)), ...appCatalog.map(catalogEntryId).filter((id) => !managed.has(id) && selectedIgnored.has(id))])];
|
|
9903
|
+
const { ignoredApps: _previous, ...rest } = manifest;
|
|
9904
|
+
return ignoredApps.length === 0 ? rest : {
|
|
9905
|
+
...rest,
|
|
9906
|
+
ignoredApps
|
|
9907
|
+
};
|
|
9908
|
+
}
|
|
9909
|
+
//#endregion
|
|
9811
9910
|
//#region src/setup/instructions.ts
|
|
9812
9911
|
function instructionTargets(model) {
|
|
9813
9912
|
return {
|
|
@@ -10148,7 +10247,7 @@ function planManifestWrite(context, desired, createManifest, apps) {
|
|
|
10148
10247
|
}
|
|
10149
10248
|
/** Decides whether desired manifest bytes or their required private mode differ from disk. */
|
|
10150
10249
|
function shouldWriteManifest(context, desired, createManifest, apps) {
|
|
10151
|
-
return context.manifest.status === "ready" ? differsFromDisk(context.manifest, desired) : createManifest || answersSomethingToRecord(context.selections, apps);
|
|
10250
|
+
return context.manifest.status === "ready" ? differsFromDisk(context.manifest, desired) : createManifest || context.preset?.explicit === true || answersSomethingToRecord(context.selections, apps);
|
|
10152
10251
|
}
|
|
10153
10252
|
function differsFromDisk(state, desired) {
|
|
10154
10253
|
const mode = state.mode;
|
|
@@ -10180,6 +10279,7 @@ function planSetupMcp(context, desiredManifest) {
|
|
|
10180
10279
|
const missingDefinitions = new Set(context.mcpCatalog.missingRequiredIds);
|
|
10181
10280
|
const selectedCatalogIds = new Set(manifest.mcpServers.flatMap((server) => server.catalogId === void 0 ? [] : [server.catalogId]));
|
|
10182
10281
|
const overriddenRequiredIds = new Set(context.selections.mcp.overriddenRequiredIds ?? []);
|
|
10282
|
+
if (context.preset !== void 0) manifest = withRequiredOverrides(manifest, context.mcpCatalog.requiredIds, overriddenRequiredIds);
|
|
10183
10283
|
for (const id of context.mcpCatalog.requiredIds) if (!missingDefinitions.has(id) && !selectedCatalogIds.has(id) && !overriddenRequiredIds.has(id)) blockers.push({
|
|
10184
10284
|
path: presetPath,
|
|
10185
10285
|
reason: unconfiguredRequirement(context, id)
|
|
@@ -10202,6 +10302,19 @@ function planSetupMcp(context, desiredManifest) {
|
|
|
10202
10302
|
operations
|
|
10203
10303
|
};
|
|
10204
10304
|
}
|
|
10305
|
+
function withRequiredOverrides(manifest, requiredIds, overriddenIds) {
|
|
10306
|
+
const requiredMcpServers = [...requiredIds].filter((id) => overriddenIds.has(id));
|
|
10307
|
+
const { requiredMcpServers: _previousRequired, ...extension } = manifest.overrides ?? {};
|
|
10308
|
+
const overrides = requiredMcpServers.length === 0 && Object.keys(extension).length === 0 ? void 0 : {
|
|
10309
|
+
...extension,
|
|
10310
|
+
...requiredMcpServers.length === 0 ? {} : { requiredMcpServers }
|
|
10311
|
+
};
|
|
10312
|
+
const { overrides: _previousOverrides, ...rest } = manifest;
|
|
10313
|
+
return overrides === void 0 ? rest : {
|
|
10314
|
+
...rest,
|
|
10315
|
+
overrides
|
|
10316
|
+
};
|
|
10317
|
+
}
|
|
10205
10318
|
/**
|
|
10206
10319
|
* Says which of the two ways a requirement went unmet happened, because the fix differs.
|
|
10207
10320
|
*
|
|
@@ -10236,6 +10349,24 @@ function credentialSteps(context, manifest) {
|
|
|
10236
10349
|
}));
|
|
10237
10350
|
}
|
|
10238
10351
|
//#endregion
|
|
10352
|
+
//#region src/setup/preset-policy.ts
|
|
10353
|
+
/**
|
|
10354
|
+
* The preset's own check settings, shown as read-only policy.
|
|
10355
|
+
*
|
|
10356
|
+
* The preset is named once in the group heading rather than on every line: with a preset that
|
|
10357
|
+
* configures a dozen checks, a repeated "(from preset acme)" is noise that hides the settings the
|
|
10358
|
+
* user is actually meant to read.
|
|
10359
|
+
*/
|
|
10360
|
+
function presetPolicyNotices(context) {
|
|
10361
|
+
const preset = context.preset;
|
|
10362
|
+
if (preset === void 0 || preset.checkSummary.length === 0) return [];
|
|
10363
|
+
return preset.checkSummary.map((message, index) => ({
|
|
10364
|
+
...index === 0 ? { heading: `Effective check policy from preset ${preset.name} (not copied into your manifest):` } : {},
|
|
10365
|
+
kind: "policy",
|
|
10366
|
+
message
|
|
10367
|
+
}));
|
|
10368
|
+
}
|
|
10369
|
+
//#endregion
|
|
10239
10370
|
//#region src/setup/snippet-planner.ts
|
|
10240
10371
|
/** Reconcile notes the user has to see, and the heading each one belongs under. */
|
|
10241
10372
|
const NOTICE_KINDS = Object.freeze({
|
|
@@ -10260,11 +10391,17 @@ function planSnippets(context, source) {
|
|
|
10260
10391
|
});
|
|
10261
10392
|
const unavailable = selectedEntries.filter((entry) => entry.status === "unavailable");
|
|
10262
10393
|
const pinned = selection.selected.filter((id) => previousById.get(id)?.pinned === true);
|
|
10394
|
+
const held = heldRevisions(selection.selected, catalog, previousById, new Set(selection.updates));
|
|
10395
|
+
const heldIds = new Set(held.map((entry) => entry.id));
|
|
10263
10396
|
const pinnedIds = new Set(pinned);
|
|
10264
|
-
const preserved = [
|
|
10397
|
+
const preserved = [
|
|
10398
|
+
...unavailable.map((entry) => entry.id),
|
|
10399
|
+
...pinned,
|
|
10400
|
+
...heldIds
|
|
10401
|
+
];
|
|
10265
10402
|
const parsed = readManagedBlock(source);
|
|
10266
10403
|
const blockers = snippetBlockers(context.model.sharedInstructions.path, parsed, unavailable, pinned.filter((id) => !unavailable.some((entry) => entry.id === id)));
|
|
10267
|
-
const desired = selectedEntries.flatMap((entry) => entry.status === "available" && !pinnedIds.has(entry.id) ? [{
|
|
10404
|
+
const desired = selectedEntries.flatMap((entry) => entry.status === "available" && !pinnedIds.has(entry.id) && !heldIds.has(entry.id) ? [{
|
|
10268
10405
|
content: entry.content,
|
|
10269
10406
|
id: entry.id
|
|
10270
10407
|
}] : []);
|
|
@@ -10274,14 +10411,14 @@ function planSnippets(context, source) {
|
|
|
10274
10411
|
previousSnippetHashes: new Map(previous.map((entry) => [entry.id, entry.hash]))
|
|
10275
10412
|
});
|
|
10276
10413
|
collectReconcileBlockers(context.model.sharedInstructions.path, parsed, reconciled, blockers);
|
|
10277
|
-
const manifestSnippets = desiredManifestSnippets(selection.selected, catalog, previousById);
|
|
10278
|
-
const notices = reconciled.notes.flatMap((note) => {
|
|
10414
|
+
const manifestSnippets = desiredManifestSnippets(selection.selected, catalog, previousById, heldIds);
|
|
10415
|
+
const notices = [...reconciled.notes.flatMap((note) => {
|
|
10279
10416
|
const kind = NOTICE_KINDS[note.code];
|
|
10280
10417
|
return kind === void 0 ? [] : [{
|
|
10281
10418
|
kind,
|
|
10282
10419
|
message: note.message
|
|
10283
10420
|
}];
|
|
10284
|
-
});
|
|
10421
|
+
}), ...held.map(heldNotice)];
|
|
10285
10422
|
return {
|
|
10286
10423
|
blockers,
|
|
10287
10424
|
content: reconciled.content,
|
|
@@ -10290,6 +10427,39 @@ function planSnippets(context, source) {
|
|
|
10290
10427
|
updated: reconciled.status === "updated"
|
|
10291
10428
|
};
|
|
10292
10429
|
}
|
|
10430
|
+
/**
|
|
10431
|
+
* The selections whose source moved but whose recorded revision this run keeps.
|
|
10432
|
+
*
|
|
10433
|
+
* Uses the same comparison the review form asks with, so "the step never offered it" and "the plan
|
|
10434
|
+
* never applied it" cannot drift apart: any revision the user was not asked about is held, and any
|
|
10435
|
+
* revision held is one the user was asked about and declined.
|
|
10436
|
+
*/
|
|
10437
|
+
function heldRevisions(selectedIds, catalog, previousById, accepted) {
|
|
10438
|
+
return selectedIds.flatMap((id) => {
|
|
10439
|
+
const installed = previousById.get(id);
|
|
10440
|
+
const available = catalog.get(id);
|
|
10441
|
+
if (installed === void 0 || installed.pinned === true || available?.status !== "available" || accepted.has(id)) return [];
|
|
10442
|
+
const status = managedContentRevisionStatus(installed.version, installed.hash, available.version, hashManagedSnippet(available.content));
|
|
10443
|
+
return status === "current" ? [] : [{
|
|
10444
|
+
availableVersion: available.version,
|
|
10445
|
+
id,
|
|
10446
|
+
installedVersion: installed.version,
|
|
10447
|
+
status
|
|
10448
|
+
}];
|
|
10449
|
+
});
|
|
10450
|
+
}
|
|
10451
|
+
/**
|
|
10452
|
+
* Says out loud that a snippet was left at its recorded revision.
|
|
10453
|
+
*
|
|
10454
|
+
* Holding content back is the safe default, but a run that holds silently is indistinguishable
|
|
10455
|
+
* from one that had nothing to do — which is exactly the reading a `--yes` run would get wrong.
|
|
10456
|
+
*/
|
|
10457
|
+
function heldNotice(held) {
|
|
10458
|
+
return {
|
|
10459
|
+
kind: "held",
|
|
10460
|
+
message: held.status === "update" ? `${held.id} stays at ${held.installedVersion}; ${held.availableVersion} is available. Run setup interactively to review it.` : `${held.id} stays at ${held.installedVersion}; the installed plugin offers ${held.availableVersion}, which is not newer.`
|
|
10461
|
+
};
|
|
10462
|
+
}
|
|
10293
10463
|
function snippetBlockers(path, parsed, unavailable, pinned) {
|
|
10294
10464
|
const blockers = [];
|
|
10295
10465
|
if (parsed.status === "invalid") blockers.push({
|
|
@@ -10317,10 +10487,11 @@ function collectReconcileBlockers(path, parsed, reconciled, blockers) {
|
|
|
10317
10487
|
reason: reconciled.problems.map((problem) => problem.message).join(" ")
|
|
10318
10488
|
});
|
|
10319
10489
|
}
|
|
10320
|
-
function desiredManifestSnippets(selectedIds, catalog, previousById) {
|
|
10490
|
+
function desiredManifestSnippets(selectedIds, catalog, previousById, heldUpdates) {
|
|
10321
10491
|
return selectedIds.flatMap((id) => {
|
|
10322
10492
|
const preserved = previousById.get(id);
|
|
10323
10493
|
if (preserved?.pinned === true) return [preserved];
|
|
10494
|
+
if (heldUpdates.has(id) && preserved !== void 0) return [preserved];
|
|
10324
10495
|
const entry = catalog.get(id);
|
|
10325
10496
|
if (entry?.status === "available") return [{
|
|
10326
10497
|
hash: hashManagedSnippet(entry.content),
|
|
@@ -10334,7 +10505,7 @@ function desiredManifestSnippets(selectedIds, catalog, previousById) {
|
|
|
10334
10505
|
//#endregion
|
|
10335
10506
|
//#region src/setup/skill-planner-links.ts
|
|
10336
10507
|
function sharedRoot(context) {
|
|
10337
|
-
return
|
|
10508
|
+
return sharedSkillsRoot(context.model.homeDir);
|
|
10338
10509
|
}
|
|
10339
10510
|
function planLinks(id, context, state) {
|
|
10340
10511
|
for (const app of managedApps(context)) for (const directory of app.skillDirectories ?? []) {
|
|
@@ -10377,10 +10548,6 @@ function removeSkillEntries(entries) {
|
|
|
10377
10548
|
function skillIdentity(source, id) {
|
|
10378
10549
|
return `${source}\0${id}`;
|
|
10379
10550
|
}
|
|
10380
|
-
function isStrictDescendant(root, candidate) {
|
|
10381
|
-
const difference = relative(resolve(root), resolve(candidate));
|
|
10382
|
-
return difference.length > 0 && difference !== ".." && !difference.startsWith(`..${sep}`) && !difference.startsWith(sep);
|
|
10383
|
-
}
|
|
10384
10551
|
//#endregion
|
|
10385
10552
|
//#region src/setup/skill-planner-policy.ts
|
|
10386
10553
|
/**
|
|
@@ -10400,56 +10567,28 @@ function disallowedSkillBlockers(selected, policy, sharedRoot) {
|
|
|
10400
10567
|
}));
|
|
10401
10568
|
}
|
|
10402
10569
|
//#endregion
|
|
10403
|
-
//#region src/setup/skill-planner.ts
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
const selected = context.selections.skills?.selected ?? previous.map(toSelection);
|
|
10407
|
-
const duplicate = duplicateLocalId(selected);
|
|
10408
|
-
if (duplicate !== void 0) return abandoned(previous, {
|
|
10409
|
-
path: sharedRoot(context),
|
|
10410
|
-
reason: `Skill "${duplicate}" was selected from more than one source. Choose one source for each installed skill ID.`
|
|
10411
|
-
});
|
|
10412
|
-
const disallowed = disallowedSkillBlockers(selected, context.skillCatalog.policy, sharedRoot(context));
|
|
10413
|
-
if (disallowed.length > 0) return {
|
|
10414
|
-
blockers: disallowed,
|
|
10415
|
-
manifestSkills: previous,
|
|
10416
|
-
manualSteps: [],
|
|
10417
|
-
operations: []
|
|
10418
|
-
};
|
|
10419
|
-
const state = {
|
|
10420
|
-
blockers: [],
|
|
10421
|
-
manifestSkills: [],
|
|
10422
|
-
manualSteps: [],
|
|
10423
|
-
operations: []
|
|
10424
|
-
};
|
|
10425
|
-
const catalog = new Map([...context.model.availableSkills ?? [], ...context.selections.skills?.resolved ?? []].map((skill) => [skillIdentity(skill.source.id, skill.id), skill]));
|
|
10426
|
-
const previousByIdentity = new Map(previous.map((skill) => [skillIdentity(skill.source, skill.id), skill]));
|
|
10427
|
-
const previousById = new Map(previous.map((skill) => [skill.id, skill]));
|
|
10428
|
-
const sharedById = new Map((context.model.sharedSkills ?? []).map((skill) => [skill.id, skill]));
|
|
10429
|
-
for (const selection of selected) reconcileSelection(selection, previousByIdentity.get(skillIdentity(selection.source, selection.id)), previousById.get(selection.id), catalog.get(skillIdentity(selection.source, selection.id)), sharedById.get(selection.id), context, state);
|
|
10430
|
-
const desiredIds = new Set(selected.map((selection) => selection.id));
|
|
10431
|
-
for (const skill of previous) if (!desiredIds.has(skill.id)) reconcileRemoval(skill, sharedById.get(skill.id), context, state);
|
|
10432
|
-
return {
|
|
10433
|
-
blockers: Object.freeze(state.blockers),
|
|
10434
|
-
manifestSkills: Object.freeze(state.manifestSkills),
|
|
10435
|
-
manualSteps: Object.freeze(state.manualSteps),
|
|
10436
|
-
operations: Object.freeze(state.operations)
|
|
10437
|
-
};
|
|
10438
|
-
}
|
|
10439
|
-
function abandoned(previous, blocker) {
|
|
10570
|
+
//#region src/setup/skill-planner-reconcile.ts
|
|
10571
|
+
/** The manifest row one resolved pack becomes. */
|
|
10572
|
+
function manifestSkill(skill) {
|
|
10440
10573
|
return {
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
|
|
10574
|
+
id: skill.id,
|
|
10575
|
+
pinned: false,
|
|
10576
|
+
source: skill.source.id,
|
|
10577
|
+
treeHash: skill.treeHash,
|
|
10578
|
+
version: skill.version
|
|
10445
10579
|
};
|
|
10446
10580
|
}
|
|
10447
|
-
function reconcileSelection(selection, previousIdentity, previousAtId,
|
|
10581
|
+
function reconcileSelection(selection, previousIdentity, previousAtId, offered, shared, context, state) {
|
|
10448
10582
|
const previous = previousIdentity ?? previousAtId;
|
|
10449
10583
|
if (previous?.pinned === true && previousIdentity !== void 0) {
|
|
10450
|
-
preserveOrRestore(previous, available, shared, context, state);
|
|
10584
|
+
preserveOrRestore(previous, offered.available, shared, context, state);
|
|
10451
10585
|
return;
|
|
10452
10586
|
}
|
|
10587
|
+
if (offered.held && previousIdentity !== void 0) {
|
|
10588
|
+
keepRecordedRevision(previousIdentity, offered.available, shared, context, state);
|
|
10589
|
+
return;
|
|
10590
|
+
}
|
|
10591
|
+
const available = offered.available;
|
|
10453
10592
|
if (available === void 0) {
|
|
10454
10593
|
if (previousIdentity !== void 0) preserveOrRestore(previousIdentity, void 0, shared, context, state);
|
|
10455
10594
|
else state.blockers.push({
|
|
@@ -10476,6 +10615,23 @@ function reconcileSelection(selection, previousIdentity, previousAtId, available
|
|
|
10476
10615
|
if (shared?.treeHash !== desired.treeHash) planTreeReplacement(shared, available, context, state.operations);
|
|
10477
10616
|
planLinks(selection.id, context, state);
|
|
10478
10617
|
}
|
|
10618
|
+
/**
|
|
10619
|
+
* Keeps a skill at the revision the manifest records after its update went unreviewed.
|
|
10620
|
+
*
|
|
10621
|
+
* A missing shared tree is the one case that cannot simply be left alone: the recorded revision is
|
|
10622
|
+
* gone from disk and the only pack on hand is the newer one the user declined, so the step says
|
|
10623
|
+
* exactly that instead of reporting the skill as unavailable or as pinned.
|
|
10624
|
+
*/
|
|
10625
|
+
function keepRecordedRevision(previous, available, shared, context, state) {
|
|
10626
|
+
state.manifestSkills.push(previous);
|
|
10627
|
+
state.notices.push({
|
|
10628
|
+
kind: "held",
|
|
10629
|
+
message: `${previous.id} stays at ${previous.version}; ${available?.version ?? "a new revision"} is available. Run setup interactively to review it.`
|
|
10630
|
+
});
|
|
10631
|
+
if (shared === void 0) state.manualSteps.push(`Skill "${previous.id}" stays at version ${previous.version}, but its files are missing from ${join(sharedRoot(context), previous.id)}. Re-run setup and accept version ${available?.version ?? "the available revision"}, or reinstall the recorded revision from its source.`);
|
|
10632
|
+
else if (shared.treeHash !== previous.treeHash) state.manualSteps.push(`Review the local edits in ${shared.path} before updating skill "${previous.id}"; Aura left its files and provenance unchanged.`);
|
|
10633
|
+
planLinks(previous.id, context, state);
|
|
10634
|
+
}
|
|
10479
10635
|
function preserveOrRestore(previous, available, shared, context, state) {
|
|
10480
10636
|
state.manifestSkills.push(previous);
|
|
10481
10637
|
if (shared === void 0) {
|
|
@@ -10495,13 +10651,67 @@ function reconcileRemoval(previous, shared, context, state) {
|
|
|
10495
10651
|
}
|
|
10496
10652
|
function planTreeReplacement(shared, desired, context, operations) {
|
|
10497
10653
|
const root = join(sharedRoot(context), desired.id);
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10654
|
+
operations.push(...planSharedSkillTreeUpdate(root, shared?.entries ?? [], desired));
|
|
10655
|
+
}
|
|
10656
|
+
//#endregion
|
|
10657
|
+
//#region src/setup/skill-planner.ts
|
|
10658
|
+
function planSkills(context) {
|
|
10659
|
+
const previous = context.manifest.status === "ready" ? context.manifest.value.skills : [];
|
|
10660
|
+
const selected = context.selections.skills?.selected ?? previous.map(toSelection);
|
|
10661
|
+
const duplicate = duplicateLocalId(selected);
|
|
10662
|
+
if (duplicate !== void 0) return abandoned(previous, {
|
|
10663
|
+
path: sharedRoot(context),
|
|
10664
|
+
reason: `Skill "${duplicate}" was selected from more than one source. Choose one source for each installed skill ID.`
|
|
10665
|
+
});
|
|
10666
|
+
const disallowed = disallowedSkillBlockers(selected, context.skillCatalog.policy, sharedRoot(context));
|
|
10667
|
+
if (disallowed.length > 0) return {
|
|
10668
|
+
blockers: disallowed,
|
|
10669
|
+
manifestSkills: previous,
|
|
10670
|
+
manualSteps: [],
|
|
10671
|
+
notices: [],
|
|
10672
|
+
operations: []
|
|
10673
|
+
};
|
|
10674
|
+
const state = {
|
|
10675
|
+
blockers: [],
|
|
10676
|
+
manifestSkills: [],
|
|
10677
|
+
manualSteps: [],
|
|
10678
|
+
notices: [],
|
|
10679
|
+
operations: []
|
|
10680
|
+
};
|
|
10681
|
+
const catalog = new Map([...context.model.availableSkills ?? [], ...context.selections.skills?.resolved ?? []].map((skill) => [skillIdentity(skill.source.id, skill.id), skill]));
|
|
10682
|
+
const previousByIdentity = new Map(previous.map((skill) => [skillIdentity(skill.source, skill.id), skill]));
|
|
10683
|
+
const previousById = new Map(previous.map((skill) => [skill.id, skill]));
|
|
10684
|
+
const acceptedUpdates = new Set(context.selections.skills?.updates ?? []);
|
|
10685
|
+
const sharedById = new Map((context.model.sharedSkills ?? []).map((skill) => [skill.id, skill]));
|
|
10686
|
+
for (const selection of selected) {
|
|
10687
|
+
const identity = skillIdentity(selection.source, selection.id);
|
|
10688
|
+
const previousAtIdentity = previousByIdentity.get(identity);
|
|
10689
|
+
reconcileSelection(selection, previousAtIdentity, previousById.get(selection.id), offeredRevision(previousAtIdentity, catalog.get(identity), acceptedUpdates.has(identity)), sharedById.get(selection.id), context, state);
|
|
10690
|
+
}
|
|
10691
|
+
const desiredIds = new Set(selected.map((selection) => selection.id));
|
|
10692
|
+
for (const skill of previous) if (!desiredIds.has(skill.id)) reconcileRemoval(skill, sharedById.get(skill.id), context, state);
|
|
10693
|
+
return {
|
|
10694
|
+
blockers: Object.freeze(state.blockers),
|
|
10695
|
+
manifestSkills: Object.freeze(state.manifestSkills),
|
|
10696
|
+
manualSteps: Object.freeze(state.manualSteps),
|
|
10697
|
+
notices: Object.freeze(state.notices),
|
|
10698
|
+
operations: Object.freeze(state.operations)
|
|
10699
|
+
};
|
|
10700
|
+
}
|
|
10701
|
+
function offeredRevision(previous, available, accepted) {
|
|
10702
|
+
return {
|
|
10703
|
+
available,
|
|
10704
|
+
held: previous !== void 0 && available !== void 0 && previous.treeHash !== available.treeHash && !accepted
|
|
10705
|
+
};
|
|
10706
|
+
}
|
|
10707
|
+
function abandoned(previous, blocker) {
|
|
10708
|
+
return {
|
|
10709
|
+
blockers: [blocker],
|
|
10710
|
+
manifestSkills: previous,
|
|
10711
|
+
manualSteps: [],
|
|
10712
|
+
notices: [],
|
|
10713
|
+
operations: []
|
|
10714
|
+
};
|
|
10505
10715
|
}
|
|
10506
10716
|
function duplicateLocalId(selected) {
|
|
10507
10717
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -10510,15 +10720,6 @@ function duplicateLocalId(selected) {
|
|
|
10510
10720
|
seen.add(skill.id);
|
|
10511
10721
|
}
|
|
10512
10722
|
}
|
|
10513
|
-
function manifestSkill(skill) {
|
|
10514
|
-
return {
|
|
10515
|
-
id: skill.id,
|
|
10516
|
-
pinned: false,
|
|
10517
|
-
source: skill.source.id,
|
|
10518
|
-
treeHash: skill.treeHash,
|
|
10519
|
-
version: skill.version
|
|
10520
|
-
};
|
|
10521
|
-
}
|
|
10522
10723
|
function toSelection(skill) {
|
|
10523
10724
|
return {
|
|
10524
10725
|
id: skill.id,
|
|
@@ -10546,7 +10747,7 @@ function planSetup(context) {
|
|
|
10546
10747
|
blockers.push(...snippetPlan.blockers);
|
|
10547
10748
|
const skillPlan = planSkills(context);
|
|
10548
10749
|
blockers.push(...skillPlan.blockers);
|
|
10549
|
-
const mcpPlan = planSetupMcp(context, desiredManifest(context.manifest, apps, context.appCatalog, instructionPlan.ownership, context.selections.snippets === void 0 ? void 0 : snippetPlan.manifestSnippets, skillPlan.manifestSkills));
|
|
10750
|
+
const mcpPlan = planSetupMcp(context, desiredManifest(context.manifest, apps, context.appCatalog, instructionPlan.ownership, context.selections.snippets === void 0 ? void 0 : snippetPlan.manifestSnippets, skillPlan.manifestSkills, context));
|
|
10550
10751
|
blockers.push(...mcpPlan.blockers);
|
|
10551
10752
|
const manifest = mcpPlan.manifest;
|
|
10552
10753
|
const baseline = context.selections.baseline;
|
|
@@ -10566,7 +10767,11 @@ function planSetup(context) {
|
|
|
10566
10767
|
return Object.freeze({
|
|
10567
10768
|
blockers: Object.freeze(blockers),
|
|
10568
10769
|
manifest,
|
|
10569
|
-
notices:
|
|
10770
|
+
notices: [
|
|
10771
|
+
...snippetPlan.notices,
|
|
10772
|
+
...skillPlan.notices,
|
|
10773
|
+
...presetPolicyNotices(context)
|
|
10774
|
+
],
|
|
10570
10775
|
plan: Object.freeze({
|
|
10571
10776
|
manualSteps: Object.freeze([
|
|
10572
10777
|
...appManualSteps(context),
|
|
@@ -10579,24 +10784,28 @@ function planSetup(context) {
|
|
|
10579
10784
|
})
|
|
10580
10785
|
});
|
|
10581
10786
|
}
|
|
10582
|
-
function desiredManifest(state, apps, appCatalog, ownershipUpdates, snippets, skills) {
|
|
10787
|
+
function desiredManifest(state, apps, appCatalog, ownershipUpdates, snippets, skills, context) {
|
|
10583
10788
|
const base = state.status === "ready" ? state.value : createEmptyAuraManifest();
|
|
10584
10789
|
const ownership = desiredOwnership(base, ownershipUpdates);
|
|
10585
10790
|
const withSkills = {
|
|
10586
10791
|
...apps === void 0 ? {
|
|
10587
10792
|
...base,
|
|
10588
10793
|
ownership
|
|
10589
|
-
} : {
|
|
10794
|
+
} : withIgnoredAppSelections({
|
|
10590
10795
|
...base,
|
|
10591
10796
|
apps: desiredApps(base, apps, appCatalog),
|
|
10592
10797
|
ownership
|
|
10593
|
-
},
|
|
10798
|
+
}, base, apps, appCatalog),
|
|
10594
10799
|
skills
|
|
10595
10800
|
};
|
|
10596
|
-
|
|
10801
|
+
const withSnippets = snippets === void 0 ? withSkills : {
|
|
10597
10802
|
...withSkills,
|
|
10598
10803
|
snippets
|
|
10599
10804
|
};
|
|
10805
|
+
return context.preset?.explicit === true ? {
|
|
10806
|
+
...withSnippets,
|
|
10807
|
+
preset: context.preset.reference
|
|
10808
|
+
} : withSnippets;
|
|
10600
10809
|
}
|
|
10601
10810
|
function planSetupSnippets(context, instructionOperations) {
|
|
10602
10811
|
return planSnippets(context, plannedSharedContent(instructionOperations, context.model.sharedInstructions.path) ?? context.model.sharedInstructions.content ?? "");
|
|
@@ -10659,35 +10868,6 @@ function desiredApps(base, apps, appCatalog) {
|
|
|
10659
10868
|
for (const id of managed) if (!next.has(id)) next.set(id, { managed: true });
|
|
10660
10869
|
return Object.fromEntries(next);
|
|
10661
10870
|
}
|
|
10662
|
-
/**
|
|
10663
|
-
* Summary lines the plan cannot express as file operations: installing an app Aura does not
|
|
10664
|
-
* install itself, and what unchecking a previously-managed app does — the manifest flips to
|
|
10665
|
-
* `managed: false` while the app's own configuration stays in place (ownership cleanup is a later
|
|
10666
|
-
* milestone).
|
|
10667
|
-
*/
|
|
10668
|
-
function appManualSteps(context) {
|
|
10669
|
-
const apps = context.selections.apps;
|
|
10670
|
-
if (apps === void 0) return [];
|
|
10671
|
-
return [...installSteps(context, apps.managed), ...stopManagingSteps(context, apps.managed)];
|
|
10672
|
-
}
|
|
10673
|
-
function installSteps(context, managed) {
|
|
10674
|
-
const steps = [];
|
|
10675
|
-
for (const id of managed) {
|
|
10676
|
-
const entry = context.appCatalog.find((candidate) => catalogEntryId(candidate) === id);
|
|
10677
|
-
if (entry?.kind === "undetected") steps.push(entry.installHint === void 0 ? `Install ${entry.displayName} — its adapter provides no install instructions.` : `Install ${entry.displayName}: ${entry.installHint}`);
|
|
10678
|
-
}
|
|
10679
|
-
return steps;
|
|
10680
|
-
}
|
|
10681
|
-
function stopManagingSteps(context, managed) {
|
|
10682
|
-
const previous = context.manifest.status === "ready" ? context.manifest.value.apps : {};
|
|
10683
|
-
const managedIds = new Set(managed);
|
|
10684
|
-
const steps = [];
|
|
10685
|
-
for (const [id, app] of Object.entries(previous)) {
|
|
10686
|
-
const entry = context.appCatalog.find((candidate) => catalogEntryId(candidate) === id);
|
|
10687
|
-
if (entry !== void 0 && app.managed && !managedIds.has(id)) steps.push(`Aura stops managing ${catalogEntryName(entry)}; its existing configuration is left in place.`);
|
|
10688
|
-
}
|
|
10689
|
-
return steps;
|
|
10690
|
-
}
|
|
10691
10871
|
//#endregion
|
|
10692
10872
|
//#region src/setup/summary.ts
|
|
10693
10873
|
/** The already-converged variant of the summary: nothing to confirm, at most context to show. */
|
|
@@ -10711,14 +10891,22 @@ function renderSetupSummary(preview, blockers, notices, withDetail, output) {
|
|
|
10711
10891
|
if (!withDetail && preview.changedOperationCount > 0) output.write("\nRe-run with --detail to see the full diff of every change.\n");
|
|
10712
10892
|
}
|
|
10713
10893
|
const NOTICE_HEADINGS = Object.freeze({
|
|
10894
|
+
held: "Managed content kept at its recorded revision:",
|
|
10714
10895
|
overwritten: "Hand edits Aura is replacing:",
|
|
10896
|
+
policy: "Effective check policy:",
|
|
10715
10897
|
preserved: "Preserved content Aura does not own:"
|
|
10716
10898
|
});
|
|
10717
10899
|
function renderNotices(notices, output) {
|
|
10718
|
-
for (const kind of [
|
|
10900
|
+
for (const kind of [
|
|
10901
|
+
"overwritten",
|
|
10902
|
+
"preserved",
|
|
10903
|
+
"held",
|
|
10904
|
+
"policy"
|
|
10905
|
+
]) {
|
|
10719
10906
|
const matching = notices.filter((notice) => notice.kind === kind);
|
|
10720
10907
|
if (matching.length === 0) continue;
|
|
10721
|
-
|
|
10908
|
+
const heading = matching.find((notice) => notice.heading !== void 0)?.heading;
|
|
10909
|
+
output.write(`\n${safe(heading ?? NOTICE_HEADINGS[kind])}\n`);
|
|
10722
10910
|
for (const notice of matching) output.write(` · ${safe(notice.message)}\n`);
|
|
10723
10911
|
}
|
|
10724
10912
|
}
|
|
@@ -11080,13 +11268,13 @@ function requiringPreset(preset, config) {
|
|
|
11080
11268
|
* rest of the run, so a plugin cannot hand us an arbitrarily large one.
|
|
11081
11269
|
*/
|
|
11082
11270
|
const MAX_SNIPPET_BYTES = 65536;
|
|
11083
|
-
function createSnippetCatalog(snippets, manifest) {
|
|
11271
|
+
function createSnippetCatalog(snippets, manifest, presetIds = []) {
|
|
11084
11272
|
let resolved = [];
|
|
11085
11273
|
let pending;
|
|
11086
11274
|
return {
|
|
11087
11275
|
entries: () => resolved,
|
|
11088
11276
|
load: () => {
|
|
11089
|
-
pending ??= resolveSnippetCatalog(snippets, manifest).then((entries) => {
|
|
11277
|
+
pending ??= resolveSnippetCatalog(snippets, manifest, presetIds).then((entries) => {
|
|
11090
11278
|
resolved = entries;
|
|
11091
11279
|
return entries;
|
|
11092
11280
|
});
|
|
@@ -11095,19 +11283,32 @@ function createSnippetCatalog(snippets, manifest) {
|
|
|
11095
11283
|
};
|
|
11096
11284
|
}
|
|
11097
11285
|
/** Resolves every registry source once and retains manifest-only selections as unavailable rows. */
|
|
11098
|
-
async function resolveSnippetCatalog(snippets, manifest) {
|
|
11286
|
+
async function resolveSnippetCatalog(snippets, manifest, presetIds = []) {
|
|
11099
11287
|
const registered = new Set(snippets.map((snippet) => snippet.id));
|
|
11100
11288
|
const resolved = await Promise.all(snippets.map(resolveSnippet));
|
|
11101
11289
|
const previous = manifest.status === "ready" ? manifest.value.snippets : [];
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11290
|
+
const previousIds = new Set(previous.map((snippet) => snippet.id));
|
|
11291
|
+
return Object.freeze([
|
|
11292
|
+
...resolved,
|
|
11293
|
+
...previous.filter((snippet) => !registered.has(snippet.id)).map((snippet) => Object.freeze({
|
|
11294
|
+
category: "general",
|
|
11295
|
+
description: "Previously selected, but no installed plugin currently provides it.",
|
|
11296
|
+
id: snippet.id,
|
|
11297
|
+
name: snippet.id,
|
|
11298
|
+
reason: "The contributing plugin is unavailable.",
|
|
11299
|
+
status: "unavailable",
|
|
11300
|
+
version: snippet.version
|
|
11301
|
+
})),
|
|
11302
|
+
...presetIds.filter((id) => !registered.has(id) && !previousIds.has(id)).map((id) => Object.freeze({
|
|
11303
|
+
category: "general",
|
|
11304
|
+
description: "Selected by the active team preset, but unavailable in this build.",
|
|
11305
|
+
id,
|
|
11306
|
+
name: id,
|
|
11307
|
+
reason: "No installed plugin provides this preset selection.",
|
|
11308
|
+
status: "unavailable",
|
|
11309
|
+
version: "unknown"
|
|
11310
|
+
}))
|
|
11311
|
+
]);
|
|
11111
11312
|
}
|
|
11112
11313
|
async function resolveSnippet(snippet) {
|
|
11113
11314
|
const common = {
|
|
@@ -11160,7 +11361,7 @@ function errorMessage(error) {
|
|
|
11160
11361
|
const appsStep = {
|
|
11161
11362
|
gather: async (context, io) => {
|
|
11162
11363
|
const catalog = context.appCatalog;
|
|
11163
|
-
if (catalog.length === 0) return emptyCatalogOutcome$
|
|
11364
|
+
if (catalog.length === 0) return emptyCatalogOutcome$2(context, io);
|
|
11164
11365
|
const previouslyManaged = previouslyManagedIds(context);
|
|
11165
11366
|
const detectedCount = detectedAppCount(catalog);
|
|
11166
11367
|
if (context.revisited !== true) emitBanner(catalog, detectedCount, io);
|
|
@@ -11174,21 +11375,21 @@ const appsStep = {
|
|
|
11174
11375
|
prompt: "Which applications should Aura manage?"
|
|
11175
11376
|
}, io, detectedCount > 0 || previouslyManaged.size > 0);
|
|
11176
11377
|
if (selected === SETUP_ABORTED || selected === SETUP_BACK) return selected;
|
|
11177
|
-
return withApps(context, selected);
|
|
11378
|
+
return withApps(context, selected, catalog);
|
|
11178
11379
|
},
|
|
11179
11380
|
compactTitle: "Apps",
|
|
11180
11381
|
id: "apps",
|
|
11181
11382
|
title: "Applications"
|
|
11182
11383
|
};
|
|
11183
11384
|
/** With no adapters registered there is nothing to ask; ← keeps walking, → passes through. */
|
|
11184
|
-
function emptyCatalogOutcome$
|
|
11385
|
+
function emptyCatalogOutcome$2(context, io) {
|
|
11185
11386
|
if (context.enteredBackward === true) return SETUP_BACK;
|
|
11186
11387
|
if (context.revisited !== true) io.note("No agent application adapters are registered.");
|
|
11187
11388
|
return context.selections;
|
|
11188
11389
|
}
|
|
11189
11390
|
/** A re-entered step shows the answer this run already gave it, not its cold-start proposal. */
|
|
11190
11391
|
function initialSelection(context, catalog, previouslyManaged) {
|
|
11191
|
-
return context.selections.apps?.managed ?? catalog.filter((entry) => entry.kind === "detected"
|
|
11392
|
+
return context.selections.apps?.managed ?? catalog.filter((entry) => previouslyManaged.has(catalogEntryId(entry)) || entry.kind === "detected" && !ignoredAppIds(context).has(catalogEntryId(entry))).map(catalogEntryId);
|
|
11192
11393
|
}
|
|
11193
11394
|
function detectedAppCount(catalog) {
|
|
11194
11395
|
return catalog.filter((entry) => entry.kind === "detected").length;
|
|
@@ -11210,12 +11411,27 @@ async function askForSelection(question, io, mustConfirmEmpty) {
|
|
|
11210
11411
|
if (confirmation === "aborted") return SETUP_ABORTED;
|
|
11211
11412
|
}
|
|
11212
11413
|
}
|
|
11213
|
-
function withApps(context, managed) {
|
|
11414
|
+
function withApps(context, managed, catalog) {
|
|
11415
|
+
const selected = new Set(managed);
|
|
11416
|
+
const manifestApps = context.manifest.status === "ready" ? new Set(Object.keys(context.manifest.value.apps)) : /* @__PURE__ */ new Set();
|
|
11417
|
+
const preserved = ignoredAppIds(context);
|
|
11418
|
+
const ignored = catalog.flatMap((entry) => {
|
|
11419
|
+
const id = catalogEntryId(entry);
|
|
11420
|
+
if (selected.has(id)) return [];
|
|
11421
|
+
if (entry.kind === "detected" && !manifestApps.has(id)) return [id];
|
|
11422
|
+
return preserved.has(id) ? [id] : [];
|
|
11423
|
+
});
|
|
11214
11424
|
return {
|
|
11215
11425
|
...context.selections,
|
|
11216
|
-
apps: { managed }
|
|
11426
|
+
apps: ignored.length === 0 ? { managed } : {
|
|
11427
|
+
ignored,
|
|
11428
|
+
managed
|
|
11429
|
+
}
|
|
11217
11430
|
};
|
|
11218
11431
|
}
|
|
11432
|
+
function ignoredAppIds(context) {
|
|
11433
|
+
return new Set(context.manifest.status === "ready" ? context.manifest.value.ignoredApps ?? [] : []);
|
|
11434
|
+
}
|
|
11219
11435
|
function previouslyManagedIds(context) {
|
|
11220
11436
|
if (context.manifest.status !== "ready") return /* @__PURE__ */ new Set();
|
|
11221
11437
|
return new Set(Object.entries(context.manifest.value.apps).filter(([, app]) => app.managed).map(([id]) => id));
|
|
@@ -11965,7 +12181,6 @@ function appDisabledReason(entry, managed, supportedApps) {
|
|
|
11965
12181
|
//#endregion
|
|
11966
12182
|
//#region src/setup/steps/mcp.ts
|
|
11967
12183
|
const ADD_CUSTOM = "mcp:add-custom";
|
|
11968
|
-
/** Selects catalog and custom MCP servers, then captures their target name, scope, and apps. */
|
|
11969
12184
|
const mcpStep = {
|
|
11970
12185
|
addKind: "mcp",
|
|
11971
12186
|
compactTitle: "MCP",
|
|
@@ -11992,8 +12207,8 @@ async function gatherMcp(context, io) {
|
|
|
11992
12207
|
mcp: { servers: [] }
|
|
11993
12208
|
};
|
|
11994
12209
|
}
|
|
11995
|
-
|
|
11996
|
-
|
|
12210
|
+
const overriddenRequiredIds = new Set(context.selections.mcp?.overriddenRequiredIds ?? (context.manifest.status === "ready" ? context.manifest.value.overrides?.requiredMcpServers ?? [] : []));
|
|
12211
|
+
let selectedKeys = initialKeys(entries, context.interactive, overriddenRequiredIds);
|
|
11997
12212
|
for (;;) {
|
|
11998
12213
|
const picked = await pickServers(context, entries, selectedKeys, overriddenRequiredIds, io);
|
|
11999
12214
|
if (picked === SETUP_ABORTED || picked === SETUP_BACK) return picked;
|
|
@@ -12063,8 +12278,8 @@ function matchesEntry(server, entry) {
|
|
|
12063
12278
|
* sight is exactly the first-configuration the skills step refuses for the same reason. Left
|
|
12064
12279
|
* unchecked the requirement becomes a blocker naming the interactive run, never a silent skip.
|
|
12065
12280
|
*/
|
|
12066
|
-
function initialKeys(entries, interactive) {
|
|
12067
|
-
return new Set(entries.filter((entry) => entry.selectedServer !== void 0 || entry.required && interactive).map((entry) => entry.key));
|
|
12281
|
+
function initialKeys(entries, interactive, overriddenRequiredIds) {
|
|
12282
|
+
return new Set(entries.filter((entry) => entry.selectedServer !== void 0 || entry.required && interactive && (entry.catalog === void 0 || !overriddenRequiredIds.has(entry.catalog.id))).map((entry) => entry.key));
|
|
12068
12283
|
}
|
|
12069
12284
|
async function pickServers(context, entries, initial, overriddenRequiredIds, io) {
|
|
12070
12285
|
const question = {
|
|
@@ -12086,7 +12301,7 @@ async function pickServers(context, entries, initial, overriddenRequiredIds, io)
|
|
|
12086
12301
|
const selected = new Set(selectedValues(result["mcp-servers"]));
|
|
12087
12302
|
const missingRequired = reconcileRequiredOverrides(entries, selected, overriddenRequiredIds);
|
|
12088
12303
|
if (missingRequired.length === 0 || !context.interactive) return selected;
|
|
12089
|
-
const confirmation = await io.confirm(`Override the team preset
|
|
12304
|
+
const confirmation = await io.confirm(`Override the team preset on this machine and omit ${missingRequired.map(safe).join(", ")}?`);
|
|
12090
12305
|
if (confirmation === "accepted") {
|
|
12091
12306
|
for (const id of missingRequired) overriddenRequiredIds.add(id);
|
|
12092
12307
|
return selected;
|
|
@@ -12098,14 +12313,14 @@ function reconcileRequiredOverrides(entries, selected, overriddenRequiredIds) {
|
|
|
12098
12313
|
const requiredIds = requiredCatalogIds(entries);
|
|
12099
12314
|
const missing = requiredIds.filter((id) => !entries.some((entry) => entry.catalog?.id === id && selected.has(entry.key)));
|
|
12100
12315
|
for (const id of requiredIds) if (!missing.includes(id)) overriddenRequiredIds.delete(id);
|
|
12101
|
-
return missing;
|
|
12316
|
+
return missing.filter((id) => !overriddenRequiredIds.has(id));
|
|
12102
12317
|
}
|
|
12103
12318
|
function requiredCatalogIds(entries) {
|
|
12104
12319
|
return [...new Set(entries.flatMap((entry) => entry.required && entry.catalog !== void 0 ? [entry.catalog.id] : []))];
|
|
12105
12320
|
}
|
|
12106
12321
|
function serverOption(entry) {
|
|
12107
12322
|
const configured = entry.selectedServer !== void 0 || entry.existing !== void 0;
|
|
12108
|
-
const tags = [...entry.required ? ["preset required"] : [], ...configured ? [entry.catalog === void 0 ? "custom" : "configured"] : []];
|
|
12323
|
+
const tags = [...entry.required ? ["from preset", "required"] : [], ...configured ? [entry.catalog === void 0 ? "custom" : "configured"] : []];
|
|
12109
12324
|
const suffix = tags.length === 0 ? "" : ` (${tags.join(", ")})`;
|
|
12110
12325
|
return {
|
|
12111
12326
|
description: entry.sourceName === void 0 ? "Custom server" : `Plugin: ${entry.sourceName}`,
|
|
@@ -12229,6 +12444,7 @@ function pickerOptions(inputs) {
|
|
|
12229
12444
|
const unavailableById = new Map(inputs.listing.unavailableSources.map((source) => [source.id, source]));
|
|
12230
12445
|
const policy = inputs.catalog.policy;
|
|
12231
12446
|
const unsupported = !hasSkillsHome(inputs.managedApps);
|
|
12447
|
+
const preset = new Set(inputs.presetSkills.map((skill) => skillIdentity(skill.source, skill.id)));
|
|
12232
12448
|
const entryRows = sortForDisplay(inputs.listing.entries, (entry) => [
|
|
12233
12449
|
entry.sourceName,
|
|
12234
12450
|
entry.name,
|
|
@@ -12240,7 +12456,7 @@ function pickerOptions(inputs) {
|
|
|
12240
12456
|
disabledNote: "no selected app supports skills"
|
|
12241
12457
|
} : {},
|
|
12242
12458
|
group: entry.sourceName,
|
|
12243
|
-
label: entry.name
|
|
12459
|
+
label: `${entry.name}${preset.has(entry.identity) ? " (from preset)" : ""}`,
|
|
12244
12460
|
...entry.preview === void 0 ? {} : { preview: entry.preview },
|
|
12245
12461
|
value: entry.identity
|
|
12246
12462
|
}));
|
|
@@ -12272,17 +12488,92 @@ function pickerOptions(inputs) {
|
|
|
12272
12488
|
label: source.name,
|
|
12273
12489
|
value: `source:${source.id}`
|
|
12274
12490
|
}));
|
|
12491
|
+
const represented = /* @__PURE__ */ new Set([...inputs.listing.entries.map((entry) => entry.identity), ...inputs.manifestSkills.map((skill) => skillIdentity(skill.source, skill.id))]);
|
|
12492
|
+
const presetRows = inputs.presetSkills.filter((skill) => !represented.has(skillIdentity(skill.source, skill.id))).map((skill) => ({
|
|
12493
|
+
description: "Selected by the active team preset, but unavailable in this run.",
|
|
12494
|
+
disabled: true,
|
|
12495
|
+
disabledNote: "preset selection unavailable",
|
|
12496
|
+
group: skill.source,
|
|
12497
|
+
label: `${skill.id} (from preset)`,
|
|
12498
|
+
value: skillIdentity(skill.source, skill.id)
|
|
12499
|
+
}));
|
|
12275
12500
|
return [
|
|
12276
12501
|
...entryRows,
|
|
12277
12502
|
...manifestRows,
|
|
12503
|
+
...presetRows,
|
|
12278
12504
|
...sourceRows
|
|
12279
12505
|
];
|
|
12280
12506
|
}
|
|
12281
12507
|
//#endregion
|
|
12508
|
+
//#region src/setup/steps/skill-review.ts
|
|
12509
|
+
const REVIEW_PREFIX$1 = "review:";
|
|
12510
|
+
/**
|
|
12511
|
+
* Whether the source revision differs from the recorded one at all.
|
|
12512
|
+
*
|
|
12513
|
+
* A diverged revision — a rollback, or a pair this build cannot order — is reviewable for the same
|
|
12514
|
+
* reason an upgrade is: the planner holds the recorded revision either way, so a revision that is
|
|
12515
|
+
* never offered here is one the user can never resolve, and it would sit held with nothing on
|
|
12516
|
+
* screen naming it.
|
|
12517
|
+
*/
|
|
12518
|
+
function movedFrom(previous, available) {
|
|
12519
|
+
return managedContentRevisionStatus(previous.version, previous.treeHash, available.version, available.treeHash) !== "current";
|
|
12520
|
+
}
|
|
12521
|
+
/** No review for an unchanged manifest skill or a failed fetch; both preserve the prior entry. */
|
|
12522
|
+
function reviewQuestion$1(identity, decisions, resolution, recorded, entry, selection) {
|
|
12523
|
+
const pack = resolution.resolved.get(identity);
|
|
12524
|
+
const previous = recorded.get(identity);
|
|
12525
|
+
if (previous !== void 0 && (pack === void 0 || !movedFrom(previous, pack))) return [];
|
|
12526
|
+
const localId = entry?.id ?? selection?.id ?? identity;
|
|
12527
|
+
const change = revisionChange(previous, pack);
|
|
12528
|
+
return [{
|
|
12529
|
+
id: `${REVIEW_PREFIX$1}${identity}`,
|
|
12530
|
+
initial: [decisions[identity] ?? "skip"],
|
|
12531
|
+
kind: "select",
|
|
12532
|
+
label: `Review ${localId}`,
|
|
12533
|
+
options: [skipOption(change), installOption(localId, change, pack, entry, resolution.problems.get(identity))],
|
|
12534
|
+
prompt: reviewPrompt$1(localId, change, pack, entry)
|
|
12535
|
+
}];
|
|
12536
|
+
}
|
|
12537
|
+
const CHANGE_VERBS = Object.freeze({
|
|
12538
|
+
install: "Install",
|
|
12539
|
+
switch: "Switch to",
|
|
12540
|
+
update: "Update to"
|
|
12541
|
+
});
|
|
12542
|
+
function revisionChange(previous, pack) {
|
|
12543
|
+
if (previous === void 0) return "install";
|
|
12544
|
+
if (pack === void 0) return "update";
|
|
12545
|
+
return managedContentRevisionStatus(previous.version, previous.treeHash, pack.version, pack.treeHash) === "update" ? "update" : "switch";
|
|
12546
|
+
}
|
|
12547
|
+
function skipOption(change) {
|
|
12548
|
+
return {
|
|
12549
|
+
description: change === "install" ? "Leave this skill out of the plan." : "Keep the installed version and its manifest entry.",
|
|
12550
|
+
label: change === "install" ? "Skip — do not install" : "Skip — keep the installed version",
|
|
12551
|
+
value: "skip"
|
|
12552
|
+
};
|
|
12553
|
+
}
|
|
12554
|
+
function reviewPrompt$1(localId, change, pack, entry) {
|
|
12555
|
+
const sourceName = entry?.sourceName ?? "its directory";
|
|
12556
|
+
if (pack === void 0) return `"${localId}" could not be fetched from ${sourceName}.`;
|
|
12557
|
+
return `${CHANGE_VERBS[change]} "${pack.name}" ${pack.version} from ${sourceName}? Press p on the install row to read its full SKILL.md before deciding.`;
|
|
12558
|
+
}
|
|
12559
|
+
function installOption(localId, change, pack, entry, problem) {
|
|
12560
|
+
if (pack === void 0) return {
|
|
12561
|
+
description: problem ?? "This skill could not be fetched.",
|
|
12562
|
+
disabled: true,
|
|
12563
|
+
label: `Install ${localId}`,
|
|
12564
|
+
value: "install"
|
|
12565
|
+
};
|
|
12566
|
+
return {
|
|
12567
|
+
description: entry?.sourceUrl ?? entry?.sourceName ?? "",
|
|
12568
|
+
label: `${CHANGE_VERBS[change]} ${localId} ${pack.version}`,
|
|
12569
|
+
preview: pack.files.find((file) => file.path === "SKILL.md")?.content,
|
|
12570
|
+
value: "install"
|
|
12571
|
+
};
|
|
12572
|
+
}
|
|
12573
|
+
//#endregion
|
|
12282
12574
|
//#region src/setup/steps/skill-stages.ts
|
|
12283
|
-
const REVIEW_PREFIX = "review:";
|
|
12284
12575
|
function skillStages(inputs) {
|
|
12285
|
-
return [pickerStage(inputs), reviewStage(inputs)];
|
|
12576
|
+
return [pickerStage$1(inputs), reviewStage$1(inputs)];
|
|
12286
12577
|
}
|
|
12287
12578
|
/** A remote identity's install needs a fetch, and therefore the review boundary. */
|
|
12288
12579
|
function isRemoteIdentity(identity) {
|
|
@@ -12302,15 +12593,19 @@ function selectionsByIdentity(inputs) {
|
|
|
12302
12593
|
source: skill.source
|
|
12303
12594
|
});
|
|
12304
12595
|
}
|
|
12596
|
+
for (const skill of inputs.presetSkills) {
|
|
12597
|
+
const identity = skillIdentity(skill.source, skill.id);
|
|
12598
|
+
if (!selections.has(identity)) selections.set(identity, skill);
|
|
12599
|
+
}
|
|
12305
12600
|
return selections;
|
|
12306
12601
|
}
|
|
12307
12602
|
function manifestByIdentity(manifestSkills) {
|
|
12308
12603
|
return new Map(manifestSkills.map((skill) => [skillIdentity(skill.source, skill.id), skill]));
|
|
12309
12604
|
}
|
|
12310
|
-
function pickerStage(inputs) {
|
|
12605
|
+
function pickerStage$1(inputs) {
|
|
12311
12606
|
const supportsSkills = hasSkillsHome(inputs.managedApps);
|
|
12312
12607
|
const selectable = new Set(supportsSkills ? inputs.listing.entries.map((entry) => entry.identity) : []);
|
|
12313
|
-
const previous = new Set(inputs.manifestSkills.map((skill) => skillIdentity(skill.source, skill.id)));
|
|
12608
|
+
const previous = new Set([...inputs.manifestSkills, ...inputs.presetSkills].map((skill) => skillIdentity(skill.source, skill.id)));
|
|
12314
12609
|
return {
|
|
12315
12610
|
apply: (state, answers) => ({
|
|
12316
12611
|
...state,
|
|
@@ -12331,75 +12626,43 @@ function pickerStage(inputs) {
|
|
|
12331
12626
|
}
|
|
12332
12627
|
};
|
|
12333
12628
|
}
|
|
12334
|
-
function reviewStage(inputs) {
|
|
12629
|
+
function reviewStage$1(inputs) {
|
|
12335
12630
|
const recorded = manifestByIdentity(inputs.manifestSkills);
|
|
12336
12631
|
const offered = selectionsByIdentity(inputs);
|
|
12337
12632
|
const entries = new Map(inputs.listing.entries.map((entry) => [entry.identity, entry]));
|
|
12633
|
+
const bundled = bundledByIdentity(inputs.availableSkills);
|
|
12634
|
+
const reviewable = (identity) => needsReview(identity, recorded, bundled);
|
|
12338
12635
|
return {
|
|
12339
|
-
apply: (state, answers) => {
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
...state,
|
|
12344
|
-
decisions
|
|
12345
|
-
};
|
|
12346
|
-
},
|
|
12636
|
+
apply: (state, answers) => ({
|
|
12637
|
+
...state,
|
|
12638
|
+
decisions: foldDecisions(state.decisions, answers, REVIEW_PREFIX$1, "skip")
|
|
12639
|
+
}),
|
|
12347
12640
|
compactLabel: "Review",
|
|
12348
|
-
isApplicable: (state) => state.selected.some(
|
|
12641
|
+
isApplicable: (state) => state.selected.some(reviewable),
|
|
12349
12642
|
label: "Review",
|
|
12350
12643
|
questions: async (state) => {
|
|
12351
12644
|
const remote = state.selected.filter(isRemoteIdentity);
|
|
12352
|
-
if (remote.length === 0) return;
|
|
12353
12645
|
const resolution = await inputs.catalog.resolve(remote.flatMap((identity) => {
|
|
12354
12646
|
const selection = offered.get(identity);
|
|
12355
12647
|
return selection === void 0 ? [] : [selection];
|
|
12356
12648
|
}), inputs.approvedPrivateSourceIds);
|
|
12357
|
-
const
|
|
12649
|
+
const resolutionWithBundled = {
|
|
12650
|
+
problems: resolution.problems,
|
|
12651
|
+
resolved: new Map([...resolution.resolved, ...bundled])
|
|
12652
|
+
};
|
|
12653
|
+
const questions = state.selected.filter(reviewable).flatMap((identity) => reviewQuestion$1(identity, state.decisions, resolutionWithBundled, recorded, entries.get(identity), offered.get(identity)));
|
|
12358
12654
|
return questions.length === 0 ? void 0 : questions;
|
|
12359
12655
|
}
|
|
12360
12656
|
};
|
|
12361
12657
|
}
|
|
12362
|
-
|
|
12363
|
-
|
|
12364
|
-
const pack = resolution.resolved.get(identity);
|
|
12365
|
-
const previous = recorded.get(identity);
|
|
12366
|
-
if (previous !== void 0 && (pack === void 0 || pack.treeHash === previous.treeHash)) return [];
|
|
12367
|
-
const localId = entry?.id ?? selection?.id ?? identity;
|
|
12368
|
-
const update = previous !== void 0;
|
|
12369
|
-
return [{
|
|
12370
|
-
id: `${REVIEW_PREFIX}${identity}`,
|
|
12371
|
-
initial: [state.decisions[identity] ?? "skip"],
|
|
12372
|
-
kind: "select",
|
|
12373
|
-
label: `Review ${localId}`,
|
|
12374
|
-
options: [skipOption(update), installOption(localId, update, pack, entry, resolution.problems.get(identity))],
|
|
12375
|
-
prompt: reviewPrompt(localId, update, pack, entry)
|
|
12376
|
-
}];
|
|
12658
|
+
function bundledByIdentity(packs) {
|
|
12659
|
+
return new Map(packs.map((pack) => [skillIdentity(pack.source.id, pack.id), pack]));
|
|
12377
12660
|
}
|
|
12378
|
-
function
|
|
12379
|
-
return
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
};
|
|
12384
|
-
}
|
|
12385
|
-
function reviewPrompt(localId, update, pack, entry) {
|
|
12386
|
-
const sourceName = entry?.sourceName ?? "its directory";
|
|
12387
|
-
if (pack === void 0) return `"${localId}" could not be fetched from ${sourceName}.`;
|
|
12388
|
-
return `${update ? "Update" : "Install"} "${pack.name}" ${pack.version} from ${sourceName}? Press p on the install row to read its full SKILL.md before deciding.`;
|
|
12389
|
-
}
|
|
12390
|
-
function installOption(localId, update, pack, entry, problem) {
|
|
12391
|
-
if (pack === void 0) return {
|
|
12392
|
-
description: problem ?? "This skill could not be fetched.",
|
|
12393
|
-
disabled: true,
|
|
12394
|
-
label: `Install ${localId}`,
|
|
12395
|
-
value: "install"
|
|
12396
|
-
};
|
|
12397
|
-
return {
|
|
12398
|
-
description: entry?.sourceUrl ?? entry?.sourceName ?? "",
|
|
12399
|
-
label: `${update ? "Update to" : "Install"} ${localId} ${pack.version}`,
|
|
12400
|
-
preview: pack.files.find((file) => file.path === "SKILL.md")?.content,
|
|
12401
|
-
value: "install"
|
|
12402
|
-
};
|
|
12661
|
+
function needsReview(identity, recorded, bundled) {
|
|
12662
|
+
if (isRemoteIdentity(identity)) return true;
|
|
12663
|
+
const previous = recorded.get(identity);
|
|
12664
|
+
const available = bundled.get(identity);
|
|
12665
|
+
return previous !== void 0 && available !== void 0 && movedFrom(previous, available);
|
|
12403
12666
|
}
|
|
12404
12667
|
//#endregion
|
|
12405
12668
|
//#region src/setup/steps/skill-finalize.ts
|
|
@@ -12490,13 +12753,15 @@ async function gatherApprovedSkills(context, io, approval) {
|
|
|
12490
12753
|
const listing = await context.skillCatalog.load(approvedPrivateSourceIds);
|
|
12491
12754
|
const manifestSkills = recordedSkills(context);
|
|
12492
12755
|
emitNotes(context, io, listing, manifestSkills);
|
|
12493
|
-
if (isEmptyCatalog(listing, manifestSkills)) return emptyCatalogOutcome(context);
|
|
12756
|
+
if (isEmptyCatalog(listing, manifestSkills)) return emptyCatalogOutcome$1(context);
|
|
12494
12757
|
const inputs = {
|
|
12495
12758
|
approvedPrivateSourceIds,
|
|
12759
|
+
availableSkills: context.model.availableSkills ?? [],
|
|
12496
12760
|
catalog: context.skillCatalog,
|
|
12497
12761
|
listing,
|
|
12498
12762
|
managedApps: managedSkillApps(context),
|
|
12499
|
-
manifestSkills
|
|
12763
|
+
manifestSkills,
|
|
12764
|
+
presetSkills: context.preset?.skills ?? []
|
|
12500
12765
|
};
|
|
12501
12766
|
const opening = openingSelection(context, inputs, manifestSkills);
|
|
12502
12767
|
noteHeldBack(inputs, io, opening.heldBack);
|
|
@@ -12505,7 +12770,7 @@ async function gatherApprovedSkills(context, io, approval) {
|
|
|
12505
12770
|
flow: context.flow
|
|
12506
12771
|
});
|
|
12507
12772
|
if (result === SETUP_ABORTED || result === SETUP_BACK) return result;
|
|
12508
|
-
return completedSelections(context, approval, await finalizeSkills(result, inputs, manifestSkills), manifestSkills);
|
|
12773
|
+
return completedSelections$1(context, approval, await finalizeSkills(result, inputs, manifestSkills), manifestSkills, result.decisions);
|
|
12509
12774
|
}
|
|
12510
12775
|
/**
|
|
12511
12776
|
* Says out loud that a changed Apps answer cleared picks the user had already made.
|
|
@@ -12522,13 +12787,15 @@ function noteHeldBack(inputs, io, heldBack) {
|
|
|
12522
12787
|
function recordedSkills(context) {
|
|
12523
12788
|
return context.manifest.status === "ready" ? context.manifest.value.skills : [];
|
|
12524
12789
|
}
|
|
12525
|
-
function emptyCatalogOutcome(context) {
|
|
12790
|
+
function emptyCatalogOutcome$1(context) {
|
|
12526
12791
|
return context.enteredBackward === true ? SETUP_BACK : { ...context.selections };
|
|
12527
12792
|
}
|
|
12528
12793
|
function chainEntry(context) {
|
|
12529
12794
|
return context.enteredBackward === true ? "end" : "start";
|
|
12530
12795
|
}
|
|
12531
|
-
function completedSelections(context, approval, skills, manifestSkills) {
|
|
12796
|
+
function completedSelections$1(context, approval, skills, manifestSkills, decisions) {
|
|
12797
|
+
const recorded = new Set(manifestSkills.map((skill) => skillIdentity(skill.source, skill.id)));
|
|
12798
|
+
const updates = Object.entries(decisions).filter(([identity, decision]) => decision === "install" && recorded.has(identity)).map(([identity]) => identity);
|
|
12532
12799
|
if (skills.selected.length === 0 && manifestSkills.length === 0) {
|
|
12533
12800
|
const { skills: _staleSkills, ...selections } = context.selections;
|
|
12534
12801
|
return approval.length === 0 ? selections : {
|
|
@@ -12539,10 +12806,14 @@ function completedSelections(context, approval, skills, manifestSkills) {
|
|
|
12539
12806
|
}
|
|
12540
12807
|
};
|
|
12541
12808
|
}
|
|
12809
|
+
const selection = updates.length === 0 ? skills : {
|
|
12810
|
+
...skills,
|
|
12811
|
+
updates
|
|
12812
|
+
};
|
|
12542
12813
|
return {
|
|
12543
12814
|
...context.selections,
|
|
12544
|
-
skills: approval.length === 0 ?
|
|
12545
|
-
...
|
|
12815
|
+
skills: approval.length === 0 ? selection : {
|
|
12816
|
+
...selection,
|
|
12546
12817
|
approvedPrivateSourceIds: approval
|
|
12547
12818
|
}
|
|
12548
12819
|
};
|
|
@@ -12582,47 +12853,152 @@ function isEmptyCatalog(listing, manifestSkills) {
|
|
|
12582
12853
|
/** The chain's opening state, plus whatever the current Apps answer forces it to leave out. */
|
|
12583
12854
|
function openingSelection(context, inputs, manifestSkills) {
|
|
12584
12855
|
const recorded = new Set(manifestSkills.map((skill) => skillIdentity(skill.source, skill.id)));
|
|
12585
|
-
const opening = initialSkillSelection(context.selections.skills?.selected.map((skill) => skillIdentity(skill.source, skill.id)) ?? [...recorded], recorded, inputs.managedApps);
|
|
12856
|
+
const opening = initialSkillSelection(context.selections.skills?.selected.map((skill) => skillIdentity(skill.source, skill.id)) ?? (context.manifest.status === "ready" ? [...recorded] : inputs.presetSkills.map((skill) => skillIdentity(skill.source, skill.id))), recorded, inputs.managedApps);
|
|
12586
12857
|
return {
|
|
12587
12858
|
heldBack: opening.heldBack,
|
|
12588
12859
|
state: {
|
|
12589
|
-
decisions:
|
|
12860
|
+
decisions: Object.fromEntries((context.selections.skills?.updates ?? []).map((identity) => [identity, "install"])),
|
|
12590
12861
|
selected: opening.selected
|
|
12591
12862
|
}
|
|
12592
12863
|
};
|
|
12593
12864
|
}
|
|
12594
12865
|
//#endregion
|
|
12866
|
+
//#region src/setup/steps/snippet-stages.ts
|
|
12867
|
+
const REVIEW_PREFIX = "snippet-update:";
|
|
12868
|
+
function snippetStages(inputs) {
|
|
12869
|
+
return [pickerStage(inputs), reviewStage(inputs)];
|
|
12870
|
+
}
|
|
12871
|
+
function snippetOptions(inputs) {
|
|
12872
|
+
const { catalog, preset, previous } = inputs;
|
|
12873
|
+
return sortForDisplay(catalog, (entry) => [
|
|
12874
|
+
entry.category,
|
|
12875
|
+
entry.name,
|
|
12876
|
+
entry.id
|
|
12877
|
+
]).map((entry) => entry.status === "available" ? {
|
|
12878
|
+
description: entry.description,
|
|
12879
|
+
group: entry.category,
|
|
12880
|
+
label: `${entry.name}${preset.has(entry.id) ? " (from preset)" : ""}`,
|
|
12881
|
+
preview: entry.content,
|
|
12882
|
+
value: entry.id
|
|
12883
|
+
} : {
|
|
12884
|
+
description: previous.has(entry.id) ? `${entry.reason} Clear it to remove the snippet.` : `${entry.description} ${entry.reason}`,
|
|
12885
|
+
disabled: true,
|
|
12886
|
+
group: entry.category,
|
|
12887
|
+
label: `${previous.has(entry.id) ? `${entry.name} (preserved)` : entry.name}${preset.has(entry.id) ? " (from preset)" : ""}`,
|
|
12888
|
+
value: entry.id
|
|
12889
|
+
});
|
|
12890
|
+
}
|
|
12891
|
+
/** The ids whose review answer accepted the available revision. */
|
|
12892
|
+
function acceptedUpdates(state) {
|
|
12893
|
+
return state.selected.filter((id) => state.decisions[id] === "update");
|
|
12894
|
+
}
|
|
12895
|
+
function pickerStage(inputs) {
|
|
12896
|
+
const options = snippetOptions(inputs);
|
|
12897
|
+
const selectable = new Set(options.filter((option) => option.disabled !== true).map((option) => option.value));
|
|
12898
|
+
return {
|
|
12899
|
+
apply: (state, answers) => ({
|
|
12900
|
+
...state,
|
|
12901
|
+
selected: selectedValues(answers["snippets"]).filter((id) => selectable.has(id) || inputs.retained.has(id))
|
|
12902
|
+
}),
|
|
12903
|
+
label: "Snippets",
|
|
12904
|
+
questions: (state) => options.length === 0 ? void 0 : [{
|
|
12905
|
+
id: "snippets",
|
|
12906
|
+
initial: state.selected,
|
|
12907
|
+
kind: "multiselect",
|
|
12908
|
+
label: "Snippets",
|
|
12909
|
+
options,
|
|
12910
|
+
prompt: "Which snippets should Aura add to the shared instructions?"
|
|
12911
|
+
}]
|
|
12912
|
+
};
|
|
12913
|
+
}
|
|
12914
|
+
function reviewStage(inputs) {
|
|
12915
|
+
const installed = installedById(inputs.context);
|
|
12916
|
+
const available = new Map(inputs.catalog.map((entry) => [entry.id, entry]));
|
|
12917
|
+
return {
|
|
12918
|
+
apply: (state, answers) => ({
|
|
12919
|
+
...state,
|
|
12920
|
+
decisions: foldDecisions(state.decisions, answers, REVIEW_PREFIX, "keep")
|
|
12921
|
+
}),
|
|
12922
|
+
compactLabel: "Review",
|
|
12923
|
+
label: "Review",
|
|
12924
|
+
questions: (state) => {
|
|
12925
|
+
const questions = state.selected.flatMap((id) => reviewQuestion(id, state, inputs, installed.get(id), available.get(id)));
|
|
12926
|
+
return questions.length === 0 ? void 0 : questions;
|
|
12927
|
+
}
|
|
12928
|
+
};
|
|
12929
|
+
}
|
|
12930
|
+
/**
|
|
12931
|
+
* The one form asking about a snippet whose source revision no longer matches the recorded one.
|
|
12932
|
+
*
|
|
12933
|
+
* A diverged revision — a rollback, or one this build cannot order — is offered too, because the
|
|
12934
|
+
* alternative is holding the snippet at its recorded revision forever with nothing on screen. Both
|
|
12935
|
+
* default to keeping what is installed, which is what makes a non-interactive run safe.
|
|
12936
|
+
*/
|
|
12937
|
+
function reviewQuestion(id, state, inputs, installed, available) {
|
|
12938
|
+
if (installed === void 0 || available?.status !== "available") return [];
|
|
12939
|
+
const status = managedContentRevisionStatus(installed.version, installed.hash, available.version, hashManagedSnippet(available.content));
|
|
12940
|
+
if (status === "current") return [];
|
|
12941
|
+
const preview = updatePreview(inputs.context, installed, available.content);
|
|
12942
|
+
return [{
|
|
12943
|
+
id: `${REVIEW_PREFIX}${id}`,
|
|
12944
|
+
initial: [state.decisions[id] ?? "keep"],
|
|
12945
|
+
kind: "select",
|
|
12946
|
+
label: `Review ${id}`,
|
|
12947
|
+
options: [{
|
|
12948
|
+
description: `Keep installed version ${installed.version}.`,
|
|
12949
|
+
label: "Keep installed version",
|
|
12950
|
+
value: "keep"
|
|
12951
|
+
}, {
|
|
12952
|
+
...preview === void 0 ? {
|
|
12953
|
+
disabled: true,
|
|
12954
|
+
disabledNote: "resolve MGD-001 first"
|
|
12955
|
+
} : { preview },
|
|
12956
|
+
description: `Review and install version ${available.version}.`,
|
|
12957
|
+
label: `${status === "update" ? "Update to" : "Switch to"} ${available.version}`,
|
|
12958
|
+
value: "update"
|
|
12959
|
+
}],
|
|
12960
|
+
prompt: reviewPrompt(id, status, installed.version, available.version)
|
|
12961
|
+
}];
|
|
12962
|
+
}
|
|
12963
|
+
function reviewPrompt(id, status, installedVersion, availableVersion) {
|
|
12964
|
+
return status === "update" ? `A newer source revision is available for ${id}.` : `The installed plugin offers ${id} at version ${availableVersion}, which is not newer than the recorded version ${installedVersion}. Aura keeps the recorded revision unless you switch to it here.`;
|
|
12965
|
+
}
|
|
12966
|
+
function installedById(context) {
|
|
12967
|
+
return new Map(context.manifest.status === "ready" ? context.manifest.value.snippets.map((snippet) => [snippet.id, snippet]) : []);
|
|
12968
|
+
}
|
|
12969
|
+
/** The diff shown on the update row, or nothing when local edits make it unsafe to offer. */
|
|
12970
|
+
function updatePreview(context, installed, availableContent) {
|
|
12971
|
+
const document = context.model.sharedInstructions;
|
|
12972
|
+
if (document.content === void 0) return;
|
|
12973
|
+
const parsed = readManagedBlock(document.content);
|
|
12974
|
+
if (parsed.status !== "present") return;
|
|
12975
|
+
const snippet = parsed.block.snippets.find((candidate) => candidate.id === installed.id);
|
|
12976
|
+
if (snippet === void 0 || !snippet.hashMatches || snippet.computedHash !== installed.hash) return;
|
|
12977
|
+
return diffManagedSnippet(document.path, snippet.content, availableContent);
|
|
12978
|
+
}
|
|
12979
|
+
//#endregion
|
|
12595
12980
|
//#region src/setup/steps/snippets.ts
|
|
12981
|
+
/**
|
|
12982
|
+
* The snippets step: a picker over every available snippet, then a review form per pending update.
|
|
12983
|
+
*
|
|
12984
|
+
* The two forms run as one chain so ← walks back from the review into the picker rather than out
|
|
12985
|
+
* of the step: leaving would discard both the boxes just ticked and every review already answered.
|
|
12986
|
+
* Every review defaults to keeping the installed version, which is what holds managed content at
|
|
12987
|
+
* its recorded revision under `--yes`.
|
|
12988
|
+
*/
|
|
12596
12989
|
const snippetsStep = {
|
|
12597
12990
|
addKind: "snippet",
|
|
12598
12991
|
gather: async (context, io) => {
|
|
12599
12992
|
const catalog = await context.snippetCatalog.load();
|
|
12600
|
-
const
|
|
12601
|
-
const options = snippetOptions(catalog, previous);
|
|
12993
|
+
const inputs = stageInputs(context, catalog);
|
|
12602
12994
|
if (context.revisited !== true) emitUnavailableNotes(catalog, io.note);
|
|
12603
|
-
if (
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
}
|
|
12611
|
-
const result = await io.ask([{
|
|
12612
|
-
id: "snippets",
|
|
12613
|
-
initial: context.selections.snippets?.selected ?? options.filter((option) => previous.has(option.value)).map((option) => option.value),
|
|
12614
|
-
kind: "multiselect",
|
|
12615
|
-
label: "Snippets",
|
|
12616
|
-
options,
|
|
12617
|
-
prompt: "Which snippets should Aura add to the shared instructions?"
|
|
12618
|
-
}]);
|
|
12619
|
-
if (result === "aborted") return SETUP_ABORTED;
|
|
12620
|
-
if (result === "back") return SETUP_BACK;
|
|
12621
|
-
const selected = selectedSnippetIds(options, previous, selectedValues(result["snippets"]));
|
|
12622
|
-
return {
|
|
12623
|
-
...context.selections,
|
|
12624
|
-
snippets: { selected }
|
|
12625
|
-
};
|
|
12995
|
+
if (snippetOptions(inputs).length === 0) return emptyCatalogOutcome(context, io);
|
|
12996
|
+
const result = await runFormChain(snippetStages(inputs), openingState(context, inputs), io, {
|
|
12997
|
+
entry: context.enteredBackward === true ? "end" : "start",
|
|
12998
|
+
flow: context.flow
|
|
12999
|
+
});
|
|
13000
|
+
if (result === SETUP_ABORTED || result === SETUP_BACK) return result;
|
|
13001
|
+
return completedSelections(context, result);
|
|
12626
13002
|
},
|
|
12627
13003
|
id: "snippets",
|
|
12628
13004
|
prerequisites: [{
|
|
@@ -12632,42 +13008,49 @@ const snippetsStep = {
|
|
|
12632
13008
|
}],
|
|
12633
13009
|
title: "Snippets"
|
|
12634
13010
|
};
|
|
13011
|
+
function stageInputs(context, catalog) {
|
|
13012
|
+
const previous = previousSnippetIds(context);
|
|
13013
|
+
const preset = new Set(context.preset?.snippets ?? []);
|
|
13014
|
+
return {
|
|
13015
|
+
catalog,
|
|
13016
|
+
context,
|
|
13017
|
+
preset,
|
|
13018
|
+
previous,
|
|
13019
|
+
retained: context.manifest.status === "ready" ? previous : preset
|
|
13020
|
+
};
|
|
13021
|
+
}
|
|
13022
|
+
/** The chain's opening state: this run's answers if it already has some, else the retained set. */
|
|
13023
|
+
function openingState(context, inputs) {
|
|
13024
|
+
const selected = context.selections.snippets?.selected ?? snippetOptions(inputs).filter((option) => inputs.retained.has(option.value)).map((option) => option.value);
|
|
13025
|
+
return {
|
|
13026
|
+
decisions: Object.fromEntries((context.selections.snippets?.updates ?? []).map((id) => [id, "update"])),
|
|
13027
|
+
selected
|
|
13028
|
+
};
|
|
13029
|
+
}
|
|
13030
|
+
function completedSelections(context, state) {
|
|
13031
|
+
const updates = acceptedUpdates(state);
|
|
13032
|
+
return {
|
|
13033
|
+
...context.selections,
|
|
13034
|
+
snippets: updates.length === 0 ? { selected: state.selected } : {
|
|
13035
|
+
selected: state.selected,
|
|
13036
|
+
updates
|
|
13037
|
+
}
|
|
13038
|
+
};
|
|
13039
|
+
}
|
|
13040
|
+
function emptyCatalogOutcome(context, io) {
|
|
13041
|
+
if (context.enteredBackward === true) return SETUP_BACK;
|
|
13042
|
+
if (context.revisited !== true) io.note("No snippets are available from the installed plugins.");
|
|
13043
|
+
return {
|
|
13044
|
+
...context.selections,
|
|
13045
|
+
snippets: { selected: [] }
|
|
13046
|
+
};
|
|
13047
|
+
}
|
|
12635
13048
|
function previousSnippetIds(context) {
|
|
12636
13049
|
return new Set(context.manifest.status === "ready" ? context.manifest.value.snippets.map((snippet) => snippet.id) : []);
|
|
12637
13050
|
}
|
|
12638
13051
|
function emitUnavailableNotes(catalog, note) {
|
|
12639
13052
|
for (const entry of catalog) if (entry.status === "unavailable") note(`Snippet ${entry.id} is unavailable: ${entry.reason}`);
|
|
12640
13053
|
}
|
|
12641
|
-
/**
|
|
12642
|
-
* Keeps what the user answered, in the order they were offered.
|
|
12643
|
-
*
|
|
12644
|
-
* An unavailable row cannot be checked, but one carried over from a previous run stays eligible so
|
|
12645
|
-
* clearing it is how a user drops a snippet whose plugin is gone. Re-adding it here instead would
|
|
12646
|
-
* make that selection permanent.
|
|
12647
|
-
*/
|
|
12648
|
-
function selectedSnippetIds(options, previous, answered) {
|
|
12649
|
-
const selectable = new Set(options.filter((option) => option.disabled !== true).map((option) => option.value));
|
|
12650
|
-
return answered.filter((id) => selectable.has(id) || previous.has(id));
|
|
12651
|
-
}
|
|
12652
|
-
function snippetOptions(catalog, previous) {
|
|
12653
|
-
return sortForDisplay(catalog, (entry) => [
|
|
12654
|
-
entry.category,
|
|
12655
|
-
entry.name,
|
|
12656
|
-
entry.id
|
|
12657
|
-
]).map((entry) => entry.status === "available" ? {
|
|
12658
|
-
description: entry.description,
|
|
12659
|
-
group: entry.category,
|
|
12660
|
-
label: entry.name,
|
|
12661
|
-
preview: entry.content,
|
|
12662
|
-
value: entry.id
|
|
12663
|
-
} : {
|
|
12664
|
-
description: previous.has(entry.id) ? `${entry.reason} Clear it to remove the snippet.` : `${entry.description} ${entry.reason}`,
|
|
12665
|
-
disabled: true,
|
|
12666
|
-
group: entry.category,
|
|
12667
|
-
label: previous.has(entry.id) ? `${entry.name} (preserved)` : entry.name,
|
|
12668
|
-
value: entry.id
|
|
12669
|
-
});
|
|
12670
|
-
}
|
|
12671
13054
|
//#endregion
|
|
12672
13055
|
//#region src/setup/steps/index.ts
|
|
12673
13056
|
/**
|
|
@@ -12747,6 +13130,7 @@ async function runSetup(request) {
|
|
|
12747
13130
|
presetOrigin: configured.presetOrigin,
|
|
12748
13131
|
registry: request.registry
|
|
12749
13132
|
});
|
|
13133
|
+
const preset = setupPresetContext(request, configured.config, configured.preset);
|
|
12750
13134
|
const stepContext = {
|
|
12751
13135
|
appCatalog: buildAppCatalog(request.registry.adapters, model, scan.skipped),
|
|
12752
13136
|
...initialFindings === void 0 ? {} : { findings: initialFindings },
|
|
@@ -12756,7 +13140,8 @@ async function runSetup(request) {
|
|
|
12756
13140
|
mcpCatalog: catalogs.mcpCatalog,
|
|
12757
13141
|
model: effectiveModel,
|
|
12758
13142
|
skillCatalog: catalogs.skillCatalog,
|
|
12759
|
-
snippetCatalog: createSnippetCatalog(request.registry.snippets, model.manifest)
|
|
13143
|
+
snippetCatalog: createSnippetCatalog(request.registry.snippets, model.manifest, preset?.snippets ?? []),
|
|
13144
|
+
...preset === void 0 ? {} : { preset }
|
|
12760
13145
|
};
|
|
12761
13146
|
let start = {
|
|
12762
13147
|
index: 0,
|
|
@@ -12791,6 +13176,32 @@ async function runSetup(request) {
|
|
|
12791
13176
|
manifest: ready.manifest
|
|
12792
13177
|
});
|
|
12793
13178
|
}
|
|
13179
|
+
function setupPresetContext(request, config, preset) {
|
|
13180
|
+
const selected = config.preset;
|
|
13181
|
+
if (selected === void 0 || preset === void 0) return;
|
|
13182
|
+
return Object.freeze({
|
|
13183
|
+
checkSummary: presetCheckSummary(config),
|
|
13184
|
+
explicit: request.cliReference !== void 0,
|
|
13185
|
+
name: selected.name,
|
|
13186
|
+
reference: request.cliReference ?? selected.reference,
|
|
13187
|
+
skills: Object.freeze([...preset.skills ?? []]),
|
|
13188
|
+
snippets: Object.freeze([...preset.snippets ?? []])
|
|
13189
|
+
});
|
|
13190
|
+
}
|
|
13191
|
+
function presetCheckSummary(config) {
|
|
13192
|
+
const lines = [];
|
|
13193
|
+
for (const [id, check] of Object.entries(config.checks)) {
|
|
13194
|
+
if (check.enabled.provenance.layer === "preset") lines.push(`${id}: ${check.enabled.value ? "enabled" : "disabled"}`);
|
|
13195
|
+
if (check.severity.provenance.layer === "preset") lines.push(`${id}: severity ${check.severity.value}`);
|
|
13196
|
+
if (check.thresholds.provenance.layer === "preset") lines.push(`${id}: ${formatThresholds(check.thresholds.value)}`);
|
|
13197
|
+
}
|
|
13198
|
+
return Object.freeze(lines);
|
|
13199
|
+
}
|
|
13200
|
+
/** Thresholds read as settings, not as the JSON they happen to be stored in. */
|
|
13201
|
+
function formatThresholds(thresholds) {
|
|
13202
|
+
const pairs = Object.entries(thresholds).map(([name, value]) => `${name}=${typeof value === "object" ? JSON.stringify(value) : String(value)}`);
|
|
13203
|
+
return pairs.length === 0 ? "no thresholds" : pairs.join(", ");
|
|
13204
|
+
}
|
|
12794
13205
|
//#endregion
|
|
12795
13206
|
//#region src/setup/wizard-scripted.ts
|
|
12796
13207
|
/**
|