@curiousnerd/keel 0.1.0 → 0.2.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/README.md +12 -5
- package/dist/cli.js +25 -3
- package/dist/cli.js.map +1 -1
- package/dist/util/spinner.d.ts +13 -0
- package/dist/util/spinner.js +48 -0
- package/dist/util/spinner.js.map +1 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -80,13 +80,16 @@ keel check /path/to/your/project # any JavaScript, TypeScript, or Python
|
|
|
80
80
|
That's it. Run it again any time — it remembers what it already scanned, so repeat
|
|
81
81
|
runs are nearly instant.
|
|
82
82
|
|
|
83
|
-
**From source** (for contributing):
|
|
83
|
+
**From source** (clone & build — for contributing, or to run without installing from npm):
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
|
-
git clone
|
|
87
|
-
npm install
|
|
88
|
-
npm run build
|
|
89
|
-
|
|
86
|
+
git clone https://github.com/aditya20t/Keel.git keel && cd keel
|
|
87
|
+
npm install # install dependencies
|
|
88
|
+
npm run build # compile TypeScript to dist/
|
|
89
|
+
npm link # optional: makes `keel` available globally
|
|
90
|
+
|
|
91
|
+
keel check /path/to/your/project # if you ran `npm link`
|
|
92
|
+
node dist/cli.js check /path/to/your/project # otherwise, run the built file directly
|
|
90
93
|
```
|
|
91
94
|
|
|
92
95
|
### Commands and options
|
|
@@ -245,6 +248,10 @@ languages.
|
|
|
245
248
|
|
|
246
249
|
Want to help or understand the internals? See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
247
250
|
|
|
251
|
+
## Credits
|
|
252
|
+
|
|
253
|
+
Built by **Aditya**, with the help of **awesome Claude** 🤖.
|
|
254
|
+
|
|
248
255
|
## License
|
|
249
256
|
|
|
250
257
|
[MIT](LICENSE)
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import pc from "picocolors";
|
|
4
|
-
import { resolve } from "node:path";
|
|
4
|
+
import { resolve, dirname, join, basename } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { createSpinner } from "./util/spinner.js";
|
|
5
7
|
import { loadConfig } from "./config.js";
|
|
6
8
|
import { loadFacts } from "./extract/scan.js";
|
|
7
9
|
import { parseClaims } from "./claims/parseBlock.js";
|
|
@@ -11,17 +13,27 @@ import { renderText, sortFindings, DEFAULT_FINDING_LIMIT } from "./report/text.j
|
|
|
11
13
|
import { renderJson } from "./report/json.js";
|
|
12
14
|
import { renderMarkdown, improvementSummary } from "./report/markdown.js";
|
|
13
15
|
import { suggestImprovements } from "./llm/improve.js";
|
|
14
|
-
import { writeFileSync } from "node:fs";
|
|
16
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
15
17
|
import { resolveProvider } from "./llm/provider.js";
|
|
16
18
|
import { LLMCache } from "./llm/cache.js";
|
|
17
19
|
import { explainFindings } from "./llm/explain.js";
|
|
18
20
|
import { extractClaimsFromDocs, mergeInferredClaims } from "./llm/claimsFromDocs.js";
|
|
19
21
|
import { startMcpServer } from "./mcp/server.js";
|
|
22
|
+
/** Read the real version from package.json (works from source and when installed). */
|
|
23
|
+
function packageVersion() {
|
|
24
|
+
try {
|
|
25
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
26
|
+
return JSON.parse(readFileSync(pkgPath, "utf8")).version ?? "0.0.0";
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return "0.0.0";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
20
32
|
const program = new Command();
|
|
21
33
|
program
|
|
22
34
|
.name("keel")
|
|
23
35
|
.description("A local-first verifier that keeps an AI coding agent honest.")
|
|
24
|
-
.version(
|
|
36
|
+
.version(packageVersion());
|
|
25
37
|
program
|
|
26
38
|
.command("check", { isDefault: true })
|
|
27
39
|
.description("Scan a repo and report drift, library conflicts, and duplication.")
|
|
@@ -40,6 +52,8 @@ program
|
|
|
40
52
|
const root = resolve(path);
|
|
41
53
|
const config = loadConfig(root);
|
|
42
54
|
const respectGitignore = opts.gitignore === false ? false : config.scan.respectGitignore;
|
|
55
|
+
const spinner = createSpinner();
|
|
56
|
+
spinner.start(`Scanning ${basename(root)}…`);
|
|
43
57
|
// LLM setup (once) — off by default; powers both Tier-2 doc-claim extraction
|
|
44
58
|
// and plain-language explanations. Resolved before analyze so inferred claims
|
|
45
59
|
// can flow through the deterministic drift engine.
|
|
@@ -58,13 +72,16 @@ program
|
|
|
58
72
|
let durationMs = Number(process.hrtime.bigint() - t0) / 1e6;
|
|
59
73
|
// Tier-2 doc drift: extend claims with ones the LLM infers from prose (untimed).
|
|
60
74
|
if (provider && llmCache) {
|
|
75
|
+
spinner.update("Reading docs for stated stack…");
|
|
61
76
|
const inferred = await extractClaimsFromDocs(root, config, provider, llmCache);
|
|
62
77
|
claims = { ...claims, stack: mergeInferredClaims(claims.stack, inferred) };
|
|
63
78
|
}
|
|
79
|
+
spinner.update("Analyzing…");
|
|
64
80
|
const analyzeStart = process.hrtime.bigint();
|
|
65
81
|
const { findings, score, breakdown } = analyze(facts, claims, config, suppressions);
|
|
66
82
|
durationMs += Number(process.hrtime.bigint() - analyzeStart) / 1e6;
|
|
67
83
|
if (opts.facts) {
|
|
84
|
+
spinner.stop();
|
|
68
85
|
process.stdout.write(JSON.stringify(facts, null, 2) + "\n");
|
|
69
86
|
return;
|
|
70
87
|
}
|
|
@@ -76,16 +93,19 @@ program
|
|
|
76
93
|
};
|
|
77
94
|
// LLM extras (off by default): per-finding explanations + how-to-improve plan.
|
|
78
95
|
if (provider && llmCache && findings.length > 0) {
|
|
96
|
+
spinner.update(`Asking ${llmLabel} for explanations…`);
|
|
79
97
|
const limit = opts.limit && opts.limit > 0 ? opts.limit : DEFAULT_FINDING_LIMIT;
|
|
80
98
|
await explainFindings(sortFindings(findings).slice(0, limit), provider, llmCache);
|
|
81
99
|
}
|
|
82
100
|
let improvement = improvementSummary(report);
|
|
83
101
|
if (provider && llmCache) {
|
|
102
|
+
spinner.update("Generating improvement plan…");
|
|
84
103
|
const plan = await suggestImprovements(report, provider, llmCache);
|
|
85
104
|
if (plan)
|
|
86
105
|
improvement = plan;
|
|
87
106
|
}
|
|
88
107
|
llmCache?.save();
|
|
108
|
+
spinner.stop();
|
|
89
109
|
// Optional full Markdown report — written to the cwd, never into the scanned repo.
|
|
90
110
|
let reportFile;
|
|
91
111
|
if (opts.outputMd !== undefined) {
|
|
@@ -130,6 +150,8 @@ program
|
|
|
130
150
|
await startMcpServer(resolve(path));
|
|
131
151
|
});
|
|
132
152
|
program.parseAsync(process.argv).catch((err) => {
|
|
153
|
+
if (process.stderr.isTTY)
|
|
154
|
+
process.stderr.write("\r\x1b[K"); // clear any spinner line
|
|
133
155
|
process.stderr.write(pc.red(`keel: ${err instanceof Error ? err.message : String(err)}\n`));
|
|
134
156
|
process.exitCode = 1;
|
|
135
157
|
});
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface Spinner {
|
|
2
|
+
start(label: string): void;
|
|
3
|
+
update(label: string): void;
|
|
4
|
+
stop(): void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A minimal terminal spinner that writes to stderr, so it never interferes with
|
|
8
|
+
* the report on stdout (`--json`, `--output-md`, pipes). It only animates on an
|
|
9
|
+
* interactive TTY — in CI, pipes, or when NO_COLOR is set it's a silent no-op,
|
|
10
|
+
* keeping logs clean. The first frame is drawn synchronously so a message is
|
|
11
|
+
* visible even before a synchronous workload blocks the event loop.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createSpinner(stream?: NodeJS.WriteStream): Spinner;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
3
|
+
const INTERVAL_MS = 80;
|
|
4
|
+
/**
|
|
5
|
+
* A minimal terminal spinner that writes to stderr, so it never interferes with
|
|
6
|
+
* the report on stdout (`--json`, `--output-md`, pipes). It only animates on an
|
|
7
|
+
* interactive TTY — in CI, pipes, or when NO_COLOR is set it's a silent no-op,
|
|
8
|
+
* keeping logs clean. The first frame is drawn synchronously so a message is
|
|
9
|
+
* visible even before a synchronous workload blocks the event loop.
|
|
10
|
+
*/
|
|
11
|
+
export function createSpinner(stream = process.stderr) {
|
|
12
|
+
const enabled = stream.isTTY === true && !process.env.NO_COLOR && !process.env.CI;
|
|
13
|
+
let timer;
|
|
14
|
+
let frame = 0;
|
|
15
|
+
let label = "";
|
|
16
|
+
const render = () => {
|
|
17
|
+
stream.write(`\r${pc.cyan(FRAMES[frame % FRAMES.length])} ${label}\x1b[K`);
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
start(text) {
|
|
21
|
+
if (!enabled)
|
|
22
|
+
return;
|
|
23
|
+
label = text;
|
|
24
|
+
frame = 0;
|
|
25
|
+
render(); // draw immediately, before any sync work blocks the loop
|
|
26
|
+
timer = setInterval(() => {
|
|
27
|
+
frame += 1;
|
|
28
|
+
render();
|
|
29
|
+
}, INTERVAL_MS);
|
|
30
|
+
timer.unref?.(); // never keep the process alive on our account
|
|
31
|
+
},
|
|
32
|
+
update(text) {
|
|
33
|
+
if (!enabled)
|
|
34
|
+
return;
|
|
35
|
+
label = text;
|
|
36
|
+
render();
|
|
37
|
+
},
|
|
38
|
+
stop() {
|
|
39
|
+
if (!enabled)
|
|
40
|
+
return;
|
|
41
|
+
if (timer)
|
|
42
|
+
clearInterval(timer);
|
|
43
|
+
timer = undefined;
|
|
44
|
+
stream.write("\r\x1b[K"); // clear the spinner line
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/util/spinner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAClE,MAAM,WAAW,GAAG,EAAE,CAAC;AAQvB;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,SAA6B,OAAO,CAAC,MAAM;IACvE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;IAClF,IAAI,KAAiC,CAAC;IACtC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,EAAE,CAAC;IAEf,MAAM,MAAM,GAAG,GAAS,EAAE;QACxB,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAE,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC9E,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,IAAI;YACR,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,KAAK,GAAG,IAAI,CAAC;YACb,KAAK,GAAG,CAAC,CAAC;YACV,MAAM,EAAE,CAAC,CAAC,yDAAyD;YACnE,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;gBACvB,KAAK,IAAI,CAAC,CAAC;gBACX,MAAM,EAAE,CAAC;YACX,CAAC,EAAE,WAAW,CAAC,CAAC;YAChB,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,8CAA8C;QACjE,CAAC;QACD,MAAM,CAAC,IAAI;YACT,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,KAAK,GAAG,IAAI,CAAC;YACb,MAAM,EAAE,CAAC;QACX,CAAC;QACD,IAAI;YACF,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,IAAI,KAAK;gBAAE,aAAa,CAAC,KAAK,CAAC,CAAC;YAChC,KAAK,GAAG,SAAS,CAAC;YAClB,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,yBAAyB;QACrD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@curiousnerd/keel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A local-first verifier that keeps an AI coding agent honest: catches drift, library conflicts, and duplication across JS/TS and Python.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Aditya",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/aditya20t/Keel.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/aditya20t/Keel#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/aditya20t/Keel/issues"
|
|
15
|
+
},
|
|
8
16
|
"keywords": [
|
|
9
17
|
"ai",
|
|
10
18
|
"coding-agent",
|