@bantay/cli 0.1.1 → 0.3.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/package.json +3 -3
- package/src/aide/discovery.ts +88 -0
- package/src/aide/index.ts +9 -0
- package/src/cli.ts +133 -11
- package/src/commands/aide.ts +432 -48
- package/src/commands/check.ts +9 -4
- package/src/commands/ci.ts +228 -0
- package/src/commands/diff.ts +387 -0
- package/src/commands/init.ts +38 -1
- package/src/commands/status.ts +311 -0
- package/src/commands/tasks.ts +220 -0
- package/src/export/all.ts +5 -1
- package/src/export/claude.ts +8 -5
- package/src/export/codex.ts +72 -0
- package/src/export/cursor.ts +5 -3
- package/src/export/index.ts +1 -0
- package/src/export/invariants.ts +7 -5
- package/src/generators/claude-commands.ts +61 -0
- package/src/templates/commands/bantay-check.md +58 -0
- package/src/templates/commands/bantay-interview.md +207 -0
- package/src/templates/commands/bantay-orchestrate.md +164 -0
- package/src/templates/commands/bantay-status.md +39 -0
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bantay/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Write down the rules your system must never break. We enforce them on every PR.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"bantay": "
|
|
7
|
+
"bantay": "src/cli.ts"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"src",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/zcancio/bantay.git"
|
|
31
|
+
"url": "git+https://github.com/zcancio/bantay.git"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"js-yaml": "^4.1.0"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readdir } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
|
|
4
|
+
export interface DiscoveryResult {
|
|
5
|
+
found: string[];
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ResolvedAidePath {
|
|
10
|
+
path: string;
|
|
11
|
+
filename: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Discover .aide files in a directory
|
|
16
|
+
*/
|
|
17
|
+
export async function discoverAideFiles(cwd: string): Promise<DiscoveryResult> {
|
|
18
|
+
try {
|
|
19
|
+
const files = await readdir(cwd);
|
|
20
|
+
const aideFiles = files.filter((f) => f.endsWith(".aide"));
|
|
21
|
+
return { found: aideFiles };
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return { found: [], error: error instanceof Error ? error.message : String(error) };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the aide file path for a project directory
|
|
29
|
+
* - If explicitPath is provided, use it
|
|
30
|
+
* - Otherwise, glob for *.aide in projectPath
|
|
31
|
+
* - If exactly one found, use it
|
|
32
|
+
* - If multiple found, throw error
|
|
33
|
+
* - If none found, throw error
|
|
34
|
+
*
|
|
35
|
+
* @param projectPath - The project directory to search in
|
|
36
|
+
* @param explicitPath - Optional explicit path to an aide file
|
|
37
|
+
* @returns The resolved aide file path
|
|
38
|
+
* @throws Error if no aide file found or multiple found without explicit path
|
|
39
|
+
*/
|
|
40
|
+
export async function resolveAidePath(
|
|
41
|
+
projectPath: string,
|
|
42
|
+
explicitPath?: string
|
|
43
|
+
): Promise<ResolvedAidePath> {
|
|
44
|
+
// If explicit path provided, use it
|
|
45
|
+
if (explicitPath) {
|
|
46
|
+
const fullPath = explicitPath.startsWith("/")
|
|
47
|
+
? explicitPath
|
|
48
|
+
: join(projectPath, explicitPath);
|
|
49
|
+
const filename = explicitPath.split("/").pop() || explicitPath;
|
|
50
|
+
return { path: fullPath, filename };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Auto-discover
|
|
54
|
+
const { found, error } = await discoverAideFiles(projectPath);
|
|
55
|
+
|
|
56
|
+
if (error) {
|
|
57
|
+
throw new Error(`Error discovering aide files: ${error}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (found.length === 0) {
|
|
61
|
+
throw new Error("No .aide file found. Run 'bantay aide init' to create one.");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (found.length > 1) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Multiple .aide files found. Specify one with --aide <path>\nFound: ${found.join(", ")}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
path: join(projectPath, found[0]),
|
|
72
|
+
filename: found[0],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Try to resolve aide path, returning null if not found (non-throwing version)
|
|
78
|
+
*/
|
|
79
|
+
export async function tryResolveAidePath(
|
|
80
|
+
projectPath: string,
|
|
81
|
+
explicitPath?: string
|
|
82
|
+
): Promise<ResolvedAidePath | null> {
|
|
83
|
+
try {
|
|
84
|
+
return await resolveAidePath(projectPath, explicitPath);
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/aide/index.ts
CHANGED
|
@@ -16,6 +16,15 @@ import {
|
|
|
16
16
|
// Re-export types
|
|
17
17
|
export type { AideTree, Entity, Relationship } from "./types";
|
|
18
18
|
|
|
19
|
+
// Re-export discovery functions
|
|
20
|
+
export {
|
|
21
|
+
discoverAideFiles,
|
|
22
|
+
resolveAidePath,
|
|
23
|
+
tryResolveAidePath,
|
|
24
|
+
type DiscoveryResult,
|
|
25
|
+
type ResolvedAidePath,
|
|
26
|
+
} from "./discovery";
|
|
27
|
+
|
|
19
28
|
/**
|
|
20
29
|
* Read and parse a .aide YAML file
|
|
21
30
|
*/
|
package/src/cli.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { runInit } from "./commands/init";
|
|
|
3
3
|
import { runCheck, formatCheckResults, formatCheckResultsJson } from "./commands/check";
|
|
4
4
|
import { checkAllPrerequisites } from "./prerequisites";
|
|
5
5
|
import {
|
|
6
|
+
handleAideInit,
|
|
6
7
|
handleAideAdd,
|
|
7
8
|
handleAideUpdate,
|
|
8
9
|
handleAideRemove,
|
|
@@ -10,9 +11,14 @@ import {
|
|
|
10
11
|
handleAideShow,
|
|
11
12
|
handleAideValidate,
|
|
12
13
|
handleAideLock,
|
|
14
|
+
handleAideDiff,
|
|
13
15
|
printAideHelp,
|
|
14
16
|
} from "./commands/aide";
|
|
15
|
-
import { exportInvariants, exportClaude, exportCursor, exportAll } from "./export";
|
|
17
|
+
import { exportInvariants, exportClaude, exportCursor, exportCodex, exportAll } from "./export";
|
|
18
|
+
import { runStatus, formatStatus } from "./commands/status";
|
|
19
|
+
import { runCi, type CiOptions } from "./commands/ci";
|
|
20
|
+
import { runTasks, formatTasks } from "./commands/tasks";
|
|
21
|
+
import { handleDiff } from "./commands/diff";
|
|
16
22
|
|
|
17
23
|
const args = process.argv.slice(2);
|
|
18
24
|
const command = args[0];
|
|
@@ -33,10 +39,15 @@ async function main() {
|
|
|
33
39
|
} else if (command === "aide") {
|
|
34
40
|
await handleAide(args.slice(1));
|
|
35
41
|
} else if (command === "ci") {
|
|
36
|
-
|
|
37
|
-
process.exit(1);
|
|
42
|
+
await handleCi(args.slice(1));
|
|
38
43
|
} else if (command === "export") {
|
|
39
44
|
await handleExport(args.slice(1));
|
|
45
|
+
} else if (command === "status") {
|
|
46
|
+
await handleStatus(args.slice(1));
|
|
47
|
+
} else if (command === "tasks") {
|
|
48
|
+
await handleTasks(args.slice(1));
|
|
49
|
+
} else if (command === "diff") {
|
|
50
|
+
await handleDiff(args.slice(1));
|
|
40
51
|
} else {
|
|
41
52
|
console.error(`Unknown command: ${command}`);
|
|
42
53
|
console.error('Run "bantay help" for usage information.');
|
|
@@ -69,24 +80,34 @@ Usage: bantay <command> [options]
|
|
|
69
80
|
Commands:
|
|
70
81
|
init Initialize Bantay in the current project
|
|
71
82
|
check Check all invariants against the codebase
|
|
83
|
+
diff Show classified aide changes (wraps aide diff)
|
|
72
84
|
aide Manage the aide entity tree (add, remove, link, show, validate, lock)
|
|
73
85
|
ci Generate CI workflow configuration
|
|
74
86
|
export Export invariants to agent context files
|
|
87
|
+
status Show scenario implementation status
|
|
88
|
+
tasks Generate task list from aide CUJs
|
|
75
89
|
|
|
76
90
|
Options:
|
|
77
91
|
-h, --help Show this help message
|
|
78
92
|
|
|
79
93
|
Examples:
|
|
80
94
|
bantay init Initialize in current directory
|
|
95
|
+
bantay init --force Regenerate slash commands
|
|
81
96
|
bantay check Run full invariant check
|
|
82
97
|
bantay check --diff HEAD~1 Check only affected invariants
|
|
98
|
+
bantay diff Show classified aide changes
|
|
99
|
+
bantay diff --json Output changes as JSON
|
|
83
100
|
bantay aide show Show the aide entity tree
|
|
84
101
|
bantay aide add inv_test --parent invariants --prop "statement=Test"
|
|
85
102
|
bantay ci --github-actions Generate GitHub Actions workflow
|
|
86
|
-
bantay export all
|
|
87
|
-
bantay export invariants
|
|
88
|
-
bantay export claude
|
|
89
|
-
bantay export cursor
|
|
103
|
+
bantay export all Export all targets
|
|
104
|
+
bantay export invariants Generate invariants.md from bantay.aide
|
|
105
|
+
bantay export claude Export to CLAUDE.md
|
|
106
|
+
bantay export cursor Export to .cursorrules
|
|
107
|
+
bantay status Show scenario implementation status
|
|
108
|
+
bantay status --json Output as JSON
|
|
109
|
+
bantay tasks Generate tasks for changed CUJs (requires lock)
|
|
110
|
+
bantay tasks --all Generate tasks for all CUJs
|
|
90
111
|
|
|
91
112
|
Run "bantay aide help" for aide subcommand details.
|
|
92
113
|
`);
|
|
@@ -95,6 +116,7 @@ Run "bantay aide help" for aide subcommand details.
|
|
|
95
116
|
async function handleInit(args: string[]) {
|
|
96
117
|
const projectPath = process.cwd();
|
|
97
118
|
const regenerateConfig = args.includes("--regenerate-config");
|
|
119
|
+
const force = args.includes("--force");
|
|
98
120
|
const dryRun = args.includes("--dry-run");
|
|
99
121
|
|
|
100
122
|
console.log("Initializing Bantay...\n");
|
|
@@ -105,7 +127,7 @@ async function handleInit(args: string[]) {
|
|
|
105
127
|
}
|
|
106
128
|
|
|
107
129
|
try {
|
|
108
|
-
const result = await runInit(projectPath, { regenerateConfig });
|
|
130
|
+
const result = await runInit(projectPath, { regenerateConfig, force });
|
|
109
131
|
|
|
110
132
|
// Display detection results
|
|
111
133
|
console.log("Stack Detection:");
|
|
@@ -229,14 +251,16 @@ async function handleExport(args: string[]) {
|
|
|
229
251
|
console.error("Usage: bantay export <target>");
|
|
230
252
|
console.error("");
|
|
231
253
|
console.error("Targets:");
|
|
232
|
-
console.error(" all Export all targets (invariants, claude, cursor)");
|
|
254
|
+
console.error(" all Export all targets (invariants, claude, cursor, codex)");
|
|
233
255
|
console.error(" invariants Generate invariants.md from bantay.aide");
|
|
234
256
|
console.error(" claude Export to CLAUDE.md with section markers");
|
|
235
257
|
console.error(" cursor Export to .cursorrules with section markers");
|
|
258
|
+
console.error(" codex Export to AGENTS.md with section markers");
|
|
236
259
|
console.error("");
|
|
237
260
|
console.error("Examples:");
|
|
238
261
|
console.error(" bantay export invariants");
|
|
239
262
|
console.error(" bantay export claude");
|
|
263
|
+
console.error(" bantay export codex");
|
|
240
264
|
console.error(" bantay export --target cursor");
|
|
241
265
|
process.exit(1);
|
|
242
266
|
}
|
|
@@ -262,9 +286,13 @@ async function handleExport(args: string[]) {
|
|
|
262
286
|
const result = await exportCursor(projectPath, { dryRun });
|
|
263
287
|
console.log(`Exported to ${result.outputPath}`);
|
|
264
288
|
console.log(` ${result.bytesWritten} bytes written`);
|
|
289
|
+
} else if (target === "codex") {
|
|
290
|
+
const result = await exportCodex(projectPath, { dryRun });
|
|
291
|
+
console.log(`Exported to ${result.outputPath}`);
|
|
292
|
+
console.log(` ${result.bytesWritten} bytes written`);
|
|
265
293
|
} else {
|
|
266
294
|
console.error(`Unknown export target: ${target}`);
|
|
267
|
-
console.error('Valid targets: all, invariants, claude, cursor');
|
|
295
|
+
console.error('Valid targets: all, invariants, claude, cursor, codex');
|
|
268
296
|
process.exit(1);
|
|
269
297
|
}
|
|
270
298
|
|
|
@@ -283,6 +311,96 @@ async function handleExport(args: string[]) {
|
|
|
283
311
|
}
|
|
284
312
|
}
|
|
285
313
|
|
|
314
|
+
async function handleCi(args: string[]) {
|
|
315
|
+
const projectPath = process.cwd();
|
|
316
|
+
|
|
317
|
+
// Parse provider from args
|
|
318
|
+
const hasGitHub = args.includes("--github-actions") || args.includes("--github");
|
|
319
|
+
const hasGitLab = args.includes("--gitlab");
|
|
320
|
+
const force = args.includes("--force");
|
|
321
|
+
|
|
322
|
+
let provider: "github-actions" | "gitlab" | "generic";
|
|
323
|
+
|
|
324
|
+
if (hasGitHub) {
|
|
325
|
+
provider = "github-actions";
|
|
326
|
+
} else if (hasGitLab) {
|
|
327
|
+
provider = "gitlab";
|
|
328
|
+
} else {
|
|
329
|
+
provider = "generic";
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
const result = await runCi(projectPath, { provider, force });
|
|
334
|
+
|
|
335
|
+
if (result.alreadyExists) {
|
|
336
|
+
console.error(`${result.outputPath} already exists.`);
|
|
337
|
+
console.error("Use --force to overwrite.");
|
|
338
|
+
process.exit(1);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (provider === "generic") {
|
|
342
|
+
console.log(result.content);
|
|
343
|
+
} else {
|
|
344
|
+
console.log(`Generated ${result.outputPath}`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
process.exit(0);
|
|
348
|
+
} catch (error) {
|
|
349
|
+
if (error instanceof Error) {
|
|
350
|
+
console.error(`Error: ${error.message}`);
|
|
351
|
+
} else {
|
|
352
|
+
console.error("Error running ci:", error);
|
|
353
|
+
}
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async function handleStatus(args: string[]) {
|
|
359
|
+
const projectPath = process.cwd();
|
|
360
|
+
const jsonOutput = args.includes("--json");
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const result = await runStatus(projectPath, { json: jsonOutput });
|
|
364
|
+
|
|
365
|
+
if (jsonOutput) {
|
|
366
|
+
console.log(JSON.stringify(result, null, 2));
|
|
367
|
+
} else {
|
|
368
|
+
console.log(formatStatus(result));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
process.exit(0);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
if (error instanceof Error) {
|
|
374
|
+
console.error(`Error: ${error.message}`);
|
|
375
|
+
} else {
|
|
376
|
+
console.error("Error running status:", error);
|
|
377
|
+
}
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function handleTasks(args: string[]) {
|
|
383
|
+
const projectPath = process.cwd();
|
|
384
|
+
const allFlag = args.includes("--all");
|
|
385
|
+
|
|
386
|
+
// Parse --aide option
|
|
387
|
+
const aideIndex = args.indexOf("--aide");
|
|
388
|
+
const aideFile = aideIndex !== -1 ? args[aideIndex + 1] : undefined;
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
const result = await runTasks(projectPath, { all: allFlag, aide: aideFile });
|
|
392
|
+
console.log(formatTasks(result));
|
|
393
|
+
process.exit(0);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
if (error instanceof Error) {
|
|
396
|
+
console.error(`Error: ${error.message}`);
|
|
397
|
+
} else {
|
|
398
|
+
console.error("Error running tasks:", error);
|
|
399
|
+
}
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
286
404
|
async function handleAide(args: string[]) {
|
|
287
405
|
const subcommand = args[0];
|
|
288
406
|
|
|
@@ -293,7 +411,9 @@ async function handleAide(args: string[]) {
|
|
|
293
411
|
|
|
294
412
|
const subArgs = args.slice(1);
|
|
295
413
|
|
|
296
|
-
if (subcommand === "
|
|
414
|
+
if (subcommand === "init") {
|
|
415
|
+
await handleAideInit(subArgs);
|
|
416
|
+
} else if (subcommand === "add") {
|
|
297
417
|
await handleAideAdd(subArgs);
|
|
298
418
|
} else if (subcommand === "update") {
|
|
299
419
|
await handleAideUpdate(subArgs);
|
|
@@ -307,6 +427,8 @@ async function handleAide(args: string[]) {
|
|
|
307
427
|
await handleAideValidate(subArgs);
|
|
308
428
|
} else if (subcommand === "lock") {
|
|
309
429
|
await handleAideLock(subArgs);
|
|
430
|
+
} else if (subcommand === "diff") {
|
|
431
|
+
await handleAideDiff(subArgs);
|
|
310
432
|
} else {
|
|
311
433
|
console.error(`Unknown aide subcommand: ${subcommand}`);
|
|
312
434
|
console.error('Run "bantay aide help" for usage information.');
|