getaura 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +293 -135
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Command, Option } from "commander";
|
|
|
10
10
|
|
|
11
11
|
// src/commands/apply.ts
|
|
12
12
|
import { existsSync as existsSync19, rmSync as rmSync6 } from "fs";
|
|
13
|
-
import
|
|
13
|
+
import path21 from "path";
|
|
14
14
|
|
|
15
15
|
// src/core/agents.ts
|
|
16
16
|
var AGENT_LAYOUT = {
|
|
@@ -1009,10 +1009,10 @@ function mergeDefs(...defs) {
|
|
|
1009
1009
|
function cloneDef(schema) {
|
|
1010
1010
|
return mergeDefs(schema._zod.def);
|
|
1011
1011
|
}
|
|
1012
|
-
function getElementAtPath(obj,
|
|
1013
|
-
if (!
|
|
1012
|
+
function getElementAtPath(obj, path28) {
|
|
1013
|
+
if (!path28)
|
|
1014
1014
|
return obj;
|
|
1015
|
-
return
|
|
1015
|
+
return path28.reduce((acc, key) => acc?.[key], obj);
|
|
1016
1016
|
}
|
|
1017
1017
|
function promiseAllObject(promisesObj) {
|
|
1018
1018
|
const keys = Object.keys(promisesObj);
|
|
@@ -1352,11 +1352,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1352
1352
|
}
|
|
1353
1353
|
return false;
|
|
1354
1354
|
}
|
|
1355
|
-
function prefixIssues(
|
|
1355
|
+
function prefixIssues(path28, issues) {
|
|
1356
1356
|
return issues.map((iss) => {
|
|
1357
1357
|
var _a3;
|
|
1358
1358
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1359
|
-
iss.path.unshift(
|
|
1359
|
+
iss.path.unshift(path28);
|
|
1360
1360
|
return iss;
|
|
1361
1361
|
});
|
|
1362
1362
|
}
|
|
@@ -1806,16 +1806,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
1806
1806
|
}
|
|
1807
1807
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
1808
1808
|
const fieldErrors = { _errors: [] };
|
|
1809
|
-
const processError = (error63,
|
|
1809
|
+
const processError = (error63, path28 = []) => {
|
|
1810
1810
|
for (const issue2 of error63.issues) {
|
|
1811
1811
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1812
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1812
|
+
issue2.errors.map((issues) => processError({ issues }, [...path28, ...issue2.path]));
|
|
1813
1813
|
} else if (issue2.code === "invalid_key") {
|
|
1814
|
-
processError({ issues: issue2.issues }, [...
|
|
1814
|
+
processError({ issues: issue2.issues }, [...path28, ...issue2.path]);
|
|
1815
1815
|
} else if (issue2.code === "invalid_element") {
|
|
1816
|
-
processError({ issues: issue2.issues }, [...
|
|
1816
|
+
processError({ issues: issue2.issues }, [...path28, ...issue2.path]);
|
|
1817
1817
|
} else {
|
|
1818
|
-
const fullpath = [...
|
|
1818
|
+
const fullpath = [...path28, ...issue2.path];
|
|
1819
1819
|
if (fullpath.length === 0) {
|
|
1820
1820
|
fieldErrors._errors.push(mapper(issue2));
|
|
1821
1821
|
} else {
|
|
@@ -1854,17 +1854,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
1854
1854
|
}
|
|
1855
1855
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
1856
1856
|
const result = { errors: [] };
|
|
1857
|
-
const processError = (error63,
|
|
1857
|
+
const processError = (error63, path28 = []) => {
|
|
1858
1858
|
var _a3;
|
|
1859
1859
|
for (const issue2 of error63.issues) {
|
|
1860
1860
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1861
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1861
|
+
issue2.errors.map((issues) => processError({ issues }, [...path28, ...issue2.path]));
|
|
1862
1862
|
} else if (issue2.code === "invalid_key") {
|
|
1863
|
-
processError({ issues: issue2.issues }, [...
|
|
1863
|
+
processError({ issues: issue2.issues }, [...path28, ...issue2.path]);
|
|
1864
1864
|
} else if (issue2.code === "invalid_element") {
|
|
1865
|
-
processError({ issues: issue2.issues }, [...
|
|
1865
|
+
processError({ issues: issue2.issues }, [...path28, ...issue2.path]);
|
|
1866
1866
|
} else {
|
|
1867
|
-
const fullpath = [...
|
|
1867
|
+
const fullpath = [...path28, ...issue2.path];
|
|
1868
1868
|
if (fullpath.length === 0) {
|
|
1869
1869
|
result.errors.push(mapper(issue2));
|
|
1870
1870
|
continue;
|
|
@@ -1903,8 +1903,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
1903
1903
|
}
|
|
1904
1904
|
function toDotPath(_path) {
|
|
1905
1905
|
const segs = [];
|
|
1906
|
-
const
|
|
1907
|
-
for (const seg of
|
|
1906
|
+
const path28 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
1907
|
+
for (const seg of path28) {
|
|
1908
1908
|
if (typeof seg === "number")
|
|
1909
1909
|
segs.push(`[${seg}]`);
|
|
1910
1910
|
else if (typeof seg === "symbol")
|
|
@@ -19006,13 +19006,13 @@ function resolveRef(ref, ctx) {
|
|
|
19006
19006
|
if (!ref.startsWith("#")) {
|
|
19007
19007
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
19008
19008
|
}
|
|
19009
|
-
const
|
|
19010
|
-
if (
|
|
19009
|
+
const path28 = ref.slice(1).split("/").filter(Boolean);
|
|
19010
|
+
if (path28.length === 0) {
|
|
19011
19011
|
return ctx.rootSchema;
|
|
19012
19012
|
}
|
|
19013
19013
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
19014
|
-
if (
|
|
19015
|
-
const key =
|
|
19014
|
+
if (path28[0] === defsKey) {
|
|
19015
|
+
const key = path28[1] === void 0 ? void 0 : decodeJSONPointerSegment(path28[1]);
|
|
19016
19016
|
if (!key || !ctx.defs[key]) {
|
|
19017
19017
|
throw new Error(`Reference not found: ${ref}`);
|
|
19018
19018
|
}
|
|
@@ -20130,6 +20130,26 @@ async function commandExists(cmd) {
|
|
|
20130
20130
|
import { mkdirSync, rmSync, writeFileSync } from "fs";
|
|
20131
20131
|
import path2 from "path";
|
|
20132
20132
|
|
|
20133
|
+
// src/core/local-files.ts
|
|
20134
|
+
var LOCAL_ONLY = [
|
|
20135
|
+
{ rule: "node_modules/", test: /(?:^|\/)node_modules\//, what: "installed packages" },
|
|
20136
|
+
{ rule: ".next/", test: /(?:^|\/)\.next\//, what: "Next.js build output" },
|
|
20137
|
+
{ rule: ".vercel/", test: /(?:^|\/)\.vercel\//, what: "the Vercel project link" },
|
|
20138
|
+
{ rule: "supabase/.temp/", test: /(?:^|\/)supabase\/\.temp\//, what: "Supabase CLI working files" },
|
|
20139
|
+
{ rule: ".turbo/", test: /(?:^|\/)\.turbo\//, what: "Turborepo cache" },
|
|
20140
|
+
{ rule: "coverage/", test: /(?:^|\/)coverage\//, what: "test coverage reports" },
|
|
20141
|
+
{ rule: ".env*.local", test: /(?:^|\/)\.env(?:\.[^/]+)?\.local$/, what: "local secrets" },
|
|
20142
|
+
{ rule: ".claude/settings.local.json", test: /(?:^|\/)\.claude\/settings\.local\.json$/, what: "personal Claude Code settings" },
|
|
20143
|
+
{ rule: ".DS_Store", test: /(?:^|\/)\.DS_Store$/, what: "macOS folder files" },
|
|
20144
|
+
{ rule: "Thumbs.db", test: /(?:^|\/)Thumbs\.db$/i, what: "Windows thumbnail files" },
|
|
20145
|
+
{ rule: "*.log", test: /\.log$/, what: "log files" },
|
|
20146
|
+
{ rule: "*.tsbuildinfo", test: /\.tsbuildinfo$/, what: "TypeScript build caches" },
|
|
20147
|
+
{ rule: ".eslintcache", test: /(?:^|\/)\.eslintcache$/, what: "ESLint cache" }
|
|
20148
|
+
];
|
|
20149
|
+
function isLocalOnly(file2) {
|
|
20150
|
+
return LOCAL_ONLY.some(({ test }) => test.test(file2) || test.test(`${file2}x`));
|
|
20151
|
+
}
|
|
20152
|
+
|
|
20133
20153
|
// src/core/repo.ts
|
|
20134
20154
|
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
20135
20155
|
import path from "path";
|
|
@@ -20216,7 +20236,7 @@ async function ghStatus(root) {
|
|
|
20216
20236
|
var AURA_OWNED = /^(?:\.aura\/|\.claude\/skills\/aura\/|\.agents\/skills\/aura(?:-(?:scan|next|fix|setup))?\/|\.cursor\/commands\/aura-[a-z]+\.md$|\.pi\/prompts\/aura-[a-z]+\.md$|\.claude\/settings\.json$)/;
|
|
20217
20237
|
async function dirtyFiles(root) {
|
|
20218
20238
|
const r = await git(root, "status", "--porcelain", "-uall");
|
|
20219
|
-
return r.stdout.split("\n").filter(Boolean).map((l) => l.slice(3).replace(/^"|"$/g, "").split(" -> ").pop()).filter((f) => !AURA_OWNED.test(f));
|
|
20239
|
+
return r.stdout.split("\n").filter(Boolean).map((l) => l.slice(3).replace(/^"|"$/g, "").split(" -> ").pop()).filter((f) => !AURA_OWNED.test(f) && !isLocalOnly(f));
|
|
20220
20240
|
}
|
|
20221
20241
|
async function currentBranch(root) {
|
|
20222
20242
|
return (await git(root, "rev-parse", "--abbrev-ref", "HEAD")).stdout.trim();
|
|
@@ -20358,9 +20378,9 @@ var github = {
|
|
|
20358
20378
|
isPrivate: Boolean(info.isPrivate),
|
|
20359
20379
|
defaultBranch: info.defaultBranchRef?.name || "main",
|
|
20360
20380
|
gh,
|
|
20361
|
-
get: (
|
|
20362
|
-
if (!cache.has(
|
|
20363
|
-
return cache.get(
|
|
20381
|
+
get: (path28) => {
|
|
20382
|
+
if (!cache.has(path28)) cache.set(path28, gh(["api", path28]));
|
|
20383
|
+
return cache.get(path28);
|
|
20364
20384
|
}
|
|
20365
20385
|
};
|
|
20366
20386
|
return { ok: true, ctx, target: `${ctx.repo} ${ctx.isPrivate ? "(private)" : "(public)"}` };
|
|
@@ -21046,7 +21066,7 @@ function pmCommands(pm) {
|
|
|
21046
21066
|
// package.json
|
|
21047
21067
|
var package_default = {
|
|
21048
21068
|
name: "getaura",
|
|
21049
|
-
version: "0.1.
|
|
21069
|
+
version: "0.1.10",
|
|
21050
21070
|
description: "Aura helps you and your coding agent follow best practices while you vibe code.",
|
|
21051
21071
|
type: "module",
|
|
21052
21072
|
bin: {
|
|
@@ -21284,6 +21304,15 @@ var ACTIONS = [
|
|
|
21284
21304
|
},
|
|
21285
21305
|
{ id: "add-security-headers", title: "Add security headers", category: "security", delivery: "pr", resolves: ["security.headers"] },
|
|
21286
21306
|
// Structure
|
|
21307
|
+
{
|
|
21308
|
+
id: "ignore-local-files",
|
|
21309
|
+
buildSafe: true,
|
|
21310
|
+
title: "Keep local and tool files out of git",
|
|
21311
|
+
category: "structure",
|
|
21312
|
+
delivery: "pr",
|
|
21313
|
+
resolves: ["structure.local-files"]
|
|
21314
|
+
},
|
|
21315
|
+
{ id: "save-aura-setup", buildSafe: true, title: "Save Aura\u2019s setup in git", category: "agent-setup", delivery: "pr", resolves: ["agent.setup-saved"] },
|
|
21287
21316
|
{ id: "remove-dead-files", title: "Remove dead files", category: "structure", delivery: "pr", resolves: ["structure.dead-files"] },
|
|
21288
21317
|
{ id: "move-misplaced-files", title: "Move misplaced files", category: "structure", delivery: "pr", resolves: ["structure.layout"] },
|
|
21289
21318
|
{
|
|
@@ -21454,8 +21483,8 @@ function nextSteps(scan, opts = {}) {
|
|
|
21454
21483
|
}
|
|
21455
21484
|
|
|
21456
21485
|
// src/actions/generators.ts
|
|
21457
|
-
import { existsSync as existsSync9, readFileSync as
|
|
21458
|
-
import
|
|
21486
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
|
|
21487
|
+
import path13 from "path";
|
|
21459
21488
|
|
|
21460
21489
|
// src/checks/agent-setup.ts
|
|
21461
21490
|
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
@@ -21491,6 +21520,81 @@ function lineOf(text, index) {
|
|
|
21491
21520
|
return line;
|
|
21492
21521
|
}
|
|
21493
21522
|
|
|
21523
|
+
// src/checks/git-hygiene.ts
|
|
21524
|
+
async function untracked(root, collapse) {
|
|
21525
|
+
const r = await run("git", ["ls-files", "--others", "--exclude-standard", ...collapse ? ["--directory"] : []], { cwd: root });
|
|
21526
|
+
return r.code === 0 ? r.stdout.split("\n").filter(Boolean) : null;
|
|
21527
|
+
}
|
|
21528
|
+
async function modified(root) {
|
|
21529
|
+
const r = await run("git", ["diff", "--name-only", "HEAD"], { cwd: root });
|
|
21530
|
+
return r.code === 0 ? r.stdout.split("\n").filter(Boolean) : [];
|
|
21531
|
+
}
|
|
21532
|
+
async function localFileProblems(ctx) {
|
|
21533
|
+
const loose = await untracked(ctx.root, true);
|
|
21534
|
+
if (!loose || !ctx.tracked) return null;
|
|
21535
|
+
return LOCAL_ONLY.map(({ rule, test, what }) => ({
|
|
21536
|
+
rule,
|
|
21537
|
+
what,
|
|
21538
|
+
// Committed .env files are already a security finding, so they aren't counted twice here.
|
|
21539
|
+
committed: rule === ".env*.local" ? [] : [...ctx.tracked].filter((f) => test.test(f)),
|
|
21540
|
+
// A collapsed folder like "supabase/.temp/" matches through a file inside it.
|
|
21541
|
+
notIgnored: loose.filter((f) => test.test(f) || test.test(`${f}x`))
|
|
21542
|
+
})).filter((p2) => p2.committed.length || p2.notIgnored.length);
|
|
21543
|
+
}
|
|
21544
|
+
var localFiles = {
|
|
21545
|
+
id: "structure.local-files",
|
|
21546
|
+
category: "structure",
|
|
21547
|
+
title: "Local and tool files are kept out of git",
|
|
21548
|
+
weight: 2,
|
|
21549
|
+
applies: (ctx) => ctx.tracked !== null,
|
|
21550
|
+
async run(ctx) {
|
|
21551
|
+
const problems = await localFileProblems(ctx);
|
|
21552
|
+
if (!problems) return { status: "unknown", summary: "Couldn\u2019t ask git which files it ignores." };
|
|
21553
|
+
if (problems.length === 0) return { status: "pass", summary: "Tool and machine files are ignored by git." };
|
|
21554
|
+
const committed = problems.flatMap((p2) => p2.committed);
|
|
21555
|
+
const finding = makeFinding(this, {
|
|
21556
|
+
severity: committed.length ? "medium" : "low",
|
|
21557
|
+
title: committed.length ? `${committed.length} local file(s) are committed to git` : `${problems.length} kind(s) of local file aren\u2019t ignored by git`,
|
|
21558
|
+
detail: problems.map(
|
|
21559
|
+
(p2) => `${p2.what} (${[...p2.committed, ...p2.notIgnored].slice(0, 3).join(", ")}): add \`${p2.rule}\` to .gitignore${p2.committed.length ? " and stop tracking them" : ""}`
|
|
21560
|
+
).join("; "),
|
|
21561
|
+
why: "Files your computer or a tool makes for itself can hold logins and machine paths, and they clutter every change. Ignored, they can\u2019t be committed by accident.",
|
|
21562
|
+
locations: [{ file: ".gitignore" }],
|
|
21563
|
+
actionId: "ignore-local-files",
|
|
21564
|
+
key: "local-files"
|
|
21565
|
+
});
|
|
21566
|
+
return { status: committed.length ? "fail" : "partial", score: committed.length ? 0 : 0.5, summary: finding.title, findings: [finding] };
|
|
21567
|
+
}
|
|
21568
|
+
};
|
|
21569
|
+
async function unsavedSetup(ctx) {
|
|
21570
|
+
const loose = await untracked(ctx.root, false);
|
|
21571
|
+
if (!loose) return null;
|
|
21572
|
+
return [.../* @__PURE__ */ new Set([...loose, ...await modified(ctx.root)])].filter((f) => AURA_OWNED.test(f)).sort();
|
|
21573
|
+
}
|
|
21574
|
+
var setupSaved = {
|
|
21575
|
+
id: "agent.setup-saved",
|
|
21576
|
+
category: "agent-setup",
|
|
21577
|
+
title: "Aura\u2019s setup is saved in git",
|
|
21578
|
+
weight: 2,
|
|
21579
|
+
// Before the first-run PR is merged, that PR is the next step, not this.
|
|
21580
|
+
applies: (ctx) => Boolean(ctx.tracked?.has(".aura/brief.md")),
|
|
21581
|
+
async run(ctx) {
|
|
21582
|
+
const files = await unsavedSetup(ctx);
|
|
21583
|
+
if (!files) return { status: "unknown", summary: "Couldn\u2019t ask git for uncommitted files." };
|
|
21584
|
+
if (files.length === 0) return { status: "pass", summary: "Everything Aura set up is committed." };
|
|
21585
|
+
const finding = makeFinding(this, {
|
|
21586
|
+
severity: "low",
|
|
21587
|
+
title: `${files.length} Aura setup file(s) aren\u2019t committed`,
|
|
21588
|
+
detail: `Not in git yet: ${files.slice(0, 8).join(", ")}${files.length > 8 ? "\u2026" : ""}.`,
|
|
21589
|
+
why: "Your teammates, CI and your coding agent on other computers only get the setup that\u2019s committed.",
|
|
21590
|
+
locations: files.map((file2) => ({ file: file2 })),
|
|
21591
|
+
actionId: "save-aura-setup",
|
|
21592
|
+
key: "setup-saved"
|
|
21593
|
+
});
|
|
21594
|
+
return { status: "partial", score: 0.5, summary: finding.title, findings: [finding] };
|
|
21595
|
+
}
|
|
21596
|
+
};
|
|
21597
|
+
|
|
21494
21598
|
// src/checks/agent-setup.ts
|
|
21495
21599
|
function exists(ctx, file2) {
|
|
21496
21600
|
return existsSync7(path8.join(ctx.root, file2));
|
|
@@ -21663,7 +21767,7 @@ var brief = {
|
|
|
21663
21767
|
return { status: file2 ? "partial" : "fail", score: file2 ? 0.5 : 0, summary: finding.title, findings: [finding] };
|
|
21664
21768
|
}
|
|
21665
21769
|
};
|
|
21666
|
-
var agentChecks = [rulesFile, rulesCurrent, skills, auraSkill, brief];
|
|
21770
|
+
var agentChecks = [rulesFile, rulesCurrent, skills, auraSkill, brief, setupSaved];
|
|
21667
21771
|
|
|
21668
21772
|
// src/core/graph.ts
|
|
21669
21773
|
import path9 from "path";
|
|
@@ -21805,12 +21909,54 @@ function commandList(agent) {
|
|
|
21805
21909
|
return AURA_COMMANDS.map((c) => invocation(agent, c)).join(", ");
|
|
21806
21910
|
}
|
|
21807
21911
|
|
|
21808
|
-
// src/actions/
|
|
21809
|
-
import {
|
|
21912
|
+
// src/actions/git-hygiene.ts
|
|
21913
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
21810
21914
|
import path11 from "path";
|
|
21915
|
+
var ignoreLocalFiles = async ({ root, ctx }) => {
|
|
21916
|
+
const problems = await localFileProblems(ctx) ?? [];
|
|
21917
|
+
if (problems.length === 0) return { skip: "Tool and machine files are already ignored." };
|
|
21918
|
+
let current2 = "";
|
|
21919
|
+
try {
|
|
21920
|
+
current2 = readFileSync8(path11.join(root, ".gitignore"), "utf8");
|
|
21921
|
+
} catch {
|
|
21922
|
+
}
|
|
21923
|
+
const lines = new Set(current2.split(/\r?\n/).map((l) => l.trim()));
|
|
21924
|
+
const missing = problems.map((p2) => p2.rule).filter((rule) => !lines.has(rule));
|
|
21925
|
+
const committed = problems.flatMap((p2) => p2.committed);
|
|
21926
|
+
const changes = missing.length ? [
|
|
21927
|
+
{
|
|
21928
|
+
path: ".gitignore",
|
|
21929
|
+
content: `${current2}${current2 && !current2.endsWith("\n") ? "\n" : ""}
|
|
21930
|
+
# Local and tool files (added by Aura)
|
|
21931
|
+
${missing.join("\n")}
|
|
21932
|
+
`
|
|
21933
|
+
}
|
|
21934
|
+
] : [];
|
|
21935
|
+
return {
|
|
21936
|
+
changes,
|
|
21937
|
+
commands: committed.length ? [["git", "rm", "-r", "-q", "--cached", "--", ...committed]] : [],
|
|
21938
|
+
details: [
|
|
21939
|
+
...problems.map(
|
|
21940
|
+
(p2) => `- ${p2.what}: \`${p2.rule}\`${p2.committed.length ? ` (stops tracking ${p2.committed.length} file(s); they stay on your computer)` : ""}`
|
|
21941
|
+
)
|
|
21942
|
+
]
|
|
21943
|
+
};
|
|
21944
|
+
};
|
|
21945
|
+
var saveAuraSetup = async ({ root }) => {
|
|
21946
|
+
const files = await unsavedSetup({ root }) ?? [];
|
|
21947
|
+
if (files.length === 0) return { skip: "Everything Aura set up is already committed." };
|
|
21948
|
+
return {
|
|
21949
|
+
changes: files.map((file2) => ({ path: file2, content: readFileSync8(path11.join(root, file2), "utf8") })),
|
|
21950
|
+
details: files.map((f) => `- \`${f}\``)
|
|
21951
|
+
};
|
|
21952
|
+
};
|
|
21953
|
+
|
|
21954
|
+
// src/actions/agent-files.ts
|
|
21955
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
|
|
21956
|
+
import path12 from "path";
|
|
21811
21957
|
function current(root, file2) {
|
|
21812
|
-
const full =
|
|
21813
|
-
return existsSync8(full) ?
|
|
21958
|
+
const full = path12.join(root, file2);
|
|
21959
|
+
return existsSync8(full) ? readFileSync9(full, "utf8") : null;
|
|
21814
21960
|
}
|
|
21815
21961
|
function upsertBlock(existing, body) {
|
|
21816
21962
|
const block = `${AURA_BLOCK_START}
|
|
@@ -21854,8 +22000,8 @@ function skillChanges(agents, names) {
|
|
|
21854
22000
|
|
|
21855
22001
|
// src/actions/generators.ts
|
|
21856
22002
|
function read(root, file2) {
|
|
21857
|
-
const full =
|
|
21858
|
-
return existsSync9(full) ?
|
|
22003
|
+
const full = path13.join(root, file2);
|
|
22004
|
+
return existsSync9(full) ? readFileSync10(full, "utf8") : null;
|
|
21859
22005
|
}
|
|
21860
22006
|
function withScripts(root, scripts, overwrite = false) {
|
|
21861
22007
|
const text = read(root, "package.json");
|
|
@@ -21876,7 +22022,7 @@ function findingMeta(scan, checkId, key) {
|
|
|
21876
22022
|
return scan.checks.find((c) => c.checkId === checkId)?.findings[0]?.meta?.[key];
|
|
21877
22023
|
}
|
|
21878
22024
|
var addBrief = ({ root }) => {
|
|
21879
|
-
const brief2 = BRIEF_PATHS.find((f) => existsSync9(
|
|
22025
|
+
const brief2 = BRIEF_PATHS.find((f) => existsSync9(path13.join(root, f)));
|
|
21880
22026
|
if (!brief2) return { skip: "There is no product brief yet. Run `aura init` to draft and confirm it first." };
|
|
21881
22027
|
const changes = [
|
|
21882
22028
|
{ path: brief2, content: read(root, brief2) },
|
|
@@ -21901,14 +22047,14 @@ var installSkills = ({ agents, skills: skills2 }) => {
|
|
|
21901
22047
|
};
|
|
21902
22048
|
var tokenEfficiency = (g) => {
|
|
21903
22049
|
const changes = [];
|
|
21904
|
-
if (!existsSync9(
|
|
22050
|
+
if (!existsSync9(path13.join(g.root, "docs/index.md"))) changes.push({ path: "docs/index.md", content: render("docs/index.md", g.vars) });
|
|
21905
22051
|
changes.push(...rulesChanges(g.root, g.agents, g.vars));
|
|
21906
22052
|
return { changes, details: ["- `docs/index.md`: a map of the repo your agent reads first", "- Lean-context rules in the agent rules file"] };
|
|
21907
22053
|
};
|
|
21908
22054
|
var addCi = ({ root, ctx, vars }) => {
|
|
21909
22055
|
const existing = ctx.files.filter((f) => /^\.github\/workflows\//.test(f));
|
|
21910
22056
|
const file2 = existing.includes(".github/workflows/ci.yml") ? ".github/workflows/checks.yml" : ".github/workflows/ci.yml";
|
|
21911
|
-
if (existsSync9(
|
|
22057
|
+
if (existsSync9(path13.join(root, file2))) return { skip: `${file2} already exists.` };
|
|
21912
22058
|
return {
|
|
21913
22059
|
changes: [{ path: file2, content: render("github/workflows/ci.yml", vars) }],
|
|
21914
22060
|
details: [`- \`${file2}\`: lint, type check, tests and build on every pull request`]
|
|
@@ -21932,7 +22078,7 @@ var setupTesting = ({ root, ctx, vars }) => {
|
|
|
21932
22078
|
};
|
|
21933
22079
|
};
|
|
21934
22080
|
var addEnvExample = ({ root, scan }) => {
|
|
21935
|
-
const file2 = [".env.example", ".env.sample", ".env.template"].find((f) => existsSync9(
|
|
22081
|
+
const file2 = [".env.example", ".env.sample", ".env.template"].find((f) => existsSync9(path13.join(root, f))) ?? ".env.example";
|
|
21936
22082
|
const existing = read(root, file2);
|
|
21937
22083
|
const declared = new Set(
|
|
21938
22084
|
(existing ?? "").split(/\r?\n/).map((l) => l.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/)?.[1]).filter(Boolean)
|
|
@@ -21989,7 +22135,7 @@ var addLinting = ({ root, ctx }) => {
|
|
|
21989
22135
|
return { changes, devDeps, details };
|
|
21990
22136
|
};
|
|
21991
22137
|
var addPreCommit = ({ root, ctx, vars }) => {
|
|
21992
|
-
if (existsSync9(
|
|
22138
|
+
if (existsSync9(path13.join(root, ".husky/pre-commit"))) return { skip: "A husky pre-commit hook already exists." };
|
|
21993
22139
|
const changes = [{ path: ".husky/pre-commit", content: render("configs/husky-pre-commit", vars) }];
|
|
21994
22140
|
const scripts = withScripts(root, { prepare: "husky" }, false);
|
|
21995
22141
|
if (scripts) changes.push(scripts);
|
|
@@ -22019,7 +22165,7 @@ ${current2.trim()}
|
|
|
22019
22165
|
};
|
|
22020
22166
|
};
|
|
22021
22167
|
var enableDependencyUpdates = ({ root, vars }) => {
|
|
22022
|
-
if ([".github/dependabot.yml", ".github/dependabot.yaml", "renovate.json", ".github/renovate.json"].some((f) => existsSync9(
|
|
22168
|
+
if ([".github/dependabot.yml", ".github/dependabot.yaml", "renovate.json", ".github/renovate.json"].some((f) => existsSync9(path13.join(root, f)))) {
|
|
22023
22169
|
return { skip: "Dependabot or Renovate is already configured." };
|
|
22024
22170
|
}
|
|
22025
22171
|
return {
|
|
@@ -22099,7 +22245,7 @@ function planMoves(root, ctx, moves) {
|
|
|
22099
22245
|
const alias = aliasFor(toFile);
|
|
22100
22246
|
if (alias) return alias[0] + noExt.slice(alias[1].length);
|
|
22101
22247
|
}
|
|
22102
|
-
let rel =
|
|
22248
|
+
let rel = path13.posix.relative(path13.posix.dirname(fromFile), noExt);
|
|
22103
22249
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
22104
22250
|
return rel;
|
|
22105
22251
|
};
|
|
@@ -22133,7 +22279,7 @@ function planMoves(root, ctx, moves) {
|
|
|
22133
22279
|
}
|
|
22134
22280
|
var moveMisplacedFiles = ({ root, ctx, scan }) => {
|
|
22135
22281
|
const raw = findingMeta(scan, "structure.layout", "moves") ?? [];
|
|
22136
|
-
const moves = raw.map((m) => m.split("=>")).filter(([, to]) => !existsSync9(
|
|
22282
|
+
const moves = raw.map((m) => m.split("=>")).filter(([, to]) => !existsSync9(path13.join(root, to))).slice(0, MAX_MOVES);
|
|
22137
22283
|
if (moves.length === 0) return { skip: "No files to move (or their targets already exist)." };
|
|
22138
22284
|
return { changes: planMoves(root, ctx, moves), details: moves.map(([from, to]) => `- \`${from}\` \u2192 \`${to}\``), verifyTypes: true };
|
|
22139
22285
|
};
|
|
@@ -22169,16 +22315,20 @@ var GENERATORS = {
|
|
|
22169
22315
|
"add-security-headers": addSecurityHeaders,
|
|
22170
22316
|
"remove-dead-files": removeDeadFiles,
|
|
22171
22317
|
"move-misplaced-files": moveMisplacedFiles,
|
|
22172
|
-
"add-aura-workflow": addAuraWorkflow
|
|
22318
|
+
"add-aura-workflow": addAuraWorkflow,
|
|
22319
|
+
"ignore-local-files": ignoreLocalFiles,
|
|
22320
|
+
"save-aura-setup": saveAuraSetup
|
|
22173
22321
|
};
|
|
22174
|
-
|
|
22322
|
+
|
|
22323
|
+
// src/actions/foundation.ts
|
|
22324
|
+
async function foundationPlan(g) {
|
|
22175
22325
|
const parts = ["add-brief", "install-skills", "token-efficiency", "add-env-example", "add-aura-workflow"];
|
|
22176
22326
|
const ciFailing = g.scan.checks.find((c) => c.checkId === "foundations.ci")?.status !== "pass";
|
|
22177
22327
|
if (g.scan.stack.recognized && ciFailing) parts.push("add-ci");
|
|
22178
22328
|
const byPath = /* @__PURE__ */ new Map();
|
|
22179
22329
|
const details = [];
|
|
22180
22330
|
for (const id of parts) {
|
|
22181
|
-
const plan = GENERATORS[id](g);
|
|
22331
|
+
const plan = await GENERATORS[id](g);
|
|
22182
22332
|
if ("skip" in plan) continue;
|
|
22183
22333
|
for (const c of plan.changes) byPath.set(c.path, c);
|
|
22184
22334
|
details.push(`**${id.replace(/-/g, " ").replace(/^\w/, (c) => c.toUpperCase())}**`, ...plan.details, "");
|
|
@@ -22224,7 +22374,7 @@ var AuraClient = class _AuraClient {
|
|
|
22224
22374
|
get hasKey() {
|
|
22225
22375
|
return Boolean(this.key);
|
|
22226
22376
|
}
|
|
22227
|
-
async request(method,
|
|
22377
|
+
async request(method, path28, body, timeoutMs = 6e4) {
|
|
22228
22378
|
if (!this.key) throw new AuraApiError("no_key", "No pilot key");
|
|
22229
22379
|
let payload;
|
|
22230
22380
|
if (body !== void 0) {
|
|
@@ -22234,7 +22384,7 @@ var AuraClient = class _AuraClient {
|
|
|
22234
22384
|
}
|
|
22235
22385
|
let res;
|
|
22236
22386
|
try {
|
|
22237
|
-
res = await fetch(`${this.baseUrl}/v1/${
|
|
22387
|
+
res = await fetch(`${this.baseUrl}/v1/${path28}`, {
|
|
22238
22388
|
method,
|
|
22239
22389
|
headers: { authorization: `Bearer ${this.key}`, "content-type": "application/json", "user-agent": `getaura/${AURA_VERSION}` },
|
|
22240
22390
|
body: payload,
|
|
@@ -22298,13 +22448,13 @@ var say = {
|
|
|
22298
22448
|
};
|
|
22299
22449
|
|
|
22300
22450
|
// src/fix-tracking.ts
|
|
22301
|
-
import { existsSync as existsSync10, readFileSync as
|
|
22451
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
22302
22452
|
var FILE = "fixes.json";
|
|
22303
22453
|
var MAX_AGE_MS = 60 * 24 * 60 * 60 * 1e3;
|
|
22304
22454
|
var MAX_CHECKS_PER_SCAN = 5;
|
|
22305
22455
|
function loadTrackedFixes(root) {
|
|
22306
22456
|
try {
|
|
22307
|
-
return existsSync10(auraPath(root, FILE)) ? JSON.parse(
|
|
22457
|
+
return existsSync10(auraPath(root, FILE)) ? JSON.parse(readFileSync11(auraPath(root, FILE), "utf8")) : [];
|
|
22308
22458
|
} catch {
|
|
22309
22459
|
return [];
|
|
22310
22460
|
}
|
|
@@ -22612,8 +22762,8 @@ var typecheck = {
|
|
|
22612
22762
|
async run(ctx) {
|
|
22613
22763
|
if (ctx.options.fast) return { status: "unknown", summary: "Skipped in fast mode." };
|
|
22614
22764
|
const { existsSync: existsSync21 } = await import("fs");
|
|
22615
|
-
const
|
|
22616
|
-
const tsc =
|
|
22765
|
+
const path28 = await import("path");
|
|
22766
|
+
const tsc = path28.join(ctx.root, "node_modules", ".bin", process.platform === "win32" ? "tsc.cmd" : "tsc");
|
|
22617
22767
|
if (!existsSync21(tsc)) return { status: "unknown", summary: "Dependencies not installed, so the type check was skipped." };
|
|
22618
22768
|
const r = await run(tsc, ["--noEmit", "--pretty", "false"], { cwd: ctx.root, timeoutMs: 18e4 });
|
|
22619
22769
|
if (r.timedOut) return { status: "unknown", summary: "Type check timed out." };
|
|
@@ -22635,7 +22785,7 @@ var typecheck = {
|
|
|
22635
22785
|
var foundationChecks = [ciWorkflow, tests, readme, envExample, lockfile, lintFormat, typecheck];
|
|
22636
22786
|
|
|
22637
22787
|
// src/connectors/snapshot.ts
|
|
22638
|
-
import { existsSync as existsSync11, readFileSync as
|
|
22788
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12 } from "fs";
|
|
22639
22789
|
var REFUSAL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
22640
22790
|
function withRefusals(states, snap, now = Date.now()) {
|
|
22641
22791
|
return Object.fromEntries(
|
|
@@ -22648,7 +22798,7 @@ function withRefusals(states, snap, now = Date.now()) {
|
|
|
22648
22798
|
var FILE2 = "cache/connectors.json";
|
|
22649
22799
|
function loadSnapshots(root) {
|
|
22650
22800
|
try {
|
|
22651
|
-
return existsSync11(auraPath(root, FILE2)) ? JSON.parse(
|
|
22801
|
+
return existsSync11(auraPath(root, FILE2)) ? JSON.parse(readFileSync12(auraPath(root, FILE2), "utf8")) : {};
|
|
22652
22802
|
} catch {
|
|
22653
22803
|
return {};
|
|
22654
22804
|
}
|
|
@@ -22809,7 +22959,7 @@ var apiRoutes = {
|
|
|
22809
22959
|
import { createHash as createHash3 } from "crypto";
|
|
22810
22960
|
import { chmodSync as chmodSync2, existsSync as existsSync12, mkdirSync as mkdirSync3, mkdtempSync, readdirSync as readdirSync4, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
22811
22961
|
import os3 from "os";
|
|
22812
|
-
import
|
|
22962
|
+
import path14 from "path";
|
|
22813
22963
|
var GITLEAKS_SHA = {
|
|
22814
22964
|
darwin_arm64: "b40ab0ae55c505963e365f271a8d3846efbc170aa17f2607f13df610a9aeb6a5",
|
|
22815
22965
|
darwin_x64: "dfe101a4db2255fc85120ac7f3d25e4342c3c20cf749f2c20a18081af1952709",
|
|
@@ -22827,7 +22977,7 @@ var OSV_SHA = {
|
|
|
22827
22977
|
windows_arm64: "ca1379da0e408279ef2c85cde0846a903495738a91c7b065a208048437c1686c"
|
|
22828
22978
|
};
|
|
22829
22979
|
function tarCommand(platform = process.platform, systemRoot = process.env.SystemRoot) {
|
|
22830
|
-
return platform === "win32" ?
|
|
22980
|
+
return platform === "win32" ? path14.win32.join(systemRoot || "C:\\Windows", "System32", "tar.exe") : "tar";
|
|
22831
22981
|
}
|
|
22832
22982
|
var TOOLS = {
|
|
22833
22983
|
gitleaks: {
|
|
@@ -22863,7 +23013,7 @@ function ensureTool(name, noDownload = false) {
|
|
|
22863
23013
|
async function resolveTool(name, noDownload) {
|
|
22864
23014
|
const spec = TOOLS[name];
|
|
22865
23015
|
const exe = process.platform === "win32" ? `${name}.exe` : name;
|
|
22866
|
-
const cached2 =
|
|
23016
|
+
const cached2 = path14.join(auraBinDir(), `${name}-${spec.version}`, exe);
|
|
22867
23017
|
if (existsSync12(cached2)) return { path: cached2, version: spec.version };
|
|
22868
23018
|
if (await commandExists(name)) {
|
|
22869
23019
|
const v = await run(name, ["version"]);
|
|
@@ -22884,20 +23034,20 @@ async function download(name, spec, target) {
|
|
|
22884
23034
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
22885
23035
|
const digest = createHash3("sha256").update(buf).digest("hex");
|
|
22886
23036
|
if (digest !== asset.sha256) throw new Error(`Checksum mismatch for ${asset.file}`);
|
|
22887
|
-
const dir =
|
|
23037
|
+
const dir = path14.dirname(target);
|
|
22888
23038
|
mkdirSync3(dir, { recursive: true });
|
|
22889
|
-
const tmp = mkdtempSync(
|
|
23039
|
+
const tmp = mkdtempSync(path14.join(os3.tmpdir(), `aura-${name}-`));
|
|
22890
23040
|
try {
|
|
22891
23041
|
if (asset.archive === null) {
|
|
22892
23042
|
writeFileSync3(target, buf);
|
|
22893
23043
|
} else {
|
|
22894
|
-
const archivePath =
|
|
23044
|
+
const archivePath = path14.join(tmp, asset.file);
|
|
22895
23045
|
writeFileSync3(archivePath, buf);
|
|
22896
23046
|
const r = await run(tarCommand(), ["-xf", archivePath, "-C", tmp]);
|
|
22897
23047
|
if (r.code !== 0) return null;
|
|
22898
23048
|
const exe = readdirSync4(tmp).find((f) => f === name || f === `${name}.exe`);
|
|
22899
23049
|
if (!exe) return null;
|
|
22900
|
-
renameSync(
|
|
23050
|
+
renameSync(path14.join(tmp, exe), target);
|
|
22901
23051
|
}
|
|
22902
23052
|
chmodSync2(target, 493);
|
|
22903
23053
|
return { path: target, version: spec.version };
|
|
@@ -23168,9 +23318,9 @@ var rls = {
|
|
|
23168
23318
|
};
|
|
23169
23319
|
|
|
23170
23320
|
// src/checks/security/secrets.ts
|
|
23171
|
-
import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readFileSync as
|
|
23321
|
+
import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readFileSync as readFileSync13, rmSync as rmSync3 } from "fs";
|
|
23172
23322
|
import os4 from "os";
|
|
23173
|
-
import
|
|
23323
|
+
import path15 from "path";
|
|
23174
23324
|
|
|
23175
23325
|
// src/core/ignore.ts
|
|
23176
23326
|
function globToRegex(glob) {
|
|
@@ -23208,15 +23358,15 @@ async function gitleaksHistory(ctx) {
|
|
|
23208
23358
|
if (!ctx.tracked) return null;
|
|
23209
23359
|
const bin = await ensureTool("gitleaks", ctx.options.noDownload);
|
|
23210
23360
|
if (!bin) return null;
|
|
23211
|
-
const dir = mkdtempSync2(
|
|
23212
|
-
const report =
|
|
23361
|
+
const dir = mkdtempSync2(path15.join(os4.tmpdir(), "aura-gitleaks-"));
|
|
23362
|
+
const report = path15.join(dir, "report.json");
|
|
23213
23363
|
try {
|
|
23214
23364
|
const r = await run(bin.path, ["git", "--no-banner", "--redact", "--report-format", "json", "--report-path", report, "--exit-code", "0", "."], {
|
|
23215
23365
|
cwd: ctx.root,
|
|
23216
23366
|
timeoutMs: 18e4
|
|
23217
23367
|
});
|
|
23218
23368
|
if (r.code !== 0 || !existsSync13(report)) return null;
|
|
23219
|
-
return { hits: JSON.parse(
|
|
23369
|
+
return { hits: JSON.parse(readFileSync13(report, "utf8")), engine: `gitleaks ${bin.version}` };
|
|
23220
23370
|
} catch {
|
|
23221
23371
|
return null;
|
|
23222
23372
|
} finally {
|
|
@@ -23331,7 +23481,7 @@ var envFiles = {
|
|
|
23331
23481
|
var securityChecks = [secrets, envFiles, deps, rls, apiRoutes, headers, ...CONNECTORS.map(toolSettingsCheck)];
|
|
23332
23482
|
|
|
23333
23483
|
// src/checks/structure.ts
|
|
23334
|
-
import
|
|
23484
|
+
import path16 from "path";
|
|
23335
23485
|
var GENERATED = /(?:^|\/)(?:supabase\/migrations|generated|__generated__|\.generated)\/|\.(?:d\.ts|min\.js)$|(?:^|\/)(?:database|supabase)\.types\.ts$|(?:^|\/)components\/ui\//;
|
|
23336
23486
|
function appSource(ctx) {
|
|
23337
23487
|
return ctx.files.filter((f) => isSourceFile(f) && !GENERATED.test(f));
|
|
@@ -23420,7 +23570,7 @@ var duplicates = {
|
|
|
23420
23570
|
const findings = pairs.map(
|
|
23421
23571
|
([a, b, sim]) => makeFinding(this, {
|
|
23422
23572
|
severity: "low",
|
|
23423
|
-
title: `${
|
|
23573
|
+
title: `${path16.basename(a)} and ${path16.basename(b)} are near-duplicates`,
|
|
23424
23574
|
detail: `${a} and ${b} are ${Math.round(sim * 100)}% the same.`,
|
|
23425
23575
|
why: "Duplicates drift apart over time: a bug gets fixed in one copy and not the other, and your agent can\u2019t tell which one is right.",
|
|
23426
23576
|
locations: [{ file: a }, { file: b }],
|
|
@@ -23434,7 +23584,7 @@ var duplicates = {
|
|
|
23434
23584
|
};
|
|
23435
23585
|
var ROOT_ALLOWED = /^(?:[^/]+\.config\.[cm]?[jt]s|middleware\.[cm]?[jt]s|proxy\.[cm]?[jt]s|instrumentation(?:-client)?\.[cm]?[jt]s|next-env\.d\.ts|global\.d\.ts|env\.d\.ts|sentry\.[\w.]+\.[cm]?[jt]s)$/;
|
|
23436
23586
|
function suggestTarget(file2, srcDir) {
|
|
23437
|
-
const base =
|
|
23587
|
+
const base = path16.posix.basename(file2);
|
|
23438
23588
|
const prefix = srcDir ? "src/" : "";
|
|
23439
23589
|
const isComponent = /^[A-Z][A-Za-z0-9]*\.(?:tsx|jsx)$/.test(base);
|
|
23440
23590
|
if (isComponent) return `${prefix}components/${base}`;
|
|
@@ -23484,7 +23634,7 @@ var layout = {
|
|
|
23484
23634
|
return { status: "fail", score: Math.max(0, 1 - misplaced.length / 8), summary: finding.title, findings: [finding] };
|
|
23485
23635
|
}
|
|
23486
23636
|
};
|
|
23487
|
-
var structureChecks = [deadFiles, oversized, duplicates, layout];
|
|
23637
|
+
var structureChecks = [deadFiles, oversized, duplicates, layout, localFiles];
|
|
23488
23638
|
|
|
23489
23639
|
// src/scan.ts
|
|
23490
23640
|
var ALL_CHECKS = [...securityChecks, ...agentChecks, ...foundationChecks, ...structureChecks];
|
|
@@ -23547,21 +23697,21 @@ async function runScan(root, options, onProgress) {
|
|
|
23547
23697
|
}
|
|
23548
23698
|
|
|
23549
23699
|
// src/commands/fix.ts
|
|
23550
|
-
import { existsSync as existsSync18, readFileSync as
|
|
23551
|
-
import
|
|
23700
|
+
import { existsSync as existsSync18, readFileSync as readFileSync20, writeFileSync as writeFileSync6 } from "fs";
|
|
23701
|
+
import path20 from "path";
|
|
23552
23702
|
|
|
23553
23703
|
// src/agent/tools.ts
|
|
23554
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as
|
|
23555
|
-
import
|
|
23704
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync14, rmSync as rmSync4, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
23705
|
+
import path17 from "path";
|
|
23556
23706
|
var MAX_OUTPUT = 12e3;
|
|
23557
23707
|
var MAX_FILE_LINES = 800;
|
|
23558
23708
|
var FORBIDDEN = /^(?:\.git|node_modules|\.next|\.vercel)(?:\/|$)|(?:^|\/)\.env(?:\.(?!example$|sample$|template$)[^/]+)?$/;
|
|
23559
23709
|
function safePath(root, rel) {
|
|
23560
23710
|
if (typeof rel !== "string" || !rel.trim()) throw new Error("A file path is required.");
|
|
23561
|
-
const normalized =
|
|
23562
|
-
if (normalized.startsWith("../") || normalized === ".." ||
|
|
23711
|
+
const normalized = path17.posix.normalize(rel.replace(/\\/g, "/").replace(/^\.\//, ""));
|
|
23712
|
+
if (normalized.startsWith("../") || normalized === ".." || path17.isAbsolute(normalized)) throw new Error(`Path is outside the repo: ${rel}`);
|
|
23563
23713
|
if (FORBIDDEN.test(normalized)) throw new Error(`Aura doesn\u2019t let the agent touch ${normalized}.`);
|
|
23564
|
-
const full =
|
|
23714
|
+
const full = path17.join(root, normalized);
|
|
23565
23715
|
if (!isInside(root, full)) throw new Error(`Path is outside the repo: ${rel}`);
|
|
23566
23716
|
return normalized;
|
|
23567
23717
|
}
|
|
@@ -23582,10 +23732,10 @@ async function runTool(root, call, allowedCommands2) {
|
|
|
23582
23732
|
}
|
|
23583
23733
|
case "read_file": {
|
|
23584
23734
|
const rel = safePath(root, input2.path);
|
|
23585
|
-
const full =
|
|
23735
|
+
const full = path17.join(root, rel);
|
|
23586
23736
|
if (!existsSync14(full)) return { output: `File not found: ${rel}` };
|
|
23587
23737
|
if (statSync2(full).size > 1e6) return { output: `${rel} is too large to read.` };
|
|
23588
|
-
const lines =
|
|
23738
|
+
const lines = readFileSync14(full, "utf8").split("\n");
|
|
23589
23739
|
const start = Math.max(1, Number(input2.startLine) || 1);
|
|
23590
23740
|
const end = Math.min(lines.length, Number(input2.endLine) || start + MAX_FILE_LINES - 1);
|
|
23591
23741
|
const body = lines.slice(start - 1, end).map((l, i) => `${start + i}: ${l}`).join("\n");
|
|
@@ -23602,7 +23752,7 @@ async function runTool(root, call, allowedCommands2) {
|
|
|
23602
23752
|
if (FORBIDDEN.test(f) || glob && !glob.test(f)) continue;
|
|
23603
23753
|
let text;
|
|
23604
23754
|
try {
|
|
23605
|
-
text =
|
|
23755
|
+
text = readFileSync14(path17.join(root, f), "utf8");
|
|
23606
23756
|
} catch {
|
|
23607
23757
|
continue;
|
|
23608
23758
|
}
|
|
@@ -23621,14 +23771,14 @@ async function runTool(root, call, allowedCommands2) {
|
|
|
23621
23771
|
output: "Refused: the content contains a [REDACTED:\u2026] placeholder. Secrets are hidden from you on purpose. Read the value from an environment variable (process.env.NAME) instead of writing it."
|
|
23622
23772
|
};
|
|
23623
23773
|
}
|
|
23624
|
-
mkdirSync4(
|
|
23625
|
-
writeFileSync4(
|
|
23774
|
+
mkdirSync4(path17.dirname(path17.join(root, rel)), { recursive: true });
|
|
23775
|
+
writeFileSync4(path17.join(root, rel), content);
|
|
23626
23776
|
return { output: `Wrote ${rel} (${content.split("\n").length} lines).`, touched: rel };
|
|
23627
23777
|
}
|
|
23628
23778
|
case "delete_file": {
|
|
23629
23779
|
const rel = safePath(root, input2.path);
|
|
23630
|
-
if (!existsSync14(
|
|
23631
|
-
rmSync4(
|
|
23780
|
+
if (!existsSync14(path17.join(root, rel))) return { output: `File not found: ${rel}` };
|
|
23781
|
+
rmSync4(path17.join(root, rel));
|
|
23632
23782
|
return { output: `Deleted ${rel}.`, touched: rel };
|
|
23633
23783
|
}
|
|
23634
23784
|
case "run_command": {
|
|
@@ -23650,7 +23800,7 @@ ${(r.stdout + "\n" + r.stderr).trim().slice(-6e3)}`;
|
|
|
23650
23800
|
|
|
23651
23801
|
// src/api/explain.ts
|
|
23652
23802
|
import { createHash as createHash4 } from "crypto";
|
|
23653
|
-
import { existsSync as existsSync15, readFileSync as
|
|
23803
|
+
import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
|
|
23654
23804
|
|
|
23655
23805
|
// src/report.ts
|
|
23656
23806
|
import pc2 from "picocolors";
|
|
@@ -23839,7 +23989,7 @@ var CACHE_FILE = "cache/explanations.json";
|
|
|
23839
23989
|
var MAX_EXPLAIN = 25;
|
|
23840
23990
|
function loadCache(root) {
|
|
23841
23991
|
try {
|
|
23842
|
-
return JSON.parse(
|
|
23992
|
+
return JSON.parse(readFileSync15(auraPath(root, CACHE_FILE), "utf8"));
|
|
23843
23993
|
} catch {
|
|
23844
23994
|
return {};
|
|
23845
23995
|
}
|
|
@@ -23861,7 +24011,7 @@ function excerptFor(root, f, context = 4) {
|
|
|
23861
24011
|
function productContext(root) {
|
|
23862
24012
|
const file2 = auraPath(root, "brief.md");
|
|
23863
24013
|
if (!existsSync15(file2)) return void 0;
|
|
23864
|
-
return redact(
|
|
24014
|
+
return redact(readFileSync15(file2, "utf8")).slice(0, 1800);
|
|
23865
24015
|
}
|
|
23866
24016
|
async function addExplanations(root, scan, client) {
|
|
23867
24017
|
const cache = loadCache(root);
|
|
@@ -23905,22 +24055,30 @@ async function addExplanations(root, scan, client) {
|
|
|
23905
24055
|
|
|
23906
24056
|
// src/commands/simple.ts
|
|
23907
24057
|
import { execFileSync } from "child_process";
|
|
23908
|
-
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as
|
|
23909
|
-
import
|
|
24058
|
+
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync19 } from "fs";
|
|
24059
|
+
import path19 from "path";
|
|
23910
24060
|
import YAML2 from "yaml";
|
|
23911
24061
|
|
|
23912
24062
|
// src/commands/inventory.ts
|
|
23913
|
-
import { readFileSync as
|
|
24063
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
23914
24064
|
function accountState(root, services) {
|
|
23915
24065
|
const inv = loadInventory(root);
|
|
24066
|
+
const read2 = Object.fromEntries(
|
|
24067
|
+
Object.entries(loadSnapshots(root)).map(([id, snap]) => [id, Object.values(snap.settings).every((state) => state !== "off")])
|
|
24068
|
+
);
|
|
23916
24069
|
return {
|
|
23917
24070
|
inventoryComplete: services.every((s) => inv.services[s]?.owner) && Boolean(inv.githubOrg),
|
|
23918
|
-
secured:
|
|
24071
|
+
secured: Object.fromEntries(
|
|
24072
|
+
[.../* @__PURE__ */ new Set([...Object.keys(read2), ...Object.keys(inv.secured ?? {}), "github"])].map((id) => [
|
|
24073
|
+
id,
|
|
24074
|
+
Boolean(read2[id] || inv.secured?.[id] || id === "github" && inv.githubSecurity)
|
|
24075
|
+
])
|
|
24076
|
+
)
|
|
23919
24077
|
};
|
|
23920
24078
|
}
|
|
23921
24079
|
function loadInventory(root) {
|
|
23922
24080
|
try {
|
|
23923
|
-
return JSON.parse(
|
|
24081
|
+
return JSON.parse(readFileSync16(auraPath(root, "inventory.json"), "utf8"));
|
|
23924
24082
|
} catch {
|
|
23925
24083
|
return { version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), services: {} };
|
|
23926
24084
|
}
|
|
@@ -24007,10 +24165,10 @@ async function inventoryCommand(flags) {
|
|
|
24007
24165
|
}
|
|
24008
24166
|
|
|
24009
24167
|
// src/history.ts
|
|
24010
|
-
import { readFileSync as
|
|
24168
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
24011
24169
|
function loadHistory(root) {
|
|
24012
24170
|
try {
|
|
24013
|
-
const data = JSON.parse(
|
|
24171
|
+
const data = JSON.parse(readFileSync17(auraPath(root, "history.json"), "utf8"));
|
|
24014
24172
|
return Array.isArray(data) ? data : [];
|
|
24015
24173
|
} catch {
|
|
24016
24174
|
return [];
|
|
@@ -24058,10 +24216,10 @@ function banner(colors = pc3.isColorSupported) {
|
|
|
24058
24216
|
// src/api/connect.ts
|
|
24059
24217
|
import { spawn as spawn2 } from "child_process";
|
|
24060
24218
|
import { constants, generateKeyPairSync, privateDecrypt } from "crypto";
|
|
24061
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as
|
|
24219
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync18, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
24062
24220
|
import os5 from "os";
|
|
24063
|
-
import
|
|
24064
|
-
var pendingFile = () =>
|
|
24221
|
+
import path18 from "path";
|
|
24222
|
+
var pendingFile = () => path18.join(os5.homedir(), ".aura", "pending-connect.json");
|
|
24065
24223
|
async function postJson(apiUrl, route, body) {
|
|
24066
24224
|
let res;
|
|
24067
24225
|
try {
|
|
@@ -24080,7 +24238,7 @@ async function postJson(apiUrl, route, body) {
|
|
|
24080
24238
|
}
|
|
24081
24239
|
function loadPending(apiUrl, now) {
|
|
24082
24240
|
try {
|
|
24083
|
-
const p2 = JSON.parse(
|
|
24241
|
+
const p2 = JSON.parse(readFileSync18(pendingFile(), "utf8"));
|
|
24084
24242
|
if (p2.apiUrl === apiUrl && Date.parse(p2.expiresAt) - now > 6e4) return p2;
|
|
24085
24243
|
} catch {
|
|
24086
24244
|
}
|
|
@@ -24099,7 +24257,7 @@ async function startOrResumeConnect(apiUrl, now = Date.now()) {
|
|
|
24099
24257
|
});
|
|
24100
24258
|
const res = ConnectStartResponse.parse(await postJson(apiUrl, "connect", { publicKey }));
|
|
24101
24259
|
const pending = { apiUrl, privateKey, ...res };
|
|
24102
|
-
mkdirSync5(
|
|
24260
|
+
mkdirSync5(path18.dirname(pendingFile()), { recursive: true });
|
|
24103
24261
|
writeFileSync5(pendingFile(), JSON.stringify(pending, null, 2) + "\n", { mode: 384 });
|
|
24104
24262
|
return { ...pending, resumed: false };
|
|
24105
24263
|
}
|
|
@@ -24247,7 +24405,7 @@ async function latestScan(root) {
|
|
|
24247
24405
|
const file2 = auraPath(root, "scan.json");
|
|
24248
24406
|
if (existsSync17(file2)) {
|
|
24249
24407
|
try {
|
|
24250
|
-
const scan = JSON.parse(
|
|
24408
|
+
const scan = JSON.parse(readFileSync19(file2, "utf8"));
|
|
24251
24409
|
const head = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
24252
24410
|
const dirty = execFileSync("git", ["status", "--porcelain"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
24253
24411
|
if (scan.commit === head && !dirty && Date.now() - Date.parse(scan.scannedAt) < 24 * 36e5) return scan;
|
|
@@ -24362,9 +24520,9 @@ async function configCommand(key, value) {
|
|
|
24362
24520
|
return 0;
|
|
24363
24521
|
}
|
|
24364
24522
|
function loadGuides() {
|
|
24365
|
-
const dir =
|
|
24523
|
+
const dir = path19.join(templatesDir(), "guides");
|
|
24366
24524
|
return readdirSync5(dir).filter((f) => f.endsWith(".md")).map((f) => {
|
|
24367
|
-
const text =
|
|
24525
|
+
const text = readFileSync19(path19.join(dir, f), "utf8");
|
|
24368
24526
|
const fm = text.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
24369
24527
|
const meta3 = fm ? YAML2.parse(fm[1]) : {};
|
|
24370
24528
|
return { id: f.replace(/\.md$/, ""), title: meta3.title ?? f, summary: meta3.summary ?? "", body: fm ? text.slice(fm[0].length) : text };
|
|
@@ -24432,7 +24590,7 @@ function allowedCommands(root, pm, typescript) {
|
|
|
24432
24590
|
const pkg = readJson(root, "package.json");
|
|
24433
24591
|
const run2 = pmCommands(pm).run;
|
|
24434
24592
|
const cmds = ["test", "lint", "typecheck", "build"].filter((s) => pkg?.scripts?.[s]).map((s) => `${run2} ${s}`);
|
|
24435
|
-
if (typescript && !pkg?.scripts?.typecheck && existsSync18(
|
|
24593
|
+
if (typescript && !pkg?.scripts?.typecheck && existsSync18(path20.join(root, "node_modules/.bin/tsc"))) cmds.push(`${pmCommands(pm).exec} tsc --noEmit`);
|
|
24436
24594
|
return cmds;
|
|
24437
24595
|
}
|
|
24438
24596
|
function taskFor(root, scan, action2, findings) {
|
|
@@ -24483,8 +24641,8 @@ async function untrackEnvFiles(root, findings) {
|
|
|
24483
24641
|
const files = findings.filter((f) => f.checkId === "security.env-files").map((f) => f.locations[0].file);
|
|
24484
24642
|
for (const f of files) await git(root, "rm", "-q", "--cached", "--", f);
|
|
24485
24643
|
if (files.length) {
|
|
24486
|
-
const gi =
|
|
24487
|
-
const current2 = existsSync18(gi) ?
|
|
24644
|
+
const gi = path20.join(root, ".gitignore");
|
|
24645
|
+
const current2 = existsSync18(gi) ? readFileSync20(gi, "utf8") : "";
|
|
24488
24646
|
if (!/^\.env\*?$/m.test(current2)) writeFileSync6(gi, `${current2.trimEnd()}
|
|
24489
24647
|
|
|
24490
24648
|
# Environment files hold secrets
|
|
@@ -24598,7 +24756,7 @@ ${pc.bold(action2.title)} ${pc.dim(`${findings.length} finding(s)`)}`);
|
|
|
24598
24756
|
messages.push({ role: "tool", content: results });
|
|
24599
24757
|
}
|
|
24600
24758
|
s.stop(finish ? "Aura\u2019s agent finished" : "Aura\u2019s agent stopped");
|
|
24601
|
-
const
|
|
24759
|
+
const untracked2 = action2.id === "remove-leaked-secrets" ? await untrackEnvFiles(root, findings) : [];
|
|
24602
24760
|
for (const f of touched) await git(root, "add", "-A", "--", f);
|
|
24603
24761
|
const staged = (await git(root, "diff", "--cached", "--name-only")).stdout.trim();
|
|
24604
24762
|
if (!staged) {
|
|
@@ -24620,7 +24778,7 @@ ${pc.bold(action2.title)} ${pc.dim(`${findings.length} finding(s)`)}`);
|
|
|
24620
24778
|
"",
|
|
24621
24779
|
...findings.slice(0, 20).map((f) => `- ${f.title}`),
|
|
24622
24780
|
"",
|
|
24623
|
-
...
|
|
24781
|
+
...untracked2.length ? ["### Removed from git", "", ...untracked2.map((f) => `- \`${f}\` (still on your computer, now ignored by git)`), ""] : [],
|
|
24624
24782
|
...finish?.testsRun.length ? ["### Checks run", "", ...finish.testsRun.map((t) => `- ${t}`), ""] : [],
|
|
24625
24783
|
...finish?.risks.length ? ["### Things to check before merging", "", ...finish.risks.map((r) => `- ${r}`), ""] : [],
|
|
24626
24784
|
...guides.length ? [
|
|
@@ -24828,7 +24986,7 @@ async function applyAction(actionId, flags, report) {
|
|
|
24828
24986
|
);
|
|
24829
24987
|
}
|
|
24830
24988
|
const g = { root, scan, ctx, agents: [...agents], vars: templateVars(root, scan, ctx.pkg), skills: skills2 };
|
|
24831
|
-
const plan = actionId === "foundation" ? foundationPlan(g) : GENERATORS[actionId]?.(g) ?? { skip: "Nothing to do." };
|
|
24989
|
+
const plan = actionId === "foundation" ? await foundationPlan(g) : await GENERATORS[actionId]?.(g) ?? { skip: "Nothing to do." };
|
|
24832
24990
|
if ("skip" in plan || plan.changes.length === 0 && !plan.commands?.length && !plan.devDeps?.length) {
|
|
24833
24991
|
say.info("skip" in plan ? plan.skip : "Nothing to change.");
|
|
24834
24992
|
report.outcome = "no_changes";
|
|
@@ -24837,7 +24995,7 @@ async function applyAction(actionId, flags, report) {
|
|
|
24837
24995
|
console.log(`
|
|
24838
24996
|
${pc.bold(action2.title)}`);
|
|
24839
24997
|
for (const c of plan.changes)
|
|
24840
|
-
console.log(` ${c.content === null ? pc.red("delete") : existsSync19(
|
|
24998
|
+
console.log(` ${c.content === null ? pc.red("delete") : existsSync19(path21.join(root, c.path)) ? pc.yellow("update") : pc.green("add ")} ${c.path}`);
|
|
24841
24999
|
const pm = pmCommands(scan.stack.packageManager);
|
|
24842
25000
|
if (plan.devDeps?.length) console.log(` ${pc.cyan("install")} ${plan.devDeps.join(", ")} ${pc.dim(`(${pm.addDev})`)}`);
|
|
24843
25001
|
for (const cmd of plan.commands ?? []) console.log(` ${pc.cyan("run ")} ${cmd.join(" ")}`);
|
|
@@ -24869,7 +25027,7 @@ ${pc.bold(action2.title)}`);
|
|
|
24869
25027
|
report.outcome = "cancelled";
|
|
24870
25028
|
return 0;
|
|
24871
25029
|
}
|
|
24872
|
-
const tsc =
|
|
25030
|
+
const tsc = path21.join(root, "node_modules", ".bin", process.platform === "win32" ? "tsc.cmd" : "tsc");
|
|
24873
25031
|
const checkTypes = plan.verifyTypes && scan.stack.typescript && existsSync19(tsc);
|
|
24874
25032
|
const typeErrors = async () => {
|
|
24875
25033
|
const r = await run(tsc, ["--noEmit", "--pretty", "false"], { cwd: root, timeoutMs: 3e5 });
|
|
@@ -24979,17 +25137,17 @@ async function undoPlan(root, plan) {
|
|
|
24979
25137
|
for (const file2 of touched) {
|
|
24980
25138
|
const inHead = (await git(root, "cat-file", "-e", `HEAD:${file2}`)).code === 0;
|
|
24981
25139
|
if (inHead) await git(root, "checkout", "HEAD", "--", file2);
|
|
24982
|
-
else if (plan.changes.some((c) => c.path === file2)) rmSync6(
|
|
25140
|
+
else if (plan.changes.some((c) => c.path === file2)) rmSync6(path21.join(root, file2), { force: true });
|
|
24983
25141
|
}
|
|
24984
25142
|
}
|
|
24985
25143
|
|
|
24986
25144
|
// src/commands/init.ts
|
|
24987
|
-
import { existsSync as existsSync20, readFileSync as
|
|
25145
|
+
import { existsSync as existsSync20, readFileSync as readFileSync22 } from "fs";
|
|
24988
25146
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
24989
|
-
import
|
|
25147
|
+
import path25 from "path";
|
|
24990
25148
|
|
|
24991
25149
|
// src/brief.ts
|
|
24992
|
-
import
|
|
25150
|
+
import path22 from "path";
|
|
24993
25151
|
function repoSummary(ctx) {
|
|
24994
25152
|
const routes = ctx.files.filter((f) => /^(?:src\/)?app\/(?:.+\/)?(?:page|route)\.[cm]?[jt]sx?$/.test(f) || /^(?:src\/)?pages\/.+\.[cm]?[jt]sx?$/.test(f)).map(
|
|
24995
25153
|
(f) => "/" + f.replace(/^(?:src\/)?(?:app|pages)\//, "").replace(/(?:^|\/)(?:page|route|index)\.[cm]?[jt]sx?$|\.[cm]?[jt]sx?$/, "").replace(/\([^)]+\)\//g, "")
|
|
@@ -25005,7 +25163,7 @@ function repoSummary(ctx) {
|
|
|
25005
25163
|
const readme2 = ctx.files.find((f) => /^readme\.mdx?$/i.test(f));
|
|
25006
25164
|
const readmeText = readme2 ? ctx.read(readme2) ?? "" : "";
|
|
25007
25165
|
return {
|
|
25008
|
-
name: ctx.pkg?.name ??
|
|
25166
|
+
name: ctx.pkg?.name ?? path22.basename(ctx.root),
|
|
25009
25167
|
packageJson: ctx.pkg ? {
|
|
25010
25168
|
name: ctx.pkg.name,
|
|
25011
25169
|
description: ctx.pkg.description,
|
|
@@ -25099,9 +25257,9 @@ function installFallback(result, platform = process.platform) {
|
|
|
25099
25257
|
// src/core/claude-plugin.ts
|
|
25100
25258
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
25101
25259
|
import os6 from "os";
|
|
25102
|
-
import
|
|
25260
|
+
import path23 from "path";
|
|
25103
25261
|
var CLAUDE_PLUGIN_ID = "aura@aura";
|
|
25104
|
-
var marketplaceDir = () =>
|
|
25262
|
+
var marketplaceDir = () => path23.join(os6.homedir(), ".aura", "claude-marketplace");
|
|
25105
25263
|
var MARKETPLACE = {
|
|
25106
25264
|
name: "aura",
|
|
25107
25265
|
description: "Aura for Claude Code",
|
|
@@ -25116,8 +25274,8 @@ var MARKETPLACE = {
|
|
|
25116
25274
|
};
|
|
25117
25275
|
function writeMarketplace() {
|
|
25118
25276
|
const dir = marketplaceDir();
|
|
25119
|
-
mkdirSync6(
|
|
25120
|
-
writeFileSync7(
|
|
25277
|
+
mkdirSync6(path23.join(dir, ".claude-plugin"), { recursive: true });
|
|
25278
|
+
writeFileSync7(path23.join(dir, ".claude-plugin", "marketplace.json"), `${JSON.stringify(MARKETPLACE, null, 2)}
|
|
25121
25279
|
`);
|
|
25122
25280
|
return dir;
|
|
25123
25281
|
}
|
|
@@ -25221,15 +25379,15 @@ function describeInferred(inferred) {
|
|
|
25221
25379
|
// src/commands/scan.ts
|
|
25222
25380
|
import { mkdtempSync as mkdtempSync3, rmSync as rmSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
25223
25381
|
import os7 from "os";
|
|
25224
|
-
import
|
|
25382
|
+
import path24 from "path";
|
|
25225
25383
|
|
|
25226
25384
|
// src/connectors/refresh.ts
|
|
25227
|
-
import { readFileSync as
|
|
25385
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
25228
25386
|
async function refreshToolSettings(root) {
|
|
25229
25387
|
if (process.env.AURA_NO_CONNECTORS) return;
|
|
25230
25388
|
let previous = null;
|
|
25231
25389
|
try {
|
|
25232
|
-
previous = JSON.parse(
|
|
25390
|
+
previous = JSON.parse(readFileSync21(auraPath(root, "scan.json"), "utf8"));
|
|
25233
25391
|
} catch {
|
|
25234
25392
|
}
|
|
25235
25393
|
const snapshots = loadSnapshots(root);
|
|
@@ -25242,8 +25400,8 @@ async function refreshToolSettings(root) {
|
|
|
25242
25400
|
|
|
25243
25401
|
// src/commands/scan.ts
|
|
25244
25402
|
async function scanRef(root, ref, opts) {
|
|
25245
|
-
const dir = mkdtempSync3(
|
|
25246
|
-
const wt =
|
|
25403
|
+
const dir = mkdtempSync3(path24.join(os7.tmpdir(), "aura-base-"));
|
|
25404
|
+
const wt = path24.join(dir, "tree");
|
|
25247
25405
|
const add = await run("git", ["worktree", "add", "--detach", wt, ref], { cwd: root });
|
|
25248
25406
|
if (add.code !== 0) {
|
|
25249
25407
|
rmSync7(dir, { recursive: true, force: true });
|
|
@@ -25333,7 +25491,7 @@ async function scanCommand(flags) {
|
|
|
25333
25491
|
// src/commands/init.ts
|
|
25334
25492
|
var AGENT_IDS = ["claude", "codex", "cursor", "pi"];
|
|
25335
25493
|
function detectAgents(root) {
|
|
25336
|
-
return detectAgentsFrom((rel) => existsSync20(
|
|
25494
|
+
return detectAgentsFrom((rel) => existsSync20(path25.join(root, rel)));
|
|
25337
25495
|
}
|
|
25338
25496
|
function openEditor(file2) {
|
|
25339
25497
|
const fallback = process.platform === "win32" ? "notepad" : spawnSync3("sh", ["-c", "command -v nano"]).status === 0 ? "nano" : "vi";
|
|
@@ -25448,13 +25606,13 @@ async function initCommand(flags) {
|
|
|
25448
25606
|
let brief2 = null;
|
|
25449
25607
|
let keptBrief = false;
|
|
25450
25608
|
if (flags.briefFile) {
|
|
25451
|
-
brief2 =
|
|
25609
|
+
brief2 = readFileSync22(flags.briefFile, "utf8");
|
|
25452
25610
|
} else if (!flags.answers && existsSync20(briefPath) && !(tty && unwrap(await p.confirm({ message: "A product brief already exists. Draft a new one?", initialValue: false })))) {
|
|
25453
25611
|
keptBrief = true;
|
|
25454
25612
|
} else {
|
|
25455
25613
|
let answers = null;
|
|
25456
25614
|
if (flags.answers) {
|
|
25457
|
-
answers = OnboardingAnswersSchema.parse(JSON.parse(
|
|
25615
|
+
answers = OnboardingAnswersSchema.parse(JSON.parse(readFileSync22(flags.answers, "utf8")));
|
|
25458
25616
|
} else if (tty) {
|
|
25459
25617
|
const inferred = inferAnswers(ctx);
|
|
25460
25618
|
p.note(describeInferred(inferred).join("\n"), "Here\u2019s what Aura found about your app");
|
|
@@ -25586,7 +25744,7 @@ ${pc.magenta("\u2726")} ${pc.bold("Give the user the aura command")}`);
|
|
|
25586
25744
|
}
|
|
25587
25745
|
|
|
25588
25746
|
// src/commands/summary.ts
|
|
25589
|
-
import
|
|
25747
|
+
import path26 from "path";
|
|
25590
25748
|
async function activity(root, since) {
|
|
25591
25749
|
const log = await git(root, "log", `--since=${since.toISOString()}`, "--no-merges", "--format=%H%x09%an%x09%s");
|
|
25592
25750
|
const commits = log.stdout.split("\n").filter(Boolean).slice(0, 300).map((l) => {
|
|
@@ -25643,7 +25801,7 @@ async function summaryCommand(flags) {
|
|
|
25643
25801
|
const beforeIds = new Set(before ? allFindings(before).map((f) => f.id) : []);
|
|
25644
25802
|
const nowIds = new Set(allFindings(now).map((f) => f.id));
|
|
25645
25803
|
const req = {
|
|
25646
|
-
repo:
|
|
25804
|
+
repo: path26.basename(root),
|
|
25647
25805
|
since: since.toISOString(),
|
|
25648
25806
|
until: until.toISOString(),
|
|
25649
25807
|
product: productContext(root),
|
|
@@ -25691,11 +25849,11 @@ ${body}`);
|
|
|
25691
25849
|
import pc4 from "picocolors";
|
|
25692
25850
|
|
|
25693
25851
|
// src/core/update-check.ts
|
|
25694
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
25852
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "fs";
|
|
25695
25853
|
import os8 from "os";
|
|
25696
|
-
import
|
|
25854
|
+
import path27 from "path";
|
|
25697
25855
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
25698
|
-
var cacheFile = () =>
|
|
25856
|
+
var cacheFile = () => path27.join(os8.homedir(), ".aura", "update-check.json");
|
|
25699
25857
|
function isNewer(a, b) {
|
|
25700
25858
|
if (!/^\d+\.\d+\.\d+$/.test(a) || !/^\d+\.\d+\.\d+$/.test(b)) return false;
|
|
25701
25859
|
const [x, y] = [a, b].map((v) => v.split(".").map(Number));
|
|
@@ -25715,13 +25873,13 @@ async function newerVersion(opts = {}) {
|
|
|
25715
25873
|
const now = opts.now ?? Date.now();
|
|
25716
25874
|
let latest = null;
|
|
25717
25875
|
try {
|
|
25718
|
-
const cached2 = JSON.parse(
|
|
25876
|
+
const cached2 = JSON.parse(readFileSync23(cacheFile(), "utf8"));
|
|
25719
25877
|
if (now - cached2.checkedAt < DAY_MS) latest = cached2.latest;
|
|
25720
25878
|
else throw new Error("stale");
|
|
25721
25879
|
} catch {
|
|
25722
25880
|
latest = await (opts.fetchLatest ?? fetchLatest)();
|
|
25723
25881
|
try {
|
|
25724
|
-
mkdirSync7(
|
|
25882
|
+
mkdirSync7(path27.dirname(cacheFile()), { recursive: true });
|
|
25725
25883
|
writeFileSync9(cacheFile(), JSON.stringify({ checkedAt: now, latest }));
|
|
25726
25884
|
} catch {
|
|
25727
25885
|
}
|