dagward 0.1.1 → 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/README.md +5 -1
- package/dist/cli.js +22 -12
- package/dist/graph.js +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Requirements: Node.js ≥ 20, a TypeScript project with a `tsconfig.json`. Monor
|
|
|
47
47
|
## Visualize
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
npx dagward
|
|
50
|
+
npx dagward init && npx dagward viz
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
`dagward viz` renders the graphs in `dagward-out/` to a single self-contained `viz.html` — no network, no CDN, works offline — and opens it in your default browser (`--no-open` to skip). Tabs switch between folder, file, and function level; dependencies point downward, type-only imports are dashed, and cycle members are flagged red.
|
|
@@ -134,6 +134,10 @@ With `--format agent`, a violation looks like this:
|
|
|
134
134
|
|
|
135
135
|
The Claude Code hook feeds this back automatically after each edit. In practice, agents fix their own violations on the next turn without human intervention.
|
|
136
136
|
|
|
137
|
+
## Annotations
|
|
138
|
+
|
|
139
|
+
Graph nodes can carry an optional `annotation` object — `summary`, `inputs`, `outputs`, `should`, `shouldNot`, `side` (frontend/backend/shared/tooling), `pure` — written by a human or an AI pass, never by dagward itself. Dagward preserves annotations by node id when regenerating graphs, so `dagward init` can run on every change without losing them. Enforcement remains purely deterministic; annotations are context for humans and agents reading the graph.
|
|
140
|
+
|
|
137
141
|
## Exemptions
|
|
138
142
|
|
|
139
143
|
Real architectures have exceptions. Unexplained exceptions are how architectures rot — so every exemption requires a reason, and lives in git:
|
package/dist/cli.js
CHANGED
|
@@ -7,16 +7,15 @@ import { parseArgs } from "node:util";
|
|
|
7
7
|
import { buildFileGraph } from "./fileGraph.js";
|
|
8
8
|
import { buildFolderGraph } from "./folderGraph.js";
|
|
9
9
|
import { buildFunctionGraph } from "./functionGraph.js";
|
|
10
|
-
import { serializeGraph } from "./graph.js";
|
|
10
|
+
import { carryAnnotations, serializeGraph } from "./graph.js";
|
|
11
11
|
import { ConfigError, loadProject } from "./project.js";
|
|
12
12
|
import { renderArchitectureMd } from "./report.js";
|
|
13
13
|
import { renderVizHtml } from "./viz.js";
|
|
14
14
|
const HELP = `dagward — multi-level dependency graphs for TypeScript projects
|
|
15
15
|
|
|
16
16
|
Usage:
|
|
17
|
-
dagward
|
|
18
|
-
dagward
|
|
19
|
-
dagward viz [dir] [options] Render dagward-out graphs to an interactive viz.html
|
|
17
|
+
dagward init [dir] [options] Analyze the project and write graphs + report (dir defaults to .)
|
|
18
|
+
dagward viz [dir] [options] Render dagward-out graphs to an interactive viz.html
|
|
20
19
|
|
|
21
20
|
Options:
|
|
22
21
|
--project <path> Explicit tsconfig.json (default: nearest to [dir])
|
|
@@ -60,18 +59,16 @@ export function main(argv) {
|
|
|
60
59
|
return 0;
|
|
61
60
|
}
|
|
62
61
|
const [command, dirArg] = positionals;
|
|
63
|
-
if (command !== "
|
|
62
|
+
if (command !== "init" && command !== "viz") {
|
|
64
63
|
console.error(command ? `Unknown command: ${command}` : HELP);
|
|
65
64
|
return command ? 2 : 0;
|
|
66
65
|
}
|
|
67
|
-
if (command === "init") {
|
|
68
|
-
console.error("Note: `init` currently behaves like `graph`; rule inference is coming later.");
|
|
69
|
-
}
|
|
70
66
|
const targetDir = path.resolve(dirArg ?? ".");
|
|
71
67
|
const outDir = path.resolve(values.out ?? path.join(targetDir, "dagward-out"));
|
|
72
68
|
if (command === "viz") {
|
|
73
69
|
return runViz(outDir, values["no-open"] ?? false);
|
|
74
70
|
}
|
|
71
|
+
console.error(`Initializing dagward in ${targetDir}`);
|
|
75
72
|
let project;
|
|
76
73
|
try {
|
|
77
74
|
project = timed((p) => `load project (${p.sourceFiles.length} files, tsconfig at ${p.rootDir})`, () => loadProject(targetDir, values.project ? path.resolve(values.project) : undefined));
|
|
@@ -88,9 +85,9 @@ export function main(argv) {
|
|
|
88
85
|
const functions = timed("function graph", () => buildFunctionGraph(project));
|
|
89
86
|
timed("write outputs", () => {
|
|
90
87
|
fs.mkdirSync(outDir, { recursive: true });
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
writeGraph(path.join(outDir, "graph.folders.json"), folders);
|
|
89
|
+
writeGraph(path.join(outDir, "graph.files.json"), files);
|
|
90
|
+
writeGraph(path.join(outDir, "graph.functions.json"), functions);
|
|
94
91
|
fs.writeFileSync(path.join(outDir, "ARCHITECTURE.md"), renderArchitectureMd({ folders, files, functions, skippedDynamicImports, version: version() }));
|
|
95
92
|
});
|
|
96
93
|
for (const graph of [folders, files, functions]) {
|
|
@@ -100,6 +97,19 @@ export function main(argv) {
|
|
|
100
97
|
console.error(`Wrote 4 files to ${outDir}`);
|
|
101
98
|
return 0;
|
|
102
99
|
}
|
|
100
|
+
// Node annotations are authored externally (AI or human) but live on the
|
|
101
|
+
// graph nodes; regenerating must not wipe them.
|
|
102
|
+
function writeGraph(file, graph) {
|
|
103
|
+
try {
|
|
104
|
+
const prev = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
105
|
+
if (Array.isArray(prev?.nodes))
|
|
106
|
+
carryAnnotations(prev, graph);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// absent or malformed previous output: nothing to preserve
|
|
110
|
+
}
|
|
111
|
+
fs.writeFileSync(file, serializeGraph(graph));
|
|
112
|
+
}
|
|
103
113
|
function runViz(outDir, noOpen) {
|
|
104
114
|
const graphs = {};
|
|
105
115
|
for (const level of ["folders", "files", "functions"]) {
|
|
@@ -108,7 +118,7 @@ function runViz(outDir, noOpen) {
|
|
|
108
118
|
graphs[level] = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
109
119
|
}
|
|
110
120
|
catch {
|
|
111
|
-
console.error(`Cannot read ${file}. Run \`dagward
|
|
121
|
+
console.error(`Cannot read ${file}. Run \`dagward init\` first.`);
|
|
112
122
|
return 2;
|
|
113
123
|
}
|
|
114
124
|
}
|
package/dist/graph.js
CHANGED
|
@@ -86,3 +86,16 @@ export function buildGraph(level, root, nodes, edges) {
|
|
|
86
86
|
export function serializeGraph(graph) {
|
|
87
87
|
return JSON.stringify(graph, null, 2) + "\n";
|
|
88
88
|
}
|
|
89
|
+
// Copy node annotations from a previous graph onto a freshly built one, by
|
|
90
|
+
// node id. A node that was rebuilt with its own annotation keeps it.
|
|
91
|
+
export function carryAnnotations(prev, next) {
|
|
92
|
+
const previous = new Map(prev.nodes.filter((n) => n.annotation).map((n) => [n.id, n.annotation]));
|
|
93
|
+
for (const node of next.nodes) {
|
|
94
|
+
if (!node.annotation) {
|
|
95
|
+
const annotation = previous.get(node.id);
|
|
96
|
+
if (annotation)
|
|
97
|
+
node.annotation = annotation;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return next;
|
|
101
|
+
}
|
package/package.json
CHANGED