@groundctl/cli 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.
Files changed (2) hide show
  1. package/dist/index.js +108 -18
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
+ import { createRequire } from "module";
5
6
 
6
7
  // src/commands/init.ts
7
8
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, chmodSync, readFileSync as readFileSync3, appendFileSync } from "fs";
@@ -104,13 +105,42 @@ function applySchema(db) {
104
105
  }
105
106
 
106
107
  // src/storage/db.ts
107
- var GROUNDCTL_DIR = join(homedir(), ".groundctl");
108
- var DB_PATH = join(GROUNDCTL_DIR, "db.sqlite");
108
+ var GLOBAL_DIR = join(homedir(), ".groundctl");
109
+ var GLOBAL_DB_PATH = join(GLOBAL_DIR, "db.sqlite");
109
110
  var _db = null;
110
- var _dbPath = DB_PATH;
111
- async function openDb(path) {
111
+ var _dbPath = GLOBAL_DB_PATH;
112
+ var _groundctlDir = GLOBAL_DIR;
113
+ function findProjectDir(startDir) {
114
+ let dir = startDir ?? process.cwd();
115
+ const root = dirname(dir);
116
+ for (let i = 0; i < 10; i++) {
117
+ if (existsSync(join(dir, ".groundctl"))) {
118
+ return join(dir, ".groundctl");
119
+ }
120
+ if (existsSync(join(dir, ".git"))) {
121
+ return join(dir, ".groundctl");
122
+ }
123
+ const parent = dirname(dir);
124
+ if (parent === dir) break;
125
+ dir = parent;
126
+ }
127
+ return null;
128
+ }
129
+ async function openDb(explicitPath) {
112
130
  if (_db) return _db;
113
- const dbPath = path ?? DB_PATH;
131
+ let dbPath;
132
+ if (explicitPath) {
133
+ dbPath = explicitPath;
134
+ } else {
135
+ const projectDir = findProjectDir();
136
+ if (projectDir) {
137
+ _groundctlDir = projectDir;
138
+ dbPath = join(projectDir, "db.sqlite");
139
+ } else {
140
+ _groundctlDir = GLOBAL_DIR;
141
+ dbPath = GLOBAL_DB_PATH;
142
+ }
143
+ }
114
144
  _dbPath = dbPath;
115
145
  const dir = dirname(dbPath);
116
146
  if (!existsSync(dir)) {
@@ -380,11 +410,14 @@ function parseProjectStateMd(content) {
380
410
  section = trimmed.toLowerCase();
381
411
  continue;
382
412
  }
413
+ if (section.includes("decision") || section.includes("session") || section.includes("debt") || section.includes("note")) continue;
383
414
  if (!trimmed.startsWith("- ") && !trimmed.startsWith("* ")) continue;
384
415
  const item = trimmed.slice(2).trim();
385
416
  if (!item || item.length < 3) continue;
386
417
  const name = item.split("(")[0].split("\u2192")[0].split("\u2014")[0].trim();
387
- if (!name || name.length < 3) continue;
418
+ if (!name || name.length < 3 || name.length > 80) continue;
419
+ if (/^\d{4}-\d{2}-\d{2}/.test(name)) continue;
420
+ if (name.split(" ").length > 8) continue;
388
421
  let status = "pending";
389
422
  let priority = "medium";
390
423
  if (section.includes("built") || section.includes("done") || section.includes("complete")) {
@@ -703,14 +736,40 @@ function timeSince(isoDate) {
703
736
  // src/commands/claim.ts
704
737
  import chalk3 from "chalk";
705
738
  import { randomUUID } from "crypto";
706
- async function claimCommand(featureIdOrName, options) {
707
- const db = await openDb();
708
- const feature = queryOne(
739
+ function findFeature(db, term) {
740
+ return queryOne(
709
741
  db,
710
742
  `SELECT id, name, status FROM features
711
- WHERE id = ? OR name = ? OR name LIKE ?`,
712
- [featureIdOrName, featureIdOrName, `%${featureIdOrName}%`]
743
+ WHERE id = ? OR name = ?
744
+ UNION ALL
745
+ SELECT id, name, status FROM features
746
+ WHERE (id LIKE ? OR name LIKE ?)
747
+ AND id != ? AND name != ?
748
+ UNION ALL
749
+ SELECT id, name, status FROM features
750
+ WHERE (id LIKE ? OR name LIKE ?)
751
+ AND id NOT LIKE ? AND name NOT LIKE ?
752
+ AND id != ? AND name != ?
753
+ LIMIT 1`,
754
+ [
755
+ term,
756
+ term,
757
+ `${term}%`,
758
+ `${term}%`,
759
+ term,
760
+ term,
761
+ `%${term}%`,
762
+ `%${term}%`,
763
+ `${term}%`,
764
+ `${term}%`,
765
+ term,
766
+ term
767
+ ]
713
768
  );
769
+ }
770
+ async function claimCommand(featureIdOrName, options) {
771
+ const db = await openDb();
772
+ const feature = findFeature(db, featureIdOrName);
714
773
  if (!feature) {
715
774
  console.log(chalk3.red(`
716
775
  Feature "${featureIdOrName}" not found.
@@ -788,12 +847,7 @@ async function claimCommand(featureIdOrName, options) {
788
847
  }
789
848
  async function completeCommand(featureIdOrName) {
790
849
  const db = await openDb();
791
- const feature = queryOne(
792
- db,
793
- `SELECT id, name, status FROM features
794
- WHERE id = ? OR name = ? OR name LIKE ?`,
795
- [featureIdOrName, featureIdOrName, `%${featureIdOrName}%`]
796
- );
850
+ const feature = findFeature(db, featureIdOrName);
797
851
  if (!feature) {
798
852
  console.log(chalk3.red(`
799
853
  Feature "${featureIdOrName}" not found.
@@ -1613,9 +1667,44 @@ async function healthCommand() {
1613
1667
  }
1614
1668
  }
1615
1669
 
1670
+ // src/commands/dashboard.ts
1671
+ import { existsSync as existsSync5 } from "fs";
1672
+ import { join as join7, dirname as dirname2 } from "path";
1673
+ import { fileURLToPath } from "url";
1674
+ import { spawn } from "child_process";
1675
+ import chalk11 from "chalk";
1676
+ var __dirname = dirname2(fileURLToPath(import.meta.url));
1677
+ async function dashboardCommand(options) {
1678
+ const port = options.port ?? "4242";
1679
+ const serverPath = join7(__dirname, "..", "..", "..", "dashboard", "src", "server.js");
1680
+ if (!existsSync5(serverPath)) {
1681
+ console.log(chalk11.red(`
1682
+ Dashboard server not found at: ${serverPath}
1683
+ `));
1684
+ console.log(chalk11.gray(" If running from source: npm run build"));
1685
+ return;
1686
+ }
1687
+ console.log(chalk11.bold(`
1688
+ groundctl dashboard \u2192 http://localhost:${port}
1689
+ `));
1690
+ console.log(chalk11.gray(" Auto-refreshes every 10s. Press Ctrl+C to stop.\n"));
1691
+ const child = spawn(process.execPath, [serverPath], {
1692
+ stdio: "inherit",
1693
+ env: { ...process.env, GROUNDCTL_PORT: port }
1694
+ });
1695
+ child.on("error", (err) => {
1696
+ console.error(chalk11.red(` Error: ${err.message}`));
1697
+ });
1698
+ await new Promise((resolve2) => {
1699
+ child.on("close", () => resolve2());
1700
+ });
1701
+ }
1702
+
1616
1703
  // src/index.ts
1704
+ var require2 = createRequire(import.meta.url);
1705
+ var pkg = require2("../package.json");
1617
1706
  var program = new Command();
1618
- program.name("groundctl").description("The shared memory your agents and you actually need.").version("0.1.0");
1707
+ program.name("groundctl").description("The shared memory your agents and you actually need.").version(pkg.version);
1619
1708
  program.command("init").description("Setup hooks + initial state for the current project").option("--import-from-git", "Bootstrap sessions and features from git history").action((opts) => initCommand({ importFromGit: opts.importFromGit }));
1620
1709
  program.command("status").description("Show macro view of the product state").action(statusCommand);
1621
1710
  program.command("claim <feature>").description("Reserve a feature for the current session").option("-s, --session <id>", "Session ID (auto-generated if omitted)").action(claimCommand);
@@ -1635,4 +1724,5 @@ program.command("ingest").description("Parse a transcript and write session data
1635
1724
  );
1636
1725
  program.command("report").description("Generate SESSION_REPORT.md from SQLite").option("-s, --session <id>", "Report for a specific session").option("--all", "Generate report for all sessions").action(reportCommand);
1637
1726
  program.command("health").description("Show product health score").action(healthCommand);
1727
+ program.command("dashboard").description("Start web dashboard on port 4242").option("-p, --port <port>", "Port number", "4242").action(dashboardCommand);
1638
1728
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundctl/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Product memory for AI agent builders",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -24,10 +24,10 @@
24
24
  "homepage": "https://groundctl.org",
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "git+https://github.com/groundctl/groundctl.git"
27
+ "url": "git+https://github.com/patrickjoubert/groundctl.git"
28
28
  },
29
29
  "bugs": {
30
- "url": "https://github.com/groundctl/groundctl/issues"
30
+ "url": "https://github.com/patrickjoubert/groundctl/issues"
31
31
  },
32
32
  "scripts": {
33
33
  "build": "tsup src/index.ts --format esm --dts --clean",