getaura 0.1.8 → 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 +337 -142
- 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)"}` };
|
|
@@ -20536,8 +20556,21 @@ var supabase = {
|
|
|
20536
20556
|
return cache.get(endpoint);
|
|
20537
20557
|
}
|
|
20538
20558
|
};
|
|
20539
|
-
const
|
|
20540
|
-
if (
|
|
20559
|
+
const read2 = await call("GET", `/v1/projects/${ref}`);
|
|
20560
|
+
if (read2.status === 401)
|
|
20561
|
+
return { ok: false, problem: "Your Supabase login has expired. Run `npx supabase login`, then try again.", errorType: "CliNotSignedIn" };
|
|
20562
|
+
if (read2.status !== 200 || !read2.json?.name)
|
|
20563
|
+
return {
|
|
20564
|
+
ok: false,
|
|
20565
|
+
// Projects created through Vercel's Supabase integration live in a Supabase account linked to Vercel.
|
|
20566
|
+
problem: [
|
|
20567
|
+
`The Supabase account you\u2019re signed in to can\u2019t open this project (${ref}).`,
|
|
20568
|
+
"If you created the database through Vercel: in Vercel open Storage, click the database, then Open in Supabase. In that same browser, run `npx supabase login` and approve it. Then try again.",
|
|
20569
|
+
"Otherwise run `npx supabase login` with the Supabase account that owns the project."
|
|
20570
|
+
].join("\n "),
|
|
20571
|
+
errorType: "NoAccess"
|
|
20572
|
+
};
|
|
20573
|
+
const project = read2.json;
|
|
20541
20574
|
return { ok: true, ctx, target: project.name };
|
|
20542
20575
|
},
|
|
20543
20576
|
settings: [
|
|
@@ -21033,7 +21066,7 @@ function pmCommands(pm) {
|
|
|
21033
21066
|
// package.json
|
|
21034
21067
|
var package_default = {
|
|
21035
21068
|
name: "getaura",
|
|
21036
|
-
version: "0.1.
|
|
21069
|
+
version: "0.1.10",
|
|
21037
21070
|
description: "Aura helps you and your coding agent follow best practices while you vibe code.",
|
|
21038
21071
|
type: "module",
|
|
21039
21072
|
bin: {
|
|
@@ -21271,6 +21304,15 @@ var ACTIONS = [
|
|
|
21271
21304
|
},
|
|
21272
21305
|
{ id: "add-security-headers", title: "Add security headers", category: "security", delivery: "pr", resolves: ["security.headers"] },
|
|
21273
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"] },
|
|
21274
21316
|
{ id: "remove-dead-files", title: "Remove dead files", category: "structure", delivery: "pr", resolves: ["structure.dead-files"] },
|
|
21275
21317
|
{ id: "move-misplaced-files", title: "Move misplaced files", category: "structure", delivery: "pr", resolves: ["structure.layout"] },
|
|
21276
21318
|
{
|
|
@@ -21441,8 +21483,8 @@ function nextSteps(scan, opts = {}) {
|
|
|
21441
21483
|
}
|
|
21442
21484
|
|
|
21443
21485
|
// src/actions/generators.ts
|
|
21444
|
-
import { existsSync as existsSync9, readFileSync as
|
|
21445
|
-
import
|
|
21486
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
|
|
21487
|
+
import path13 from "path";
|
|
21446
21488
|
|
|
21447
21489
|
// src/checks/agent-setup.ts
|
|
21448
21490
|
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
@@ -21478,6 +21520,81 @@ function lineOf(text, index) {
|
|
|
21478
21520
|
return line;
|
|
21479
21521
|
}
|
|
21480
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
|
+
|
|
21481
21598
|
// src/checks/agent-setup.ts
|
|
21482
21599
|
function exists(ctx, file2) {
|
|
21483
21600
|
return existsSync7(path8.join(ctx.root, file2));
|
|
@@ -21650,7 +21767,7 @@ var brief = {
|
|
|
21650
21767
|
return { status: file2 ? "partial" : "fail", score: file2 ? 0.5 : 0, summary: finding.title, findings: [finding] };
|
|
21651
21768
|
}
|
|
21652
21769
|
};
|
|
21653
|
-
var agentChecks = [rulesFile, rulesCurrent, skills, auraSkill, brief];
|
|
21770
|
+
var agentChecks = [rulesFile, rulesCurrent, skills, auraSkill, brief, setupSaved];
|
|
21654
21771
|
|
|
21655
21772
|
// src/core/graph.ts
|
|
21656
21773
|
import path9 from "path";
|
|
@@ -21792,12 +21909,54 @@ function commandList(agent) {
|
|
|
21792
21909
|
return AURA_COMMANDS.map((c) => invocation(agent, c)).join(", ");
|
|
21793
21910
|
}
|
|
21794
21911
|
|
|
21795
|
-
// src/actions/
|
|
21796
|
-
import {
|
|
21912
|
+
// src/actions/git-hygiene.ts
|
|
21913
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
21797
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";
|
|
21798
21957
|
function current(root, file2) {
|
|
21799
|
-
const full =
|
|
21800
|
-
return existsSync8(full) ?
|
|
21958
|
+
const full = path12.join(root, file2);
|
|
21959
|
+
return existsSync8(full) ? readFileSync9(full, "utf8") : null;
|
|
21801
21960
|
}
|
|
21802
21961
|
function upsertBlock(existing, body) {
|
|
21803
21962
|
const block = `${AURA_BLOCK_START}
|
|
@@ -21841,8 +22000,8 @@ function skillChanges(agents, names) {
|
|
|
21841
22000
|
|
|
21842
22001
|
// src/actions/generators.ts
|
|
21843
22002
|
function read(root, file2) {
|
|
21844
|
-
const full =
|
|
21845
|
-
return existsSync9(full) ?
|
|
22003
|
+
const full = path13.join(root, file2);
|
|
22004
|
+
return existsSync9(full) ? readFileSync10(full, "utf8") : null;
|
|
21846
22005
|
}
|
|
21847
22006
|
function withScripts(root, scripts, overwrite = false) {
|
|
21848
22007
|
const text = read(root, "package.json");
|
|
@@ -21863,7 +22022,7 @@ function findingMeta(scan, checkId, key) {
|
|
|
21863
22022
|
return scan.checks.find((c) => c.checkId === checkId)?.findings[0]?.meta?.[key];
|
|
21864
22023
|
}
|
|
21865
22024
|
var addBrief = ({ root }) => {
|
|
21866
|
-
const brief2 = BRIEF_PATHS.find((f) => existsSync9(
|
|
22025
|
+
const brief2 = BRIEF_PATHS.find((f) => existsSync9(path13.join(root, f)));
|
|
21867
22026
|
if (!brief2) return { skip: "There is no product brief yet. Run `aura init` to draft and confirm it first." };
|
|
21868
22027
|
const changes = [
|
|
21869
22028
|
{ path: brief2, content: read(root, brief2) },
|
|
@@ -21888,14 +22047,14 @@ var installSkills = ({ agents, skills: skills2 }) => {
|
|
|
21888
22047
|
};
|
|
21889
22048
|
var tokenEfficiency = (g) => {
|
|
21890
22049
|
const changes = [];
|
|
21891
|
-
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) });
|
|
21892
22051
|
changes.push(...rulesChanges(g.root, g.agents, g.vars));
|
|
21893
22052
|
return { changes, details: ["- `docs/index.md`: a map of the repo your agent reads first", "- Lean-context rules in the agent rules file"] };
|
|
21894
22053
|
};
|
|
21895
22054
|
var addCi = ({ root, ctx, vars }) => {
|
|
21896
22055
|
const existing = ctx.files.filter((f) => /^\.github\/workflows\//.test(f));
|
|
21897
22056
|
const file2 = existing.includes(".github/workflows/ci.yml") ? ".github/workflows/checks.yml" : ".github/workflows/ci.yml";
|
|
21898
|
-
if (existsSync9(
|
|
22057
|
+
if (existsSync9(path13.join(root, file2))) return { skip: `${file2} already exists.` };
|
|
21899
22058
|
return {
|
|
21900
22059
|
changes: [{ path: file2, content: render("github/workflows/ci.yml", vars) }],
|
|
21901
22060
|
details: [`- \`${file2}\`: lint, type check, tests and build on every pull request`]
|
|
@@ -21919,7 +22078,7 @@ var setupTesting = ({ root, ctx, vars }) => {
|
|
|
21919
22078
|
};
|
|
21920
22079
|
};
|
|
21921
22080
|
var addEnvExample = ({ root, scan }) => {
|
|
21922
|
-
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";
|
|
21923
22082
|
const existing = read(root, file2);
|
|
21924
22083
|
const declared = new Set(
|
|
21925
22084
|
(existing ?? "").split(/\r?\n/).map((l) => l.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/)?.[1]).filter(Boolean)
|
|
@@ -21976,7 +22135,7 @@ var addLinting = ({ root, ctx }) => {
|
|
|
21976
22135
|
return { changes, devDeps, details };
|
|
21977
22136
|
};
|
|
21978
22137
|
var addPreCommit = ({ root, ctx, vars }) => {
|
|
21979
|
-
if (existsSync9(
|
|
22138
|
+
if (existsSync9(path13.join(root, ".husky/pre-commit"))) return { skip: "A husky pre-commit hook already exists." };
|
|
21980
22139
|
const changes = [{ path: ".husky/pre-commit", content: render("configs/husky-pre-commit", vars) }];
|
|
21981
22140
|
const scripts = withScripts(root, { prepare: "husky" }, false);
|
|
21982
22141
|
if (scripts) changes.push(scripts);
|
|
@@ -22006,7 +22165,7 @@ ${current2.trim()}
|
|
|
22006
22165
|
};
|
|
22007
22166
|
};
|
|
22008
22167
|
var enableDependencyUpdates = ({ root, vars }) => {
|
|
22009
|
-
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)))) {
|
|
22010
22169
|
return { skip: "Dependabot or Renovate is already configured." };
|
|
22011
22170
|
}
|
|
22012
22171
|
return {
|
|
@@ -22086,7 +22245,7 @@ function planMoves(root, ctx, moves) {
|
|
|
22086
22245
|
const alias = aliasFor(toFile);
|
|
22087
22246
|
if (alias) return alias[0] + noExt.slice(alias[1].length);
|
|
22088
22247
|
}
|
|
22089
|
-
let rel =
|
|
22248
|
+
let rel = path13.posix.relative(path13.posix.dirname(fromFile), noExt);
|
|
22090
22249
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
22091
22250
|
return rel;
|
|
22092
22251
|
};
|
|
@@ -22120,7 +22279,7 @@ function planMoves(root, ctx, moves) {
|
|
|
22120
22279
|
}
|
|
22121
22280
|
var moveMisplacedFiles = ({ root, ctx, scan }) => {
|
|
22122
22281
|
const raw = findingMeta(scan, "structure.layout", "moves") ?? [];
|
|
22123
|
-
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);
|
|
22124
22283
|
if (moves.length === 0) return { skip: "No files to move (or their targets already exist)." };
|
|
22125
22284
|
return { changes: planMoves(root, ctx, moves), details: moves.map(([from, to]) => `- \`${from}\` \u2192 \`${to}\``), verifyTypes: true };
|
|
22126
22285
|
};
|
|
@@ -22156,16 +22315,20 @@ var GENERATORS = {
|
|
|
22156
22315
|
"add-security-headers": addSecurityHeaders,
|
|
22157
22316
|
"remove-dead-files": removeDeadFiles,
|
|
22158
22317
|
"move-misplaced-files": moveMisplacedFiles,
|
|
22159
|
-
"add-aura-workflow": addAuraWorkflow
|
|
22318
|
+
"add-aura-workflow": addAuraWorkflow,
|
|
22319
|
+
"ignore-local-files": ignoreLocalFiles,
|
|
22320
|
+
"save-aura-setup": saveAuraSetup
|
|
22160
22321
|
};
|
|
22161
|
-
|
|
22322
|
+
|
|
22323
|
+
// src/actions/foundation.ts
|
|
22324
|
+
async function foundationPlan(g) {
|
|
22162
22325
|
const parts = ["add-brief", "install-skills", "token-efficiency", "add-env-example", "add-aura-workflow"];
|
|
22163
22326
|
const ciFailing = g.scan.checks.find((c) => c.checkId === "foundations.ci")?.status !== "pass";
|
|
22164
22327
|
if (g.scan.stack.recognized && ciFailing) parts.push("add-ci");
|
|
22165
22328
|
const byPath = /* @__PURE__ */ new Map();
|
|
22166
22329
|
const details = [];
|
|
22167
22330
|
for (const id of parts) {
|
|
22168
|
-
const plan = GENERATORS[id](g);
|
|
22331
|
+
const plan = await GENERATORS[id](g);
|
|
22169
22332
|
if ("skip" in plan) continue;
|
|
22170
22333
|
for (const c of plan.changes) byPath.set(c.path, c);
|
|
22171
22334
|
details.push(`**${id.replace(/-/g, " ").replace(/^\w/, (c) => c.toUpperCase())}**`, ...plan.details, "");
|
|
@@ -22211,7 +22374,7 @@ var AuraClient = class _AuraClient {
|
|
|
22211
22374
|
get hasKey() {
|
|
22212
22375
|
return Boolean(this.key);
|
|
22213
22376
|
}
|
|
22214
|
-
async request(method,
|
|
22377
|
+
async request(method, path28, body, timeoutMs = 6e4) {
|
|
22215
22378
|
if (!this.key) throw new AuraApiError("no_key", "No pilot key");
|
|
22216
22379
|
let payload;
|
|
22217
22380
|
if (body !== void 0) {
|
|
@@ -22221,7 +22384,7 @@ var AuraClient = class _AuraClient {
|
|
|
22221
22384
|
}
|
|
22222
22385
|
let res;
|
|
22223
22386
|
try {
|
|
22224
|
-
res = await fetch(`${this.baseUrl}/v1/${
|
|
22387
|
+
res = await fetch(`${this.baseUrl}/v1/${path28}`, {
|
|
22225
22388
|
method,
|
|
22226
22389
|
headers: { authorization: `Bearer ${this.key}`, "content-type": "application/json", "user-agent": `getaura/${AURA_VERSION}` },
|
|
22227
22390
|
body: payload,
|
|
@@ -22285,13 +22448,13 @@ var say = {
|
|
|
22285
22448
|
};
|
|
22286
22449
|
|
|
22287
22450
|
// src/fix-tracking.ts
|
|
22288
|
-
import { existsSync as existsSync10, readFileSync as
|
|
22451
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
22289
22452
|
var FILE = "fixes.json";
|
|
22290
22453
|
var MAX_AGE_MS = 60 * 24 * 60 * 60 * 1e3;
|
|
22291
22454
|
var MAX_CHECKS_PER_SCAN = 5;
|
|
22292
22455
|
function loadTrackedFixes(root) {
|
|
22293
22456
|
try {
|
|
22294
|
-
return existsSync10(auraPath(root, FILE)) ? JSON.parse(
|
|
22457
|
+
return existsSync10(auraPath(root, FILE)) ? JSON.parse(readFileSync11(auraPath(root, FILE), "utf8")) : [];
|
|
22295
22458
|
} catch {
|
|
22296
22459
|
return [];
|
|
22297
22460
|
}
|
|
@@ -22599,8 +22762,8 @@ var typecheck = {
|
|
|
22599
22762
|
async run(ctx) {
|
|
22600
22763
|
if (ctx.options.fast) return { status: "unknown", summary: "Skipped in fast mode." };
|
|
22601
22764
|
const { existsSync: existsSync21 } = await import("fs");
|
|
22602
|
-
const
|
|
22603
|
-
const tsc =
|
|
22765
|
+
const path28 = await import("path");
|
|
22766
|
+
const tsc = path28.join(ctx.root, "node_modules", ".bin", process.platform === "win32" ? "tsc.cmd" : "tsc");
|
|
22604
22767
|
if (!existsSync21(tsc)) return { status: "unknown", summary: "Dependencies not installed, so the type check was skipped." };
|
|
22605
22768
|
const r = await run(tsc, ["--noEmit", "--pretty", "false"], { cwd: ctx.root, timeoutMs: 18e4 });
|
|
22606
22769
|
if (r.timedOut) return { status: "unknown", summary: "Type check timed out." };
|
|
@@ -22622,18 +22785,35 @@ var typecheck = {
|
|
|
22622
22785
|
var foundationChecks = [ciWorkflow, tests, readme, envExample, lockfile, lintFormat, typecheck];
|
|
22623
22786
|
|
|
22624
22787
|
// src/connectors/snapshot.ts
|
|
22625
|
-
import { existsSync as existsSync11, readFileSync as
|
|
22788
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12 } from "fs";
|
|
22789
|
+
var REFUSAL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
22790
|
+
function withRefusals(states, snap, now = Date.now()) {
|
|
22791
|
+
return Object.fromEntries(
|
|
22792
|
+
Object.entries(states).map(([id, s]) => {
|
|
22793
|
+
const refusal = snap?.refused?.[id];
|
|
22794
|
+
return [id, s.state === "off" && refusal && now - Date.parse(refusal.at) < REFUSAL_MS ? { state: "unavailable", why: refusal.why } : s];
|
|
22795
|
+
})
|
|
22796
|
+
);
|
|
22797
|
+
}
|
|
22626
22798
|
var FILE2 = "cache/connectors.json";
|
|
22627
22799
|
function loadSnapshots(root) {
|
|
22628
22800
|
try {
|
|
22629
|
-
return existsSync11(auraPath(root, FILE2)) ? JSON.parse(
|
|
22801
|
+
return existsSync11(auraPath(root, FILE2)) ? JSON.parse(readFileSync12(auraPath(root, FILE2), "utf8")) : {};
|
|
22630
22802
|
} catch {
|
|
22631
22803
|
return {};
|
|
22632
22804
|
}
|
|
22633
22805
|
}
|
|
22634
|
-
function saveSnapshot(root, toolId, states) {
|
|
22806
|
+
function saveSnapshot(root, toolId, states, refusedNow = {}) {
|
|
22635
22807
|
const all = loadSnapshots(root);
|
|
22636
|
-
|
|
22808
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22809
|
+
const refused = { ...all[toolId]?.refused };
|
|
22810
|
+
for (const [id, why] of Object.entries(refusedNow)) refused[id] = { why, at: now };
|
|
22811
|
+
for (const [id, s] of Object.entries(states)) if (s.state === "on") delete refused[id];
|
|
22812
|
+
all[toolId] = {
|
|
22813
|
+
checkedAt: now,
|
|
22814
|
+
settings: Object.fromEntries(Object.entries(states).map(([id, s]) => [id, s.state])),
|
|
22815
|
+
...Object.keys(refused).length ? { refused } : {}
|
|
22816
|
+
};
|
|
22637
22817
|
writeAuraFile(root, FILE2, `${JSON.stringify(all, null, 2)}
|
|
22638
22818
|
`);
|
|
22639
22819
|
}
|
|
@@ -22779,7 +22959,7 @@ var apiRoutes = {
|
|
|
22779
22959
|
import { createHash as createHash3 } from "crypto";
|
|
22780
22960
|
import { chmodSync as chmodSync2, existsSync as existsSync12, mkdirSync as mkdirSync3, mkdtempSync, readdirSync as readdirSync4, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
22781
22961
|
import os3 from "os";
|
|
22782
|
-
import
|
|
22962
|
+
import path14 from "path";
|
|
22783
22963
|
var GITLEAKS_SHA = {
|
|
22784
22964
|
darwin_arm64: "b40ab0ae55c505963e365f271a8d3846efbc170aa17f2607f13df610a9aeb6a5",
|
|
22785
22965
|
darwin_x64: "dfe101a4db2255fc85120ac7f3d25e4342c3c20cf749f2c20a18081af1952709",
|
|
@@ -22797,7 +22977,7 @@ var OSV_SHA = {
|
|
|
22797
22977
|
windows_arm64: "ca1379da0e408279ef2c85cde0846a903495738a91c7b065a208048437c1686c"
|
|
22798
22978
|
};
|
|
22799
22979
|
function tarCommand(platform = process.platform, systemRoot = process.env.SystemRoot) {
|
|
22800
|
-
return platform === "win32" ?
|
|
22980
|
+
return platform === "win32" ? path14.win32.join(systemRoot || "C:\\Windows", "System32", "tar.exe") : "tar";
|
|
22801
22981
|
}
|
|
22802
22982
|
var TOOLS = {
|
|
22803
22983
|
gitleaks: {
|
|
@@ -22833,7 +23013,7 @@ function ensureTool(name, noDownload = false) {
|
|
|
22833
23013
|
async function resolveTool(name, noDownload) {
|
|
22834
23014
|
const spec = TOOLS[name];
|
|
22835
23015
|
const exe = process.platform === "win32" ? `${name}.exe` : name;
|
|
22836
|
-
const cached2 =
|
|
23016
|
+
const cached2 = path14.join(auraBinDir(), `${name}-${spec.version}`, exe);
|
|
22837
23017
|
if (existsSync12(cached2)) return { path: cached2, version: spec.version };
|
|
22838
23018
|
if (await commandExists(name)) {
|
|
22839
23019
|
const v = await run(name, ["version"]);
|
|
@@ -22854,20 +23034,20 @@ async function download(name, spec, target) {
|
|
|
22854
23034
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
22855
23035
|
const digest = createHash3("sha256").update(buf).digest("hex");
|
|
22856
23036
|
if (digest !== asset.sha256) throw new Error(`Checksum mismatch for ${asset.file}`);
|
|
22857
|
-
const dir =
|
|
23037
|
+
const dir = path14.dirname(target);
|
|
22858
23038
|
mkdirSync3(dir, { recursive: true });
|
|
22859
|
-
const tmp = mkdtempSync(
|
|
23039
|
+
const tmp = mkdtempSync(path14.join(os3.tmpdir(), `aura-${name}-`));
|
|
22860
23040
|
try {
|
|
22861
23041
|
if (asset.archive === null) {
|
|
22862
23042
|
writeFileSync3(target, buf);
|
|
22863
23043
|
} else {
|
|
22864
|
-
const archivePath =
|
|
23044
|
+
const archivePath = path14.join(tmp, asset.file);
|
|
22865
23045
|
writeFileSync3(archivePath, buf);
|
|
22866
23046
|
const r = await run(tarCommand(), ["-xf", archivePath, "-C", tmp]);
|
|
22867
23047
|
if (r.code !== 0) return null;
|
|
22868
23048
|
const exe = readdirSync4(tmp).find((f) => f === name || f === `${name}.exe`);
|
|
22869
23049
|
if (!exe) return null;
|
|
22870
|
-
renameSync(
|
|
23050
|
+
renameSync(path14.join(tmp, exe), target);
|
|
22871
23051
|
}
|
|
22872
23052
|
chmodSync2(target, 493);
|
|
22873
23053
|
return { path: target, version: spec.version };
|
|
@@ -23138,9 +23318,9 @@ var rls = {
|
|
|
23138
23318
|
};
|
|
23139
23319
|
|
|
23140
23320
|
// src/checks/security/secrets.ts
|
|
23141
|
-
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";
|
|
23142
23322
|
import os4 from "os";
|
|
23143
|
-
import
|
|
23323
|
+
import path15 from "path";
|
|
23144
23324
|
|
|
23145
23325
|
// src/core/ignore.ts
|
|
23146
23326
|
function globToRegex(glob) {
|
|
@@ -23178,15 +23358,15 @@ async function gitleaksHistory(ctx) {
|
|
|
23178
23358
|
if (!ctx.tracked) return null;
|
|
23179
23359
|
const bin = await ensureTool("gitleaks", ctx.options.noDownload);
|
|
23180
23360
|
if (!bin) return null;
|
|
23181
|
-
const dir = mkdtempSync2(
|
|
23182
|
-
const report =
|
|
23361
|
+
const dir = mkdtempSync2(path15.join(os4.tmpdir(), "aura-gitleaks-"));
|
|
23362
|
+
const report = path15.join(dir, "report.json");
|
|
23183
23363
|
try {
|
|
23184
23364
|
const r = await run(bin.path, ["git", "--no-banner", "--redact", "--report-format", "json", "--report-path", report, "--exit-code", "0", "."], {
|
|
23185
23365
|
cwd: ctx.root,
|
|
23186
23366
|
timeoutMs: 18e4
|
|
23187
23367
|
});
|
|
23188
23368
|
if (r.code !== 0 || !existsSync13(report)) return null;
|
|
23189
|
-
return { hits: JSON.parse(
|
|
23369
|
+
return { hits: JSON.parse(readFileSync13(report, "utf8")), engine: `gitleaks ${bin.version}` };
|
|
23190
23370
|
} catch {
|
|
23191
23371
|
return null;
|
|
23192
23372
|
} finally {
|
|
@@ -23301,7 +23481,7 @@ var envFiles = {
|
|
|
23301
23481
|
var securityChecks = [secrets, envFiles, deps, rls, apiRoutes, headers, ...CONNECTORS.map(toolSettingsCheck)];
|
|
23302
23482
|
|
|
23303
23483
|
// src/checks/structure.ts
|
|
23304
|
-
import
|
|
23484
|
+
import path16 from "path";
|
|
23305
23485
|
var GENERATED = /(?:^|\/)(?:supabase\/migrations|generated|__generated__|\.generated)\/|\.(?:d\.ts|min\.js)$|(?:^|\/)(?:database|supabase)\.types\.ts$|(?:^|\/)components\/ui\//;
|
|
23306
23486
|
function appSource(ctx) {
|
|
23307
23487
|
return ctx.files.filter((f) => isSourceFile(f) && !GENERATED.test(f));
|
|
@@ -23390,7 +23570,7 @@ var duplicates = {
|
|
|
23390
23570
|
const findings = pairs.map(
|
|
23391
23571
|
([a, b, sim]) => makeFinding(this, {
|
|
23392
23572
|
severity: "low",
|
|
23393
|
-
title: `${
|
|
23573
|
+
title: `${path16.basename(a)} and ${path16.basename(b)} are near-duplicates`,
|
|
23394
23574
|
detail: `${a} and ${b} are ${Math.round(sim * 100)}% the same.`,
|
|
23395
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.",
|
|
23396
23576
|
locations: [{ file: a }, { file: b }],
|
|
@@ -23404,7 +23584,7 @@ var duplicates = {
|
|
|
23404
23584
|
};
|
|
23405
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)$/;
|
|
23406
23586
|
function suggestTarget(file2, srcDir) {
|
|
23407
|
-
const base =
|
|
23587
|
+
const base = path16.posix.basename(file2);
|
|
23408
23588
|
const prefix = srcDir ? "src/" : "";
|
|
23409
23589
|
const isComponent = /^[A-Z][A-Za-z0-9]*\.(?:tsx|jsx)$/.test(base);
|
|
23410
23590
|
if (isComponent) return `${prefix}components/${base}`;
|
|
@@ -23454,7 +23634,7 @@ var layout = {
|
|
|
23454
23634
|
return { status: "fail", score: Math.max(0, 1 - misplaced.length / 8), summary: finding.title, findings: [finding] };
|
|
23455
23635
|
}
|
|
23456
23636
|
};
|
|
23457
|
-
var structureChecks = [deadFiles, oversized, duplicates, layout];
|
|
23637
|
+
var structureChecks = [deadFiles, oversized, duplicates, layout, localFiles];
|
|
23458
23638
|
|
|
23459
23639
|
// src/scan.ts
|
|
23460
23640
|
var ALL_CHECKS = [...securityChecks, ...agentChecks, ...foundationChecks, ...structureChecks];
|
|
@@ -23517,21 +23697,21 @@ async function runScan(root, options, onProgress) {
|
|
|
23517
23697
|
}
|
|
23518
23698
|
|
|
23519
23699
|
// src/commands/fix.ts
|
|
23520
|
-
import { existsSync as existsSync18, readFileSync as
|
|
23521
|
-
import
|
|
23700
|
+
import { existsSync as existsSync18, readFileSync as readFileSync20, writeFileSync as writeFileSync6 } from "fs";
|
|
23701
|
+
import path20 from "path";
|
|
23522
23702
|
|
|
23523
23703
|
// src/agent/tools.ts
|
|
23524
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as
|
|
23525
|
-
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";
|
|
23526
23706
|
var MAX_OUTPUT = 12e3;
|
|
23527
23707
|
var MAX_FILE_LINES = 800;
|
|
23528
23708
|
var FORBIDDEN = /^(?:\.git|node_modules|\.next|\.vercel)(?:\/|$)|(?:^|\/)\.env(?:\.(?!example$|sample$|template$)[^/]+)?$/;
|
|
23529
23709
|
function safePath(root, rel) {
|
|
23530
23710
|
if (typeof rel !== "string" || !rel.trim()) throw new Error("A file path is required.");
|
|
23531
|
-
const normalized =
|
|
23532
|
-
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}`);
|
|
23533
23713
|
if (FORBIDDEN.test(normalized)) throw new Error(`Aura doesn\u2019t let the agent touch ${normalized}.`);
|
|
23534
|
-
const full =
|
|
23714
|
+
const full = path17.join(root, normalized);
|
|
23535
23715
|
if (!isInside(root, full)) throw new Error(`Path is outside the repo: ${rel}`);
|
|
23536
23716
|
return normalized;
|
|
23537
23717
|
}
|
|
@@ -23552,10 +23732,10 @@ async function runTool(root, call, allowedCommands2) {
|
|
|
23552
23732
|
}
|
|
23553
23733
|
case "read_file": {
|
|
23554
23734
|
const rel = safePath(root, input2.path);
|
|
23555
|
-
const full =
|
|
23735
|
+
const full = path17.join(root, rel);
|
|
23556
23736
|
if (!existsSync14(full)) return { output: `File not found: ${rel}` };
|
|
23557
23737
|
if (statSync2(full).size > 1e6) return { output: `${rel} is too large to read.` };
|
|
23558
|
-
const lines =
|
|
23738
|
+
const lines = readFileSync14(full, "utf8").split("\n");
|
|
23559
23739
|
const start = Math.max(1, Number(input2.startLine) || 1);
|
|
23560
23740
|
const end = Math.min(lines.length, Number(input2.endLine) || start + MAX_FILE_LINES - 1);
|
|
23561
23741
|
const body = lines.slice(start - 1, end).map((l, i) => `${start + i}: ${l}`).join("\n");
|
|
@@ -23572,7 +23752,7 @@ async function runTool(root, call, allowedCommands2) {
|
|
|
23572
23752
|
if (FORBIDDEN.test(f) || glob && !glob.test(f)) continue;
|
|
23573
23753
|
let text;
|
|
23574
23754
|
try {
|
|
23575
|
-
text =
|
|
23755
|
+
text = readFileSync14(path17.join(root, f), "utf8");
|
|
23576
23756
|
} catch {
|
|
23577
23757
|
continue;
|
|
23578
23758
|
}
|
|
@@ -23591,14 +23771,14 @@ async function runTool(root, call, allowedCommands2) {
|
|
|
23591
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."
|
|
23592
23772
|
};
|
|
23593
23773
|
}
|
|
23594
|
-
mkdirSync4(
|
|
23595
|
-
writeFileSync4(
|
|
23774
|
+
mkdirSync4(path17.dirname(path17.join(root, rel)), { recursive: true });
|
|
23775
|
+
writeFileSync4(path17.join(root, rel), content);
|
|
23596
23776
|
return { output: `Wrote ${rel} (${content.split("\n").length} lines).`, touched: rel };
|
|
23597
23777
|
}
|
|
23598
23778
|
case "delete_file": {
|
|
23599
23779
|
const rel = safePath(root, input2.path);
|
|
23600
|
-
if (!existsSync14(
|
|
23601
|
-
rmSync4(
|
|
23780
|
+
if (!existsSync14(path17.join(root, rel))) return { output: `File not found: ${rel}` };
|
|
23781
|
+
rmSync4(path17.join(root, rel));
|
|
23602
23782
|
return { output: `Deleted ${rel}.`, touched: rel };
|
|
23603
23783
|
}
|
|
23604
23784
|
case "run_command": {
|
|
@@ -23620,7 +23800,7 @@ ${(r.stdout + "\n" + r.stderr).trim().slice(-6e3)}`;
|
|
|
23620
23800
|
|
|
23621
23801
|
// src/api/explain.ts
|
|
23622
23802
|
import { createHash as createHash4 } from "crypto";
|
|
23623
|
-
import { existsSync as existsSync15, readFileSync as
|
|
23803
|
+
import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
|
|
23624
23804
|
|
|
23625
23805
|
// src/report.ts
|
|
23626
23806
|
import pc2 from "picocolors";
|
|
@@ -23809,7 +23989,7 @@ var CACHE_FILE = "cache/explanations.json";
|
|
|
23809
23989
|
var MAX_EXPLAIN = 25;
|
|
23810
23990
|
function loadCache(root) {
|
|
23811
23991
|
try {
|
|
23812
|
-
return JSON.parse(
|
|
23992
|
+
return JSON.parse(readFileSync15(auraPath(root, CACHE_FILE), "utf8"));
|
|
23813
23993
|
} catch {
|
|
23814
23994
|
return {};
|
|
23815
23995
|
}
|
|
@@ -23831,7 +24011,7 @@ function excerptFor(root, f, context = 4) {
|
|
|
23831
24011
|
function productContext(root) {
|
|
23832
24012
|
const file2 = auraPath(root, "brief.md");
|
|
23833
24013
|
if (!existsSync15(file2)) return void 0;
|
|
23834
|
-
return redact(
|
|
24014
|
+
return redact(readFileSync15(file2, "utf8")).slice(0, 1800);
|
|
23835
24015
|
}
|
|
23836
24016
|
async function addExplanations(root, scan, client) {
|
|
23837
24017
|
const cache = loadCache(root);
|
|
@@ -23875,22 +24055,30 @@ async function addExplanations(root, scan, client) {
|
|
|
23875
24055
|
|
|
23876
24056
|
// src/commands/simple.ts
|
|
23877
24057
|
import { execFileSync } from "child_process";
|
|
23878
|
-
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as
|
|
23879
|
-
import
|
|
24058
|
+
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync19 } from "fs";
|
|
24059
|
+
import path19 from "path";
|
|
23880
24060
|
import YAML2 from "yaml";
|
|
23881
24061
|
|
|
23882
24062
|
// src/commands/inventory.ts
|
|
23883
|
-
import { readFileSync as
|
|
24063
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
23884
24064
|
function accountState(root, services) {
|
|
23885
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
|
+
);
|
|
23886
24069
|
return {
|
|
23887
24070
|
inventoryComplete: services.every((s) => inv.services[s]?.owner) && Boolean(inv.githubOrg),
|
|
23888
|
-
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
|
+
)
|
|
23889
24077
|
};
|
|
23890
24078
|
}
|
|
23891
24079
|
function loadInventory(root) {
|
|
23892
24080
|
try {
|
|
23893
|
-
return JSON.parse(
|
|
24081
|
+
return JSON.parse(readFileSync16(auraPath(root, "inventory.json"), "utf8"));
|
|
23894
24082
|
} catch {
|
|
23895
24083
|
return { version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), services: {} };
|
|
23896
24084
|
}
|
|
@@ -23977,10 +24165,10 @@ async function inventoryCommand(flags) {
|
|
|
23977
24165
|
}
|
|
23978
24166
|
|
|
23979
24167
|
// src/history.ts
|
|
23980
|
-
import { readFileSync as
|
|
24168
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
23981
24169
|
function loadHistory(root) {
|
|
23982
24170
|
try {
|
|
23983
|
-
const data = JSON.parse(
|
|
24171
|
+
const data = JSON.parse(readFileSync17(auraPath(root, "history.json"), "utf8"));
|
|
23984
24172
|
return Array.isArray(data) ? data : [];
|
|
23985
24173
|
} catch {
|
|
23986
24174
|
return [];
|
|
@@ -24028,10 +24216,10 @@ function banner(colors = pc3.isColorSupported) {
|
|
|
24028
24216
|
// src/api/connect.ts
|
|
24029
24217
|
import { spawn as spawn2 } from "child_process";
|
|
24030
24218
|
import { constants, generateKeyPairSync, privateDecrypt } from "crypto";
|
|
24031
|
-
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";
|
|
24032
24220
|
import os5 from "os";
|
|
24033
|
-
import
|
|
24034
|
-
var pendingFile = () =>
|
|
24221
|
+
import path18 from "path";
|
|
24222
|
+
var pendingFile = () => path18.join(os5.homedir(), ".aura", "pending-connect.json");
|
|
24035
24223
|
async function postJson(apiUrl, route, body) {
|
|
24036
24224
|
let res;
|
|
24037
24225
|
try {
|
|
@@ -24050,7 +24238,7 @@ async function postJson(apiUrl, route, body) {
|
|
|
24050
24238
|
}
|
|
24051
24239
|
function loadPending(apiUrl, now) {
|
|
24052
24240
|
try {
|
|
24053
|
-
const p2 = JSON.parse(
|
|
24241
|
+
const p2 = JSON.parse(readFileSync18(pendingFile(), "utf8"));
|
|
24054
24242
|
if (p2.apiUrl === apiUrl && Date.parse(p2.expiresAt) - now > 6e4) return p2;
|
|
24055
24243
|
} catch {
|
|
24056
24244
|
}
|
|
@@ -24069,7 +24257,7 @@ async function startOrResumeConnect(apiUrl, now = Date.now()) {
|
|
|
24069
24257
|
});
|
|
24070
24258
|
const res = ConnectStartResponse.parse(await postJson(apiUrl, "connect", { publicKey }));
|
|
24071
24259
|
const pending = { apiUrl, privateKey, ...res };
|
|
24072
|
-
mkdirSync5(
|
|
24260
|
+
mkdirSync5(path18.dirname(pendingFile()), { recursive: true });
|
|
24073
24261
|
writeFileSync5(pendingFile(), JSON.stringify(pending, null, 2) + "\n", { mode: 384 });
|
|
24074
24262
|
return { ...pending, resumed: false };
|
|
24075
24263
|
}
|
|
@@ -24217,7 +24405,7 @@ async function latestScan(root) {
|
|
|
24217
24405
|
const file2 = auraPath(root, "scan.json");
|
|
24218
24406
|
if (existsSync17(file2)) {
|
|
24219
24407
|
try {
|
|
24220
|
-
const scan = JSON.parse(
|
|
24408
|
+
const scan = JSON.parse(readFileSync19(file2, "utf8"));
|
|
24221
24409
|
const head = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
24222
24410
|
const dirty = execFileSync("git", ["status", "--porcelain"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
24223
24411
|
if (scan.commit === head && !dirty && Date.now() - Date.parse(scan.scannedAt) < 24 * 36e5) return scan;
|
|
@@ -24332,9 +24520,9 @@ async function configCommand(key, value) {
|
|
|
24332
24520
|
return 0;
|
|
24333
24521
|
}
|
|
24334
24522
|
function loadGuides() {
|
|
24335
|
-
const dir =
|
|
24523
|
+
const dir = path19.join(templatesDir(), "guides");
|
|
24336
24524
|
return readdirSync5(dir).filter((f) => f.endsWith(".md")).map((f) => {
|
|
24337
|
-
const text =
|
|
24525
|
+
const text = readFileSync19(path19.join(dir, f), "utf8");
|
|
24338
24526
|
const fm = text.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
24339
24527
|
const meta3 = fm ? YAML2.parse(fm[1]) : {};
|
|
24340
24528
|
return { id: f.replace(/\.md$/, ""), title: meta3.title ?? f, summary: meta3.summary ?? "", body: fm ? text.slice(fm[0].length) : text };
|
|
@@ -24402,7 +24590,7 @@ function allowedCommands(root, pm, typescript) {
|
|
|
24402
24590
|
const pkg = readJson(root, "package.json");
|
|
24403
24591
|
const run2 = pmCommands(pm).run;
|
|
24404
24592
|
const cmds = ["test", "lint", "typecheck", "build"].filter((s) => pkg?.scripts?.[s]).map((s) => `${run2} ${s}`);
|
|
24405
|
-
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`);
|
|
24406
24594
|
return cmds;
|
|
24407
24595
|
}
|
|
24408
24596
|
function taskFor(root, scan, action2, findings) {
|
|
@@ -24453,8 +24641,8 @@ async function untrackEnvFiles(root, findings) {
|
|
|
24453
24641
|
const files = findings.filter((f) => f.checkId === "security.env-files").map((f) => f.locations[0].file);
|
|
24454
24642
|
for (const f of files) await git(root, "rm", "-q", "--cached", "--", f);
|
|
24455
24643
|
if (files.length) {
|
|
24456
|
-
const gi =
|
|
24457
|
-
const current2 = existsSync18(gi) ?
|
|
24644
|
+
const gi = path20.join(root, ".gitignore");
|
|
24645
|
+
const current2 = existsSync18(gi) ? readFileSync20(gi, "utf8") : "";
|
|
24458
24646
|
if (!/^\.env\*?$/m.test(current2)) writeFileSync6(gi, `${current2.trimEnd()}
|
|
24459
24647
|
|
|
24460
24648
|
# Environment files hold secrets
|
|
@@ -24568,7 +24756,7 @@ ${pc.bold(action2.title)} ${pc.dim(`${findings.length} finding(s)`)}`);
|
|
|
24568
24756
|
messages.push({ role: "tool", content: results });
|
|
24569
24757
|
}
|
|
24570
24758
|
s.stop(finish ? "Aura\u2019s agent finished" : "Aura\u2019s agent stopped");
|
|
24571
|
-
const
|
|
24759
|
+
const untracked2 = action2.id === "remove-leaked-secrets" ? await untrackEnvFiles(root, findings) : [];
|
|
24572
24760
|
for (const f of touched) await git(root, "add", "-A", "--", f);
|
|
24573
24761
|
const staged = (await git(root, "diff", "--cached", "--name-only")).stdout.trim();
|
|
24574
24762
|
if (!staged) {
|
|
@@ -24590,7 +24778,7 @@ ${pc.bold(action2.title)} ${pc.dim(`${findings.length} finding(s)`)}`);
|
|
|
24590
24778
|
"",
|
|
24591
24779
|
...findings.slice(0, 20).map((f) => `- ${f.title}`),
|
|
24592
24780
|
"",
|
|
24593
|
-
...
|
|
24781
|
+
...untracked2.length ? ["### Removed from git", "", ...untracked2.map((f) => `- \`${f}\` (still on your computer, now ignored by git)`), ""] : [],
|
|
24594
24782
|
...finish?.testsRun.length ? ["### Checks run", "", ...finish.testsRun.map((t) => `- ${t}`), ""] : [],
|
|
24595
24783
|
...finish?.risks.length ? ["### Things to check before merging", "", ...finish.risks.map((r) => `- ${r}`), ""] : [],
|
|
24596
24784
|
...guides.length ? [
|
|
@@ -24662,7 +24850,7 @@ async function secureCommand(connector, flags, report = {}, fake) {
|
|
|
24662
24850
|
return 1;
|
|
24663
24851
|
}
|
|
24664
24852
|
const { ctx, target } = connection;
|
|
24665
|
-
const states = await readSettings(connector, ctx);
|
|
24853
|
+
const states = withRefusals(await readSettings(connector, ctx), loadSnapshots(root)[connector.id]);
|
|
24666
24854
|
saveSnapshot(root, connector.id, states);
|
|
24667
24855
|
const toTurnOn = connector.settings.filter((s) => states[s.id].state === "off").map((s) => s.id);
|
|
24668
24856
|
console.log(`
|
|
@@ -24726,10 +24914,17 @@ Nothing was changed. Show the user the changes above, and once they agree run: a
|
|
|
24726
24914
|
}
|
|
24727
24915
|
report.outcome = results.some((r) => r.result === "failed") ? "failed" : "settings_changed";
|
|
24728
24916
|
}
|
|
24917
|
+
const refusedNow = Object.fromEntries(results.filter((r) => r.result === "unavailable" && states[r.id].state === "off" && r.why).map((r) => [r.id, r.why]));
|
|
24729
24918
|
saveSnapshot(
|
|
24730
24919
|
root,
|
|
24731
24920
|
connector.id,
|
|
24732
|
-
Object.fromEntries(
|
|
24921
|
+
Object.fromEntries(
|
|
24922
|
+
Object.entries(states).map(([id, s]) => {
|
|
24923
|
+
const r = results.find((x) => x.id === id);
|
|
24924
|
+
return [id, r?.result === "turned-on" ? { state: "on" } : refusedNow[id] ? { state: "unavailable", why: refusedNow[id] } : s];
|
|
24925
|
+
})
|
|
24926
|
+
),
|
|
24927
|
+
refusedNow
|
|
24733
24928
|
);
|
|
24734
24929
|
const left = await connector.leftForYou(ctx, results);
|
|
24735
24930
|
if (left.length) {
|
|
@@ -24791,7 +24986,7 @@ async function applyAction(actionId, flags, report) {
|
|
|
24791
24986
|
);
|
|
24792
24987
|
}
|
|
24793
24988
|
const g = { root, scan, ctx, agents: [...agents], vars: templateVars(root, scan, ctx.pkg), skills: skills2 };
|
|
24794
|
-
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." };
|
|
24795
24990
|
if ("skip" in plan || plan.changes.length === 0 && !plan.commands?.length && !plan.devDeps?.length) {
|
|
24796
24991
|
say.info("skip" in plan ? plan.skip : "Nothing to change.");
|
|
24797
24992
|
report.outcome = "no_changes";
|
|
@@ -24800,7 +24995,7 @@ async function applyAction(actionId, flags, report) {
|
|
|
24800
24995
|
console.log(`
|
|
24801
24996
|
${pc.bold(action2.title)}`);
|
|
24802
24997
|
for (const c of plan.changes)
|
|
24803
|
-
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}`);
|
|
24804
24999
|
const pm = pmCommands(scan.stack.packageManager);
|
|
24805
25000
|
if (plan.devDeps?.length) console.log(` ${pc.cyan("install")} ${plan.devDeps.join(", ")} ${pc.dim(`(${pm.addDev})`)}`);
|
|
24806
25001
|
for (const cmd of plan.commands ?? []) console.log(` ${pc.cyan("run ")} ${cmd.join(" ")}`);
|
|
@@ -24832,7 +25027,7 @@ ${pc.bold(action2.title)}`);
|
|
|
24832
25027
|
report.outcome = "cancelled";
|
|
24833
25028
|
return 0;
|
|
24834
25029
|
}
|
|
24835
|
-
const tsc =
|
|
25030
|
+
const tsc = path21.join(root, "node_modules", ".bin", process.platform === "win32" ? "tsc.cmd" : "tsc");
|
|
24836
25031
|
const checkTypes = plan.verifyTypes && scan.stack.typescript && existsSync19(tsc);
|
|
24837
25032
|
const typeErrors = async () => {
|
|
24838
25033
|
const r = await run(tsc, ["--noEmit", "--pretty", "false"], { cwd: root, timeoutMs: 3e5 });
|
|
@@ -24942,17 +25137,17 @@ async function undoPlan(root, plan) {
|
|
|
24942
25137
|
for (const file2 of touched) {
|
|
24943
25138
|
const inHead = (await git(root, "cat-file", "-e", `HEAD:${file2}`)).code === 0;
|
|
24944
25139
|
if (inHead) await git(root, "checkout", "HEAD", "--", file2);
|
|
24945
|
-
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 });
|
|
24946
25141
|
}
|
|
24947
25142
|
}
|
|
24948
25143
|
|
|
24949
25144
|
// src/commands/init.ts
|
|
24950
|
-
import { existsSync as existsSync20, readFileSync as
|
|
25145
|
+
import { existsSync as existsSync20, readFileSync as readFileSync22 } from "fs";
|
|
24951
25146
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
24952
|
-
import
|
|
25147
|
+
import path25 from "path";
|
|
24953
25148
|
|
|
24954
25149
|
// src/brief.ts
|
|
24955
|
-
import
|
|
25150
|
+
import path22 from "path";
|
|
24956
25151
|
function repoSummary(ctx) {
|
|
24957
25152
|
const routes = ctx.files.filter((f) => /^(?:src\/)?app\/(?:.+\/)?(?:page|route)\.[cm]?[jt]sx?$/.test(f) || /^(?:src\/)?pages\/.+\.[cm]?[jt]sx?$/.test(f)).map(
|
|
24958
25153
|
(f) => "/" + f.replace(/^(?:src\/)?(?:app|pages)\//, "").replace(/(?:^|\/)(?:page|route|index)\.[cm]?[jt]sx?$|\.[cm]?[jt]sx?$/, "").replace(/\([^)]+\)\//g, "")
|
|
@@ -24968,7 +25163,7 @@ function repoSummary(ctx) {
|
|
|
24968
25163
|
const readme2 = ctx.files.find((f) => /^readme\.mdx?$/i.test(f));
|
|
24969
25164
|
const readmeText = readme2 ? ctx.read(readme2) ?? "" : "";
|
|
24970
25165
|
return {
|
|
24971
|
-
name: ctx.pkg?.name ??
|
|
25166
|
+
name: ctx.pkg?.name ?? path22.basename(ctx.root),
|
|
24972
25167
|
packageJson: ctx.pkg ? {
|
|
24973
25168
|
name: ctx.pkg.name,
|
|
24974
25169
|
description: ctx.pkg.description,
|
|
@@ -25062,9 +25257,9 @@ function installFallback(result, platform = process.platform) {
|
|
|
25062
25257
|
// src/core/claude-plugin.ts
|
|
25063
25258
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
25064
25259
|
import os6 from "os";
|
|
25065
|
-
import
|
|
25260
|
+
import path23 from "path";
|
|
25066
25261
|
var CLAUDE_PLUGIN_ID = "aura@aura";
|
|
25067
|
-
var marketplaceDir = () =>
|
|
25262
|
+
var marketplaceDir = () => path23.join(os6.homedir(), ".aura", "claude-marketplace");
|
|
25068
25263
|
var MARKETPLACE = {
|
|
25069
25264
|
name: "aura",
|
|
25070
25265
|
description: "Aura for Claude Code",
|
|
@@ -25079,8 +25274,8 @@ var MARKETPLACE = {
|
|
|
25079
25274
|
};
|
|
25080
25275
|
function writeMarketplace() {
|
|
25081
25276
|
const dir = marketplaceDir();
|
|
25082
|
-
mkdirSync6(
|
|
25083
|
-
writeFileSync7(
|
|
25277
|
+
mkdirSync6(path23.join(dir, ".claude-plugin"), { recursive: true });
|
|
25278
|
+
writeFileSync7(path23.join(dir, ".claude-plugin", "marketplace.json"), `${JSON.stringify(MARKETPLACE, null, 2)}
|
|
25084
25279
|
`);
|
|
25085
25280
|
return dir;
|
|
25086
25281
|
}
|
|
@@ -25184,29 +25379,29 @@ function describeInferred(inferred) {
|
|
|
25184
25379
|
// src/commands/scan.ts
|
|
25185
25380
|
import { mkdtempSync as mkdtempSync3, rmSync as rmSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
25186
25381
|
import os7 from "os";
|
|
25187
|
-
import
|
|
25382
|
+
import path24 from "path";
|
|
25188
25383
|
|
|
25189
25384
|
// src/connectors/refresh.ts
|
|
25190
|
-
import { readFileSync as
|
|
25385
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
25191
25386
|
async function refreshToolSettings(root) {
|
|
25192
25387
|
if (process.env.AURA_NO_CONNECTORS) return;
|
|
25193
25388
|
let previous = null;
|
|
25194
25389
|
try {
|
|
25195
|
-
previous = JSON.parse(
|
|
25390
|
+
previous = JSON.parse(readFileSync21(auraPath(root, "scan.json"), "utf8"));
|
|
25196
25391
|
} catch {
|
|
25197
25392
|
}
|
|
25198
25393
|
const snapshots = loadSnapshots(root);
|
|
25199
25394
|
for (const connector of CONNECTORS) {
|
|
25200
25395
|
if (!(previous ? connector.detect(previous) : connector.id === "github") || !isStale(snapshots[connector.id])) continue;
|
|
25201
25396
|
const connection = await connector.connect(root).catch(() => null);
|
|
25202
|
-
if (connection?.ok) saveSnapshot(root, connector.id, await readSettings(connector, connection.ctx));
|
|
25397
|
+
if (connection?.ok) saveSnapshot(root, connector.id, withRefusals(await readSettings(connector, connection.ctx), snapshots[connector.id]));
|
|
25203
25398
|
}
|
|
25204
25399
|
}
|
|
25205
25400
|
|
|
25206
25401
|
// src/commands/scan.ts
|
|
25207
25402
|
async function scanRef(root, ref, opts) {
|
|
25208
|
-
const dir = mkdtempSync3(
|
|
25209
|
-
const wt =
|
|
25403
|
+
const dir = mkdtempSync3(path24.join(os7.tmpdir(), "aura-base-"));
|
|
25404
|
+
const wt = path24.join(dir, "tree");
|
|
25210
25405
|
const add = await run("git", ["worktree", "add", "--detach", wt, ref], { cwd: root });
|
|
25211
25406
|
if (add.code !== 0) {
|
|
25212
25407
|
rmSync7(dir, { recursive: true, force: true });
|
|
@@ -25296,7 +25491,7 @@ async function scanCommand(flags) {
|
|
|
25296
25491
|
// src/commands/init.ts
|
|
25297
25492
|
var AGENT_IDS = ["claude", "codex", "cursor", "pi"];
|
|
25298
25493
|
function detectAgents(root) {
|
|
25299
|
-
return detectAgentsFrom((rel) => existsSync20(
|
|
25494
|
+
return detectAgentsFrom((rel) => existsSync20(path25.join(root, rel)));
|
|
25300
25495
|
}
|
|
25301
25496
|
function openEditor(file2) {
|
|
25302
25497
|
const fallback = process.platform === "win32" ? "notepad" : spawnSync3("sh", ["-c", "command -v nano"]).status === 0 ? "nano" : "vi";
|
|
@@ -25411,13 +25606,13 @@ async function initCommand(flags) {
|
|
|
25411
25606
|
let brief2 = null;
|
|
25412
25607
|
let keptBrief = false;
|
|
25413
25608
|
if (flags.briefFile) {
|
|
25414
|
-
brief2 =
|
|
25609
|
+
brief2 = readFileSync22(flags.briefFile, "utf8");
|
|
25415
25610
|
} else if (!flags.answers && existsSync20(briefPath) && !(tty && unwrap(await p.confirm({ message: "A product brief already exists. Draft a new one?", initialValue: false })))) {
|
|
25416
25611
|
keptBrief = true;
|
|
25417
25612
|
} else {
|
|
25418
25613
|
let answers = null;
|
|
25419
25614
|
if (flags.answers) {
|
|
25420
|
-
answers = OnboardingAnswersSchema.parse(JSON.parse(
|
|
25615
|
+
answers = OnboardingAnswersSchema.parse(JSON.parse(readFileSync22(flags.answers, "utf8")));
|
|
25421
25616
|
} else if (tty) {
|
|
25422
25617
|
const inferred = inferAnswers(ctx);
|
|
25423
25618
|
p.note(describeInferred(inferred).join("\n"), "Here\u2019s what Aura found about your app");
|
|
@@ -25549,7 +25744,7 @@ ${pc.magenta("\u2726")} ${pc.bold("Give the user the aura command")}`);
|
|
|
25549
25744
|
}
|
|
25550
25745
|
|
|
25551
25746
|
// src/commands/summary.ts
|
|
25552
|
-
import
|
|
25747
|
+
import path26 from "path";
|
|
25553
25748
|
async function activity(root, since) {
|
|
25554
25749
|
const log = await git(root, "log", `--since=${since.toISOString()}`, "--no-merges", "--format=%H%x09%an%x09%s");
|
|
25555
25750
|
const commits = log.stdout.split("\n").filter(Boolean).slice(0, 300).map((l) => {
|
|
@@ -25606,7 +25801,7 @@ async function summaryCommand(flags) {
|
|
|
25606
25801
|
const beforeIds = new Set(before ? allFindings(before).map((f) => f.id) : []);
|
|
25607
25802
|
const nowIds = new Set(allFindings(now).map((f) => f.id));
|
|
25608
25803
|
const req = {
|
|
25609
|
-
repo:
|
|
25804
|
+
repo: path26.basename(root),
|
|
25610
25805
|
since: since.toISOString(),
|
|
25611
25806
|
until: until.toISOString(),
|
|
25612
25807
|
product: productContext(root),
|
|
@@ -25654,11 +25849,11 @@ ${body}`);
|
|
|
25654
25849
|
import pc4 from "picocolors";
|
|
25655
25850
|
|
|
25656
25851
|
// src/core/update-check.ts
|
|
25657
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
25852
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "fs";
|
|
25658
25853
|
import os8 from "os";
|
|
25659
|
-
import
|
|
25854
|
+
import path27 from "path";
|
|
25660
25855
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
25661
|
-
var cacheFile = () =>
|
|
25856
|
+
var cacheFile = () => path27.join(os8.homedir(), ".aura", "update-check.json");
|
|
25662
25857
|
function isNewer(a, b) {
|
|
25663
25858
|
if (!/^\d+\.\d+\.\d+$/.test(a) || !/^\d+\.\d+\.\d+$/.test(b)) return false;
|
|
25664
25859
|
const [x, y] = [a, b].map((v) => v.split(".").map(Number));
|
|
@@ -25678,13 +25873,13 @@ async function newerVersion(opts = {}) {
|
|
|
25678
25873
|
const now = opts.now ?? Date.now();
|
|
25679
25874
|
let latest = null;
|
|
25680
25875
|
try {
|
|
25681
|
-
const cached2 = JSON.parse(
|
|
25876
|
+
const cached2 = JSON.parse(readFileSync23(cacheFile(), "utf8"));
|
|
25682
25877
|
if (now - cached2.checkedAt < DAY_MS) latest = cached2.latest;
|
|
25683
25878
|
else throw new Error("stale");
|
|
25684
25879
|
} catch {
|
|
25685
25880
|
latest = await (opts.fetchLatest ?? fetchLatest)();
|
|
25686
25881
|
try {
|
|
25687
|
-
mkdirSync7(
|
|
25882
|
+
mkdirSync7(path27.dirname(cacheFile()), { recursive: true });
|
|
25688
25883
|
writeFileSync9(cacheFile(), JSON.stringify({ checkedAt: now, latest }));
|
|
25689
25884
|
} catch {
|
|
25690
25885
|
}
|