@bantay/cli 0.2.0 → 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 CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@bantay/cli",
3
- "version": "0.2.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": "./src/cli.ts"
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,11 +11,14 @@ import {
10
11
  handleAideShow,
11
12
  handleAideValidate,
12
13
  handleAideLock,
14
+ handleAideDiff,
13
15
  printAideHelp,
14
16
  } from "./commands/aide";
15
17
  import { exportInvariants, exportClaude, exportCursor, exportCodex, exportAll } from "./export";
16
18
  import { runStatus, formatStatus } from "./commands/status";
17
19
  import { runCi, type CiOptions } from "./commands/ci";
20
+ import { runTasks, formatTasks } from "./commands/tasks";
21
+ import { handleDiff } from "./commands/diff";
18
22
 
19
23
  const args = process.argv.slice(2);
20
24
  const command = args[0];
@@ -40,6 +44,10 @@ async function main() {
40
44
  await handleExport(args.slice(1));
41
45
  } else if (command === "status") {
42
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));
43
51
  } else {
44
52
  console.error(`Unknown command: ${command}`);
45
53
  console.error('Run "bantay help" for usage information.');
@@ -72,18 +80,23 @@ Usage: bantay <command> [options]
72
80
  Commands:
73
81
  init Initialize Bantay in the current project
74
82
  check Check all invariants against the codebase
83
+ diff Show classified aide changes (wraps aide diff)
75
84
  aide Manage the aide entity tree (add, remove, link, show, validate, lock)
76
85
  ci Generate CI workflow configuration
77
86
  export Export invariants to agent context files
78
87
  status Show scenario implementation status
88
+ tasks Generate task list from aide CUJs
79
89
 
80
90
  Options:
81
91
  -h, --help Show this help message
82
92
 
83
93
  Examples:
84
94
  bantay init Initialize in current directory
95
+ bantay init --force Regenerate slash commands
85
96
  bantay check Run full invariant check
86
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
87
100
  bantay aide show Show the aide entity tree
88
101
  bantay aide add inv_test --parent invariants --prop "statement=Test"
89
102
  bantay ci --github-actions Generate GitHub Actions workflow
@@ -93,6 +106,8 @@ Examples:
93
106
  bantay export cursor Export to .cursorrules
94
107
  bantay status Show scenario implementation status
95
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
96
111
 
97
112
  Run "bantay aide help" for aide subcommand details.
98
113
  `);
@@ -101,6 +116,7 @@ Run "bantay aide help" for aide subcommand details.
101
116
  async function handleInit(args: string[]) {
102
117
  const projectPath = process.cwd();
103
118
  const regenerateConfig = args.includes("--regenerate-config");
119
+ const force = args.includes("--force");
104
120
  const dryRun = args.includes("--dry-run");
105
121
 
106
122
  console.log("Initializing Bantay...\n");
@@ -111,7 +127,7 @@ async function handleInit(args: string[]) {
111
127
  }
112
128
 
113
129
  try {
114
- const result = await runInit(projectPath, { regenerateConfig });
130
+ const result = await runInit(projectPath, { regenerateConfig, force });
115
131
 
116
132
  // Display detection results
117
133
  console.log("Stack Detection:");
@@ -363,6 +379,28 @@ async function handleStatus(args: string[]) {
363
379
  }
364
380
  }
365
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
+
366
404
  async function handleAide(args: string[]) {
367
405
  const subcommand = args[0];
368
406
 
@@ -373,7 +411,9 @@ async function handleAide(args: string[]) {
373
411
 
374
412
  const subArgs = args.slice(1);
375
413
 
376
- if (subcommand === "add") {
414
+ if (subcommand === "init") {
415
+ await handleAideInit(subArgs);
416
+ } else if (subcommand === "add") {
377
417
  await handleAideAdd(subArgs);
378
418
  } else if (subcommand === "update") {
379
419
  await handleAideUpdate(subArgs);
@@ -387,6 +427,8 @@ async function handleAide(args: string[]) {
387
427
  await handleAideValidate(subArgs);
388
428
  } else if (subcommand === "lock") {
389
429
  await handleAideLock(subArgs);
430
+ } else if (subcommand === "diff") {
431
+ await handleAideDiff(subArgs);
390
432
  } else {
391
433
  console.error(`Unknown aide subcommand: ${subcommand}`);
392
434
  console.error('Run "bantay aide help" for usage information.');