dotdog 0.1.7 → 0.2.4
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/cli.js +236 -39
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2534,9 +2534,9 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
2534
2534
|
var source_default = chalk;
|
|
2535
2535
|
|
|
2536
2536
|
// src/cli.ts
|
|
2537
|
-
import { existsSync, readdirSync, readFileSync, mkdirSync, writeFileSync } from "fs";
|
|
2538
|
-
import { join } from "path";
|
|
2539
|
-
import { homedir } from "os";
|
|
2537
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, mkdirSync, writeFileSync } from "fs";
|
|
2538
|
+
import { join as join2 } from "path";
|
|
2539
|
+
import { homedir as homedir2 } from "os";
|
|
2540
2540
|
|
|
2541
2541
|
// src/parser.ts
|
|
2542
2542
|
function parse(source) {
|
|
@@ -2894,13 +2894,173 @@ function parseInlineObject(value) {
|
|
|
2894
2894
|
}
|
|
2895
2895
|
return obj;
|
|
2896
2896
|
}
|
|
2897
|
-
|
|
2898
|
-
|
|
2897
|
+
// src/serve.ts
|
|
2898
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
2899
|
+
import { join } from "path";
|
|
2900
|
+
import { homedir } from "os";
|
|
2901
|
+
import * as readline from "readline";
|
|
2899
2902
|
function resolvePath(p) {
|
|
2900
2903
|
if (p.startsWith("~"))
|
|
2901
2904
|
p = join(homedir(), p.slice(1));
|
|
2902
2905
|
return p.startsWith("/") ? p : join(process.cwd(), p);
|
|
2903
2906
|
}
|
|
2907
|
+
function serve(dir = ".") {
|
|
2908
|
+
const root = resolvePath(dir);
|
|
2909
|
+
const dagCache = new Map;
|
|
2910
|
+
function loadDags() {
|
|
2911
|
+
const dirs = [join(root, "projects"), join(root, "specs"), root];
|
|
2912
|
+
for (const dd of dirs) {
|
|
2913
|
+
if (!existsSync(dd))
|
|
2914
|
+
continue;
|
|
2915
|
+
const projects = readdirSync(dd, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2916
|
+
for (const p of projects) {
|
|
2917
|
+
const dagFile = join(dd, p, `${p}.dag`);
|
|
2918
|
+
if (existsSync(dagFile)) {
|
|
2919
|
+
dagCache.set(p, JSON.parse(readFileSync(dagFile, "utf-8")));
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
return [...dagCache.keys()];
|
|
2924
|
+
}
|
|
2925
|
+
function handleRequest(req) {
|
|
2926
|
+
const { id, method, params } = req;
|
|
2927
|
+
if (method === "initialize") {
|
|
2928
|
+
return {
|
|
2929
|
+
jsonrpc: "2.0",
|
|
2930
|
+
id,
|
|
2931
|
+
result: {
|
|
2932
|
+
protocolVersion: "0.1.0",
|
|
2933
|
+
serverInfo: { name: "spec-serve", version: "0.1.0" },
|
|
2934
|
+
capabilities: { tools: {} }
|
|
2935
|
+
}
|
|
2936
|
+
};
|
|
2937
|
+
}
|
|
2938
|
+
if (method === "tools/list") {
|
|
2939
|
+
return {
|
|
2940
|
+
jsonrpc: "2.0",
|
|
2941
|
+
id,
|
|
2942
|
+
result: {
|
|
2943
|
+
tools: [
|
|
2944
|
+
{ name: "getEntity", description: "Get entity with properties, states, lifecycle", inputSchema: { type: "object", properties: { project: { type: "string" }, name: { type: "string" } }, required: ["name"] } },
|
|
2945
|
+
{ name: "traverse", description: "Traverse graph: from node, follow edges, return subgraph", inputSchema: { type: "object", properties: { project: { type: "string" }, from: { type: "string" }, depth: { type: "number", default: 1 } }, required: ["from"] } },
|
|
2946
|
+
{ name: "search", description: "Find entities by name or type", inputSchema: { type: "object", properties: { project: { type: "string" }, q: { type: "string" }, type: { type: "string" } }, required: ["q"] } },
|
|
2947
|
+
{ name: "listProjects", description: "List all projects", inputSchema: { type: "object", properties: {} } },
|
|
2948
|
+
{ name: "summary", description: "Get project summary: node count, edge count, completeness", inputSchema: { type: "object", properties: { project: { type: "string" } } } },
|
|
2949
|
+
{ name: "schema", description: "Get full property schema for an entity", inputSchema: { type: "object", properties: { project: { type: "string" }, entity: { type: "string" } }, required: ["entity"] } }
|
|
2950
|
+
]
|
|
2951
|
+
}
|
|
2952
|
+
};
|
|
2953
|
+
}
|
|
2954
|
+
if (method === "tools/call") {
|
|
2955
|
+
const { name, arguments: args } = params;
|
|
2956
|
+
loadDags();
|
|
2957
|
+
if (name === "listProjects") {
|
|
2958
|
+
return { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify([...dagCache.keys()]) }] } };
|
|
2959
|
+
}
|
|
2960
|
+
if (name === "listNodes") {
|
|
2961
|
+
const dag = dagCache.get(args.project || [...dagCache.keys()][0] || "");
|
|
2962
|
+
if (!dag)
|
|
2963
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: "Project not found" } };
|
|
2964
|
+
return { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify(dag.nodes || []) }] } };
|
|
2965
|
+
}
|
|
2966
|
+
if (name === "getEntity") {
|
|
2967
|
+
const dag = dagCache.get(args.project || [...dagCache.keys()][0] || "");
|
|
2968
|
+
if (!dag)
|
|
2969
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: "Project not found" } };
|
|
2970
|
+
const node = (dag.nodes || []).find((n) => n.id.toLowerCase() === (args.name || "").toLowerCase());
|
|
2971
|
+
if (!node)
|
|
2972
|
+
return { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: "{}" }] } };
|
|
2973
|
+
const edges = (dag.edges || []).filter((e) => e.source.toLowerCase() === node.id.toLowerCase() || e.target.toLowerCase() === node.id.toLowerCase());
|
|
2974
|
+
return { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify({ ...node, edges }) }] } };
|
|
2975
|
+
}
|
|
2976
|
+
if (name === "traverse") {
|
|
2977
|
+
const dag = dagCache.get(args.project || [...dagCache.keys()][0] || "");
|
|
2978
|
+
if (!dag)
|
|
2979
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: "Project not found" } };
|
|
2980
|
+
const depth = args.depth || 1;
|
|
2981
|
+
const visited = new Set;
|
|
2982
|
+
const subgraph = { nodes: [], edges: [] };
|
|
2983
|
+
const queue = [{ id: args.from, depth: 0 }];
|
|
2984
|
+
while (queue.length > 0) {
|
|
2985
|
+
const curr = queue.shift();
|
|
2986
|
+
if (visited.has(curr.id) || curr.depth > depth)
|
|
2987
|
+
continue;
|
|
2988
|
+
visited.add(curr.id);
|
|
2989
|
+
const node = (dag.nodes || []).find((n) => n.id.toLowerCase() === curr.id.toLowerCase());
|
|
2990
|
+
if (node)
|
|
2991
|
+
subgraph.nodes.push(node);
|
|
2992
|
+
const edges = (dag.edges || []).filter((e) => e.source.toLowerCase() === curr.id.toLowerCase() || e.target.toLowerCase() === curr.id.toLowerCase());
|
|
2993
|
+
for (const e of edges) {
|
|
2994
|
+
subgraph.edges.push(e);
|
|
2995
|
+
const next = e.source.toLowerCase() === curr.id.toLowerCase() ? e.target : e.source;
|
|
2996
|
+
if (!visited.has(next))
|
|
2997
|
+
queue.push({ id: next, depth: curr.depth + 1 });
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
return { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify(subgraph) }] } };
|
|
3001
|
+
}
|
|
3002
|
+
if (name === "search") {
|
|
3003
|
+
const dag = dagCache.get(args.project || [...dagCache.keys()][0] || "");
|
|
3004
|
+
if (!dag)
|
|
3005
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: "Project not found" } };
|
|
3006
|
+
const q = (args.q || "").toLowerCase();
|
|
3007
|
+
const type = (args.type || "").toLowerCase();
|
|
3008
|
+
const results = (dag.nodes || []).filter((n) => n.id.toLowerCase().includes(q) && (!type || (n.type || "").toLowerCase().includes(type)));
|
|
3009
|
+
return { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify(results) }] } };
|
|
3010
|
+
}
|
|
3011
|
+
if (name === "summary") {
|
|
3012
|
+
const dag = dagCache.get(args.project || [...dagCache.keys()][0] || "");
|
|
3013
|
+
if (!dag)
|
|
3014
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: "Project not found" } };
|
|
3015
|
+
const s = {
|
|
3016
|
+
project: dag.project,
|
|
3017
|
+
nodes: (dag.nodes || []).length,
|
|
3018
|
+
edges: (dag.edges || []).length,
|
|
3019
|
+
files: dag.files || dag.count || 0,
|
|
3020
|
+
compiled: dag.compiled_at
|
|
3021
|
+
};
|
|
3022
|
+
return { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify(s) }] } };
|
|
3023
|
+
}
|
|
3024
|
+
if (name === "schema") {
|
|
3025
|
+
const dag = dagCache.get(args.project || [...dagCache.keys()][0] || "");
|
|
3026
|
+
if (!dag)
|
|
3027
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: "Project not found" } };
|
|
3028
|
+
const node = (dag.nodes || []).find((n) => n.id.toLowerCase() === (args.entity || "").toLowerCase());
|
|
3029
|
+
if (!node)
|
|
3030
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: "Entity not found" } };
|
|
3031
|
+
return { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify({
|
|
3032
|
+
entity: node.id,
|
|
3033
|
+
properties: node.properties || {},
|
|
3034
|
+
states: node.states || [],
|
|
3035
|
+
lifecycle: node.lifecycle || []
|
|
3036
|
+
}) }] } };
|
|
3037
|
+
}
|
|
3038
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: `Unknown tool: ${name}` } };
|
|
3039
|
+
}
|
|
3040
|
+
return { jsonrpc: "2.0", id, error: { code: 404, message: `Unknown method: ${method}` } };
|
|
3041
|
+
}
|
|
3042
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
3043
|
+
loadDags();
|
|
3044
|
+
console.error(`[spec-serve] Loaded ${dagCache.size} projects`);
|
|
3045
|
+
rl.on("line", (line) => {
|
|
3046
|
+
try {
|
|
3047
|
+
const req = JSON.parse(line);
|
|
3048
|
+
const res = handleRequest(req);
|
|
3049
|
+
process.stdout.write(JSON.stringify(res) + `
|
|
3050
|
+
`);
|
|
3051
|
+
} catch (e) {
|
|
3052
|
+
process.stderr.write(`[spec-serve] Error: ${e}
|
|
3053
|
+
`);
|
|
3054
|
+
}
|
|
3055
|
+
});
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
// src/cli.ts
|
|
3059
|
+
function resolvePath2(p) {
|
|
3060
|
+
if (p.startsWith("~"))
|
|
3061
|
+
p = join2(homedir2(), p.slice(1));
|
|
3062
|
+
return p.startsWith("/") ? p : join2(process.cwd(), p);
|
|
3063
|
+
}
|
|
2904
3064
|
function parseSections2(markdown) {
|
|
2905
3065
|
const lines = markdown.split(`
|
|
2906
3066
|
`), sections = [];
|
|
@@ -2933,19 +3093,19 @@ function parseSections2(markdown) {
|
|
|
2933
3093
|
return sections;
|
|
2934
3094
|
}
|
|
2935
3095
|
var program2 = new Command;
|
|
2936
|
-
var pkg = JSON.parse(
|
|
3096
|
+
var pkg = JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf-8"));
|
|
2937
3097
|
program2.name("spec").alias("dotdog").description("The spec dog — validate, analyze, generate .dog files").version(pkg.version);
|
|
2938
3098
|
program2.command("validate [dir]").action((d = ".") => {
|
|
2939
|
-
const dirs = [
|
|
3099
|
+
const dirs = [join2(d, "projects"), join2(d, "specs")];
|
|
2940
3100
|
let found = false;
|
|
2941
3101
|
for (const dd of dirs) {
|
|
2942
|
-
if (!
|
|
3102
|
+
if (!existsSync2(dd))
|
|
2943
3103
|
continue;
|
|
2944
|
-
const projects =
|
|
3104
|
+
const projects = readdirSync2(dd, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2945
3105
|
for (const p of projects) {
|
|
2946
3106
|
found = true;
|
|
2947
|
-
const pd =
|
|
2948
|
-
const files =
|
|
3107
|
+
const pd = join2(dd, p, "specs");
|
|
3108
|
+
const files = existsSync2(pd) ? readdirSync2(pd).filter((f) => f.endsWith(".dog")) : [];
|
|
2949
3109
|
const missing = ["SPEC.dog", "constitution.dog", "data-model.dog"].filter((f) => !files.includes(f));
|
|
2950
3110
|
const optional = ["COPY.dog", "plan.dog", "DESIGN-SYSTEM.dog", "INDEX.dog"].filter((f) => !files.includes(f));
|
|
2951
3111
|
console.log(source_default.bold(`
|
|
@@ -2962,7 +3122,7 @@ program2.command("validate [dir]").action((d = ".") => {
|
|
|
2962
3122
|
console.log(source_default.yellow("No projects found. Run: spec init <project>"));
|
|
2963
3123
|
});
|
|
2964
3124
|
program2.command("init <project>").action((p) => {
|
|
2965
|
-
const d =
|
|
3125
|
+
const d = join2(process.cwd(), "specs", p);
|
|
2966
3126
|
mkdirSync(d, { recursive: true });
|
|
2967
3127
|
const tmpl = {
|
|
2968
3128
|
"SPEC.dog": `# Project
|
|
@@ -2997,7 +3157,7 @@ program2.command("init <project>").action((p) => {
|
|
|
2997
3157
|
`
|
|
2998
3158
|
};
|
|
2999
3159
|
for (const [f, c] of Object.entries(tmpl)) {
|
|
3000
|
-
writeFileSync(
|
|
3160
|
+
writeFileSync(join2(d, f), c);
|
|
3001
3161
|
console.log(source_default.green(` ✓ ${f}`));
|
|
3002
3162
|
}
|
|
3003
3163
|
console.log(source_default.bold(`
|
|
@@ -3005,23 +3165,23 @@ Project "${p}" initialized. Fill in SPEC.dog then run spec validate.`));
|
|
|
3005
3165
|
});
|
|
3006
3166
|
program2.command("list").action(() => {
|
|
3007
3167
|
for (const d of ["projects", "specs"]) {
|
|
3008
|
-
const dd =
|
|
3009
|
-
if (!
|
|
3168
|
+
const dd = join2(process.cwd(), d);
|
|
3169
|
+
if (!existsSync2(dd))
|
|
3010
3170
|
continue;
|
|
3011
|
-
const projects =
|
|
3171
|
+
const projects = readdirSync2(dd, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3012
3172
|
if (!projects.length)
|
|
3013
3173
|
continue;
|
|
3014
3174
|
console.log(source_default.bold(`
|
|
3015
3175
|
${d}/`));
|
|
3016
3176
|
for (const p of projects) {
|
|
3017
|
-
const sp =
|
|
3018
|
-
const n =
|
|
3177
|
+
const sp = join2(dd, p, "specs");
|
|
3178
|
+
const n = existsSync2(sp) ? readdirSync2(sp).filter((f) => f.endsWith(".dog")).length : 0;
|
|
3019
3179
|
console.log(` ${source_default.cyan(p)} — ${n} .dog files`);
|
|
3020
3180
|
}
|
|
3021
3181
|
}
|
|
3022
3182
|
});
|
|
3023
3183
|
program2.command("parse <file>").action((f) => {
|
|
3024
|
-
const c =
|
|
3184
|
+
const c = readFileSync2(f, "utf-8");
|
|
3025
3185
|
const s = parseSections2(c);
|
|
3026
3186
|
console.log(source_default.bold(`
|
|
3027
3187
|
${s.length} sections`));
|
|
@@ -3029,21 +3189,21 @@ ${s.length} sections`));
|
|
|
3029
3189
|
console.log(` ${sec.heading.padEnd(30)} ${sec.content.length} chars`);
|
|
3030
3190
|
});
|
|
3031
3191
|
program2.command("compile [dir]").option("-o, --output <file>").action((d = ".", opts) => {
|
|
3032
|
-
const dir =
|
|
3033
|
-
const dirs = [
|
|
3192
|
+
const dir = resolvePath2(d);
|
|
3193
|
+
const dirs = [join2(dir, "projects"), join2(dir, "specs"), dir];
|
|
3034
3194
|
let found = false;
|
|
3035
3195
|
for (const dd of dirs) {
|
|
3036
|
-
if (!
|
|
3196
|
+
if (!existsSync2(dd))
|
|
3037
3197
|
continue;
|
|
3038
|
-
const projects =
|
|
3198
|
+
const projects = readdirSync2(dd, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3039
3199
|
for (const p of projects) {
|
|
3040
|
-
const pd =
|
|
3041
|
-
if (!
|
|
3200
|
+
const pd = join2(dd, p, "specs");
|
|
3201
|
+
if (!existsSync2(pd))
|
|
3042
3202
|
continue;
|
|
3043
|
-
const files =
|
|
3203
|
+
const files = readdirSync2(pd).filter((f) => f.endsWith(".dog"));
|
|
3044
3204
|
const dag = { version: "1.0", project: p, compiled_at: new Date().toISOString(), nodes: [], edges: [], count: files.length };
|
|
3045
3205
|
for (const f of files) {
|
|
3046
|
-
const c =
|
|
3206
|
+
const c = readFileSync2(join2(pd, f), "utf-8");
|
|
3047
3207
|
const secs = parseSections2(c);
|
|
3048
3208
|
for (const s of secs) {
|
|
3049
3209
|
if (s.heading.includes("Entity:") || s.heading.includes("Relationship:")) {
|
|
@@ -3058,7 +3218,7 @@ program2.command("compile [dir]").option("-o, --output <file>").action((d = ".",
|
|
|
3058
3218
|
}
|
|
3059
3219
|
}
|
|
3060
3220
|
found = true;
|
|
3061
|
-
const out = opts.output ||
|
|
3221
|
+
const out = opts.output || join2(pd, "..", `${p}.dag`);
|
|
3062
3222
|
writeFileSync(out, JSON.stringify(dag, null, 2));
|
|
3063
3223
|
console.log(source_default.green(` ✓ ${out}`));
|
|
3064
3224
|
console.log(source_default.gray(` ${dag.nodes.length} nodes, ${dag.edges.length} edges, ${dag.count} files`));
|
|
@@ -3067,33 +3227,70 @@ program2.command("compile [dir]").option("-o, --output <file>").action((d = ".",
|
|
|
3067
3227
|
if (!found)
|
|
3068
3228
|
console.log(source_default.yellow("No projects found."));
|
|
3069
3229
|
});
|
|
3230
|
+
program2.command("visualize [dir]").option("-s, --save").action((d = ".", opts) => {
|
|
3231
|
+
const dir = resolvePath2(d);
|
|
3232
|
+
const dirs = [join2(dir, "projects"), join2(dir, "specs"), dir];
|
|
3233
|
+
for (const dd of dirs) {
|
|
3234
|
+
if (!existsSync2(dd))
|
|
3235
|
+
continue;
|
|
3236
|
+
const projects = readdirSync2(dd, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3237
|
+
for (const p of projects) {
|
|
3238
|
+
const dagFile = join2(dd, p, `${p}.dag`);
|
|
3239
|
+
if (!existsSync2(dagFile))
|
|
3240
|
+
continue;
|
|
3241
|
+
const dag = JSON.parse(readFileSync2(dagFile, "utf-8"));
|
|
3242
|
+
let out = "```mermaid\ngraph LR\n";
|
|
3243
|
+
for (const n of dag.nodes || [])
|
|
3244
|
+
out += ` ${n.id.replace(/\s+/g, "_")}[${n.id}]
|
|
3245
|
+
`;
|
|
3246
|
+
for (const e of dag.edges || [])
|
|
3247
|
+
out += ` ${e.source.replace(/\s+/g, "_")} -->|${e.verb || ""}| ${e.target.replace(/\s+/g, "_")}
|
|
3248
|
+
`;
|
|
3249
|
+
out += "```\n";
|
|
3250
|
+
if (opts.save) {
|
|
3251
|
+
const outFile = join2(dd, p, "..", `${p}.md`);
|
|
3252
|
+
writeFileSync(outFile, `# ${p} — Spec Graph
|
|
3253
|
+
|
|
3254
|
+
${out}`);
|
|
3255
|
+
console.log(source_default.green(` ✓ ${outFile}`));
|
|
3256
|
+
}
|
|
3257
|
+
console.log(out);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
});
|
|
3261
|
+
program2.command("serve [dir]").description("MCP server — expose .dag graph to AI agents over stdio").action((d = ".") => serve(d));
|
|
3070
3262
|
program2.command("staleness [dir]").action((d = ".") => {
|
|
3071
|
-
const dir =
|
|
3072
|
-
const dirs = [
|
|
3263
|
+
const dir = resolvePath2(d);
|
|
3264
|
+
const dirs = [join2(dir, "projects"), join2(dir, "specs"), dir];
|
|
3073
3265
|
console.log(source_default.bold(`Staleness Audit
|
|
3074
3266
|
`));
|
|
3075
3267
|
for (const dd of dirs) {
|
|
3076
|
-
if (!
|
|
3268
|
+
if (!existsSync2(dd))
|
|
3077
3269
|
continue;
|
|
3078
|
-
const projects =
|
|
3270
|
+
const projects = readdirSync2(dd, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3079
3271
|
for (const p of projects) {
|
|
3080
|
-
const pd =
|
|
3081
|
-
if (!
|
|
3272
|
+
const pd = join2(dd, p, "specs");
|
|
3273
|
+
if (!existsSync2(pd))
|
|
3082
3274
|
continue;
|
|
3083
|
-
const planFile =
|
|
3084
|
-
if (!
|
|
3275
|
+
const planFile = join2(pd, "plan.dog");
|
|
3276
|
+
if (!existsSync2(planFile)) {
|
|
3085
3277
|
console.log(source_default.yellow(` ${p}: No plan.dog`));
|
|
3086
3278
|
continue;
|
|
3087
3279
|
}
|
|
3088
|
-
const plan =
|
|
3280
|
+
const plan = readFileSync2(planFile, "utf-8");
|
|
3089
3281
|
const tasks = [...plan.matchAll(/^\s*- \[([ x])\]\s+(.+)/gm)];
|
|
3090
3282
|
let issues = 0;
|
|
3091
3283
|
for (const m of tasks) {
|
|
3092
3284
|
const done = m[1] === "x";
|
|
3093
3285
|
const text = m[2].toLowerCase();
|
|
3286
|
+
const precedingText = plan.substring(Math.max(0, m.index - 200), m.index);
|
|
3287
|
+
const phaseMatch = precedingText.match(/Phase\s+(\d+)/);
|
|
3288
|
+
const phase = phaseMatch ? parseInt(phaseMatch[1]) : 99;
|
|
3289
|
+
if (phase > 3)
|
|
3290
|
+
continue;
|
|
3094
3291
|
if (text.includes("npm publish") || text.includes("npm install")) {
|
|
3095
3292
|
try {
|
|
3096
|
-
const pkg2 = JSON.parse(
|
|
3293
|
+
const pkg2 = JSON.parse(readFileSync2(join2(resolvePath2("."), "packages/dotdog/package.json"), "utf-8"));
|
|
3097
3294
|
if (pkg2.version && !done) {
|
|
3098
3295
|
console.log(source_default.yellow(` ⚠ Should be [x]: ${m[2].trim()}`));
|
|
3099
3296
|
issues++;
|
|
@@ -3107,7 +3304,7 @@ program2.command("staleness [dir]").action((d = ".") => {
|
|
|
3107
3304
|
}
|
|
3108
3305
|
}
|
|
3109
3306
|
if (text.includes("generate") && !done) {
|
|
3110
|
-
if (
|
|
3307
|
+
if (existsSync2(join2(dir, "packages/dotdog/src/cli.ts")) || existsSync2(join2(dir, "packages/spec-cli/src/generate.ts"))) {
|
|
3111
3308
|
console.log(source_default.yellow(` ⚠ Should be [x]: ${m[2].trim()}`));
|
|
3112
3309
|
issues++;
|
|
3113
3310
|
}
|