openpitstop 1.0.0
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/LICENSE +21 -0
- package/README.md +512 -0
- package/demo-repo/README.md +24 -0
- package/demo-repo/package-lock.json +4483 -0
- package/demo-repo/package.json +17 -0
- package/demo-repo/src/config.js +5 -0
- package/demo-repo/src/db.js +31 -0
- package/demo-repo/src/server.js +13 -0
- package/demo-repo/src/userController.js +22 -0
- package/demo-repo/src/userRepo.js +18 -0
- package/demo-repo/src/userService.js +59 -0
- package/demo-repo/test/user.test.js +12 -0
- package/demo-repo-fintech/README.md +27 -0
- package/demo-repo-fintech/package-lock.json +1023 -0
- package/demo-repo-fintech/package.json +14 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-conc-1-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-retry-2-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-webhook-3-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/src/gateway.js +26 -0
- package/demo-repo-fintech/src/server.js +61 -0
- package/demo-repo-generators/package-lock.json +1508 -0
- package/demo-repo-generators/package.json +17 -0
- package/demo-repo-generators/public/index.html +10 -0
- package/demo-repo-generators/scripts/build.js +17 -0
- package/demo-repo-generators/src/counter.js +16 -0
- package/demo-repo-integrity/README.md +49 -0
- package/demo-repo-integrity/package.json +12 -0
- package/demo-repo-integrity/src/money.js +19 -0
- package/demo-repo-integrity/test/money.test.js +17 -0
- package/dist/analyzers/accessibility.d.ts +18 -0
- package/dist/analyzers/accessibility.js +248 -0
- package/dist/analyzers/accessibility.js.map +1 -0
- package/dist/analyzers/dependencyGraph.d.ts +14 -0
- package/dist/analyzers/dependencyGraph.js +206 -0
- package/dist/analyzers/dependencyGraph.js.map +1 -0
- package/dist/analyzers/devex.d.ts +19 -0
- package/dist/analyzers/devex.js +301 -0
- package/dist/analyzers/devex.js.map +1 -0
- package/dist/analyzers/duplication.d.ts +2 -0
- package/dist/analyzers/duplication.js +47 -0
- package/dist/analyzers/duplication.js.map +1 -0
- package/dist/analyzers/index.d.ts +12 -0
- package/dist/analyzers/index.js +38 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.d.ts +2 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js +164 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js.map +1 -0
- package/dist/analyzers/integrity/exceptionSwallow.d.ts +2 -0
- package/dist/analyzers/integrity/exceptionSwallow.js +198 -0
- package/dist/analyzers/integrity/exceptionSwallow.js.map +1 -0
- package/dist/analyzers/integrity/exitCheat.d.ts +2 -0
- package/dist/analyzers/integrity/exitCheat.js +31 -0
- package/dist/analyzers/integrity/exitCheat.js.map +1 -0
- package/dist/analyzers/integrity/git.d.ts +10 -0
- package/dist/analyzers/integrity/git.js +181 -0
- package/dist/analyzers/integrity/git.js.map +1 -0
- package/dist/analyzers/integrity/hardcodedMatch.d.ts +2 -0
- package/dist/analyzers/integrity/hardcodedMatch.js +93 -0
- package/dist/analyzers/integrity/hardcodedMatch.js.map +1 -0
- package/dist/analyzers/integrity/helpers.d.ts +21 -0
- package/dist/analyzers/integrity/helpers.js +66 -0
- package/dist/analyzers/integrity/helpers.js.map +1 -0
- package/dist/analyzers/integrity/index.d.ts +10 -0
- package/dist/analyzers/integrity/index.js +27 -0
- package/dist/analyzers/integrity/index.js.map +1 -0
- package/dist/analyzers/integrity/mockOverreach.d.ts +2 -0
- package/dist/analyzers/integrity/mockOverreach.js +72 -0
- package/dist/analyzers/integrity/mockOverreach.js.map +1 -0
- package/dist/analyzers/integrity/suppressionCreep.d.ts +2 -0
- package/dist/analyzers/integrity/suppressionCreep.js +27 -0
- package/dist/analyzers/integrity/suppressionCreep.js.map +1 -0
- package/dist/analyzers/integrity/testTamper.d.ts +2 -0
- package/dist/analyzers/integrity/testTamper.js +153 -0
- package/dist/analyzers/integrity/testTamper.js.map +1 -0
- package/dist/analyzers/integrity/types.d.ts +63 -0
- package/dist/analyzers/integrity/types.js +13 -0
- package/dist/analyzers/integrity/types.js.map +1 -0
- package/dist/analyzers/ledger/attacks.d.ts +17 -0
- package/dist/analyzers/ledger/attacks.js +135 -0
- package/dist/analyzers/ledger/attacks.js.map +1 -0
- package/dist/analyzers/ledger/discover.d.ts +15 -0
- package/dist/analyzers/ledger/discover.js +122 -0
- package/dist/analyzers/ledger/discover.js.map +1 -0
- package/dist/analyzers/ledger/evidence.d.ts +22 -0
- package/dist/analyzers/ledger/evidence.js +127 -0
- package/dist/analyzers/ledger/evidence.js.map +1 -0
- package/dist/analyzers/ledger/harness.d.ts +4 -0
- package/dist/analyzers/ledger/harness.js +270 -0
- package/dist/analyzers/ledger/harness.js.map +1 -0
- package/dist/analyzers/ledger/index.d.ts +14 -0
- package/dist/analyzers/ledger/index.js +116 -0
- package/dist/analyzers/ledger/index.js.map +1 -0
- package/dist/analyzers/ledger/types.d.ts +31 -0
- package/dist/analyzers/ledger/types.js +2 -0
- package/dist/analyzers/ledger/types.js.map +1 -0
- package/dist/analyzers/perf.d.ts +2 -0
- package/dist/analyzers/perf.js +106 -0
- package/dist/analyzers/perf.js.map +1 -0
- package/dist/analyzers/reliability.d.ts +22 -0
- package/dist/analyzers/reliability.js +339 -0
- package/dist/analyzers/reliability.js.map +1 -0
- package/dist/analyzers/routes.d.ts +17 -0
- package/dist/analyzers/routes.js +115 -0
- package/dist/analyzers/routes.js.map +1 -0
- package/dist/analyzers/security.d.ts +2 -0
- package/dist/analyzers/security.js +302 -0
- package/dist/analyzers/security.js.map +1 -0
- package/dist/analyzers/suiteRunner.d.ts +64 -0
- package/dist/analyzers/suiteRunner.js +381 -0
- package/dist/analyzers/suiteRunner.js.map +1 -0
- package/dist/analyzers/tests.d.ts +2 -0
- package/dist/analyzers/tests.js +215 -0
- package/dist/analyzers/tests.js.map +1 -0
- package/dist/analyzers/types.d.ts +203 -0
- package/dist/analyzers/types.js +2 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/analyzers/util.d.ts +35 -0
- package/dist/analyzers/util.js +209 -0
- package/dist/analyzers/util.js.map +1 -0
- package/dist/ciCheckA.d.ts +2 -0
- package/dist/ciCheckA.js +5 -0
- package/dist/ciCheckA.js.map +1 -0
- package/dist/ciCheckB.d.ts +2 -0
- package/dist/ciCheckB.js +6 -0
- package/dist/ciCheckB.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/budget.d.ts +33 -0
- package/dist/commands/budget.js +108 -0
- package/dist/commands/budget.js.map +1 -0
- package/dist/commands/ci.d.ts +14 -0
- package/dist/commands/ci.js +157 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/demo.d.ts +2 -0
- package/dist/commands/demo.js +163 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/digest.d.ts +8 -0
- package/dist/commands/digest.js +189 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +98 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/drive.d.ts +3 -0
- package/dist/commands/drive.js +170 -0
- package/dist/commands/drive.js.map +1 -0
- package/dist/commands/gate.d.ts +2 -0
- package/dist/commands/gate.js +136 -0
- package/dist/commands/gate.js.map +1 -0
- package/dist/commands/honesty.d.ts +2 -0
- package/dist/commands/honesty.js +182 -0
- package/dist/commands/honesty.js.map +1 -0
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +244 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +81 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/integrity.d.ts +2 -0
- package/dist/commands/integrity.js +74 -0
- package/dist/commands/integrity.js.map +1 -0
- package/dist/commands/memory.d.ts +6 -0
- package/dist/commands/memory.js +90 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/pen.d.ts +17 -0
- package/dist/commands/pen.js +158 -0
- package/dist/commands/pen.js.map +1 -0
- package/dist/commands/prompt.d.ts +2 -0
- package/dist/commands/prompt.js +37 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/readyCheck.d.ts +21 -0
- package/dist/commands/readyCheck.js +68 -0
- package/dist/commands/readyCheck.js.map +1 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.js +48 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/repro.d.ts +3 -0
- package/dist/commands/repro.js +61 -0
- package/dist/commands/repro.js.map +1 -0
- package/dist/commands/scan.d.ts +27 -0
- package/dist/commands/scan.js +348 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +163 -0
- package/dist/commands/share.js.map +1 -0
- package/dist/commands/trends.d.ts +2 -0
- package/dist/commands/trends.js +103 -0
- package/dist/commands/trends.js.map +1 -0
- package/dist/commands/try.d.ts +12 -0
- package/dist/commands/try.js +208 -0
- package/dist/commands/try.js.map +1 -0
- package/dist/commands/verify.d.ts +44 -0
- package/dist/commands/verify.js +381 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/watch.d.ts +10 -0
- package/dist/commands/watch.js +70 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/evidence.d.ts +37 -0
- package/dist/evidence.js +61 -0
- package/dist/evidence.js.map +1 -0
- package/dist/graph/correlate.d.ts +7 -0
- package/dist/graph/correlate.js +246 -0
- package/dist/graph/correlate.js.map +1 -0
- package/dist/graph/integrity.d.ts +22 -0
- package/dist/graph/integrity.js +39 -0
- package/dist/graph/integrity.js.map +1 -0
- package/dist/installer/targets.d.ts +23 -0
- package/dist/installer/targets.js +162 -0
- package/dist/installer/targets.js.map +1 -0
- package/dist/memory/store.d.ts +33 -0
- package/dist/memory/store.js +131 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/pen/dynamic.d.ts +36 -0
- package/dist/pen/dynamic.js +622 -0
- package/dist/pen/dynamic.js.map +1 -0
- package/dist/pen/fix.d.ts +35 -0
- package/dist/pen/fix.js +239 -0
- package/dist/pen/fix.js.map +1 -0
- package/dist/pen/proof.d.ts +46 -0
- package/dist/pen/proof.js +112 -0
- package/dist/pen/proof.js.map +1 -0
- package/dist/pen/report.d.ts +11 -0
- package/dist/pen/report.js +258 -0
- package/dist/pen/report.js.map +1 -0
- package/dist/pen/static.d.ts +17 -0
- package/dist/pen/static.js +565 -0
- package/dist/pen/static.js.map +1 -0
- package/dist/pen/store.d.ts +11 -0
- package/dist/pen/store.js +36 -0
- package/dist/pen/store.js.map +1 -0
- package/dist/pen/types.d.ts +112 -0
- package/dist/pen/types.js +53 -0
- package/dist/pen/types.js.map +1 -0
- package/dist/report/format.d.ts +126 -0
- package/dist/report/format.js +706 -0
- package/dist/report/format.js.map +1 -0
- package/dist/report/score.d.ts +47 -0
- package/dist/report/score.js +164 -0
- package/dist/report/score.js.map +1 -0
- package/dist/repro/framework.d.ts +26 -0
- package/dist/repro/framework.js +224 -0
- package/dist/repro/framework.js.map +1 -0
- package/dist/repro/generate.d.ts +14 -0
- package/dist/repro/generate.js +669 -0
- package/dist/repro/generate.js.map +1 -0
- package/dist/repro/ids.d.ts +32 -0
- package/dist/repro/ids.js +175 -0
- package/dist/repro/ids.js.map +1 -0
- package/dist/repro/index.d.ts +24 -0
- package/dist/repro/index.js +69 -0
- package/dist/repro/index.js.map +1 -0
- package/dist/repro/pen.d.ts +26 -0
- package/dist/repro/pen.js +321 -0
- package/dist/repro/pen.js.map +1 -0
- package/dist/repro/run.d.ts +18 -0
- package/dist/repro/run.js +36 -0
- package/dist/repro/run.js.map +1 -0
- package/dist/sandbox/proxy.d.ts +41 -0
- package/dist/sandbox/proxy.js +231 -0
- package/dist/sandbox/proxy.js.map +1 -0
- package/dist/sandbox/startCmd.d.ts +24 -0
- package/dist/sandbox/startCmd.js +71 -0
- package/dist/sandbox/startCmd.js.map +1 -0
- package/dist/ui/spinner.d.ts +18 -0
- package/dist/ui/spinner.js +26 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/verify/metrics.d.ts +13 -0
- package/dist/verify/metrics.js +72 -0
- package/dist/verify/metrics.js.map +1 -0
- package/package.json +68 -0
- package/scripts/cheat-demo.cjs +102 -0
- package/templates/ledger/preload.cjs +331 -0
- package/templates/pen/preload.cjs +196 -0
- package/templates/pitstop.prompt.md +371 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import Table from "cli-table3";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { getTargets, resolveTemplatePath, renderContent, } from "../installer/targets.js";
|
|
8
|
+
const READY_MESSAGE = "OpenPitStop is ready. Type /pitstop in Claude Code, Cursor, OpenCode, Kilo Code, Antigravity, Codex CLI or any AI coding tool installed, hit enter, and watch it work.";
|
|
9
|
+
const CODEX_APP_MESSAGE = "Codex App and Codex VS Code extension don't support custom slash commands yet (OpenAI hasn't shipped this) — use Codex CLI for /pitstop, or copy templates/pitstop.prompt.md's content directly into a Codex App chat as a one-off prompt.";
|
|
10
|
+
export const install = new Command("install")
|
|
11
|
+
.description("Install the pitstop slash-command into Claude Code, Cursor, OpenCode, Antigravity, Kilo Code, Gemini CLI, Codex")
|
|
12
|
+
.argument("[repo]", "target repo (defaults to cwd)", ".")
|
|
13
|
+
.option("--force", "overwrite existing pitstop files", false)
|
|
14
|
+
.option("-y, --yes", "same as --force (re-runnable one-liner: npx openpitstop@latest install -y)", false)
|
|
15
|
+
.option("--uninstall", "remove pitstop files only", false)
|
|
16
|
+
.action(async (repoArg, opts) => {
|
|
17
|
+
const cwd = path.resolve(repoArg);
|
|
18
|
+
const targets = getTargets(cwd);
|
|
19
|
+
const codexNote = targets.find((t) => t.note)?.note;
|
|
20
|
+
const force = Boolean(opts.force || opts.yes);
|
|
21
|
+
if (opts.uninstall) {
|
|
22
|
+
const table = new Table({
|
|
23
|
+
head: ["Tool", "Path", "Status"],
|
|
24
|
+
style: { head: ["cyan"], border: [] },
|
|
25
|
+
});
|
|
26
|
+
let removed = 0;
|
|
27
|
+
for (const t of targets) {
|
|
28
|
+
if (fs.existsSync(t.path)) {
|
|
29
|
+
fs.rmSync(t.path);
|
|
30
|
+
table.push([t.tool, t.path, chalk.red("🗑️ removed")]);
|
|
31
|
+
removed++;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
table.push([t.tool, t.path, chalk.dim("— absent")]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
console.log(table.toString());
|
|
38
|
+
console.log(chalk.bold(`\nOpenPitStop uninstalled (${removed} file(s) removed). Nothing else was touched.`));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const templateText = fs.readFileSync(resolveTemplatePath(), "utf8");
|
|
42
|
+
const table = new Table({
|
|
43
|
+
head: ["Tool", "Path", "Status"],
|
|
44
|
+
style: { head: ["cyan"], border: [] },
|
|
45
|
+
});
|
|
46
|
+
let installed = 0;
|
|
47
|
+
let skipped = 0;
|
|
48
|
+
for (const t of targets) {
|
|
49
|
+
fs.mkdirSync(path.dirname(t.path), { recursive: true });
|
|
50
|
+
const exists = fs.existsSync(t.path);
|
|
51
|
+
if (exists && !force) {
|
|
52
|
+
table.push([t.tool, t.path, chalk.yellow("⚠️ skipped — use --force")]);
|
|
53
|
+
skipped++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
fs.writeFileSync(t.path, renderContent(t, templateText));
|
|
57
|
+
table.push([t.tool, t.path, chalk.green("✅ Installed")]);
|
|
58
|
+
installed++;
|
|
59
|
+
}
|
|
60
|
+
// Codex App / Codex VS Code extension: no custom slash commands exist yet,
|
|
61
|
+
// so there is deliberately NO file written for them — only an honest status.
|
|
62
|
+
table.push([
|
|
63
|
+
"Codex App / VS Code extension",
|
|
64
|
+
"— (none written)",
|
|
65
|
+
chalk.yellow("⚠️ Manual copy needed"),
|
|
66
|
+
]);
|
|
67
|
+
console.log(table.toString());
|
|
68
|
+
console.log(chalk.yellow(CODEX_APP_MESSAGE));
|
|
69
|
+
if (codexNote) {
|
|
70
|
+
console.log(chalk.dim(`Note: ${codexNote}.`));
|
|
71
|
+
}
|
|
72
|
+
console.log(boxen(chalk.bold(READY_MESSAGE), {
|
|
73
|
+
padding: 1,
|
|
74
|
+
borderStyle: "double",
|
|
75
|
+
borderColor: "cyan",
|
|
76
|
+
}));
|
|
77
|
+
if (skipped > 0) {
|
|
78
|
+
console.log(chalk.dim(`\n(${skipped} existing file(s) kept — run with --force to overwrite them.)`));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import boxen from "boxen";
|
|
6
|
+
import { getDiff, resolveRefs } from "../analyzers/integrity/git.js";
|
|
7
|
+
import { buildIntegrityReport } from "../graph/integrity.js";
|
|
8
|
+
import { seal } from "../evidence.js";
|
|
9
|
+
export const integrity = new Command("integrity")
|
|
10
|
+
.description("Diff-scoped scan for AI-agent-cheat patterns (deleted/neutered tests, swallowed " +
|
|
11
|
+
"errors, suppressions, hardcoded-to-pass values, mocked SUT, forced exits). " +
|
|
12
|
+
"Writes .pitstop/integrity-<timestamp>.json. Exit 0=CLEAN, 1=SUSPICIOUS, 2=CONFIRMED_CHEAT.")
|
|
13
|
+
.argument("[repo]", "path to the repo to analyze", ".")
|
|
14
|
+
.option("--from <ref>", "diff base ref (default: commit before last fix)")
|
|
15
|
+
.option("--to <ref>", "diff head ref (default: current working tree)")
|
|
16
|
+
.action(async (repoArg, options) => {
|
|
17
|
+
const repo = path.resolve(repoArg);
|
|
18
|
+
const { from, to } = resolveRefs(repo, options.from, options.to);
|
|
19
|
+
console.log(chalk.cyan(`\nScanning diff ${from}..${to ?? "working tree"} for integrity cheats ...\n`));
|
|
20
|
+
const changes = getDiff(repo, from, to);
|
|
21
|
+
const report = buildIntegrityReport(repo, from, to ?? "working tree", changes);
|
|
22
|
+
const outDir = path.join(repo, ".pitstop");
|
|
23
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
24
|
+
const ts = report.timestamp.replace(/[:.]/g, "-");
|
|
25
|
+
const outPath = path.join(outDir, `integrity-${ts}.json`);
|
|
26
|
+
fs.writeFileSync(outPath, JSON.stringify(seal(report, `pitstop integrity report for ${repo}`), null, 2));
|
|
27
|
+
printReport(report, outPath);
|
|
28
|
+
process.exitCode = verdictExit(report.verdict);
|
|
29
|
+
});
|
|
30
|
+
function verdictExit(v) {
|
|
31
|
+
return v === "CLEAN" ? 0 : v === "SUSPICIOUS" ? 1 : 2;
|
|
32
|
+
}
|
|
33
|
+
function printReport(report, outPath) {
|
|
34
|
+
const { verdict, summary, findings } = report;
|
|
35
|
+
const paint = verdict === "CLEAN" ? chalk.green : verdict === "SUSPICIOUS" ? chalk.yellow : chalk.red;
|
|
36
|
+
const borderColor = verdict === "CLEAN" ? "green" : verdict === "SUSPICIOUS" ? "yellow" : "red";
|
|
37
|
+
const lines = [];
|
|
38
|
+
lines.push(`verdict: ${paint(verdict)}`);
|
|
39
|
+
lines.push(`findings: ${summary.total} (${summary.confirmed} confirmed · ${summary.suspicious} suspicious)`);
|
|
40
|
+
lines.push("");
|
|
41
|
+
if (findings.length === 0) {
|
|
42
|
+
lines.push(chalk.green("No cheat patterns detected in this diff."));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
for (const f of findings)
|
|
46
|
+
lines.push(renderFinding(f));
|
|
47
|
+
}
|
|
48
|
+
lines.push("");
|
|
49
|
+
lines.push(chalk.dim(`report written to ${outPath}`));
|
|
50
|
+
console.log(boxen(lines.join("\n"), {
|
|
51
|
+
title: ` PITSTOP — Integrity ${verdict} `,
|
|
52
|
+
titleAlignment: "center",
|
|
53
|
+
borderStyle: verdict === "CLEAN" ? "round" : "double",
|
|
54
|
+
padding: 1,
|
|
55
|
+
borderColor,
|
|
56
|
+
}));
|
|
57
|
+
if (verdict === "CONFIRMED_CHEAT") {
|
|
58
|
+
console.log(chalk.red("\nCONFIRMED_CHEAT: at least one unambiguous cheat pattern was found. Auto-block this change.\n"));
|
|
59
|
+
}
|
|
60
|
+
else if (verdict === "SUSPICIOUS") {
|
|
61
|
+
console.log(chalk.yellow("\nSUSPICIOUS: patterns need human judgment — do NOT auto-block; review the evidence below.\n"));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function renderFinding(f) {
|
|
65
|
+
const tag = f.confidence === "confirmed" ? chalk.red("CONFIRMED") : chalk.yellow("SUSPICIOUS");
|
|
66
|
+
const loc = f.line ? `${f.file}:${f.line}` : f.file;
|
|
67
|
+
const second = f.file2 ? chalk.dim(` ⟂ test: ${f.file2}${f.line2 ? ":" + f.line2 : ""}`) : "";
|
|
68
|
+
return [
|
|
69
|
+
`${tag} ${chalk.bold(f.detector)} / ${f.pattern}`,
|
|
70
|
+
` ${chalk.cyan(loc)}${second}`,
|
|
71
|
+
` ${f.evidence}`,
|
|
72
|
+
].join("\n");
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../../src/commands/integrity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAGtC,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,OAAO,CAAC,WAAW,CAAC;KAC9C,WAAW,CACV,kFAAkF;IAChF,6EAA6E;IAC7E,4FAA4F,CAC/F;KACA,QAAQ,CAAC,QAAQ,EAAE,6BAA6B,EAAE,GAAG,CAAC;KACtD,MAAM,CAAC,cAAc,EAAE,iDAAiD,CAAC;KACzE,MAAM,CAAC,YAAY,EAAE,+CAA+C,CAAC;KACrE,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,OAAuC,EAAE,EAAE;IACzE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAEjE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,IAAI,KAAK,EAAE,IAAI,cAAc,6BAA6B,CAAC,CAAC,CAAC;IAEvG,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,cAAc,EAAE,OAAO,CAAC,CAAC;IAE/E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC3C,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CACd,OAAO,EACP,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,gCAAgC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAC9E,CAAC;IAEF,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEL,SAAS,WAAW,CAAC,CAAU;IAC7B,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,WAAW,CAAC,MAA+C,EAAE,OAAe;IACnF,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAC9C,MAAM,KAAK,GACT,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;IAC1F,MAAM,WAAW,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;IAEhG,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,YAAY,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,KAAK,CAAC,IAAI,CAAC,aAAa,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,SAAS,gBAAgB,OAAO,CAAC,UAAU,cAAc,CAAC,CAAC;IAC7G,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC,CAAC;IACtE,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,CAAC,IAAI,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAC,CAAC;IAEtD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACtB,KAAK,EAAE,wBAAwB,OAAO,GAAG;QACzC,cAAc,EAAE,QAAQ;QACxB,WAAW,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ;QACrD,OAAO,EAAE,CAAC;QACV,WAAW;KACZ,CAAC,CACH,CAAC;IAEF,IAAI,OAAO,KAAK,iBAAiB,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CACP,gGAAgG,CACjG,CACF,CAAC;IACJ,CAAC;SAAM,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,8FAA8F,CAC/F,CACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,CAAmB;IACxC,MAAM,GAAG,GACP,CAAC,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACrF,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACpD,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/F,OAAO;QACL,GAAG,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE;QACjD,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;QAC/B,KAAK,CAAC,CAAC,QAAQ,EAAE;KAClB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { addEntry, relevantEntries, loadEntries, } from "../memory/store.js";
|
|
5
|
+
const memory = new Command("memory")
|
|
6
|
+
.description("Recall past decisions, fixes and rejected approaches for this repo")
|
|
7
|
+
.option("--repo <path>", "repo to operate on (defaults to cwd)", ".");
|
|
8
|
+
function repoFrom(cmd) {
|
|
9
|
+
const opts = memory.opts();
|
|
10
|
+
return path.resolve(opts.repo || ".");
|
|
11
|
+
}
|
|
12
|
+
function typeColor(t) {
|
|
13
|
+
switch (t) {
|
|
14
|
+
case "decision":
|
|
15
|
+
return chalk.cyan;
|
|
16
|
+
case "fix":
|
|
17
|
+
return chalk.green;
|
|
18
|
+
case "rejection":
|
|
19
|
+
return chalk.red;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function printEntry(e) {
|
|
23
|
+
const tc = typeColor(e.type);
|
|
24
|
+
console.log(` ${tc(e.type.toUpperCase().padEnd(9))} ${chalk.bold(e.summary)}`);
|
|
25
|
+
if (e.context)
|
|
26
|
+
console.log(` ${chalk.dim("why: " + e.context)}`);
|
|
27
|
+
if (e.relatedFiles.length) {
|
|
28
|
+
console.log(` ${chalk.dim("files: " + e.relatedFiles.join(", "))}`);
|
|
29
|
+
}
|
|
30
|
+
console.log(` ${chalk.dim(e.timestamp)}`);
|
|
31
|
+
}
|
|
32
|
+
export const add = new Command("add")
|
|
33
|
+
.description("Record a memory entry")
|
|
34
|
+
.argument("<summary>", "what happened")
|
|
35
|
+
.option("--type <type>", "entry type", "decision")
|
|
36
|
+
.option("--context <context>", "why it happened / matters")
|
|
37
|
+
.option("--file <path...>", "related files (relative to repo)")
|
|
38
|
+
.option("--mirror", "also write to repo-local .pitstop/memory.jsonl", false)
|
|
39
|
+
.action((summary, opts) => {
|
|
40
|
+
const repo = repoFrom(add);
|
|
41
|
+
const type = String(opts.type);
|
|
42
|
+
if (!["decision", "fix", "rejection"].includes(type)) {
|
|
43
|
+
console.log(chalk.red(`invalid --type "${type}" (decision|fix|rejection)`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const relatedFiles = (opts.file ?? []).map((f) => path.relative(repo, path.resolve(repo, f)));
|
|
47
|
+
const entry = addEntry(repo, {
|
|
48
|
+
type,
|
|
49
|
+
summary,
|
|
50
|
+
context: opts.context,
|
|
51
|
+
relatedFiles,
|
|
52
|
+
mirror: opts.mirror,
|
|
53
|
+
});
|
|
54
|
+
console.log(chalk.green("memory saved:"));
|
|
55
|
+
printEntry(entry);
|
|
56
|
+
});
|
|
57
|
+
export const list = new Command("list")
|
|
58
|
+
.description("List memory entries, newest first")
|
|
59
|
+
.action(() => {
|
|
60
|
+
const repo = repoFrom(list);
|
|
61
|
+
const entries = loadEntries(repo).sort((a, b) => a.timestamp < b.timestamp ? 1 : -1);
|
|
62
|
+
if (entries.length === 0) {
|
|
63
|
+
console.log(chalk.yellow("no memory entries for this repo"));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log(chalk.bold(`\nMemory for this repo (${entries.length}):\n`));
|
|
67
|
+
for (const e of entries)
|
|
68
|
+
printEntry(e);
|
|
69
|
+
console.log("");
|
|
70
|
+
});
|
|
71
|
+
export const relevant = new Command("relevant")
|
|
72
|
+
.description("Show memory entries relevant to a file (and its neighbors)")
|
|
73
|
+
.argument("<file>", "path to a file in the repo")
|
|
74
|
+
.action((file) => {
|
|
75
|
+
const repo = repoFrom(relevant);
|
|
76
|
+
const entries = relevantEntries(repo, file);
|
|
77
|
+
if (entries.length === 0) {
|
|
78
|
+
console.log(chalk.yellow(`no memory relevant to ${file}`));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
console.log(chalk.bold(`\nMemory relevant to ${file} (${entries.length}):\n`));
|
|
82
|
+
for (const e of entries)
|
|
83
|
+
printEntry(e);
|
|
84
|
+
console.log("");
|
|
85
|
+
});
|
|
86
|
+
memory.addCommand(add);
|
|
87
|
+
memory.addCommand(list);
|
|
88
|
+
memory.addCommand(relevant);
|
|
89
|
+
export { memory };
|
|
90
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/commands/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,QAAQ,EACR,eAAe,EACf,WAAW,GAGZ,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KACjC,WAAW,CAAC,oEAAoE,CAAC;KACjF,MAAM,CAAC,eAAe,EAAE,sCAAsC,EAAE,GAAG,CAAC,CAAC;AAExE,SAAS,QAAQ,CAAC,GAAY;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,SAAS,CAAC,CAAa;IAC9B,QAAQ,CAAC,EAAE,CAAC;QACV,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,KAAK,KAAK;YACR,OAAO,KAAK,CAAC,KAAK,CAAC;QACrB,KAAK,WAAW;YACd,OAAO,KAAK,CAAC,GAAG,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,CAAc;IAChC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChF,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpE,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KAClC,WAAW,CAAC,uBAAuB,CAAC;KACpC,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;KACtC,MAAM,CAAC,eAAe,EAAE,YAAY,EAAE,UAAU,CAAC;KACjD,MAAM,CAAC,qBAAqB,EAAE,2BAA2B,CAAC;KAC1D,MAAM,CAAC,kBAAkB,EAAE,kCAAkC,CAAC;KAC9D,MAAM,CAAC,UAAU,EAAE,gDAAgD,EAAE,KAAK,CAAC;KAC3E,MAAM,CAAC,CAAC,OAAe,EAAE,IAAS,EAAE,EAAE;IACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAe,CAAC;IAC7C,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,IAAI,4BAA4B,CAAC,CAAC,CAAC;QAC5E,OAAO;IACT,CAAC;IACD,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CACvD,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAC3C,CAAC;IACF,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE;QAC3B,IAAI;QACJ,OAAO;QACP,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,YAAY;QACZ,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IAC1C,UAAU,CAAC,KAAK,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KACpC,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC9C,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACnC,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,iCAAiC,CAAC,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2BAA2B,OAAO,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC;IACzE,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;KAC5C,WAAW,CAAC,4DAA4D,CAAC;KACzE,QAAQ,CAAC,QAAQ,EAAE,4BAA4B,CAAC;KAChD,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE;IACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,IAAI,KAAK,OAAO,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC;IAC/E,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AACvB,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACxB,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type PenResult } from "../pen/types.js";
|
|
3
|
+
export interface PenOptions {
|
|
4
|
+
staticOnly?: boolean;
|
|
5
|
+
fix?: boolean;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
html?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* `pitstop pen` — the headline act. Static pass first (zero setup, no
|
|
11
|
+
* network), then — unless `--static` — a LIVE dynamic pass: the app is booted
|
|
12
|
+
* under the pen sandbox and attacked. Everything is recorded, everything is
|
|
13
|
+
* sealed, and `--fix` turns every replayable finding into a regression test.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runPen(repo: string, opts: PenOptions): Promise<PenResult>;
|
|
16
|
+
export declare const pen: Command;
|
|
17
|
+
export default pen;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { analyzeStatic } from "../pen/static.js";
|
|
6
|
+
import { runDynamic } from "../pen/dynamic.js";
|
|
7
|
+
import { applyStaticProof } from "../pen/proof.js";
|
|
8
|
+
import { persistPen } from "../pen/store.js";
|
|
9
|
+
import { runFixes } from "../pen/fix.js";
|
|
10
|
+
import { renderPenBox, renderPenMarkdown, renderPenHtml } from "../pen/report.js";
|
|
11
|
+
import { summarizeFindings } from "../pen/types.js";
|
|
12
|
+
import { createSpinner } from "../ui/spinner.js";
|
|
13
|
+
/**
|
|
14
|
+
* `pitstop pen` — the headline act. Static pass first (zero setup, no
|
|
15
|
+
* network), then — unless `--static` — a LIVE dynamic pass: the app is booted
|
|
16
|
+
* under the pen sandbox and attacked. Everything is recorded, everything is
|
|
17
|
+
* sealed, and `--fix` turns every replayable finding into a regression test.
|
|
18
|
+
*/
|
|
19
|
+
export async function runPen(repo, opts) {
|
|
20
|
+
const staticStart = Date.now();
|
|
21
|
+
const staticOutcome = analyzeStatic(repo);
|
|
22
|
+
const staticMs = Date.now() - staticStart;
|
|
23
|
+
const staticFindings = [...staticOutcome.findings];
|
|
24
|
+
let dynamicEnabled = !opts.staticOnly;
|
|
25
|
+
let staticProof;
|
|
26
|
+
let dynamic = {
|
|
27
|
+
status: "ok",
|
|
28
|
+
note: undefined,
|
|
29
|
+
routesProbed: 0,
|
|
30
|
+
attacks: 0,
|
|
31
|
+
bootMs: 0,
|
|
32
|
+
durationMs: 0,
|
|
33
|
+
outboundEvents: 0,
|
|
34
|
+
};
|
|
35
|
+
const findings = [];
|
|
36
|
+
if (dynamicEnabled) {
|
|
37
|
+
const spin = createSpinner(`Booting the app under the pen sandbox and attacking ${staticOutcome.routes.length} route(s)…`);
|
|
38
|
+
try {
|
|
39
|
+
const d = await runDynamic(repo, staticOutcome.routes);
|
|
40
|
+
if (d.status === "aborted") {
|
|
41
|
+
spin.warn(`Dynamic phase aborted — ${d.note ?? "app did not respond"}`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
spin.succeed(`Dynamic phase done — ${d.routesProbed} routes, ${d.attacks} attacks`);
|
|
45
|
+
}
|
|
46
|
+
const proof = applyStaticProof(staticFindings, d);
|
|
47
|
+
findings.push(...proof.findings, ...d.findings);
|
|
48
|
+
staticProof = proof.summary;
|
|
49
|
+
dynamic = {
|
|
50
|
+
status: d.status,
|
|
51
|
+
note: d.note,
|
|
52
|
+
routesProbed: d.routesProbed,
|
|
53
|
+
attacks: d.attacks,
|
|
54
|
+
bootMs: d.bootMs,
|
|
55
|
+
durationMs: d.durationMs,
|
|
56
|
+
outboundEvents: d.outboundEvents,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
spin.fail("Dynamic phase failed");
|
|
61
|
+
const d = { status: "aborted", note: err.message, findings: [] };
|
|
62
|
+
const proof = applyStaticProof(staticFindings, d);
|
|
63
|
+
findings.push(...proof.findings);
|
|
64
|
+
staticProof = proof.summary;
|
|
65
|
+
dynamic = {
|
|
66
|
+
status: "aborted",
|
|
67
|
+
note: err.message,
|
|
68
|
+
routesProbed: 0,
|
|
69
|
+
attacks: 0,
|
|
70
|
+
bootMs: 0,
|
|
71
|
+
durationMs: 0,
|
|
72
|
+
outboundEvents: 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
findings.push(...staticFindings);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
repo,
|
|
82
|
+
mode: "pen",
|
|
83
|
+
staticEnabled: true,
|
|
84
|
+
dynamicEnabled,
|
|
85
|
+
dynamic,
|
|
86
|
+
staticProof,
|
|
87
|
+
packages: staticOutcome.packages,
|
|
88
|
+
findings,
|
|
89
|
+
summary: summarizeFindings(findings),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export const pen = new Command("pen")
|
|
93
|
+
.description("Penetration test your own app: static heuristics (secrets, taint, config hygiene) plus a LIVE " +
|
|
94
|
+
"dynamic attack phase — the app is booted under a network-interception sandbox and attacked. " +
|
|
95
|
+
"Every finding carries its proof; `--fix` writes failing-then-passing regression tests and safe patches.")
|
|
96
|
+
.argument("[repo]", "path to the repo to pen-test (default: current dir)", ".")
|
|
97
|
+
.option("--static", "static pass only — do NOT boot the app (the dynamic phase is opt-out, not opt-in)")
|
|
98
|
+
.option("--fix", "write repro tests + deterministic patches + PITSTOP_PEN_FIXES.md")
|
|
99
|
+
.option("--html", "also write PITSTOP_PEN_REPORT.html")
|
|
100
|
+
.option("--json", "print the raw pen result as JSON")
|
|
101
|
+
.action(async (repoArg, options) => {
|
|
102
|
+
const repo = path.resolve(repoArg);
|
|
103
|
+
if (!fs.existsSync(repo)) {
|
|
104
|
+
console.log(chalk.red(`repo not found: ${repo}`));
|
|
105
|
+
process.exitCode = 2;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!options.json) {
|
|
109
|
+
console.log(chalk.cyan(`\nPen-testing ${repo}${options.static ? " (static pass only)" : ""} ...\n`));
|
|
110
|
+
if (!options.static) {
|
|
111
|
+
console.log(chalk.dim(" The dynamic phase boots the app's `start` script under a sandbox that intercepts all\n" +
|
|
112
|
+
" outbound HTTP and records every spawn. Nothing reaches the real network. Raw sockets are\n" +
|
|
113
|
+
" blocked; child processes run and are logged (it is your own start script).\n"));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const result = await runPen(repo, {
|
|
117
|
+
staticOnly: options.static,
|
|
118
|
+
fix: options.fix,
|
|
119
|
+
json: options.json,
|
|
120
|
+
html: options.html,
|
|
121
|
+
});
|
|
122
|
+
const { file } = persistPen(repo, result);
|
|
123
|
+
const md = renderPenMarkdown(result);
|
|
124
|
+
fs.writeFileSync(path.join(repo, "PITSTOP_PEN_REPORT.md"), md, "utf8");
|
|
125
|
+
if (options.html) {
|
|
126
|
+
fs.writeFileSync(path.join(repo, "PITSTOP_PEN_REPORT.html"), renderPenHtml(result), "utf8");
|
|
127
|
+
}
|
|
128
|
+
if (options.json) {
|
|
129
|
+
console.log(JSON.stringify(result, null, 2));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
console.log(renderPenBox(result));
|
|
133
|
+
console.log(chalk.dim(`\nEvidence sealed to ${file}\n` +
|
|
134
|
+
`Report: ${path.join(repo, "PITSTOP_PEN_REPORT.md")}` +
|
|
135
|
+
(options.html ? ` · ${path.join(repo, "PITSTOP_PEN_REPORT.html")}` : "") +
|
|
136
|
+
"\n"));
|
|
137
|
+
if (options.fix) {
|
|
138
|
+
const outcome = runFixes(repo, result, result.packages);
|
|
139
|
+
fs.writeFileSync(path.join(repo, "PITSTOP_PEN_FIXES.md"), outcome.fixesMd, "utf8");
|
|
140
|
+
console.log(chalk.green(`\n--fix: wrote ${outcome.repros.length} repro test(s), ${outcome.patches.length} patch(es)`));
|
|
141
|
+
for (const r of outcome.repros) {
|
|
142
|
+
console.log(chalk.dim(` repro: ${r.file} (fails now → passes after the fix)`));
|
|
143
|
+
}
|
|
144
|
+
for (const p of outcome.patches) {
|
|
145
|
+
console.log(chalk.dim(` patch: git apply ${p.diffPath} — ${p.note}`));
|
|
146
|
+
}
|
|
147
|
+
console.log(chalk.dim(` plan: ${path.join(repo, "PITSTOP_PEN_FIXES.md")}`));
|
|
148
|
+
}
|
|
149
|
+
// Exit contract: 0 = no high/critical findings, 1 = high/critical present,
|
|
150
|
+
// 2 = pen could not meaningfully run (dynamic aborted with nothing found).
|
|
151
|
+
if (result.summary.critical + result.summary.high > 0)
|
|
152
|
+
process.exitCode = 1;
|
|
153
|
+
else if (result.dynamicEnabled && result.dynamic.status === "aborted" && result.findings.length === 0) {
|
|
154
|
+
process.exitCode = 2;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
export default pen;
|
|
158
|
+
//# sourceMappingURL=pen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pen.js","sourceRoot":"","sources":["../../src/commands/pen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
/**
|
|
6
|
+
* `pitstop prompt` — transparency: print the exact `/pitstop` prompt your AI
|
|
7
|
+
* tool will receive, with the user's invocation arguments substituted. Some
|
|
8
|
+
* tools show the raw prompt in their UI, some don't; this command guarantees
|
|
9
|
+
* the user can always see exactly what the agent was told.
|
|
10
|
+
*/
|
|
11
|
+
function templatePath() {
|
|
12
|
+
return fileURLToPath(new URL("../../templates/pitstop.prompt.md", import.meta.url));
|
|
13
|
+
}
|
|
14
|
+
export const prompt = new Command("prompt")
|
|
15
|
+
.description("Print the exact /pitstop prompt your AI tool receives, with arguments substituted")
|
|
16
|
+
.option("--args <text>", "invocation arguments to substitute into $ARGUMENTS", "")
|
|
17
|
+
.option("--out <path>", "write the rendered prompt to a file instead of stdout")
|
|
18
|
+
.action((options) => {
|
|
19
|
+
const p = templatePath();
|
|
20
|
+
if (!fs.existsSync(p)) {
|
|
21
|
+
console.log(chalk.red(`template not found at ${p} — this build has no templates/pitstop.prompt.md`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
let rendered = fs.readFileSync(p, "utf8").replace(/\$ARGUMENTS/g, options.args ?? "");
|
|
25
|
+
const header = `# Rendered /pitstop prompt (arguments: "${options.args ?? ""}")\n` +
|
|
26
|
+
`# This is exactly what your AI tool expands /pitstop to and sends to the agent.\n\n`;
|
|
27
|
+
if (options.out) {
|
|
28
|
+
const outPath = options.out;
|
|
29
|
+
rendered = `<!-- Rendered by \`pitstop prompt --args "${options.args ?? ""}"\` -->\n` + rendered;
|
|
30
|
+
fs.writeFileSync(outPath, rendered);
|
|
31
|
+
console.log(chalk.dim(`rendered prompt written to ${outPath}\n`));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
process.stdout.write(header);
|
|
35
|
+
process.stdout.write(rendered);
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/commands/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;GAKG;AAEH,SAAS,YAAY;IACnB,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,mCAAmC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACtF,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KACxC,WAAW,CAAC,mFAAmF,CAAC;KAChG,MAAM,CAAC,eAAe,EAAE,oDAAoD,EAAE,EAAE,CAAC;KACjF,MAAM,CAAC,cAAc,EAAE,uDAAuD,CAAC;KAC/E,MAAM,CAAC,CAAC,OAAuC,EAAE,EAAE;IAClD,MAAM,CAAC,GAAG,YAAY,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,yBAAyB,CAAC,kDAAkD,CAAC,CAAC,CAAC;QACrG,OAAO;IACT,CAAC;IACD,IAAI,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAEtF,MAAM,MAAM,GACV,2CAA2C,OAAO,CAAC,IAAI,IAAI,EAAE,MAAM;QACnE,qFAAqF,CAAC;IAExF,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;QAC5B,QAAQ,GAAG,6CAA6C,OAAO,CAAC,IAAI,IAAI,EAAE,WAAW,GAAG,QAAQ,CAAC;QACjG,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,8BAA8B,OAAO,IAAI,CAAC,CAAC,CAAC;QAClE,OAAO;IACT,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* `pitstop ready-check` — the token-economy gate.
|
|
4
|
+
*
|
|
5
|
+
* Answers ONE question: "is the working tree unchanged since the last scan?"
|
|
6
|
+
* If yes, re-running the full suite is pure waste: `scan --reuse` (or the
|
|
7
|
+
* agent loop) can skip the heavy analyzers entirely. The autonomous fix loop
|
|
8
|
+
* calls this before every re-scan instead of burning model credits on tests
|
|
9
|
+
* that cannot have changed.
|
|
10
|
+
*/
|
|
11
|
+
export interface ReadyCheckOutcome {
|
|
12
|
+
ready: boolean;
|
|
13
|
+
baselineTimestamp?: string;
|
|
14
|
+
baselineMode?: string;
|
|
15
|
+
changedFile?: string;
|
|
16
|
+
changedMtime?: number;
|
|
17
|
+
staleReason?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function readyCheck(repo: string): ReadyCheckOutcome;
|
|
20
|
+
export declare const readyCheckCmd: Command;
|
|
21
|
+
export default readyCheckCmd;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import { newestModifiedFile } from "../analyzers/util.js";
|
|
7
|
+
import { loadPenLatest } from "../pen/store.js";
|
|
8
|
+
export function readyCheck(repo) {
|
|
9
|
+
const scanPath = path.join(repo, ".pitstop", "scan-latest.json");
|
|
10
|
+
if (!fs.existsSync(scanPath)) {
|
|
11
|
+
return {
|
|
12
|
+
ready: false,
|
|
13
|
+
staleReason: "no scan baseline yet (.pitstop/scan-latest.json) — a first scan is required",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
let scan;
|
|
17
|
+
try {
|
|
18
|
+
scan = JSON.parse(fs.readFileSync(scanPath, "utf8"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return { ready: false, staleReason: "scan-latest.json is corrupt — re-run `pitstop scan`" };
|
|
22
|
+
}
|
|
23
|
+
const baselineMs = Date.parse(scan.timestamp ?? "");
|
|
24
|
+
if (!baselineMs || Number.isNaN(baselineMs)) {
|
|
25
|
+
return { ready: false, staleReason: "baseline has no readable timestamp — re-run `pitstop scan`" };
|
|
26
|
+
}
|
|
27
|
+
const newest = newestModifiedFile(repo);
|
|
28
|
+
if (!newest)
|
|
29
|
+
return { ready: true, baselineTimestamp: scan.timestamp, baselineMode: scan.mode };
|
|
30
|
+
// Ignore pitstop's own writes: they live under .pitstop which newestModifiedFile skips.
|
|
31
|
+
if (newest.mtimeMs > baselineMs) {
|
|
32
|
+
return {
|
|
33
|
+
ready: false,
|
|
34
|
+
baselineTimestamp: scan.timestamp,
|
|
35
|
+
baselineMode: scan.mode,
|
|
36
|
+
changedFile: path.relative(repo, newest.file).replace(/\\/g, "/"),
|
|
37
|
+
changedMtime: newest.mtimeMs,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { ready: true, baselineTimestamp: scan.timestamp, baselineMode: scan.mode };
|
|
41
|
+
}
|
|
42
|
+
export const readyCheckCmd = new Command("ready-check")
|
|
43
|
+
.description("Token-economy gate: reports whether the working tree changed since the last scan. " +
|
|
44
|
+
"Exit 0 = nothing changed (re-scan is waste, use `scan --reuse`), exit 1 = sources changed since baseline.")
|
|
45
|
+
.argument("[repo]", "path to the repo", ".")
|
|
46
|
+
.option("--json", "print machine-readable JSON")
|
|
47
|
+
.action((repoArg, options) => {
|
|
48
|
+
const repo = path.resolve(repoArg);
|
|
49
|
+
const r = readyCheck(repo);
|
|
50
|
+
if (options.json) {
|
|
51
|
+
console.log(JSON.stringify({ repo, ...r }, null, 2));
|
|
52
|
+
}
|
|
53
|
+
else if (r.ready) {
|
|
54
|
+
const pen = loadPenLatest(repo);
|
|
55
|
+
console.log(boxen(`${chalk.green("READY")} — nothing changed since the baseline.\n\n` +
|
|
56
|
+
`baseline: ${r.baselineTimestamp ?? "?"} (mode: ${r.baselineMode ?? "?"})${pen ? ` · pen report: ${pen.timestamp}` : ""}\n\n` +
|
|
57
|
+
chalk.dim(`The agent loop can skip the full suite: \`pitstop scan --reuse\` returns the baseline instantly.\n`), { title: " PITSTOP — Ready Check ", titleAlignment: "center", borderStyle: "round", padding: 1, borderColor: "green" }));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log(boxen(`${chalk.yellow("STALE")} — the working tree moved since the baseline.\n\n` +
|
|
61
|
+
`changed: ${chalk.cyan(r.changedFile ?? "?")}\n` +
|
|
62
|
+
`baseline: ${r.baselineTimestamp ?? "?"} (mode: ${r.baselineMode ?? "?"})\n\n` +
|
|
63
|
+
(r.staleReason ?? "a re-scan is needed before `verify`/`gate` can be trusted."), { title: " PITSTOP — Ready Check ", titleAlignment: "center", borderStyle: "round", padding: 1, borderColor: "yellow" }));
|
|
64
|
+
}
|
|
65
|
+
process.exitCode = r.ready ? 0 : 1;
|
|
66
|
+
});
|
|
67
|
+
export default readyCheckCmd;
|
|
68
|
+
//# sourceMappingURL=readyCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readyCheck.js","sourceRoot":"","sources":["../../src/commands/readyCheck.ts"],"names":[],"mappings":"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"}
|