@tryaura/aura-cli 0.1.0 → 0.2.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/README.md +3 -2
- package/dist/bin/aura.js +4 -3
- package/dist/index.d.ts +12 -2
- package/dist/index.js +2 -2
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/{plugins-n3DS8XXi.js → plugins-SZegBVXF.js} +354 -108
- package/dist/{run-pMPxJqFQ.js → run.boundary-BtMqQAEQ.js} +4004 -1351
- package/dist/{skill-deployment-plan-C4GTrVSy.js → shared-link-plan-DVgOyqP9.js} +280 -92
- package/dist/{types-DmBu6g0d.d.ts → types--E7zqKfw.d.ts} +4 -2
- package/package.json +6 -6
- package/schema/check-output-v1.schema.json +18 -1
|
@@ -3,7 +3,28 @@ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
|
3
3
|
import { Buffer, isUtf8 } from "node:buffer";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { createTwoFilesPatch } from "diff";
|
|
6
|
+
import { gt, valid } from "semver";
|
|
6
7
|
import { isDeepStrictEqual } from "node:util";
|
|
8
|
+
//#region ../core/src/display-path.ts
|
|
9
|
+
/**
|
|
10
|
+
* Names a path the way a user can address it from where they ran the command.
|
|
11
|
+
*
|
|
12
|
+
* Project-relative first, then `~/`, then the path unchanged. Every renderer goes through this so
|
|
13
|
+
* one report cannot show the same file two ways — a check that bakes a path into its message and
|
|
14
|
+
* the CLI that prints that finding's locations must agree, or the two lines read as two files.
|
|
15
|
+
*/
|
|
16
|
+
function displayPath(path, roots) {
|
|
17
|
+
const project = pathInside(roots.projectRoot ?? roots.cwd, path);
|
|
18
|
+
if (project !== void 0) return project;
|
|
19
|
+
const home = pathInside(roots.homeDir, path);
|
|
20
|
+
return home === void 0 ? path : `~/${home}`;
|
|
21
|
+
}
|
|
22
|
+
function pathInside(root, path) {
|
|
23
|
+
const difference = relative(root, path);
|
|
24
|
+
if (difference.length === 0 || difference === ".." || difference.startsWith(`..${sep}`) || isAbsolute(difference)) return;
|
|
25
|
+
return difference.split(sep).join("/");
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
7
28
|
//#region ../core/src/pluralize.ts
|
|
8
29
|
/** Selects the noun or verb form that agrees with a numeric count. */
|
|
9
30
|
function pluralize(count, singular, plural = `${singular}s`) {
|
|
@@ -51,6 +72,7 @@ var src_default = definePlugin({
|
|
|
51
72
|
id: "directory:agenticskills",
|
|
52
73
|
kind: "directory",
|
|
53
74
|
name: "agenticskills.io",
|
|
75
|
+
protocol: "agenticskills",
|
|
54
76
|
url: "https://agenticskills.io"
|
|
55
77
|
}],
|
|
56
78
|
snippets: [
|
|
@@ -161,6 +183,7 @@ function isRecord(value) {
|
|
|
161
183
|
}
|
|
162
184
|
//#endregion
|
|
163
185
|
//#region ../core/src/manifest/schema-values.ts
|
|
186
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/u;
|
|
164
187
|
var AuraManifestValidationError = class extends Error {
|
|
165
188
|
jsonPath;
|
|
166
189
|
constructor(jsonPath, message) {
|
|
@@ -330,11 +353,44 @@ function targetApps(value, path, name, scope, claimed) {
|
|
|
330
353
|
}
|
|
331
354
|
return entries;
|
|
332
355
|
}
|
|
356
|
+
const MAX_TRUSTED_PATH_LENGTH = 1024;
|
|
357
|
+
/**
|
|
358
|
+
* Reads `trustedRepoPresets`, the repository presets the user accepted during setup.
|
|
359
|
+
*
|
|
360
|
+
* Each entry binds an absolute preset path to a hash of the exact contents that were reviewed, so
|
|
361
|
+
* a file edited after acceptance is untrusted again until someone looks at the new contents. Only
|
|
362
|
+
* acceptances appear here: declining records nothing, and the next interactive setup asks again.
|
|
363
|
+
*/
|
|
364
|
+
function optionalTrustedRepoPresets(value) {
|
|
365
|
+
if (value === void 0) return {};
|
|
366
|
+
if (!Array.isArray(value)) throw invalid("$.trustedRepoPresets", "must be an array");
|
|
367
|
+
if (value.length > 64) throw invalid("$.trustedRepoPresets", `must contain at most ${String(64)} entries`);
|
|
368
|
+
const paths = /* @__PURE__ */ new Set();
|
|
369
|
+
return { trustedRepoPresets: Object.freeze(value.map((candidate, index) => {
|
|
370
|
+
const path = `$.trustedRepoPresets[${String(index)}]`;
|
|
371
|
+
const entry = requiredObject(candidate, path);
|
|
372
|
+
const presetPath = requiredString(entry, "path", path);
|
|
373
|
+
if (presetPath.length === 0 || presetPath.length > MAX_TRUSTED_PATH_LENGTH) throw invalid(`${path}.path`, `must be a non-empty path of at most ${String(MAX_TRUSTED_PATH_LENGTH)} characters`);
|
|
374
|
+
if (paths.has(presetPath)) throw invalid(`${path}.path`, "must not duplicate another trusted preset path");
|
|
375
|
+
paths.add(presetPath);
|
|
376
|
+
const hash = requiredString(entry, "hash", path);
|
|
377
|
+
if (!SHA256_PATTERN.test(hash)) throw invalid(`${path}.hash`, "must be a lowercase SHA-256 hash");
|
|
378
|
+
return Object.freeze({
|
|
379
|
+
...entry,
|
|
380
|
+
hash,
|
|
381
|
+
path: presetPath
|
|
382
|
+
});
|
|
383
|
+
})) };
|
|
384
|
+
}
|
|
333
385
|
//#endregion
|
|
334
386
|
//#region ../core/src/manifest/schema.ts
|
|
335
|
-
const
|
|
387
|
+
const APP_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/u;
|
|
388
|
+
const MCP_CATALOG_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*\/[a-zA-Z0-9][a-zA-Z0-9._-]*$/u;
|
|
336
389
|
const SKILL_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
337
390
|
const SKILL_SOURCE_PATTERN = /^(?:directory|driver|plugin):[^\s:]+$/u;
|
|
391
|
+
const OVERRIDE_KEY_PATTERN = /^[a-z][a-zA-Z0-9]{0,63}$/u;
|
|
392
|
+
/** Room for several future override kinds without leaving the forward-compat window unbounded. */
|
|
393
|
+
const MAX_OVERRIDE_KEYS = 32;
|
|
338
394
|
/**
|
|
339
395
|
* How deep the manifest may nest.
|
|
340
396
|
*
|
|
@@ -354,14 +410,50 @@ function validateAuraManifest(value) {
|
|
|
354
410
|
...source,
|
|
355
411
|
apps: apps(source["apps"]),
|
|
356
412
|
...optionalChecks(source["checks"]),
|
|
413
|
+
...optionalIdList(source["ignoredApps"], "$.ignoredApps", APP_ID_PATTERN, "ignoredApps"),
|
|
357
414
|
mcpServers: mcpServers(source["mcpServers"]),
|
|
358
415
|
ownership: ownership(source["ownership"]),
|
|
416
|
+
...optionalOverrides(source["overrides"]),
|
|
359
417
|
...optionalPreset(source["preset"]),
|
|
360
418
|
schemaVersion: 1,
|
|
361
419
|
skills: skills(source["skills"]),
|
|
362
|
-
snippets: snippets(source["snippets"])
|
|
420
|
+
snippets: snippets(source["snippets"]),
|
|
421
|
+
...optionalTrustedRepoPresets(source["trustedRepoPresets"])
|
|
363
422
|
});
|
|
364
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Reads `overrides`, keeping the extension keys a newer Aura may have written.
|
|
426
|
+
*
|
|
427
|
+
* Passing unknown keys through matches how the top-level object is normalized, so a downgrade does
|
|
428
|
+
* not quietly delete a newer build's decisions. Unlike the top level, though, this object is
|
|
429
|
+
* rebuilt from scratch on every setup run, so an unbounded bag here would be rewritten verbatim
|
|
430
|
+
* forever: the key count and spelling are bounded to keep the forward-compatibility window from
|
|
431
|
+
* doubling as unbounded manifest storage.
|
|
432
|
+
*/
|
|
433
|
+
function optionalOverrides(value) {
|
|
434
|
+
if (value === void 0) return {};
|
|
435
|
+
const source = requiredObject(value, "$.overrides");
|
|
436
|
+
const keys = Object.keys(source);
|
|
437
|
+
if (keys.length > MAX_OVERRIDE_KEYS) throw invalid("$.overrides", `must contain at most ${String(MAX_OVERRIDE_KEYS)} keys`);
|
|
438
|
+
for (const key of keys) if (!OVERRIDE_KEY_PATTERN.test(key)) throw invalid(`$.overrides.${key}`, "must be a camelCase override name");
|
|
439
|
+
const required = optionalIdList(source["requiredMcpServers"], "$.overrides.requiredMcpServers", MCP_CATALOG_ID_PATTERN, "requiredMcpServers");
|
|
440
|
+
return { overrides: Object.freeze({
|
|
441
|
+
...source,
|
|
442
|
+
...required
|
|
443
|
+
}) };
|
|
444
|
+
}
|
|
445
|
+
function optionalIdList(value, path, pattern, key) {
|
|
446
|
+
if (value === void 0) return {};
|
|
447
|
+
const ids = stringArray(value, path);
|
|
448
|
+
if (ids.length > 256) throw invalid(path, "must contain at most 256 ids");
|
|
449
|
+
const seen = /* @__PURE__ */ new Set();
|
|
450
|
+
for (const [index, id] of ids.entries()) {
|
|
451
|
+
if (!pattern.test(id)) throw invalid(`${path}[${String(index)}]`, "must be a valid id");
|
|
452
|
+
if (seen.has(id)) throw invalid(`${path}[${String(index)}]`, "must not duplicate another id");
|
|
453
|
+
seen.add(id);
|
|
454
|
+
}
|
|
455
|
+
return { [key]: ids };
|
|
456
|
+
}
|
|
365
457
|
function skills(value) {
|
|
366
458
|
if (!Array.isArray(value)) throw invalid("$.skills", "must be an array");
|
|
367
459
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -1106,6 +1198,59 @@ function stripManagedMarkers(source) {
|
|
|
1106
1198
|
return kept.join("");
|
|
1107
1199
|
}
|
|
1108
1200
|
//#endregion
|
|
1201
|
+
//#region ../core/src/managed-block/reconcile-snippet.ts
|
|
1202
|
+
/** Reconciles exactly one snippet while preserving every other source byte. */
|
|
1203
|
+
function reconcileManagedSnippet(source, snippetId, resolution) {
|
|
1204
|
+
const current = readManagedBlock(source);
|
|
1205
|
+
if (current.status === "invalid") return invalidResult$1(source, current.notes, current.problems);
|
|
1206
|
+
if (current.status === "absent") return missingSnippet(source, current.notes, snippetId, "an Aura-managed block");
|
|
1207
|
+
const snippet = current.block.snippets.find((candidate) => candidate.id === snippetId);
|
|
1208
|
+
if (snippet === void 0) return missingSnippet(source, current.notes, snippetId, "the Aura-managed block");
|
|
1209
|
+
const lineEnding = markerLineEnding(source, snippet.startOffset, snippet.contentStartOffset);
|
|
1210
|
+
const canonical = resolution.kind === "restore" ? canonicalizeManagedSnippet(resolution.content) : void 0;
|
|
1211
|
+
if (canonical !== void 0) {
|
|
1212
|
+
const problems = managedSnippetContentProblems(snippet.id, canonical);
|
|
1213
|
+
if (problems.length > 0) return invalidResult$1(source, current.notes, problems);
|
|
1214
|
+
}
|
|
1215
|
+
const content = canonical === void 0 ? snippet.content : withLineEnding(canonical, lineEnding);
|
|
1216
|
+
const hash = canonical === void 0 ? snippet.computedHash : hashCanonicalManagedSnippet(canonical);
|
|
1217
|
+
const opening = `${AURA_MANAGED_SNIPPET_BEGIN_PREFIX}${snippet.id} sha256=${hash} -->${lineEnding}`;
|
|
1218
|
+
const updated = source.slice(0, snippet.startOffset) + opening + content + source.slice(snippet.contentEndOffset);
|
|
1219
|
+
return updated === source ? Object.freeze({
|
|
1220
|
+
content: source,
|
|
1221
|
+
notes: current.notes,
|
|
1222
|
+
status: "unchanged"
|
|
1223
|
+
}) : Object.freeze({
|
|
1224
|
+
content: updated,
|
|
1225
|
+
notes: current.notes,
|
|
1226
|
+
status: "updated"
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
/** Creates the opt-in edited-versus-canonical detail used by the merge choice. */
|
|
1230
|
+
function diffManagedSnippet(path, editedContent, canonicalContent) {
|
|
1231
|
+
return createTwoFilesPatch(`${path} (edited)`, `${path} (canonical)`, canonicalizeManagedSnippet(editedContent), canonicalizeManagedSnippet(canonicalContent), "edited", "canonical", { context: 3 });
|
|
1232
|
+
}
|
|
1233
|
+
function missingSnippet(source, notes, snippetId, location) {
|
|
1234
|
+
return invalidResult$1(source, notes, [Object.freeze({
|
|
1235
|
+
code: "missing-snippet",
|
|
1236
|
+
message: `Snippet "${snippetId}" does not exist in ${location}.`
|
|
1237
|
+
})]);
|
|
1238
|
+
}
|
|
1239
|
+
function invalidResult$1(source, notes, problems) {
|
|
1240
|
+
return Object.freeze({
|
|
1241
|
+
content: source,
|
|
1242
|
+
notes,
|
|
1243
|
+
problems,
|
|
1244
|
+
status: "invalid"
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
function markerLineEnding(source, startOffset, contentStartOffset) {
|
|
1248
|
+
return source.slice(startOffset, contentStartOffset).endsWith("\r\n") ? "\r\n" : "\n";
|
|
1249
|
+
}
|
|
1250
|
+
function withLineEnding(content, lineEnding) {
|
|
1251
|
+
return lineEnding === "\n" ? content : content.replaceAll("\n", lineEnding);
|
|
1252
|
+
}
|
|
1253
|
+
//#endregion
|
|
1109
1254
|
//#region ../core/src/managed-block/reconcile-desired.ts
|
|
1110
1255
|
function prepareDesiredSnippets(snippets, options) {
|
|
1111
1256
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -1307,6 +1452,134 @@ function settle(source, content, notes) {
|
|
|
1307
1452
|
});
|
|
1308
1453
|
}
|
|
1309
1454
|
//#endregion
|
|
1455
|
+
//#region ../core/src/managed-content/revision.ts
|
|
1456
|
+
/** Whether a source revision is a reviewable update, a divergence, or already installed. */
|
|
1457
|
+
function managedContentRevisionStatus(installedVersion, installedHash, availableVersion, availableHash) {
|
|
1458
|
+
if (installedVersion === availableVersion) return installedHash === availableHash ? "current" : "update";
|
|
1459
|
+
if (valid(installedVersion) === null || valid(availableVersion) === null) return "diverged";
|
|
1460
|
+
return gt(availableVersion, installedVersion) ? "update" : "diverged";
|
|
1461
|
+
}
|
|
1462
|
+
/** Replaces one shared skill tree using the same operation ordering in setup and guided checks. */
|
|
1463
|
+
function planSharedSkillTreeUpdate(root, existingEntries, desired) {
|
|
1464
|
+
const desiredPaths = new Set(desired.files.map((file) => resolve(root, ...file.path.split("/"))));
|
|
1465
|
+
const desiredParents = ancestorDirectories(desiredPaths);
|
|
1466
|
+
return [...[...existingEntries.filter((entry) => {
|
|
1467
|
+
const path = resolve(entry.path);
|
|
1468
|
+
return !desiredPaths.has(path) && (entry.kind !== "directory" || !desiredParents.has(path));
|
|
1469
|
+
})].sort((left, right) => right.path.length - left.path.length).map((entry) => ({
|
|
1470
|
+
path: entry.path,
|
|
1471
|
+
type: "remove"
|
|
1472
|
+
})), ...desired.files.map((file) => ({
|
|
1473
|
+
content: file.content,
|
|
1474
|
+
path: join(root, ...file.path.split("/")),
|
|
1475
|
+
type: "write"
|
|
1476
|
+
}))];
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Every directory that contains at least one desired path.
|
|
1480
|
+
*
|
|
1481
|
+
* Precomputed once so the stale filter stays linear: comparing each existing directory against
|
|
1482
|
+
* every desired path pairwise re-normalized both sides on every comparison.
|
|
1483
|
+
*/
|
|
1484
|
+
function ancestorDirectories(paths) {
|
|
1485
|
+
const parents = /* @__PURE__ */ new Set();
|
|
1486
|
+
for (const path of paths) {
|
|
1487
|
+
let parent = dirname(path);
|
|
1488
|
+
while (!parents.has(parent)) {
|
|
1489
|
+
parents.add(parent);
|
|
1490
|
+
const next = dirname(parent);
|
|
1491
|
+
if (next === parent) break;
|
|
1492
|
+
parent = next;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return parents;
|
|
1496
|
+
}
|
|
1497
|
+
//#endregion
|
|
1498
|
+
//#region ../core/src/workspace/skill-deployment-plan.ts
|
|
1499
|
+
/** Plans one manifest skill's link into one adapter-declared directory. */
|
|
1500
|
+
function planSkillDeployment(app, model, skillId, directoryId, options = {}) {
|
|
1501
|
+
const directory = app.skillDirectories?.find((candidate) => candidate.id === directoryId);
|
|
1502
|
+
if (directory === void 0) return {
|
|
1503
|
+
kind: "blocked",
|
|
1504
|
+
path: skillId,
|
|
1505
|
+
reason: "The application no longer declares this skills directory."
|
|
1506
|
+
};
|
|
1507
|
+
const path = join(directory.path, skillId);
|
|
1508
|
+
const target = join(sharedSkillsRoot(model.homeDir), skillId);
|
|
1509
|
+
if (!(model.sharedSkills?.some((skill) => skill.id === skillId) === true) && options.assumeShared !== true) return {
|
|
1510
|
+
kind: "blocked",
|
|
1511
|
+
path,
|
|
1512
|
+
reason: `The shared copy at ${target} is missing. Reinstall skill "${skillId}" before deploying it.`
|
|
1513
|
+
};
|
|
1514
|
+
const status = skillDeploymentStatus(app, directory, skillId);
|
|
1515
|
+
if (status === void 0 || !status.exists) return {
|
|
1516
|
+
kind: "planned",
|
|
1517
|
+
path,
|
|
1518
|
+
plan: symlinkPlan(app, path, target)
|
|
1519
|
+
};
|
|
1520
|
+
if (status.problem !== void 0) return {
|
|
1521
|
+
kind: "blocked",
|
|
1522
|
+
path,
|
|
1523
|
+
reason: `Aura could not safely inspect ${path} (${status.problem}) and will not replace it.`
|
|
1524
|
+
};
|
|
1525
|
+
if (status.pathKind !== "symlink" || status.symlinkTarget === void 0) return {
|
|
1526
|
+
kind: "blocked",
|
|
1527
|
+
path,
|
|
1528
|
+
reason: `${path} is not an Aura-managed skill link. Move it aside before deploying skill "${skillId}" there.`
|
|
1529
|
+
};
|
|
1530
|
+
const actualTarget = absoluteTarget(path, status.symlinkTarget);
|
|
1531
|
+
if (actualTarget === resolve(target)) return {
|
|
1532
|
+
kind: "planned",
|
|
1533
|
+
path,
|
|
1534
|
+
plan: convergedPlan$1(app, skillId)
|
|
1535
|
+
};
|
|
1536
|
+
if (!isAuraOwnedSkillTarget(model.homeDir, actualTarget)) return {
|
|
1537
|
+
kind: "blocked",
|
|
1538
|
+
path,
|
|
1539
|
+
reason: `${path} is not an Aura-managed skill link because it points outside Aura's shared skills directory. It was preserved.`
|
|
1540
|
+
};
|
|
1541
|
+
return {
|
|
1542
|
+
kind: "planned",
|
|
1543
|
+
path,
|
|
1544
|
+
plan: symlinkPlan(app, path, target)
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
/** Finds the captured status for one deployed skill path. */
|
|
1548
|
+
function skillDeploymentStatus(app, directory, skillId) {
|
|
1549
|
+
const path = resolve(join(directory.path, skillId));
|
|
1550
|
+
return app.sourceFiles.find((file) => resolve(file.spec.path) === path);
|
|
1551
|
+
}
|
|
1552
|
+
/** Whether a lexical target lies within Aura's canonical shared skills root. */
|
|
1553
|
+
function isAuraOwnedSkillTarget(homeDir, targetPath) {
|
|
1554
|
+
const root = resolve(sharedSkillsRoot(homeDir));
|
|
1555
|
+
const target = resolve(targetPath);
|
|
1556
|
+
const fromRoot = relative(root, target);
|
|
1557
|
+
return fromRoot !== "" && fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`);
|
|
1558
|
+
}
|
|
1559
|
+
/** Canonical location Aura uses for shared skill trees. */
|
|
1560
|
+
function sharedSkillsRoot(homeDir) {
|
|
1561
|
+
return join(homeDir, "agents", "skills");
|
|
1562
|
+
}
|
|
1563
|
+
function absoluteTarget(path, target) {
|
|
1564
|
+
return isAbsolute(target) ? resolve(target) : resolve(dirname(path), target);
|
|
1565
|
+
}
|
|
1566
|
+
function symlinkPlan(app, path, target) {
|
|
1567
|
+
return {
|
|
1568
|
+
operations: [{
|
|
1569
|
+
path,
|
|
1570
|
+
target,
|
|
1571
|
+
type: "symlink"
|
|
1572
|
+
}],
|
|
1573
|
+
summary: `Deploy the shared skill to ${app.displayName}.`
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
function convergedPlan$1(app, skillId) {
|
|
1577
|
+
return {
|
|
1578
|
+
operations: [],
|
|
1579
|
+
summary: `${app.displayName} already loads shared skill "${skillId}".`
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
//#endregion
|
|
1310
1583
|
//#region ../core/src/workspace/mcp-classify.ts
|
|
1311
1584
|
/**
|
|
1312
1585
|
* Splits desired servers into the ones Aura may write and the collisions a person has to settle.
|
|
@@ -1823,7 +2096,7 @@ function planImportLine(app, model, link, status, sourceContent) {
|
|
|
1823
2096
|
id: SHARED_LINK_SNIPPET_ID
|
|
1824
2097
|
}]);
|
|
1825
2098
|
if (reconciled.status === "invalid") return { blocked: `The Aura-managed block in ${link.entryPath} is malformed, so Aura will not rewrite the file. Repair or delete the block and run check --fix again.` };
|
|
1826
|
-
if (reconciled.status === "unchanged" && observedStateHolds(sourceContent)) return { plan: convergedPlan
|
|
2099
|
+
if (reconciled.status === "unchanged" && observedStateHolds(sourceContent)) return { plan: convergedPlan(app) };
|
|
1827
2100
|
return { plan: writePlan(app, link, reconciled.content, model.homeDir) };
|
|
1828
2101
|
}
|
|
1829
2102
|
/**
|
|
@@ -1847,7 +2120,7 @@ function planNativeCopy(app, model, link, status, sourceContent) {
|
|
|
1847
2120
|
const content = sourceContent ?? instructionEntry(app, link.entryPath)?.content;
|
|
1848
2121
|
const refusal = nativeCopyRefusal(status, sourceContent, content, link.content);
|
|
1849
2122
|
if (refusal !== void 0) return { blocked: refusal };
|
|
1850
|
-
if (status?.exists === true && content === link.content && observedStateHolds(sourceContent)) return { plan: convergedPlan
|
|
2123
|
+
if (status?.exists === true && content === link.content && observedStateHolds(sourceContent)) return { plan: convergedPlan(app) };
|
|
1851
2124
|
return { plan: writePlan(app, link, link.content ?? "", model.homeDir) };
|
|
1852
2125
|
}
|
|
1853
2126
|
function nativeCopyRefusal(status, sourceContent, content, desired) {
|
|
@@ -1856,7 +2129,7 @@ function nativeCopyRefusal(status, sourceContent, content, desired) {
|
|
|
1856
2129
|
function planSymlink(app, model, link, status, options) {
|
|
1857
2130
|
if (status?.exists === true && status.pathKind !== "symlink" && options.sourceContent === void 0) return { blocked: "The existing file is user-owned. Consolidate its content before replacing it with a symlink." };
|
|
1858
2131
|
const target = options.symlinkTarget ?? model.sharedInstructions.path;
|
|
1859
|
-
if (observedStateHolds(options.sourceContent) && pointsAt(status, target)) return { plan: convergedPlan
|
|
2132
|
+
if (observedStateHolds(options.sourceContent) && pointsAt(status, target)) return { plan: convergedPlan(app) };
|
|
1860
2133
|
return { plan: {
|
|
1861
2134
|
operations: [{
|
|
1862
2135
|
path: link.entryPath,
|
|
@@ -1878,7 +2151,7 @@ function pointsAt(status, target) {
|
|
|
1878
2151
|
return resolve(status.symlinkTarget) === resolve(target);
|
|
1879
2152
|
}
|
|
1880
2153
|
/** No work: the entry already loads the shared source, so the summary says so rather than promising a link. */
|
|
1881
|
-
function convergedPlan
|
|
2154
|
+
function convergedPlan(app) {
|
|
1882
2155
|
return {
|
|
1883
2156
|
operations: [],
|
|
1884
2157
|
summary: `${app.displayName} already loads the shared instruction source.`
|
|
@@ -1908,89 +2181,4 @@ function entryStatus(app, path) {
|
|
|
1908
2181
|
return app.sourceFiles.find((file) => resolve(file.spec.path) === resolve(path));
|
|
1909
2182
|
}
|
|
1910
2183
|
//#endregion
|
|
1911
|
-
|
|
1912
|
-
/** Plans one manifest skill's link into one adapter-declared directory. */
|
|
1913
|
-
function planSkillDeployment(app, model, skillId, directoryId, options = {}) {
|
|
1914
|
-
const directory = app.skillDirectories?.find((candidate) => candidate.id === directoryId);
|
|
1915
|
-
if (directory === void 0) return {
|
|
1916
|
-
kind: "blocked",
|
|
1917
|
-
path: skillId,
|
|
1918
|
-
reason: "The application no longer declares this skills directory."
|
|
1919
|
-
};
|
|
1920
|
-
const path = join(directory.path, skillId);
|
|
1921
|
-
const target = join(sharedSkillsRoot(model.homeDir), skillId);
|
|
1922
|
-
if (!(model.sharedSkills?.some((skill) => skill.id === skillId) === true) && options.assumeShared !== true) return {
|
|
1923
|
-
kind: "blocked",
|
|
1924
|
-
path,
|
|
1925
|
-
reason: `The shared copy at ${target} is missing. Reinstall skill "${skillId}" before deploying it.`
|
|
1926
|
-
};
|
|
1927
|
-
const status = skillDeploymentStatus(app, directory, skillId);
|
|
1928
|
-
if (status === void 0 || !status.exists) return {
|
|
1929
|
-
kind: "planned",
|
|
1930
|
-
path,
|
|
1931
|
-
plan: symlinkPlan(app, path, target)
|
|
1932
|
-
};
|
|
1933
|
-
if (status.problem !== void 0) return {
|
|
1934
|
-
kind: "blocked",
|
|
1935
|
-
path,
|
|
1936
|
-
reason: `Aura could not safely inspect ${path} (${status.problem}) and will not replace it.`
|
|
1937
|
-
};
|
|
1938
|
-
if (status.pathKind !== "symlink" || status.symlinkTarget === void 0) return {
|
|
1939
|
-
kind: "blocked",
|
|
1940
|
-
path,
|
|
1941
|
-
reason: `${path} is not an Aura-managed skill link. Move it aside before deploying skill "${skillId}" there.`
|
|
1942
|
-
};
|
|
1943
|
-
const actualTarget = absoluteTarget(path, status.symlinkTarget);
|
|
1944
|
-
if (actualTarget === resolve(target)) return {
|
|
1945
|
-
kind: "planned",
|
|
1946
|
-
path,
|
|
1947
|
-
plan: convergedPlan(app, skillId)
|
|
1948
|
-
};
|
|
1949
|
-
if (!isAuraOwnedSkillTarget(model.homeDir, actualTarget)) return {
|
|
1950
|
-
kind: "blocked",
|
|
1951
|
-
path,
|
|
1952
|
-
reason: `${path} is not an Aura-managed skill link because it points outside Aura's shared skills directory. It was preserved.`
|
|
1953
|
-
};
|
|
1954
|
-
return {
|
|
1955
|
-
kind: "planned",
|
|
1956
|
-
path,
|
|
1957
|
-
plan: symlinkPlan(app, path, target)
|
|
1958
|
-
};
|
|
1959
|
-
}
|
|
1960
|
-
/** Finds the captured status for one deployed skill path. */
|
|
1961
|
-
function skillDeploymentStatus(app, directory, skillId) {
|
|
1962
|
-
const path = resolve(join(directory.path, skillId));
|
|
1963
|
-
return app.sourceFiles.find((file) => resolve(file.spec.path) === path);
|
|
1964
|
-
}
|
|
1965
|
-
/** Whether a lexical target lies within Aura's canonical shared skills root. */
|
|
1966
|
-
function isAuraOwnedSkillTarget(homeDir, targetPath) {
|
|
1967
|
-
const root = resolve(sharedSkillsRoot(homeDir));
|
|
1968
|
-
const target = resolve(targetPath);
|
|
1969
|
-
const fromRoot = relative(root, target);
|
|
1970
|
-
return fromRoot !== "" && fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`);
|
|
1971
|
-
}
|
|
1972
|
-
/** Canonical location Aura uses for shared skill trees. */
|
|
1973
|
-
function sharedSkillsRoot(homeDir) {
|
|
1974
|
-
return join(homeDir, "agents", "skills");
|
|
1975
|
-
}
|
|
1976
|
-
function absoluteTarget(path, target) {
|
|
1977
|
-
return isAbsolute(target) ? resolve(target) : resolve(dirname(path), target);
|
|
1978
|
-
}
|
|
1979
|
-
function symlinkPlan(app, path, target) {
|
|
1980
|
-
return {
|
|
1981
|
-
operations: [{
|
|
1982
|
-
path,
|
|
1983
|
-
target,
|
|
1984
|
-
type: "symlink"
|
|
1985
|
-
}],
|
|
1986
|
-
summary: `Deploy the shared skill to ${app.displayName}.`
|
|
1987
|
-
};
|
|
1988
|
-
}
|
|
1989
|
-
function convergedPlan(app, skillId) {
|
|
1990
|
-
return {
|
|
1991
|
-
operations: [],
|
|
1992
|
-
summary: `${app.displayName} already loads shared skill "${skillId}".`
|
|
1993
|
-
};
|
|
1994
|
-
}
|
|
1995
|
-
//#endregion
|
|
1996
|
-
export { MAX_MUTABLE_FILE_BYTES as A, AuraManifestError as B, renderRedactedWriteDiff as C, renderRemoveDiff as D, renderMoveDiff as E, parseAuraManifest as F, src_default as H, errorCode as I, errorMessage as L, assertAuraManifestWritable as M, createAuraManifestWriteOperation as N, renderSymlinkDiff as O, createEmptyAuraManifest as P, isRecord as R, hashManagedSnippet as S, renderConflict as T, pluralize as U, SHARED_INSTRUCTIONS_TEMPLATE as V, managedSnippetContentProblems as _, planSharedInstructionLink as a, canonicalizeManagedSnippet as b, planMcpSecretRemediation as c, planDesiredMcpConvergence as d, planManifestMcpConvergence as f, reconcileParsedManagedBlock as g, createAppMcpConvergence as h, skillDeploymentStatus as i, MAX_RETAINED_PLAN_BYTES as j, FILE_MODES as k, rememberMcpSecretPlanner as l, rememberMcpConvergence as m, planSkillDeployment as n, canPlanMcpSecretRemediation as o, planMcpServerRemoval as p, sharedSkillsRoot as r, createAppMcpSecretPlanner as s, isAuraOwnedSkillTarget as t, mcpConvergenceBlockers as u, readManagedBlock as v, renderArchiveDiff as w, hashCanonicalManagedSnippet as x, AURA_MANAGED_SNIPPET_BEGIN_PREFIX as y, resolveAuraManifestPath as z };
|
|
2184
|
+
export { renderRemoveDiff as A, errorMessage as B, canonicalizeManagedSnippet as C, renderArchiveDiff as D, renderRedactedWriteDiff as E, assertAuraManifestWritable as F, src_default as G, resolveAuraManifestPath as H, createAuraManifestWriteOperation as I, pluralize as K, createEmptyAuraManifest as L, FILE_MODES as M, MAX_MUTABLE_FILE_BYTES as N, renderConflict as O, MAX_RETAINED_PLAN_BYTES as P, parseAuraManifest as R, readManagedBlock as S, hashManagedSnippet as T, AuraManifestError as U, isRecord as V, SHARED_INSTRUCTIONS_TEMPLATE as W, planSharedSkillTreeUpdate as _, rememberMcpSecretPlanner as a, reconcileManagedSnippet as b, planManifestMcpConvergence as c, createAppMcpConvergence as d, isAuraOwnedSkillTarget as f, managedContentRevisionStatus as g, skillDeploymentStatus as h, planMcpSecretRemediation as i, renderSymlinkDiff as j, renderMoveDiff as k, planMcpServerRemoval as l, sharedSkillsRoot as m, canPlanMcpSecretRemediation as n, mcpConvergenceBlockers as o, planSkillDeployment as p, displayPath as q, createAppMcpSecretPlanner as r, planDesiredMcpConvergence as s, planSharedInstructionLink as t, rememberMcpConvergence as u, reconcileParsedManagedBlock as v, hashCanonicalManagedSnippet as w, managedSnippetContentProblems as x, diffManagedSnippet as y, errorCode as z };
|
|
@@ -57,8 +57,10 @@ interface CliRuntime {
|
|
|
57
57
|
/**
|
|
58
58
|
* Color depth reported to the command framework.
|
|
59
59
|
*
|
|
60
|
-
* Defaults to what the
|
|
61
|
-
* when `stdout` is injected, since
|
|
60
|
+
* Defaults to what the process's own stdout supports, honouring the CLI and environment color
|
|
61
|
+
* policy. Always no color when `stdout` is injected, since neither that stream nor the
|
|
62
|
+
* surrounding process's `FORCE_COLOR` says anything about the destination — set this to ask for
|
|
63
|
+
* color there. An explicit value stays authoritative unless the command line says `--no-color`.
|
|
62
64
|
*/
|
|
63
65
|
readonly colorDepth?: number | undefined;
|
|
64
66
|
/** Directory the command was invoked from. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryaura/aura-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The composable Aura CLI runtime and official plugin distribution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -50,18 +50,18 @@
|
|
|
50
50
|
"toml-eslint-parser": "^1.0.3",
|
|
51
51
|
"typanion": "3.14.0",
|
|
52
52
|
"undici": "7.29.0",
|
|
53
|
-
"@tryaura/aura-sdk": "0.
|
|
53
|
+
"@tryaura/aura-sdk": "0.2.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/node": "24.13.3",
|
|
57
57
|
"ajv": "8.20.0",
|
|
58
58
|
"vitest": "4.1.10",
|
|
59
|
-
"@tryaura/adapter-claude-code": "0.0.0",
|
|
60
59
|
"@tryaura/adapter-cursor": "0.0.0",
|
|
61
|
-
"@tryaura/checks-core": "0.0.0",
|
|
62
60
|
"@tryaura/adapter-codex": "0.0.0",
|
|
63
|
-
"@tryaura/
|
|
64
|
-
"@tryaura/core": "0.0.0"
|
|
61
|
+
"@tryaura/adapter-claude-code": "0.0.0",
|
|
62
|
+
"@tryaura/checks-core": "0.0.0",
|
|
63
|
+
"@tryaura/core": "0.0.0",
|
|
64
|
+
"@tryaura/content-official": "0.0.0"
|
|
65
65
|
},
|
|
66
66
|
"engines": {
|
|
67
67
|
"node": ">=24"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"required": ["label", "layer"],
|
|
22
22
|
"properties": {
|
|
23
23
|
"label": { "type": "string" },
|
|
24
|
-
"layer": { "enum": ["cli", "default", "distro", "manifest", "preset"] }
|
|
24
|
+
"layer": { "enum": ["cli", "default", "distro", "manifest", "preset", "repo"] }
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"summary": {
|
|
@@ -61,6 +61,21 @@
|
|
|
61
61
|
"phase": { "enum": ["check", "detect", "files", "fix", "parse", "read", "support"] }
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
|
+
"configuration": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"additionalProperties": false,
|
|
67
|
+
"properties": {
|
|
68
|
+
"repositoryPreset": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"required": ["path", "status"],
|
|
72
|
+
"properties": {
|
|
73
|
+
"path": { "type": "string" },
|
|
74
|
+
"status": { "enum": ["applied", "held"] }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
64
79
|
"support": {
|
|
65
80
|
"type": "object",
|
|
66
81
|
"additionalProperties": false,
|
|
@@ -186,6 +201,7 @@
|
|
|
186
201
|
],
|
|
187
202
|
"properties": {
|
|
188
203
|
"apps": { "type": "array", "items": { "$ref": "#/$defs/app" } },
|
|
204
|
+
"configuration": { "$ref": "#/$defs/configuration" },
|
|
189
205
|
"diagnostics": { "type": "array", "items": { "$ref": "#/$defs/diagnostic" } },
|
|
190
206
|
"findings": { "type": "array", "items": { "$ref": "#/$defs/finding" } },
|
|
191
207
|
"fixes": { "type": "array", "items": { "$ref": "#/$defs/fix" } },
|
|
@@ -225,6 +241,7 @@
|
|
|
225
241
|
"title"
|
|
226
242
|
],
|
|
227
243
|
"properties": {
|
|
244
|
+
"configuration": { "$ref": "#/$defs/configuration" },
|
|
228
245
|
"enabled": { "type": "boolean" },
|
|
229
246
|
"explain": { "type": "string" },
|
|
230
247
|
"fixability": { "enum": ["auto", "guided", "manual"] },
|