getaura 0.1.3 → 0.1.5
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 +4 -2
- package/dist/index.js +112 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,12 +18,14 @@ 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
|
|
|
25
27
|
- **An Aura score** from deterministic checks across security, agent setup, foundations and structure, explained in plain language.
|
|
26
|
-
- **Next steps** in priority order
|
|
28
|
+
- **Next steps** in priority order. After a scan (or with `aura next`), tick the ones you want in the terminal and Aura does them.
|
|
27
29
|
- **Fixes as pull requests you approve**: `aura apply foundation` puts your brief, agent rules, skills, CI and env example in place in one PR, and `aura fix <finding>` has Aura's agent make code changes on a branch.
|
|
28
30
|
- **An agent that keeps it that way**: the rules and skills Aura installs teach your coding agent your standards, and GitHub Actions re-scans every push.
|
|
29
31
|
|
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
|
-
|
|
20039
|
-
|
|
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.
|
|
20432
|
+
version: "0.1.5",
|
|
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 (!
|
|
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 (!
|
|
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) {
|
|
@@ -23382,6 +23394,53 @@ function explainSignIn(outcome) {
|
|
|
23382
23394
|
}
|
|
23383
23395
|
}
|
|
23384
23396
|
|
|
23397
|
+
// src/commands/pick-steps.ts
|
|
23398
|
+
function stepRun(step, hasKey) {
|
|
23399
|
+
const [, sub, arg = step.actionId] = step.command.split(" ");
|
|
23400
|
+
if (sub === "apply") return { kind: "apply", actionId: arg };
|
|
23401
|
+
if (sub === "fix") return { kind: "fix", actionId: arg, withOwnAgent: !hasKey };
|
|
23402
|
+
if (sub === "guide") return { kind: "guide", topic: arg };
|
|
23403
|
+
return { kind: "inventory" };
|
|
23404
|
+
}
|
|
23405
|
+
function hint(step, hasKey) {
|
|
23406
|
+
const run2 = stepRun(step, hasKey);
|
|
23407
|
+
const what = run2.kind === "apply" ? "opens a pull request" : run2.kind === "fix" ? run2.withOwnAgent ? "writes a task for your coding agent" : "Aura\u2019s agent fixes it in a pull request" : run2.kind === "guide" ? "shows step-by-step instructions" : "a few questions";
|
|
23408
|
+
return [what, step.impact > 0 ? `+${step.impact} points` : "", effortLabel(step.effort)].filter(Boolean).join(" \xB7 ");
|
|
23409
|
+
}
|
|
23410
|
+
async function pickAndRunSteps(steps, limit = 10) {
|
|
23411
|
+
if (steps.length === 0) {
|
|
23412
|
+
console.log(pc.green("\n Nothing to do right now."));
|
|
23413
|
+
return 0;
|
|
23414
|
+
}
|
|
23415
|
+
const hasKey = Boolean(getApiKey());
|
|
23416
|
+
console.log("");
|
|
23417
|
+
const chosen = unwrap(
|
|
23418
|
+
await p.multiselect({
|
|
23419
|
+
message: "Next steps: pick what Aura should do now (space to select, enter to confirm, enter with none to skip)",
|
|
23420
|
+
options: steps.slice(0, limit).map((s) => ({ value: s.actionId, label: s.title, hint: hint(s, hasKey) })),
|
|
23421
|
+
required: false
|
|
23422
|
+
})
|
|
23423
|
+
);
|
|
23424
|
+
if (chosen.length === 0) {
|
|
23425
|
+
say.dim("Nothing selected. Run `aura next` any time to pick again.");
|
|
23426
|
+
return 0;
|
|
23427
|
+
}
|
|
23428
|
+
const results = [];
|
|
23429
|
+
for (const step of steps.filter((s) => chosen.includes(s.actionId))) {
|
|
23430
|
+
console.log(`
|
|
23431
|
+
${pc.magenta("\u2726")} ${pc.bold(step.title)}`);
|
|
23432
|
+
const run2 = stepRun(step, hasKey);
|
|
23433
|
+
const code = run2.kind === "apply" ? await applyCommand(run2.actionId, { yes: true }) : run2.kind === "fix" ? await fixCommand(run2.actionId, run2.withOwnAgent ? { agent: true } : { yes: true }) : run2.kind === "guide" ? await guideCommand(run2.topic, {}) : await inventoryCommand({});
|
|
23434
|
+
results.push({ title: step.title, ok: code === 0 });
|
|
23435
|
+
}
|
|
23436
|
+
if (results.length > 1) {
|
|
23437
|
+
console.log(`
|
|
23438
|
+
${pc.bold(" Done")}`);
|
|
23439
|
+
for (const r of results) console.log(` ${r.ok ? pc.green("\u2714") : pc.red("\u2716")} ${r.title}`);
|
|
23440
|
+
}
|
|
23441
|
+
return results.every((r) => r.ok) ? 0 : 1;
|
|
23442
|
+
}
|
|
23443
|
+
|
|
23385
23444
|
// src/commands/simple.ts
|
|
23386
23445
|
async function latestScan(root) {
|
|
23387
23446
|
const file2 = auraPath(root, "scan.json");
|
|
@@ -23400,8 +23459,10 @@ async function nextCommand(flags) {
|
|
|
23400
23459
|
const root = await findRepoRoot();
|
|
23401
23460
|
const scan = await latestScan(root);
|
|
23402
23461
|
const steps = nextSteps(scan, { includeFoundationBundle: true, accounts: accountState(root, scan.stack.services) });
|
|
23462
|
+
const limit = flags.limit ? Number(flags.limit) : 10;
|
|
23403
23463
|
if (flags.json) printJson({ score: scan.score, nextSteps: steps });
|
|
23404
|
-
else
|
|
23464
|
+
else if (interactive()) return pickAndRunSteps(steps, limit);
|
|
23465
|
+
else console.log(renderNextSteps(steps, limit));
|
|
23405
23466
|
return 0;
|
|
23406
23467
|
}
|
|
23407
23468
|
async function historyCommand(flags) {
|
|
@@ -23845,7 +23906,7 @@ ${pc.bold(action2.title)}`);
|
|
|
23845
23906
|
const gh = await ghStatus(root);
|
|
23846
23907
|
if (!gh.installed || !gh.authed || !gh.hasRemote) {
|
|
23847
23908
|
say.error(
|
|
23848
|
-
!gh.installed ?
|
|
23909
|
+
!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
23910
|
);
|
|
23850
23911
|
say.dim("Or run with --no-pr to commit the change on a local branch.");
|
|
23851
23912
|
return 1;
|
|
@@ -24048,6 +24109,21 @@ async function draftBrief(client, answers, summary) {
|
|
|
24048
24109
|
return res.brief.trim() + "\n";
|
|
24049
24110
|
}
|
|
24050
24111
|
|
|
24112
|
+
// src/core/aura-command.ts
|
|
24113
|
+
function hasAuraCommand() {
|
|
24114
|
+
return commandExists("aura");
|
|
24115
|
+
}
|
|
24116
|
+
var INSTALL_AURA = `npm install -g getaura@${AURA_VERSION}`;
|
|
24117
|
+
async function installAuraCommand() {
|
|
24118
|
+
const r = await run("npm", ["install", "-g", `getaura@${AURA_VERSION}`], { timeoutMs: 18e4 });
|
|
24119
|
+
if (r.code !== 0) return { ok: false, needsAdmin: /EACCES|EPERM|permission denied/i.test(r.stderr + r.stdout) };
|
|
24120
|
+
return { ok: true, onPath: await hasAuraCommand() };
|
|
24121
|
+
}
|
|
24122
|
+
function installFallback(result, platform = process.platform) {
|
|
24123
|
+
const admin = result.needsAdmin && platform !== "win32" ? `Run \`sudo ${INSTALL_AURA}\` and enter your computer password, or use` : "Use";
|
|
24124
|
+
return `${admin} \`npx getaura <command>\` in place of \`aura <command>\` (for example \`npx getaura scan\`).`;
|
|
24125
|
+
}
|
|
24126
|
+
|
|
24051
24127
|
// src/commands/scan.ts
|
|
24052
24128
|
import { mkdtempSync as mkdtempSync3, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
24053
24129
|
import os5 from "os";
|
|
@@ -24113,13 +24189,15 @@ async function scanCommand(flags) {
|
|
|
24113
24189
|
return exitCode;
|
|
24114
24190
|
}
|
|
24115
24191
|
console.log(renderTerminal(scan, { previous: flags.ci ? base?.score ?? null : previous }));
|
|
24116
|
-
|
|
24192
|
+
const pick2 = !flags.ci && interactive();
|
|
24193
|
+
if (!flags.ci && !pick2) console.log(renderNextSteps(steps, 3));
|
|
24117
24194
|
say.dim(
|
|
24118
24195
|
`
|
|
24119
24196
|
Full report: .aura/report.md${client.hasKey || flags.explain === false ? "" : " \xB7 Add a pilot key (aura login) for plain-language explanations."}`
|
|
24120
24197
|
);
|
|
24121
24198
|
if (base) say.info(`
|
|
24122
24199
|
Compared with ${flags.base}: ${base.score} \u2192 ${scan.score}${exitCode ? ` (dropped more than ${maxDrop})` : ""}`);
|
|
24200
|
+
if (pick2) await pickAndRunSteps(steps);
|
|
24123
24201
|
return exitCode;
|
|
24124
24202
|
}
|
|
24125
24203
|
|
|
@@ -24174,8 +24252,7 @@ async function initCommand(flags) {
|
|
|
24174
24252
|
}
|
|
24175
24253
|
const root = await findRepoRoot(cwd);
|
|
24176
24254
|
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.");
|
|
24255
|
+
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
24256
|
else if (!gh.authed) say.warn("The GitHub CLI isn\u2019t signed in. Run `gh auth login` before applying fixes.");
|
|
24180
24257
|
else if (!gh.hasRemote) say.warn("This repo has no GitHub remote yet. Push it to GitHub before applying fixes (`gh repo create`).");
|
|
24181
24258
|
else say.ok("git and the GitHub CLI are ready.");
|
|
@@ -24318,6 +24395,26 @@ async function initCommand(flags) {
|
|
|
24318
24395
|
}
|
|
24319
24396
|
saveConfig(root, config2);
|
|
24320
24397
|
ensureGitignore(root);
|
|
24398
|
+
if (!process.env.CI && !await hasAuraCommand()) {
|
|
24399
|
+
if (tty) {
|
|
24400
|
+
if (unwrap(await p.confirm({ message: "Install the `aura` command so you can run `aura scan` any time?", initialValue: true }))) {
|
|
24401
|
+
const s = spin(true);
|
|
24402
|
+
s.start("Installing the aura command");
|
|
24403
|
+
const installed = await installAuraCommand();
|
|
24404
|
+
if (installed.ok)
|
|
24405
|
+
s.stop(installed.onPath ? "Installed. Run `aura scan` from any folder." : "Installed. Open a new terminal window, then run `aura scan`.");
|
|
24406
|
+
else {
|
|
24407
|
+
s.stop("Couldn\u2019t install the aura command");
|
|
24408
|
+
say.warn(installFallback(installed));
|
|
24409
|
+
}
|
|
24410
|
+
} else say.dim(`Use \`npx getaura <command>\` instead, or install it later with \`${INSTALL_AURA}\`.`);
|
|
24411
|
+
} else {
|
|
24412
|
+
console.log(`
|
|
24413
|
+
${pc.magenta("\u2726")} ${pc.bold("Give the user the aura command")}`);
|
|
24414
|
+
console.log(` Run \`${INSTALL_AURA}\` so they can type \`aura scan\` from now on. Until then, use \`npx getaura <command>\`.
|
|
24415
|
+
`);
|
|
24416
|
+
}
|
|
24417
|
+
}
|
|
24321
24418
|
if (flags.scan === false) {
|
|
24322
24419
|
if (tty) p.outro("Done. Run `aura scan` when you\u2019re ready.");
|
|
24323
24420
|
return 0;
|