@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.
- package/dist/index.js +108 -18
- 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
|
|
108
|
-
var
|
|
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 =
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
|
|
707
|
-
|
|
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 = ?
|
|
712
|
-
|
|
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 =
|
|
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(
|
|
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.
|
|
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/
|
|
27
|
+
"url": "git+https://github.com/patrickjoubert/groundctl.git"
|
|
28
28
|
},
|
|
29
29
|
"bugs": {
|
|
30
|
-
"url": "https://github.com/
|
|
30
|
+
"url": "https://github.com/patrickjoubert/groundctl/issues"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsup src/index.ts --format esm --dts --clean",
|