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 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 graph && npx dagward viz
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 graph [dir] [options] Analyze the project and write graphs + report
18
- dagward init [dir] [options] Alias of \`graph\` (rule inference coming later)
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 !== "graph" && command !== "init" && command !== "viz") {
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
- fs.writeFileSync(path.join(outDir, "graph.folders.json"), serializeGraph(folders));
92
- fs.writeFileSync(path.join(outDir, "graph.files.json"), serializeGraph(files));
93
- fs.writeFileSync(path.join(outDir, "graph.functions.json"), serializeGraph(functions));
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 graph\` first.`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dagward",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Deterministic architecture review for TypeScript projects: dependency graphs at folder, file, and function level.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {