@a-company/paradigm 3.20.2 → 3.22.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/graph-BFZXFAFL.js +160 -0
- package/dist/graph-server-BPKQYSQK.js +188 -0
- package/dist/{habits-KD4RLIN2.js → habits-7BORPC2F.js} +31 -8
- package/dist/index.js +18 -10
- package/dist/mcp.js +675 -29
- package/graph-ui/dist/assets/index-BJ499n6C.css +1 -0
- package/graph-ui/dist/assets/index-EP5AoOxo.js +63 -0
- package/graph-ui/dist/index.html +13 -0
- package/package.json +4 -2
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./chunk-ZXMDA7VB.js";
|
|
3
|
+
|
|
4
|
+
// src/commands/graph.ts
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as pathMod from "path";
|
|
8
|
+
async function graphCommand(path, options) {
|
|
9
|
+
const projectDir = path || process.cwd();
|
|
10
|
+
const port = parseInt(options.port || "3841", 10);
|
|
11
|
+
const shouldOpen = options.open !== false;
|
|
12
|
+
console.log(chalk.cyan("\nStarting Symbol Graph...\n"));
|
|
13
|
+
try {
|
|
14
|
+
const { startGraphServer } = await import("./graph-server-BPKQYSQK.js");
|
|
15
|
+
console.log(chalk.gray(`Project: ${projectDir}`));
|
|
16
|
+
console.log(chalk.gray(`Port: ${port}`));
|
|
17
|
+
console.log();
|
|
18
|
+
await startGraphServer({ port, projectDir, open: shouldOpen });
|
|
19
|
+
console.log(chalk.green(`
|
|
20
|
+
Symbol Graph is running at http://localhost:${port}`));
|
|
21
|
+
console.log(chalk.gray("\nPress Ctrl+C to stop\n"));
|
|
22
|
+
await new Promise(() => {
|
|
23
|
+
});
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error.code === "EADDRINUSE") {
|
|
26
|
+
console.error(chalk.red(`
|
|
27
|
+
Error: Port ${port} is already in use.`));
|
|
28
|
+
console.log(chalk.gray(`Try: paradigm graph --port ${port + 1}
|
|
29
|
+
`));
|
|
30
|
+
} else {
|
|
31
|
+
console.error(chalk.red("\nFailed to start Symbol Graph:"), error);
|
|
32
|
+
}
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
var CATEGORY_PREFIXES = {
|
|
37
|
+
component: "#",
|
|
38
|
+
flow: "$",
|
|
39
|
+
gate: "^",
|
|
40
|
+
signal: "!",
|
|
41
|
+
aspect: "~"
|
|
42
|
+
};
|
|
43
|
+
var NODE_W = 200;
|
|
44
|
+
var NODE_H = 60;
|
|
45
|
+
var NODE_GAP = 20;
|
|
46
|
+
var GPAD = 40;
|
|
47
|
+
var GHEADER = 50;
|
|
48
|
+
var GGAP = 60;
|
|
49
|
+
var SCAN_CATEGORY_MAP = {
|
|
50
|
+
components: "component",
|
|
51
|
+
flows: "flow",
|
|
52
|
+
gates: "gate",
|
|
53
|
+
signals: "signal",
|
|
54
|
+
aspects: "aspect"
|
|
55
|
+
};
|
|
56
|
+
function loadSymbolsFromIndex(projectDir) {
|
|
57
|
+
const indexPath = pathMod.join(projectDir, ".paradigm", "scan-index.json");
|
|
58
|
+
if (!fs.existsSync(indexPath)) return [];
|
|
59
|
+
const raw = JSON.parse(fs.readFileSync(indexPath, "utf8"));
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const [sectionKey, categoryName] of Object.entries(SCAN_CATEGORY_MAP)) {
|
|
62
|
+
const section = raw[sectionKey];
|
|
63
|
+
if (!section || typeof section !== "object") continue;
|
|
64
|
+
for (const [id, sym] of Object.entries(section)) {
|
|
65
|
+
const s = sym;
|
|
66
|
+
out.push({ id, name: id, category: categoryName, prefix: CATEGORY_PREFIXES[categoryName] || "#", description: s.description, path: s.path });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
function resolveSymbol(name, all) {
|
|
72
|
+
const stripped = name.replace(/^[#$^!~]/, "");
|
|
73
|
+
return all.find((s) => s.id === stripped || s.name === stripped || s.id === name || s.name === name);
|
|
74
|
+
}
|
|
75
|
+
function cliBuildGraphState(projectDir, symbolFilter, groups, links, graphName = "Generated Graph") {
|
|
76
|
+
const allSymbols = loadSymbolsFromIndex(projectDir);
|
|
77
|
+
let included = symbolFilter && symbolFilter.length > 0 ? symbolFilter.map((n) => resolveSymbol(n, allSymbols)).filter(Boolean) : allSymbols;
|
|
78
|
+
const nodes = [];
|
|
79
|
+
const edges = [];
|
|
80
|
+
const groupIdMap = /* @__PURE__ */ new Map();
|
|
81
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
82
|
+
let nextX = 0;
|
|
83
|
+
if (groups && groups.length > 0) {
|
|
84
|
+
for (const g of groups) {
|
|
85
|
+
const gid = `group-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
86
|
+
groupIdMap.set(g.label, gid);
|
|
87
|
+
const members = g.symbols.map((n) => resolveSymbol(n, included)).filter(Boolean);
|
|
88
|
+
const cols = Math.max(Math.ceil(Math.sqrt(members.length)), 1);
|
|
89
|
+
const rows = Math.max(Math.ceil(members.length / cols), 1);
|
|
90
|
+
for (let i = 0; i < members.length; i++) {
|
|
91
|
+
const sym = members[i];
|
|
92
|
+
const c = i % cols, r = Math.floor(i / cols);
|
|
93
|
+
nodes.push({ id: `sym-${sym.id}`, type: "symbolNode", position: { x: GPAD + c * (NODE_W + NODE_GAP), y: GHEADER + GPAD + r * (NODE_H + NODE_GAP) }, parentId: gid, data: { type: "symbol", symbol: sym, label: `${CATEGORY_PREFIXES[sym.category] || "#"}${sym.name}` } });
|
|
94
|
+
assigned.add(sym.id);
|
|
95
|
+
}
|
|
96
|
+
const gw = GPAD * 2 + cols * NODE_W + (cols - 1) * NODE_GAP;
|
|
97
|
+
const gh = GHEADER + GPAD * 2 + rows * NODE_H + (rows - 1) * NODE_GAP;
|
|
98
|
+
nodes.unshift({ id: gid, type: "groupNode", position: { x: nextX, y: 0 }, style: { width: gw, height: gh }, data: { type: "group", label: g.label } });
|
|
99
|
+
nextX += gw + GGAP;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const ungrouped = included.filter((s) => !assigned.has(s.id));
|
|
103
|
+
if (ungrouped.length > 0) {
|
|
104
|
+
const startY = groups && groups.length > 0 ? 400 : 0;
|
|
105
|
+
const cols = Math.max(Math.ceil(Math.sqrt(ungrouped.length)), 1);
|
|
106
|
+
for (let i = 0; i < ungrouped.length; i++) {
|
|
107
|
+
const sym = ungrouped[i];
|
|
108
|
+
const c = i % cols, r = Math.floor(i / cols);
|
|
109
|
+
nodes.push({ id: `sym-${sym.id}`, type: "symbolNode", position: { x: c * (NODE_W + NODE_GAP), y: startY + r * (NODE_H + NODE_GAP) }, data: { type: "symbol", symbol: sym, label: `${CATEGORY_PREFIXES[sym.category] || "#"}${sym.name}` } });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (links && links.length > 0) {
|
|
113
|
+
for (const l of links) {
|
|
114
|
+
const src = groupIdMap.get(l.source), tgt = groupIdMap.get(l.target);
|
|
115
|
+
if (src && tgt) edges.push({ id: `e-${src}-${tgt}`, source: src, target: tgt, type: "default", label: l.label, data: { label: l.label } });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { version: "1.0", name: graphName, projectId: pathMod.basename(projectDir), lastModified: (/* @__PURE__ */ new Date()).toISOString(), nodes, edges };
|
|
119
|
+
}
|
|
120
|
+
async function graphGenerateCommand(path, options) {
|
|
121
|
+
const projectDir = path || process.cwd();
|
|
122
|
+
try {
|
|
123
|
+
const symbolFilter = options.symbols ? options.symbols.split(",").map((s) => s.trim()) : void 0;
|
|
124
|
+
const groups = options.group?.map((g) => {
|
|
125
|
+
const colonIdx = g.indexOf(":");
|
|
126
|
+
if (colonIdx === -1) {
|
|
127
|
+
console.error(chalk.red(`Invalid group format: "${g}". Expected "Label:#sym1,#sym2"`));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
return { label: g.slice(0, colonIdx), symbols: g.slice(colonIdx + 1).split(",").map((s) => s.trim()) };
|
|
131
|
+
});
|
|
132
|
+
const links = options.link?.map((l) => {
|
|
133
|
+
const arrowIdx = l.indexOf(">");
|
|
134
|
+
if (arrowIdx === -1) {
|
|
135
|
+
console.error(chalk.red(`Invalid link format: "${l}". Expected "Source>Target:label"`));
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
const source = l.slice(0, arrowIdx);
|
|
139
|
+
const rest = l.slice(arrowIdx + 1);
|
|
140
|
+
const colonIdx = rest.indexOf(":");
|
|
141
|
+
return { source, target: colonIdx === -1 ? rest : rest.slice(0, colonIdx), label: colonIdx === -1 ? void 0 : rest.slice(colonIdx + 1) };
|
|
142
|
+
});
|
|
143
|
+
const state = cliBuildGraphState(projectDir, symbolFilter, groups, links, options.name || "Generated Graph");
|
|
144
|
+
const json = JSON.stringify(state, null, 2);
|
|
145
|
+
if (options.output) {
|
|
146
|
+
fs.writeFileSync(options.output, json, "utf8");
|
|
147
|
+
console.log(chalk.green(`Graph written to ${options.output}`));
|
|
148
|
+
console.log(chalk.gray(`${state.nodes.length} nodes, ${state.edges.length} edges`));
|
|
149
|
+
} else {
|
|
150
|
+
process.stdout.write(json + "\n");
|
|
151
|
+
}
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error(chalk.red("Failed to generate graph:"), error.message);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
graphCommand,
|
|
159
|
+
graphGenerateCommand
|
|
160
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./chunk-ZXMDA7VB.js";
|
|
3
|
+
|
|
4
|
+
// src/graph-server/index.ts
|
|
5
|
+
import express from "express";
|
|
6
|
+
import * as path2 from "path";
|
|
7
|
+
import * as fs2 from "fs";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
|
|
11
|
+
// src/graph-server/routes/symbols.ts
|
|
12
|
+
import { Router } from "express";
|
|
13
|
+
import * as fs from "fs";
|
|
14
|
+
import * as path from "path";
|
|
15
|
+
function createSymbolsRouter(projectDir) {
|
|
16
|
+
const router = Router();
|
|
17
|
+
router.get("/", (_req, res) => {
|
|
18
|
+
const indexPath = path.join(projectDir, ".paradigm", "scan-index.json");
|
|
19
|
+
if (!fs.existsSync(indexPath)) {
|
|
20
|
+
res.status(404).json({
|
|
21
|
+
error: "scan-index.json not found",
|
|
22
|
+
hint: "Run `paradigm scan` to generate the symbol index."
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const raw = fs.readFileSync(indexPath, "utf-8");
|
|
28
|
+
const index = JSON.parse(raw);
|
|
29
|
+
const categoryMap = {
|
|
30
|
+
components: "component",
|
|
31
|
+
features: "component",
|
|
32
|
+
flows: "flow",
|
|
33
|
+
state: "component",
|
|
34
|
+
gates: "gate",
|
|
35
|
+
signals: "signal",
|
|
36
|
+
aspects: "aspect",
|
|
37
|
+
screens: "component"
|
|
38
|
+
};
|
|
39
|
+
const prefixMap = {
|
|
40
|
+
component: "#",
|
|
41
|
+
flow: "$",
|
|
42
|
+
gate: "^",
|
|
43
|
+
signal: "!",
|
|
44
|
+
aspect: "~"
|
|
45
|
+
};
|
|
46
|
+
const symbols = [];
|
|
47
|
+
const seen = /* @__PURE__ */ new Set();
|
|
48
|
+
for (const [sectionKey, category] of Object.entries(categoryMap)) {
|
|
49
|
+
const entries = index[sectionKey];
|
|
50
|
+
if (!entries || typeof entries !== "object") continue;
|
|
51
|
+
const items = Array.isArray(entries) ? entries : Object.values(entries);
|
|
52
|
+
for (const entry of items) {
|
|
53
|
+
const name = entry.name || entry.id || "";
|
|
54
|
+
if (!name || seen.has(name)) continue;
|
|
55
|
+
seen.add(name);
|
|
56
|
+
symbols.push({
|
|
57
|
+
id: name,
|
|
58
|
+
name,
|
|
59
|
+
category,
|
|
60
|
+
prefix: prefixMap[category] || "#",
|
|
61
|
+
description: entry.description || "",
|
|
62
|
+
path: entry.path || entry.file || "",
|
|
63
|
+
tags: entry.tags || [],
|
|
64
|
+
related: entry.related || []
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
res.json({
|
|
69
|
+
symbols,
|
|
70
|
+
meta: {
|
|
71
|
+
total: symbols.length,
|
|
72
|
+
projectDir,
|
|
73
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
} catch (err) {
|
|
77
|
+
res.status(500).json({
|
|
78
|
+
error: "Failed to read scan-index.json",
|
|
79
|
+
details: err.message
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return router;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/graph-server/index.ts
|
|
87
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
88
|
+
var __dirname = path2.dirname(__filename);
|
|
89
|
+
var log = {
|
|
90
|
+
component(name) {
|
|
91
|
+
const symbol = chalk.magenta(`#${name}`);
|
|
92
|
+
return {
|
|
93
|
+
info: (msg, data) => {
|
|
94
|
+
const dataStr = data ? chalk.gray(` ${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(" ")}`) : "";
|
|
95
|
+
console.log(`${chalk.blue("i")} ${symbol} ${msg}${dataStr}`);
|
|
96
|
+
},
|
|
97
|
+
success: (msg, data) => {
|
|
98
|
+
const dataStr = data ? chalk.gray(` ${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(" ")}`) : "";
|
|
99
|
+
console.log(`${chalk.green("+")} ${symbol} ${msg}${dataStr}`);
|
|
100
|
+
},
|
|
101
|
+
warn: (msg, data) => {
|
|
102
|
+
const dataStr = data ? chalk.gray(` ${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(" ")}`) : "";
|
|
103
|
+
console.log(`${chalk.yellow("!")} ${symbol} ${msg}${dataStr}`);
|
|
104
|
+
},
|
|
105
|
+
error: (msg, data) => {
|
|
106
|
+
const dataStr = data ? chalk.gray(` ${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(" ")}`) : "";
|
|
107
|
+
console.error(`${chalk.red("x")} ${symbol} ${msg}${dataStr}`);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
function createGraphApp(options) {
|
|
113
|
+
const app = express();
|
|
114
|
+
app.use(express.json());
|
|
115
|
+
app.use((_req, res, next) => {
|
|
116
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
117
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
118
|
+
res.header("Access-Control-Allow-Headers", "Content-Type");
|
|
119
|
+
if (_req.method === "OPTIONS") {
|
|
120
|
+
res.sendStatus(204);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
next();
|
|
124
|
+
});
|
|
125
|
+
app.use("/api/symbols", createSymbolsRouter(options.projectDir));
|
|
126
|
+
app.get("/api/health", (_req, res) => {
|
|
127
|
+
res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
128
|
+
});
|
|
129
|
+
let uiDistPath = path2.join(__dirname, "..", "graph-ui", "dist");
|
|
130
|
+
if (!fs2.existsSync(uiDistPath)) {
|
|
131
|
+
uiDistPath = path2.join(__dirname, "..", "..", "graph-ui", "dist");
|
|
132
|
+
}
|
|
133
|
+
if (fs2.existsSync(uiDistPath)) {
|
|
134
|
+
app.use(express.static(uiDistPath));
|
|
135
|
+
app.get("{*path}", (req, res) => {
|
|
136
|
+
if (!req.path.startsWith("/api")) {
|
|
137
|
+
res.sendFile(path2.join(uiDistPath, "index.html"));
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
} else {
|
|
141
|
+
app.get("/", (_req, res) => {
|
|
142
|
+
res.send(`
|
|
143
|
+
<html>
|
|
144
|
+
<head><title>Paradigm Graph</title></head>
|
|
145
|
+
<body style="background:#0a0a0f;color:#e2e8f0;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0">
|
|
146
|
+
<div style="text-align:center">
|
|
147
|
+
<h1>Paradigm Graph</h1>
|
|
148
|
+
<p style="color:#94a3b8">UI not built yet. Run <code style="background:#1e293b;padding:4px 8px;border-radius:4px">cd graph-ui && npx vite build</code></p>
|
|
149
|
+
<p style="color:#94a3b8">API available at <a href="/api/symbols" style="color:#7dd3fc">/api/symbols</a></p>
|
|
150
|
+
</div>
|
|
151
|
+
</body>
|
|
152
|
+
</html>
|
|
153
|
+
`);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return app;
|
|
157
|
+
}
|
|
158
|
+
async function startGraphServer(options) {
|
|
159
|
+
const app = createGraphApp(options);
|
|
160
|
+
log.component("graph-server").info("Starting server", { port: options.port });
|
|
161
|
+
log.component("graph-server").info("Project directory", { path: options.projectDir });
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
const server = app.listen(options.port, () => {
|
|
164
|
+
log.component("graph-server").success("Server running", { url: `http://localhost:${options.port}` });
|
|
165
|
+
if (options.open) {
|
|
166
|
+
import("open").then((openModule) => {
|
|
167
|
+
openModule.default(`http://localhost:${options.port}`);
|
|
168
|
+
log.component("graph-server").info("Opened browser");
|
|
169
|
+
}).catch(() => {
|
|
170
|
+
log.component("graph-server").warn("Could not open browser automatically");
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
resolve();
|
|
174
|
+
});
|
|
175
|
+
server.on("error", (err) => {
|
|
176
|
+
if (err.code === "EADDRINUSE") {
|
|
177
|
+
log.component("graph-server").error("Port already in use", { port: options.port });
|
|
178
|
+
} else {
|
|
179
|
+
log.component("graph-server").error("Server error", { error: err.message });
|
|
180
|
+
}
|
|
181
|
+
reject(err);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
export {
|
|
186
|
+
createGraphApp,
|
|
187
|
+
startGraphServer
|
|
188
|
+
};
|
|
@@ -263,22 +263,45 @@ function loadHabitsFresh(rootDir) {
|
|
|
263
263
|
for (const seed of SEED_HABITS) {
|
|
264
264
|
habitsById.set(seed.id, { ...seed });
|
|
265
265
|
}
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
".paradigm",
|
|
269
|
-
"habits.yaml"
|
|
270
|
-
);
|
|
271
|
-
const globalConfig = loadHabitsYaml(globalHabitsPath);
|
|
266
|
+
const home = process.env.HOME || process.env.USERPROFILE || "~";
|
|
267
|
+
const globalConfig = loadHabitsYaml(path.join(home, ".paradigm", "habits.yaml"));
|
|
272
268
|
if (globalConfig) {
|
|
273
269
|
mergeHabits(habitsById, globalConfig);
|
|
274
270
|
}
|
|
275
|
-
const
|
|
276
|
-
const
|
|
271
|
+
const globalHabitFiles = loadHabitFiles(path.join(home, ".paradigm", "habits"));
|
|
272
|
+
for (const habit of globalHabitFiles) {
|
|
273
|
+
habitsById.set(habit.id, habit);
|
|
274
|
+
}
|
|
275
|
+
const projectConfig = loadHabitsYaml(path.join(rootDir, ".paradigm", "habits.yaml"));
|
|
277
276
|
if (projectConfig) {
|
|
278
277
|
mergeHabits(habitsById, projectConfig);
|
|
279
278
|
}
|
|
279
|
+
const projectHabitFiles = loadHabitFiles(path.join(rootDir, ".paradigm", "habits"));
|
|
280
|
+
for (const habit of projectHabitFiles) {
|
|
281
|
+
habitsById.set(habit.id, habit);
|
|
282
|
+
}
|
|
280
283
|
return Array.from(habitsById.values());
|
|
281
284
|
}
|
|
285
|
+
function loadHabitFiles(dir) {
|
|
286
|
+
if (!fs.existsSync(dir)) return [];
|
|
287
|
+
try {
|
|
288
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith(".habit")).sort();
|
|
289
|
+
const habits = [];
|
|
290
|
+
for (const file of files) {
|
|
291
|
+
try {
|
|
292
|
+
const content = fs.readFileSync(path.join(dir, file), "utf8");
|
|
293
|
+
const habit = yaml.load(content);
|
|
294
|
+
if (habit?.id && habit?.name) {
|
|
295
|
+
habits.push(habit);
|
|
296
|
+
}
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return habits;
|
|
301
|
+
} catch {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
282
305
|
function loadHabitsYaml(filePath) {
|
|
283
306
|
if (!fs.existsSync(filePath)) {
|
|
284
307
|
return null;
|
package/dist/index.js
CHANGED
|
@@ -660,45 +660,53 @@ loreCmd.option("-p, --port <port>", "Port to run on", "3840").option("--no-open"
|
|
|
660
660
|
const { loreServeCommand } = await import("./serve-DIALBCTU.js");
|
|
661
661
|
await loreServeCommand(void 0, options);
|
|
662
662
|
});
|
|
663
|
+
var graphCmd = program.command("graph").description("Interactive symbol relationship graph").argument("[path]", "Project directory", void 0).option("-p, --port <port>", "Port to run on", "3841").option("--no-open", "Don't open browser automatically").action(async (path2, options) => {
|
|
664
|
+
const { graphCommand } = await import("./graph-BFZXFAFL.js");
|
|
665
|
+
await graphCommand(path2, options);
|
|
666
|
+
});
|
|
667
|
+
graphCmd.command("generate").description("Generate GraphState JSON from symbols (for agents / piping)").argument("[path]", "Project directory", void 0).option("-s, --symbols <list>", "Comma-separated symbol names to include").option("-g, --group <spec...>", 'Group spec: "Label:#sym1,#sym2" (repeatable)').option("-l, --link <spec...>", 'Link spec: "Source>Target:label" (repeatable)').option("-n, --name <name>", "Graph name", "Generated Graph").option("-o, --output <file>", "Write to file instead of stdout").action(async (path2, options) => {
|
|
668
|
+
const { graphGenerateCommand } = await import("./graph-BFZXFAFL.js");
|
|
669
|
+
await graphGenerateCommand(path2, options);
|
|
670
|
+
});
|
|
663
671
|
var habitsCmd = program.command("habits").description("Behavioral habits - practice tracking and compliance");
|
|
664
672
|
habitsCmd.command("list").alias("ls").description("List all configured habits").option("--trigger <trigger>", "Filter by trigger: preflight, postflight, on-stop, on-commit").option("--category <category>", "Filter by category: discovery, verification, testing, documentation, collaboration, security").option("--json", "Output as JSON").action(async (options) => {
|
|
665
|
-
const { habitsListCommand } = await import("./habits-
|
|
673
|
+
const { habitsListCommand } = await import("./habits-7BORPC2F.js");
|
|
666
674
|
await habitsListCommand(options);
|
|
667
675
|
});
|
|
668
676
|
habitsCmd.command("status").description("Show practice profile with compliance rates").option("-p, --period <period>", "Time period: 7d, 30d, 90d, all", "30d").option("--json", "Output as JSON").action(async (options) => {
|
|
669
|
-
const { habitsStatusCommand } = await import("./habits-
|
|
677
|
+
const { habitsStatusCommand } = await import("./habits-7BORPC2F.js");
|
|
670
678
|
await habitsStatusCommand(options);
|
|
671
679
|
});
|
|
672
680
|
habitsCmd.command("init").description("Initialize habits.yaml with seed habits").option("-f, --force", "Overwrite existing file").action(async (options) => {
|
|
673
|
-
const { habitsInitCommand } = await import("./habits-
|
|
681
|
+
const { habitsInitCommand } = await import("./habits-7BORPC2F.js");
|
|
674
682
|
await habitsInitCommand(options);
|
|
675
683
|
});
|
|
676
684
|
habitsCmd.command("check").description("Evaluate habit compliance for a trigger point").requiredOption("-t, --trigger <trigger>", "Trigger: preflight, postflight, on-stop, on-commit").option("--record", "Record practice events to Sentinel").option("--json", "Output as JSON").option("--files <files>", "Comma-separated files modified (default: git diff)").option("--symbols <symbols>", "Comma-separated symbols touched").action(async (options) => {
|
|
677
|
-
const { habitsCheckCommand } = await import("./habits-
|
|
685
|
+
const { habitsCheckCommand } = await import("./habits-7BORPC2F.js");
|
|
678
686
|
await habitsCheckCommand(options);
|
|
679
687
|
});
|
|
680
688
|
habitsCmd.command("add").description("Add a custom habit").requiredOption("--id <id>", "Habit ID (kebab-case)").requiredOption("--name <name>", "Human-readable name").requiredOption("--description <desc>", "What this habit enforces").requiredOption("--category <category>", "Category: discovery, verification, testing, documentation, collaboration, security").requiredOption("--trigger <trigger>", "Trigger: preflight, postflight, on-stop, on-commit").option("--severity <severity>", "Severity: advisory, warn, block", "advisory").option("--tools <tools>", "Comma-separated tools to check (for tool-called check type)").option("--check-type <type>", "Check type: tool-called, file-exists, file-modified, lore-recorded, symbols-registered, gates-declared, tests-exist, git-clean", "tool-called").option("--patterns <patterns>", "Comma-separated patterns (for file-exists, file-modified, tests-exist check types)").action(async (options) => {
|
|
681
|
-
const { habitsAddCommand } = await import("./habits-
|
|
689
|
+
const { habitsAddCommand } = await import("./habits-7BORPC2F.js");
|
|
682
690
|
await habitsAddCommand({ ...options, checkType: options.checkType });
|
|
683
691
|
});
|
|
684
692
|
habitsCmd.command("edit <id>").description("Edit a habit (seed habits: only severity/enabled; custom: all fields)").option("--name <name>", "New name").option("--description <desc>", "New description").option("--category <category>", "New category").option("--trigger <trigger>", "New trigger").option("--severity <severity>", "New severity: advisory, warn, block").option("--enabled <bool>", "Enable or disable: true, false").option("--check-type <type>", "New check type").option("--patterns <patterns>", "Comma-separated patterns").option("--tools <tools>", "Comma-separated tools").action(async (id, options) => {
|
|
685
|
-
const { habitsEditCommand } = await import("./habits-
|
|
693
|
+
const { habitsEditCommand } = await import("./habits-7BORPC2F.js");
|
|
686
694
|
await habitsEditCommand(id, { ...options, checkType: options.checkType });
|
|
687
695
|
});
|
|
688
696
|
habitsCmd.command("remove <id>").description("Remove a custom habit (seed habits cannot be removed, only disabled)").option("-y, --yes", "Skip confirmation").action(async (id, options) => {
|
|
689
|
-
const { habitsRemoveCommand } = await import("./habits-
|
|
697
|
+
const { habitsRemoveCommand } = await import("./habits-7BORPC2F.js");
|
|
690
698
|
await habitsRemoveCommand(id, options);
|
|
691
699
|
});
|
|
692
700
|
habitsCmd.command("enable <id>").description("Enable a habit").action(async (id) => {
|
|
693
|
-
const { habitsToggleCommand } = await import("./habits-
|
|
701
|
+
const { habitsToggleCommand } = await import("./habits-7BORPC2F.js");
|
|
694
702
|
await habitsToggleCommand(id, "enable");
|
|
695
703
|
});
|
|
696
704
|
habitsCmd.command("disable <id>").description("Disable a habit").action(async (id) => {
|
|
697
|
-
const { habitsToggleCommand } = await import("./habits-
|
|
705
|
+
const { habitsToggleCommand } = await import("./habits-7BORPC2F.js");
|
|
698
706
|
await habitsToggleCommand(id, "disable");
|
|
699
707
|
});
|
|
700
708
|
habitsCmd.action(async () => {
|
|
701
|
-
const { habitsListCommand } = await import("./habits-
|
|
709
|
+
const { habitsListCommand } = await import("./habits-7BORPC2F.js");
|
|
702
710
|
await habitsListCommand({});
|
|
703
711
|
});
|
|
704
712
|
var sentinelCmd = program.command("sentinel").description("Sentinel \u2014 semantic error monitoring");
|