getaura 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,9 @@ Your agent runs the setup and sends you a link to connect your pilot key in the
18
18
  npx getaura init
19
19
  ```
20
20
 
21
- `init` checks git and the GitHub CLI, asks which coding agent you use, opens your browser to connect your pilot key, drafts your product brief, installs the Aura skill and runs your first scan.
21
+ `init` checks git and the GitHub CLI, asks which coding agent you use, opens your browser to connect your pilot key, drafts your product brief, installs the Aura skill, offers to install the `aura` command and runs your first scan. After that, run `aura scan` from any folder.
22
+
23
+ To install the `aura` command yourself: `npm install -g getaura`. Without it, use `npx getaura <command>` (for example `npx getaura scan`).
22
24
 
23
25
  ## What you get
24
26
 
package/dist/index.js CHANGED
@@ -20031,16 +20031,20 @@ import { fileURLToPath } from "url";
20031
20031
 
20032
20032
  // src/core/exec.ts
20033
20033
  import { spawn, spawnSync } from "child_process";
20034
+ var WINDOWS_PROGRAMS = /* @__PURE__ */ new Set(["git", "gh", "node", "tar", "where"]);
20035
+ function quoteForCmd(arg) {
20036
+ return /^[\w@%+=:,./\\-]+$/.test(arg) ? arg : `"${arg.replace(/"/g, '\\"')}"`;
20037
+ }
20038
+ function spawnPlan(cmd, args, platform = process.platform) {
20039
+ const direct = platform !== "win32" || cmd.includes("/") || cmd.includes("\\") || WINDOWS_PROGRAMS.has(cmd.toLowerCase());
20040
+ return direct ? { args, shell: false } : { args: args.map(quoteForCmd), shell: true };
20041
+ }
20034
20042
  function run(cmd, args, opts = {}) {
20035
20043
  return new Promise((resolve) => {
20036
20044
  let child;
20037
20045
  try {
20038
- child = spawn(cmd, args, {
20039
- cwd: opts.cwd,
20040
- env: opts.env ?? process.env,
20041
- stdio: ["pipe", "pipe", "pipe"],
20042
- shell: process.platform === "win32" && !cmd.includes("/") && !cmd.includes("\\")
20043
- });
20046
+ const plan = spawnPlan(cmd, args);
20047
+ child = spawn(cmd, plan.args, { cwd: opts.cwd, env: opts.env ?? process.env, stdio: ["pipe", "pipe", "pipe"], shell: plan.shell });
20044
20048
  } catch (err) {
20045
20049
  resolve({ code: 127, stdout: "", stderr: String(err) });
20046
20050
  return;
@@ -20076,7 +20080,11 @@ var AURA_DIR = ".aura";
20076
20080
  var ALWAYS_IGNORED = ["node_modules", ".next", ".git", "dist", "build", "out", ".vercel", ".turbo", "coverage", ".aura"];
20077
20081
  async function findRepoRoot(cwd = process.cwd()) {
20078
20082
  const r = await run("git", ["rev-parse", "--show-toplevel"], { cwd });
20079
- return r.code === 0 ? r.stdout.trim() : cwd;
20083
+ return path.resolve(r.code === 0 ? r.stdout.trim() : cwd);
20084
+ }
20085
+ function isInside(root, full, p2 = path) {
20086
+ const rel = p2.relative(p2.resolve(root), p2.resolve(full));
20087
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${p2.sep}`) && !p2.isAbsolute(rel);
20080
20088
  }
20081
20089
  async function isGitRepo(cwd) {
20082
20090
  return (await run("git", ["rev-parse", "--is-inside-work-tree"], { cwd })).code === 0;
@@ -20421,7 +20429,7 @@ function pmCommands(pm) {
20421
20429
  // package.json
20422
20430
  var package_default = {
20423
20431
  name: "getaura",
20424
- version: "0.1.2",
20432
+ version: "0.1.4",
20425
20433
  description: "Aura helps you and your coding agent follow best practices while you vibe code.",
20426
20434
  type: "module",
20427
20435
  bin: {
@@ -21591,6 +21599,10 @@ import path9 from "path";
21591
21599
  async function git(root, ...args) {
21592
21600
  return run("git", args, { cwd: root });
21593
21601
  }
21602
+ function ghInstallHint(platform = process.platform) {
21603
+ const how = platform === "win32" ? "Install it with `winget install --id GitHub.cli`" : platform === "darwin" ? "Install it with `brew install gh` (or download it from https://cli.github.com)" : "Install it from https://cli.github.com";
21604
+ return `${how}, open a new terminal, then run \`gh auth login\`.`;
21605
+ }
21594
21606
  async function ghStatus(root) {
21595
21607
  const v = await run("gh", ["--version"]);
21596
21608
  if (v.code !== 0) return { installed: false, authed: false, hasRemote: false };
@@ -21618,7 +21630,7 @@ async function freeBranchName(root, base) {
21618
21630
  function writeChanges(root, changes) {
21619
21631
  for (const c of changes) {
21620
21632
  const full = path9.join(root, c.path);
21621
- if (!full.startsWith(root + path9.sep)) throw new Error(`Refusing to write outside the repo: ${c.path}`);
21633
+ if (!isInside(root, full)) throw new Error(`Refusing to write outside the repo: ${c.path}`);
21622
21634
  if (c.content === null) rmSync(full, { force: true });
21623
21635
  else {
21624
21636
  mkdirSync2(path9.dirname(full), { recursive: true });
@@ -22762,7 +22774,7 @@ function safePath(root, rel) {
22762
22774
  if (normalized.startsWith("../") || normalized === ".." || path13.isAbsolute(normalized)) throw new Error(`Path is outside the repo: ${rel}`);
22763
22775
  if (FORBIDDEN.test(normalized)) throw new Error(`Aura doesn\u2019t let the agent touch ${normalized}.`);
22764
22776
  const full = path13.join(root, normalized);
22765
- if (!full.startsWith(root + path13.sep)) throw new Error(`Path is outside the repo: ${rel}`);
22777
+ if (!isInside(root, full)) throw new Error(`Path is outside the repo: ${rel}`);
22766
22778
  return normalized;
22767
22779
  }
22768
22780
  function clip(text) {
@@ -23845,7 +23857,7 @@ ${pc.bold(action2.title)}`);
23845
23857
  const gh = await ghStatus(root);
23846
23858
  if (!gh.installed || !gh.authed || !gh.hasRemote) {
23847
23859
  say.error(
23848
- !gh.installed ? "The GitHub CLI isn\u2019t installed. Run `aura guide install-github-cli`." : !gh.authed ? "Run `gh auth login` first." : "This repo has no GitHub remote. Push it to GitHub first."
23860
+ !gh.installed ? `The GitHub CLI isn\u2019t installed. ${ghInstallHint()}` : !gh.authed ? "Run `gh auth login` first." : "This repo has no GitHub remote. Push it to GitHub first."
23849
23861
  );
23850
23862
  say.dim("Or run with --no-pr to commit the change on a local branch.");
23851
23863
  return 1;
@@ -24048,6 +24060,21 @@ async function draftBrief(client, answers, summary) {
24048
24060
  return res.brief.trim() + "\n";
24049
24061
  }
24050
24062
 
24063
+ // src/core/aura-command.ts
24064
+ function hasAuraCommand() {
24065
+ return commandExists("aura");
24066
+ }
24067
+ var INSTALL_AURA = `npm install -g getaura@${AURA_VERSION}`;
24068
+ async function installAuraCommand() {
24069
+ const r = await run("npm", ["install", "-g", `getaura@${AURA_VERSION}`], { timeoutMs: 18e4 });
24070
+ if (r.code !== 0) return { ok: false, needsAdmin: /EACCES|EPERM|permission denied/i.test(r.stderr + r.stdout) };
24071
+ return { ok: true, onPath: await hasAuraCommand() };
24072
+ }
24073
+ function installFallback(result, platform = process.platform) {
24074
+ const admin = result.needsAdmin && platform !== "win32" ? `Run \`sudo ${INSTALL_AURA}\` and enter your computer password, or use` : "Use";
24075
+ return `${admin} \`npx getaura <command>\` in place of \`aura <command>\` (for example \`npx getaura scan\`).`;
24076
+ }
24077
+
24051
24078
  // src/commands/scan.ts
24052
24079
  import { mkdtempSync as mkdtempSync3, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "fs";
24053
24080
  import os5 from "os";
@@ -24174,8 +24201,7 @@ async function initCommand(flags) {
24174
24201
  }
24175
24202
  const root = await findRepoRoot(cwd);
24176
24203
  const gh = await ghStatus(root);
24177
- if (!gh.installed)
24178
- say.warn("The GitHub CLI (gh) isn\u2019t installed. Aura needs it to open pull requests. Run `aura guide install-github-cli`. You can keep going and scan now.");
24204
+ if (!gh.installed) say.warn(`The GitHub CLI (gh) isn\u2019t installed. Aura needs it to open pull requests. ${ghInstallHint()} You can keep going and scan now.`);
24179
24205
  else if (!gh.authed) say.warn("The GitHub CLI isn\u2019t signed in. Run `gh auth login` before applying fixes.");
24180
24206
  else if (!gh.hasRemote) say.warn("This repo has no GitHub remote yet. Push it to GitHub before applying fixes (`gh repo create`).");
24181
24207
  else say.ok("git and the GitHub CLI are ready.");
@@ -24196,6 +24222,8 @@ async function initCommand(flags) {
24196
24222
  required: true
24197
24223
  })
24198
24224
  ) : detected.length ? detected : ["claude"];
24225
+ } else {
24226
+ agents = [.../* @__PURE__ */ new Set([...config2.agents, ...agents])];
24199
24227
  }
24200
24228
  config2.agents = agents;
24201
24229
  let key = flags.key ?? getApiKey();
@@ -24231,10 +24259,11 @@ async function initCommand(flags) {
24231
24259
  const ctx = await buildContext(root, { fast: true, noDownload: true }, config2);
24232
24260
  const briefPath = auraPath(root, "brief.md");
24233
24261
  let brief2 = null;
24262
+ let keptBrief = false;
24234
24263
  if (flags.briefFile) {
24235
24264
  brief2 = readFileSync15(flags.briefFile, "utf8");
24236
- } else if (existsSync16(briefPath) && !(tty && unwrap(await p.confirm({ message: "A product brief already exists. Draft a new one?", initialValue: false })))) {
24237
- brief2 = readFileSync15(briefPath, "utf8");
24265
+ } else if (!flags.answers && existsSync16(briefPath) && !(tty && unwrap(await p.confirm({ message: "A product brief already exists. Draft a new one?", initialValue: false })))) {
24266
+ keptBrief = true;
24238
24267
  } else {
24239
24268
  let answers = null;
24240
24269
  if (flags.answers) {
@@ -24286,7 +24315,9 @@ async function initCommand(flags) {
24286
24315
  console.log(briefHandoff());
24287
24316
  }
24288
24317
  }
24289
- if (brief2) {
24318
+ if (keptBrief) {
24319
+ say.ok("Kept your existing product brief (.aura/brief.md).");
24320
+ } else if (brief2) {
24290
24321
  writeAuraFile(root, "brief.md", brief2);
24291
24322
  if (tty) {
24292
24323
  p.note(brief2.split("\n").slice(0, 24).join("\n") + (brief2.split("\n").length > 24 ? "\n\u2026" : ""), "Product brief draft");
@@ -24313,6 +24344,26 @@ async function initCommand(flags) {
24313
24344
  }
24314
24345
  saveConfig(root, config2);
24315
24346
  ensureGitignore(root);
24347
+ if (!process.env.CI && !await hasAuraCommand()) {
24348
+ if (tty) {
24349
+ if (unwrap(await p.confirm({ message: "Install the `aura` command so you can run `aura scan` any time?", initialValue: true }))) {
24350
+ const s = spin(true);
24351
+ s.start("Installing the aura command");
24352
+ const installed = await installAuraCommand();
24353
+ if (installed.ok)
24354
+ s.stop(installed.onPath ? "Installed. Run `aura scan` from any folder." : "Installed. Open a new terminal window, then run `aura scan`.");
24355
+ else {
24356
+ s.stop("Couldn\u2019t install the aura command");
24357
+ say.warn(installFallback(installed));
24358
+ }
24359
+ } else say.dim(`Use \`npx getaura <command>\` instead, or install it later with \`${INSTALL_AURA}\`.`);
24360
+ } else {
24361
+ console.log(`
24362
+ ${pc.magenta("\u2726")} ${pc.bold("Give the user the aura command")}`);
24363
+ console.log(` Run \`${INSTALL_AURA}\` so they can type \`aura scan\` from now on. Until then, use \`npx getaura <command>\`.
24364
+ `);
24365
+ }
24366
+ }
24316
24367
  if (flags.scan === false) {
24317
24368
  if (tty) p.outro("Done. Run `aura scan` when you\u2019re ready.");
24318
24369
  return 0;