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 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 path20 from "path";
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, path27) {
1013
- if (!path27)
1012
+ function getElementAtPath(obj, path28) {
1013
+ if (!path28)
1014
1014
  return obj;
1015
- return path27.reduce((acc, key) => acc?.[key], obj);
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(path27, issues) {
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(path27);
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, path27 = []) => {
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 }, [...path27, ...issue2.path]));
1812
+ issue2.errors.map((issues) => processError({ issues }, [...path28, ...issue2.path]));
1813
1813
  } else if (issue2.code === "invalid_key") {
1814
- processError({ issues: issue2.issues }, [...path27, ...issue2.path]);
1814
+ processError({ issues: issue2.issues }, [...path28, ...issue2.path]);
1815
1815
  } else if (issue2.code === "invalid_element") {
1816
- processError({ issues: issue2.issues }, [...path27, ...issue2.path]);
1816
+ processError({ issues: issue2.issues }, [...path28, ...issue2.path]);
1817
1817
  } else {
1818
- const fullpath = [...path27, ...issue2.path];
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, path27 = []) => {
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 }, [...path27, ...issue2.path]));
1861
+ issue2.errors.map((issues) => processError({ issues }, [...path28, ...issue2.path]));
1862
1862
  } else if (issue2.code === "invalid_key") {
1863
- processError({ issues: issue2.issues }, [...path27, ...issue2.path]);
1863
+ processError({ issues: issue2.issues }, [...path28, ...issue2.path]);
1864
1864
  } else if (issue2.code === "invalid_element") {
1865
- processError({ issues: issue2.issues }, [...path27, ...issue2.path]);
1865
+ processError({ issues: issue2.issues }, [...path28, ...issue2.path]);
1866
1866
  } else {
1867
- const fullpath = [...path27, ...issue2.path];
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 path27 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1907
- for (const seg of path27) {
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 path27 = ref.slice(1).split("/").filter(Boolean);
19010
- if (path27.length === 0) {
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 (path27[0] === defsKey) {
19015
- const key = path27[1] === void 0 ? void 0 : decodeJSONPointerSegment(path27[1]);
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: (path27) => {
20362
- if (!cache.has(path27)) cache.set(path27, gh(["api", path27]));
20363
- return cache.get(path27);
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 project = await ctx.get(`/v1/projects/${ref}`);
20540
- if (!project?.name) return { ok: false, problem: `Couldn\u2019t read the Supabase project ${ref}. Check your account can open it.`, errorType: "NoProject" };
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.8",
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 readFileSync9 } from "fs";
21445
- import path12 from "path";
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/agent-files.ts
21796
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
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 = path11.join(root, file2);
21800
- return existsSync8(full) ? readFileSync8(full, "utf8") : null;
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 = path12.join(root, file2);
21845
- return existsSync9(full) ? readFileSync9(full, "utf8") : null;
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(path12.join(root, f)));
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(path12.join(g.root, "docs/index.md"))) changes.push({ path: "docs/index.md", content: render("docs/index.md", g.vars) });
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(path12.join(root, file2))) return { skip: `${file2} already exists.` };
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(path12.join(root, f))) ?? ".env.example";
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(path12.join(root, ".husky/pre-commit"))) return { skip: "A husky pre-commit hook already exists." };
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(path12.join(root, f)))) {
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 = path12.posix.relative(path12.posix.dirname(fromFile), noExt);
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(path12.join(root, to))).slice(0, MAX_MOVES);
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
- function foundationPlan(g) {
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, path27, body, timeoutMs = 6e4) {
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/${path27}`, {
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 readFileSync10 } from "fs";
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(readFileSync10(auraPath(root, FILE), "utf8")) : [];
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 path27 = await import("path");
22603
- const tsc = path27.join(ctx.root, "node_modules", ".bin", process.platform === "win32" ? "tsc.cmd" : "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 readFileSync11 } from "fs";
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(readFileSync11(auraPath(root, FILE2), "utf8")) : {};
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
- all[toolId] = { checkedAt: (/* @__PURE__ */ new Date()).toISOString(), settings: Object.fromEntries(Object.entries(states).map(([id, s]) => [id, s.state])) };
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 path13 from "path";
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" ? path13.win32.join(systemRoot || "C:\\Windows", "System32", "tar.exe") : "tar";
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 = path13.join(auraBinDir(), `${name}-${spec.version}`, exe);
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 = path13.dirname(target);
23037
+ const dir = path14.dirname(target);
22858
23038
  mkdirSync3(dir, { recursive: true });
22859
- const tmp = mkdtempSync(path13.join(os3.tmpdir(), `aura-${name}-`));
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 = path13.join(tmp, asset.file);
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(path13.join(tmp, exe), target);
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 readFileSync12, rmSync as rmSync3 } from "fs";
23321
+ import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readFileSync as readFileSync13, rmSync as rmSync3 } from "fs";
23142
23322
  import os4 from "os";
23143
- import path14 from "path";
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(path14.join(os4.tmpdir(), "aura-gitleaks-"));
23182
- const report = path14.join(dir, "report.json");
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(readFileSync12(report, "utf8")), engine: `gitleaks ${bin.version}` };
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 path15 from "path";
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: `${path15.basename(a)} and ${path15.basename(b)} are near-duplicates`,
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 = path15.posix.basename(file2);
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 readFileSync19, writeFileSync as writeFileSync6 } from "fs";
23521
- import path19 from "path";
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 readFileSync13, rmSync as rmSync4, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
23525
- import path16 from "path";
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 = path16.posix.normalize(rel.replace(/\\/g, "/").replace(/^\.\//, ""));
23532
- if (normalized.startsWith("../") || normalized === ".." || path16.isAbsolute(normalized)) throw new Error(`Path is outside the repo: ${rel}`);
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 = path16.join(root, normalized);
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 = path16.join(root, rel);
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 = readFileSync13(full, "utf8").split("\n");
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 = readFileSync13(path16.join(root, f), "utf8");
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(path16.dirname(path16.join(root, rel)), { recursive: true });
23595
- writeFileSync4(path16.join(root, rel), content);
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(path16.join(root, rel))) return { output: `File not found: ${rel}` };
23601
- rmSync4(path16.join(root, rel));
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 readFileSync14 } from "fs";
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(readFileSync14(auraPath(root, CACHE_FILE), "utf8"));
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(readFileSync14(file2, "utf8")).slice(0, 1800);
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 readFileSync18 } from "fs";
23879
- import path18 from "path";
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 readFileSync15 } from "fs";
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: { ...inv.secured, github: Boolean(inv.githubSecurity || inv.secured?.github) }
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(readFileSync15(auraPath(root, "inventory.json"), "utf8"));
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 readFileSync16 } from "fs";
24168
+ import { readFileSync as readFileSync17 } from "fs";
23981
24169
  function loadHistory(root) {
23982
24170
  try {
23983
- const data = JSON.parse(readFileSync16(auraPath(root, "history.json"), "utf8"));
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 readFileSync17, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
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 path17 from "path";
24034
- var pendingFile = () => path17.join(os5.homedir(), ".aura", "pending-connect.json");
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(readFileSync17(pendingFile(), "utf8"));
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(path17.dirname(pendingFile()), { recursive: true });
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(readFileSync18(file2, "utf8"));
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 = path18.join(templatesDir(), "guides");
24523
+ const dir = path19.join(templatesDir(), "guides");
24336
24524
  return readdirSync5(dir).filter((f) => f.endsWith(".md")).map((f) => {
24337
- const text = readFileSync18(path18.join(dir, f), "utf8");
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(path19.join(root, "node_modules/.bin/tsc"))) cmds.push(`${pmCommands(pm).exec} tsc --noEmit`);
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 = path19.join(root, ".gitignore");
24457
- const current2 = existsSync18(gi) ? readFileSync19(gi, "utf8") : "";
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 untracked = action2.id === "remove-leaked-secrets" ? await untrackEnvFiles(root, findings) : [];
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
- ...untracked.length ? ["### Removed from git", "", ...untracked.map((f) => `- \`${f}\` (still on your computer, now ignored by git)`), ""] : [],
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(Object.entries(states).map(([id, s]) => [id, results.find((r) => r.id === id)?.result === "turned-on" ? { state: "on" } : s]))
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(path20.join(root, c.path)) ? pc.yellow("update") : pc.green("add ")} ${c.path}`);
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 = path20.join(root, "node_modules", ".bin", process.platform === "win32" ? "tsc.cmd" : "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(path20.join(root, file2), { force: true });
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 readFileSync21 } from "fs";
25145
+ import { existsSync as existsSync20, readFileSync as readFileSync22 } from "fs";
24951
25146
  import { spawnSync as spawnSync3 } from "child_process";
24952
- import path24 from "path";
25147
+ import path25 from "path";
24953
25148
 
24954
25149
  // src/brief.ts
24955
- import path21 from "path";
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 ?? path21.basename(ctx.root),
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 path22 from "path";
25260
+ import path23 from "path";
25066
25261
  var CLAUDE_PLUGIN_ID = "aura@aura";
25067
- var marketplaceDir = () => path22.join(os6.homedir(), ".aura", "claude-marketplace");
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(path22.join(dir, ".claude-plugin"), { recursive: true });
25083
- writeFileSync7(path22.join(dir, ".claude-plugin", "marketplace.json"), `${JSON.stringify(MARKETPLACE, null, 2)}
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 path23 from "path";
25382
+ import path24 from "path";
25188
25383
 
25189
25384
  // src/connectors/refresh.ts
25190
- import { readFileSync as readFileSync20 } from "fs";
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(readFileSync20(auraPath(root, "scan.json"), "utf8"));
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(path23.join(os7.tmpdir(), "aura-base-"));
25209
- const wt = path23.join(dir, "tree");
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(path24.join(root, rel)));
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 = readFileSync21(flags.briefFile, "utf8");
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(readFileSync21(flags.answers, "utf8")));
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 path25 from "path";
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: path25.basename(root),
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 readFileSync22, writeFileSync as writeFileSync9 } from "fs";
25852
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "fs";
25658
25853
  import os8 from "os";
25659
- import path26 from "path";
25854
+ import path27 from "path";
25660
25855
  var DAY_MS = 24 * 60 * 60 * 1e3;
25661
- var cacheFile = () => path26.join(os8.homedir(), ".aura", "update-check.json");
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(readFileSync22(cacheFile(), "utf8"));
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(path26.dirname(cacheFile()), { recursive: true });
25882
+ mkdirSync7(path27.dirname(cacheFile()), { recursive: true });
25688
25883
  writeFileSync9(cacheFile(), JSON.stringify({ checkedAt: now, latest }));
25689
25884
  } catch {
25690
25885
  }