create-project-arch 1.0.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.
Files changed (90) hide show
  1. package/README.md +58 -0
  2. package/dist/cli.js +232 -0
  3. package/dist/cli.test.js +8 -0
  4. package/package.json +29 -0
  5. package/templates/arch-ui/.arch/edges/decision_to_domain.json +4 -0
  6. package/templates/arch-ui/.arch/edges/milestone_to_task.json +4 -0
  7. package/templates/arch-ui/.arch/edges/task_to_decision.json +4 -0
  8. package/templates/arch-ui/.arch/edges/task_to_module.json +4 -0
  9. package/templates/arch-ui/.arch/graph.json +17 -0
  10. package/templates/arch-ui/.arch/nodes/decisions.json +4 -0
  11. package/templates/arch-ui/.arch/nodes/domains.json +4 -0
  12. package/templates/arch-ui/.arch/nodes/milestones.json +4 -0
  13. package/templates/arch-ui/.arch/nodes/modules.json +4 -0
  14. package/templates/arch-ui/.arch/nodes/tasks.json +4 -0
  15. package/templates/arch-ui/app/api/architecture/map/route.ts +13 -0
  16. package/templates/arch-ui/app/api/decisions/route.ts +23 -0
  17. package/templates/arch-ui/app/api/domain-docs/route.ts +89 -0
  18. package/templates/arch-ui/app/api/domains/route.ts +10 -0
  19. package/templates/arch-ui/app/api/graph/route.ts +16 -0
  20. package/templates/arch-ui/app/api/health/route.ts +44 -0
  21. package/templates/arch-ui/app/api/node-files/route.ts +173 -0
  22. package/templates/arch-ui/app/api/phases/route.ts +10 -0
  23. package/templates/arch-ui/app/api/route.ts +22 -0
  24. package/templates/arch-ui/app/api/search/route.ts +56 -0
  25. package/templates/arch-ui/app/api/task-doc/[taskId]/route.ts +60 -0
  26. package/templates/arch-ui/app/api/tasks/route.ts +36 -0
  27. package/templates/arch-ui/app/api/trace/file/route.ts +40 -0
  28. package/templates/arch-ui/app/api/trace/task/[taskId]/route.ts +12 -0
  29. package/templates/arch-ui/app/architecture/page.tsx +5 -0
  30. package/templates/arch-ui/app/globals.css +240 -0
  31. package/templates/arch-ui/app/health/page.tsx +48 -0
  32. package/templates/arch-ui/app/layout.tsx +19 -0
  33. package/templates/arch-ui/app/page.tsx +5 -0
  34. package/templates/arch-ui/app/work/page.tsx +265 -0
  35. package/templates/arch-ui/components/app-shell.tsx +171 -0
  36. package/templates/arch-ui/components/error-boundary.tsx +53 -0
  37. package/templates/arch-ui/components/graph/arch-node.tsx +77 -0
  38. package/templates/arch-ui/components/graph/build-graph-from-dataset.ts +196 -0
  39. package/templates/arch-ui/components/graph/build-initial-graph.ts +245 -0
  40. package/templates/arch-ui/components/graph/graph-context-menu.tsx +84 -0
  41. package/templates/arch-ui/components/graph/graph-doc-panel.tsx +46 -0
  42. package/templates/arch-ui/components/graph/graph-types.ts +82 -0
  43. package/templates/arch-ui/components/graph/use-auto-layout.ts +65 -0
  44. package/templates/arch-ui/components/graph/use-connection-validation.ts +62 -0
  45. package/templates/arch-ui/components/graph/use-flow-persistence.ts +48 -0
  46. package/templates/arch-ui/components/graph-canvas.tsx +670 -0
  47. package/templates/arch-ui/components/health-panel.tsx +49 -0
  48. package/templates/arch-ui/components/inspector-context.tsx +35 -0
  49. package/templates/arch-ui/components/inspector.tsx +895 -0
  50. package/templates/arch-ui/components/markdown-viewer.tsx +74 -0
  51. package/templates/arch-ui/components/sidebar.tsx +531 -0
  52. package/templates/arch-ui/components/topbar.tsx +187 -0
  53. package/templates/arch-ui/components/work-table.tsx +57 -0
  54. package/templates/arch-ui/components/workspace-context.tsx +274 -0
  55. package/templates/arch-ui/eslint.config.js +2 -0
  56. package/templates/arch-ui/global.d.ts +1 -0
  57. package/templates/arch-ui/lib/api.ts +93 -0
  58. package/templates/arch-ui/lib/arch-model.ts +113 -0
  59. package/templates/arch-ui/lib/graph-dataset.ts +756 -0
  60. package/templates/arch-ui/lib/graph-schema.ts +408 -0
  61. package/templates/arch-ui/lib/project-root.ts +52 -0
  62. package/templates/arch-ui/lib/types.ts +116 -0
  63. package/templates/arch-ui/next-env.d.ts +6 -0
  64. package/templates/arch-ui/next.config.js +17 -0
  65. package/templates/arch-ui/package.json +38 -0
  66. package/templates/arch-ui/postcss.config.mjs +6 -0
  67. package/templates/arch-ui/tailwind.config.ts +11 -0
  68. package/templates/arch-ui/tsconfig.json +21 -0
  69. package/templates/ui-package/eslint.config.mjs +4 -0
  70. package/templates/ui-package/package.json +26 -0
  71. package/templates/ui-package/src/accordion.tsx +10 -0
  72. package/templates/ui-package/src/badge.tsx +12 -0
  73. package/templates/ui-package/src/button.tsx +32 -0
  74. package/templates/ui-package/src/card.tsx +22 -0
  75. package/templates/ui-package/src/code.tsx +6 -0
  76. package/templates/ui-package/src/command.tsx +18 -0
  77. package/templates/ui-package/src/dialog.tsx +6 -0
  78. package/templates/ui-package/src/dropdown-menu.tsx +10 -0
  79. package/templates/ui-package/src/input.tsx +6 -0
  80. package/templates/ui-package/src/navigation-menu.tsx +6 -0
  81. package/templates/ui-package/src/scroll-area.tsx +6 -0
  82. package/templates/ui-package/src/select.tsx +6 -0
  83. package/templates/ui-package/src/separator.tsx +6 -0
  84. package/templates/ui-package/src/sheet.tsx +6 -0
  85. package/templates/ui-package/src/skeleton.tsx +6 -0
  86. package/templates/ui-package/src/table.tsx +26 -0
  87. package/templates/ui-package/src/tabs.tsx +14 -0
  88. package/templates/ui-package/src/toggle-group.tsx +10 -0
  89. package/templates/ui-package/src/utils.ts +3 -0
  90. package/templates/ui-package/tsconfig.json +10 -0
@@ -0,0 +1,408 @@
1
+ export const GRAPH_SCHEMA_VERSION = "2.0.0" as const;
2
+
3
+ export type GraphViewMode = "architecture-map" | "tasks" | "project";
4
+
5
+ export type GraphNodeType =
6
+ | "arch_folder"
7
+ | "domain_doc"
8
+ | "architecture_doc"
9
+ | "architecture_model"
10
+ | "roadmap_folder"
11
+ | "roadmap_epic"
12
+ | "roadmap_story"
13
+ | "roadmap_task"
14
+ | "project_folder"
15
+ | "app"
16
+ | "package"
17
+ | "module"
18
+ | "component";
19
+
20
+ export type GraphEdgeType =
21
+ | "references"
22
+ | "depends_on"
23
+ | "implements"
24
+ | "owned_by"
25
+ | "contains"
26
+ | "uses"
27
+ | "documents";
28
+
29
+ export type GraphEdgeAuthority = "authoritative" | "manual" | "inferred";
30
+
31
+ export type GraphNodeId = string;
32
+ export type GraphEdgeId = string;
33
+
34
+ export type GraphNodeSource = {
35
+ path: string;
36
+ scope: "arch-domains" | "arch-model" | "architecture" | "roadmap" | "apps" | "packages";
37
+ };
38
+
39
+ export type GraphNode = {
40
+ id: GraphNodeId;
41
+ type: GraphNodeType;
42
+ title: string;
43
+ description?: string;
44
+ tags?: string[];
45
+ views: GraphViewMode[];
46
+ source: GraphNodeSource;
47
+ metadata: Record<string, string | number | boolean | null | string[]>;
48
+ };
49
+
50
+ export type GraphEdge = {
51
+ id: GraphEdgeId;
52
+ type: GraphEdgeType;
53
+ source: GraphNodeId;
54
+ target: GraphNodeId;
55
+ authority: GraphEdgeAuthority;
56
+ confidence?: number;
57
+ evidence?: string[];
58
+ metadata?: Record<string, string | number | boolean | null | string[]>;
59
+ };
60
+
61
+ export type GraphDataset = {
62
+ schemaVersion: typeof GRAPH_SCHEMA_VERSION;
63
+ generatedAt: string;
64
+ nodes: GraphNode[];
65
+ edges: GraphEdge[];
66
+ };
67
+
68
+ export type GraphValidationIssue = {
69
+ ruleId: string;
70
+ severity: "error" | "warning";
71
+ message: string;
72
+ nodeId?: string;
73
+ edgeId?: string;
74
+ };
75
+
76
+ export type GraphValidationResult = {
77
+ valid: boolean;
78
+ errors: GraphValidationIssue[];
79
+ warnings: GraphValidationIssue[];
80
+ };
81
+
82
+ export const VIEW_NODE_TYPES: Record<GraphViewMode, GraphNodeType[]> = {
83
+ "architecture-map": ["arch_folder", "domain_doc", "architecture_doc", "architecture_model"],
84
+ tasks: ["roadmap_folder", "roadmap_epic", "roadmap_story", "roadmap_task"],
85
+ project: ["project_folder", "app", "package", "module", "component"],
86
+ };
87
+
88
+ export const EDGE_TYPE_RULES: Record<
89
+ GraphEdgeType,
90
+ {
91
+ from: GraphNodeType[];
92
+ to: GraphNodeType[];
93
+ }
94
+ > = {
95
+ references: {
96
+ from: [
97
+ "roadmap_task",
98
+ "roadmap_story",
99
+ "roadmap_epic",
100
+ "roadmap_folder",
101
+ "architecture_doc",
102
+ "architecture_model",
103
+ "arch_folder",
104
+ ],
105
+ to: ["domain_doc", "architecture_doc", "architecture_model"],
106
+ },
107
+ depends_on: {
108
+ from: ["project_folder", "app", "package", "module", "component"],
109
+ to: ["project_folder", "app", "package", "module", "component"],
110
+ },
111
+ implements: {
112
+ from: ["project_folder", "app", "package", "module", "component"],
113
+ to: [
114
+ "roadmap_folder",
115
+ "roadmap_task",
116
+ "roadmap_story",
117
+ "roadmap_epic",
118
+ "architecture_model",
119
+ "architecture_doc",
120
+ "arch_folder",
121
+ ],
122
+ },
123
+ owned_by: {
124
+ from: ["component", "module", "package", "app", "project_folder"],
125
+ to: ["project_folder", "app", "package", "module"],
126
+ },
127
+ contains: {
128
+ from: [
129
+ "arch_folder",
130
+ "roadmap_folder",
131
+ "project_folder",
132
+ "app",
133
+ "package",
134
+ "module",
135
+ "roadmap_epic",
136
+ "roadmap_story",
137
+ ],
138
+ to: [
139
+ "arch_folder",
140
+ "domain_doc",
141
+ "architecture_doc",
142
+ "architecture_model",
143
+ "roadmap_folder",
144
+ "roadmap_epic",
145
+ "roadmap_story",
146
+ "roadmap_task",
147
+ "project_folder",
148
+ "app",
149
+ "package",
150
+ "module",
151
+ "component",
152
+ ],
153
+ },
154
+ uses: {
155
+ from: ["project_folder", "component", "module", "app", "package", "roadmap_task"],
156
+ to: [
157
+ "project_folder",
158
+ "component",
159
+ "module",
160
+ "package",
161
+ "architecture_model",
162
+ "architecture_doc",
163
+ "arch_folder",
164
+ ],
165
+ },
166
+ documents: {
167
+ from: ["arch_folder", "domain_doc", "architecture_doc", "architecture_model"],
168
+ to: [
169
+ "project_folder",
170
+ "app",
171
+ "package",
172
+ "module",
173
+ "component",
174
+ "roadmap_folder",
175
+ "roadmap_task",
176
+ "roadmap_story",
177
+ "roadmap_epic",
178
+ ],
179
+ },
180
+ };
181
+
182
+ export const NODE_ID_PATTERN = /^[a-z][a-z0-9-]*:[a-z][a-z0-9-]*(?::[a-z0-9._/:-]+)?$/;
183
+
184
+ export const GRAPH_VALIDATION_RULES = [
185
+ {
186
+ id: "schema-version",
187
+ severity: "error",
188
+ description: `schemaVersion must equal ${GRAPH_SCHEMA_VERSION}.`,
189
+ },
190
+ {
191
+ id: "node-id-format",
192
+ severity: "error",
193
+ description: "Every node ID must follow namespaced format <scope>:<kind>[:<path-or-id>].",
194
+ },
195
+ {
196
+ id: "unique-node-id",
197
+ severity: "error",
198
+ description: "Node IDs must be globally unique.",
199
+ },
200
+ {
201
+ id: "node-view-membership",
202
+ severity: "error",
203
+ description: "A node must appear in at least one view and use valid node types for each view.",
204
+ },
205
+ {
206
+ id: "edge-endpoint-exists",
207
+ severity: "error",
208
+ description: "Every edge source and target must reference an existing node.",
209
+ },
210
+ {
211
+ id: "edge-type-compatibility",
212
+ severity: "error",
213
+ description: "Edge type must be valid for source and target node types.",
214
+ },
215
+ {
216
+ id: "duplicate-edge",
217
+ severity: "warning",
218
+ description: "Duplicate edges (same type/source/target) should be deduplicated.",
219
+ },
220
+ {
221
+ id: "inferred-confidence",
222
+ severity: "warning",
223
+ description: "Inferred edges should include a confidence score between 0 and 1.",
224
+ },
225
+ {
226
+ id: "source-path-exists",
227
+ severity: "warning",
228
+ description: "Node source paths should be present and non-empty.",
229
+ },
230
+ ] as const;
231
+
232
+ function pushIssue(
233
+ output: GraphValidationResult,
234
+ issue: GraphValidationIssue,
235
+ ) {
236
+ if (issue.severity === "error") output.errors.push(issue);
237
+ else output.warnings.push(issue);
238
+ }
239
+
240
+ function hasNonEmptyString(value: string | undefined | null): value is string {
241
+ return typeof value === "string" && value.trim().length > 0;
242
+ }
243
+
244
+ export function validateGraphDataset(dataset: GraphDataset): GraphValidationResult {
245
+ const result: GraphValidationResult = {
246
+ valid: true,
247
+ errors: [],
248
+ warnings: [],
249
+ };
250
+
251
+ if (dataset.schemaVersion !== GRAPH_SCHEMA_VERSION) {
252
+ pushIssue(result, {
253
+ ruleId: "schema-version",
254
+ severity: "error",
255
+ message: `Expected schemaVersion=${GRAPH_SCHEMA_VERSION}, received ${dataset.schemaVersion}.`,
256
+ });
257
+ }
258
+
259
+ const nodesById = new Map<string, GraphNode>();
260
+
261
+ for (const node of dataset.nodes) {
262
+ if (!NODE_ID_PATTERN.test(node.id)) {
263
+ pushIssue(result, {
264
+ ruleId: "node-id-format",
265
+ severity: "error",
266
+ message: `Invalid node id format: ${node.id}`,
267
+ nodeId: node.id,
268
+ });
269
+ }
270
+
271
+ if (nodesById.has(node.id)) {
272
+ pushIssue(result, {
273
+ ruleId: "unique-node-id",
274
+ severity: "error",
275
+ message: `Duplicate node id: ${node.id}`,
276
+ nodeId: node.id,
277
+ });
278
+ } else {
279
+ nodesById.set(node.id, node);
280
+ }
281
+
282
+ if (!Array.isArray(node.views) || node.views.length === 0) {
283
+ pushIssue(result, {
284
+ ruleId: "node-view-membership",
285
+ severity: "error",
286
+ message: `Node ${node.id} must belong to at least one view.`,
287
+ nodeId: node.id,
288
+ });
289
+ } else {
290
+ for (const view of node.views) {
291
+ const allowedTypes = VIEW_NODE_TYPES[view];
292
+ if (!allowedTypes.includes(node.type)) {
293
+ pushIssue(result, {
294
+ ruleId: "node-view-membership",
295
+ severity: "error",
296
+ message: `Node ${node.id} has type ${node.type} which is invalid for view ${view}.`,
297
+ nodeId: node.id,
298
+ });
299
+ }
300
+ }
301
+ }
302
+
303
+ if (!hasNonEmptyString(node.source.path)) {
304
+ pushIssue(result, {
305
+ ruleId: "source-path-exists",
306
+ severity: "warning",
307
+ message: `Node ${node.id} is missing source.path.`,
308
+ nodeId: node.id,
309
+ });
310
+ }
311
+ }
312
+
313
+ const seenEdgeSignatures = new Map<string, string>();
314
+
315
+ for (const edge of dataset.edges) {
316
+ const sourceNode = nodesById.get(edge.source);
317
+ const targetNode = nodesById.get(edge.target);
318
+
319
+ if (!sourceNode || !targetNode) {
320
+ pushIssue(result, {
321
+ ruleId: "edge-endpoint-exists",
322
+ severity: "error",
323
+ message: `Edge ${edge.id} references missing endpoint(s): ${edge.source} -> ${edge.target}.`,
324
+ edgeId: edge.id,
325
+ });
326
+ continue;
327
+ }
328
+
329
+ const compatibility = EDGE_TYPE_RULES[edge.type];
330
+ const validSource = compatibility.from.includes(sourceNode.type);
331
+ const validTarget = compatibility.to.includes(targetNode.type);
332
+
333
+ if (!validSource || !validTarget) {
334
+ pushIssue(result, {
335
+ ruleId: "edge-type-compatibility",
336
+ severity: "error",
337
+ message: `Edge ${edge.id} type ${edge.type} is not compatible with ${sourceNode.type} -> ${targetNode.type}.`,
338
+ edgeId: edge.id,
339
+ });
340
+ }
341
+
342
+ const signature = `${edge.type}|${edge.source}|${edge.target}`;
343
+ const existingId = seenEdgeSignatures.get(signature);
344
+ if (existingId) {
345
+ pushIssue(result, {
346
+ ruleId: "duplicate-edge",
347
+ severity: "warning",
348
+ message: `Edge ${edge.id} duplicates ${existingId} (${signature}).`,
349
+ edgeId: edge.id,
350
+ });
351
+ } else {
352
+ seenEdgeSignatures.set(signature, edge.id);
353
+ }
354
+
355
+ if (edge.authority === "inferred") {
356
+ const confidence = edge.confidence;
357
+ if (typeof confidence !== "number" || confidence < 0 || confidence > 1) {
358
+ pushIssue(result, {
359
+ ruleId: "inferred-confidence",
360
+ severity: "warning",
361
+ message: `Inferred edge ${edge.id} should include confidence in [0,1].`,
362
+ edgeId: edge.id,
363
+ });
364
+ }
365
+ }
366
+ }
367
+
368
+ result.valid = result.errors.length === 0;
369
+ return result;
370
+ }
371
+
372
+ export type GraphSelectionDirection = "upstream" | "downstream" | "both";
373
+
374
+ export type GraphViewBehaviorContract = {
375
+ view: GraphViewMode;
376
+ defaultEdgeVisibility: "none" | "all" | "selection-only";
377
+ defaultHopDepth: number;
378
+ allowedHopDepths: number[];
379
+ carriesSelectionAcrossViews: boolean;
380
+ supportsExternalDependencies: boolean;
381
+ };
382
+
383
+ export const GRAPH_VIEW_BEHAVIOR: Record<GraphViewMode, GraphViewBehaviorContract> = {
384
+ "architecture-map": {
385
+ view: "architecture-map",
386
+ defaultEdgeVisibility: "none",
387
+ defaultHopDepth: 0,
388
+ allowedHopDepths: [0, 1],
389
+ carriesSelectionAcrossViews: true,
390
+ supportsExternalDependencies: false,
391
+ },
392
+ tasks: {
393
+ view: "tasks",
394
+ defaultEdgeVisibility: "selection-only",
395
+ defaultHopDepth: 1,
396
+ allowedHopDepths: [1, 2],
397
+ carriesSelectionAcrossViews: true,
398
+ supportsExternalDependencies: false,
399
+ },
400
+ project: {
401
+ view: "project",
402
+ defaultEdgeVisibility: "selection-only",
403
+ defaultHopDepth: 1,
404
+ allowedHopDepths: [1, 2, 3],
405
+ carriesSelectionAcrossViews: true,
406
+ supportsExternalDependencies: true,
407
+ },
408
+ };
@@ -0,0 +1,52 @@
1
+ import path from "node:path";
2
+ import { existsSync } from "node:fs";
3
+
4
+ function looksLikeProjectRoot(candidate: string): boolean {
5
+ return (
6
+ existsSync(path.join(candidate, "roadmap")) && existsSync(path.join(candidate, "arch-model"))
7
+ );
8
+ }
9
+
10
+ /**
11
+ * Pure function that resolves the project root path without side-effects.
12
+ * Does NOT call process.chdir().
13
+ */
14
+ export function getProjectRoot(): string {
15
+ if (process.env.PROJECT_ROOT) {
16
+ const envRoot = path.resolve(process.env.PROJECT_ROOT);
17
+ if (looksLikeProjectRoot(envRoot)) return envRoot;
18
+ }
19
+
20
+ const cwd = process.cwd();
21
+ const candidates = [
22
+ path.resolve(cwd),
23
+ path.resolve(cwd, "../.."),
24
+ path.resolve(cwd, "testProject"),
25
+ path.resolve(cwd, "../../testProject"),
26
+ path.resolve(cwd, "../../../testProject"),
27
+ ];
28
+
29
+ for (const candidate of candidates) {
30
+ if (looksLikeProjectRoot(candidate)) {
31
+ return candidate;
32
+ }
33
+ }
34
+
35
+ return path.resolve(cwd, "../..");
36
+ }
37
+
38
+ export function normalizePath(value: string): string {
39
+ return value.split(path.sep).join("/");
40
+ }
41
+
42
+ export function mapTargetToModule(target: string): string {
43
+ const normalized = normalizePath(target);
44
+ const parts = normalized.split("/").filter(Boolean);
45
+ if (parts.length === 0) return normalized;
46
+ if ((parts[0] === "apps" || parts[0] === "packages") && parts[1])
47
+ return `${parts[0]}/${parts[1]}`;
48
+ if (parts[0] === "architecture" && parts[1]) return `${parts[0]}/${parts[1]}`;
49
+ if (parts[0] === "arch-domains") return "arch-domains";
50
+ if (parts[0] === "roadmap") return "roadmap";
51
+ return parts.slice(0, Math.min(parts.length, 2)).join("/");
52
+ }
@@ -0,0 +1,116 @@
1
+ export type ApiResult<T> = {
2
+ success?: boolean;
3
+ data?: T;
4
+ errors?: string[];
5
+ };
6
+
7
+ export type TaskNode = {
8
+ id: string;
9
+ title: string;
10
+ milestone: string;
11
+ domain: string | null;
12
+ status: string;
13
+ lane: string;
14
+ };
15
+
16
+ export type CheckData = {
17
+ ok: boolean;
18
+ errors: string[];
19
+ warnings: string[];
20
+ };
21
+
22
+ export type PhaseListData = Array<{
23
+ id: string;
24
+ active: boolean;
25
+ }>;
26
+
27
+ export type ArchitectureMapData = {
28
+ summary: {
29
+ schemaVersion: string;
30
+ nodes: Record<string, number>;
31
+ edges: Record<string, number>;
32
+ };
33
+ nodes: {
34
+ domains: Array<{ name: string; description?: string }>;
35
+ decisions: Array<{ id: string; title?: string; status?: string }>;
36
+ milestones: Array<{ id: string; phaseId: string; milestoneId: string }>;
37
+ tasks: TaskNode[];
38
+ modules: Array<{ name: string; type?: string; description?: string }>;
39
+ };
40
+ edges: {
41
+ taskToDecision: Array<{ task: string; decision: string }>;
42
+ taskToModule: Array<{ task: string; module: string }>;
43
+ decisionToDomain: Array<{ decision: string; domain: string }>;
44
+ milestoneToTask: Array<{ milestone: string; task: string }>;
45
+ };
46
+ };
47
+
48
+ export type TaskTraceData = {
49
+ task: string;
50
+ decisionRefs?: string[];
51
+ moduleRefs?: string[];
52
+ files?: string[];
53
+ };
54
+
55
+ export type FileTraceData = {
56
+ file: string;
57
+ module: string;
58
+ tasks: string[];
59
+ decisions: string[];
60
+ };
61
+
62
+ export type TaskDocumentData = {
63
+ id: string;
64
+ lane: string;
65
+ path: string;
66
+ markdown: string;
67
+ };
68
+
69
+ export type NodeFilesData = {
70
+ type: "phase" | "milestone" | "task" | "domain" | "file";
71
+ id: string;
72
+ files: Array<{ path: string; content: string }>;
73
+ };
74
+
75
+ export type SearchResultItem = {
76
+ id: string;
77
+ kind: "task" | "decision" | "domain" | "module";
78
+ title: string;
79
+ subtitle?: string;
80
+ route: string;
81
+ };
82
+
83
+ export type SearchResultData = {
84
+ query: string;
85
+ results: SearchResultItem[];
86
+ };
87
+
88
+ export type DomainDocsData = {
89
+ docs: Array<{
90
+ id: string;
91
+ scope: "arch-domains" | "arch-model" | "architecture" | "roadmap";
92
+ file: string;
93
+ path: string;
94
+ title: string;
95
+ }>;
96
+ };
97
+
98
+ export type GraphValidationIssueData = {
99
+ ruleId: string;
100
+ severity: "error" | "warning";
101
+ message: string;
102
+ nodeId?: string;
103
+ edgeId?: string;
104
+ };
105
+
106
+ export type GraphValidationData = {
107
+ valid: boolean;
108
+ errors: GraphValidationIssueData[];
109
+ warnings: GraphValidationIssueData[];
110
+ };
111
+
112
+ export type GraphDatasetResponse = {
113
+ dataset: GraphDataset;
114
+ validation: GraphValidationData;
115
+ };
116
+ import type { GraphDataset } from "./graph-schema";
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/dev/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,17 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+
4
+ const appDir = path.dirname(fileURLToPath(import.meta.url));
5
+ const projectRoot = path.resolve(appDir, "../../..");
6
+
7
+ /** @type {import('next').NextConfig} */
8
+ const nextConfig = {
9
+ experimental: {
10
+ cpus: 4,
11
+ },
12
+ turbopack: {
13
+ root: projectRoot,
14
+ },
15
+ };
16
+
17
+ export default nextConfig;
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "arch",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "next dev --port 4020",
8
+ "build": "next build",
9
+ "start": "next start --port 4020",
10
+ "lint": "eslint --max-warnings 0",
11
+ "check-types": "next typegen && tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "@repo/ui": "workspace:*",
15
+ "dagre": "^0.8.5",
16
+ "lucide-react": "^0.553.0",
17
+ "next": "16.1.5",
18
+ "react-markdown": "^10.1.0",
19
+ "react": "^19.2.0",
20
+ "react-dom": "^19.2.0",
21
+ "reactflow": "^11.11.4",
22
+ "project-arch": "link:../../../packages/project-arch"
23
+ },
24
+ "devDependencies": {
25
+ "@repo/eslint-config": "workspace:*",
26
+ "@repo/typescript-config": "workspace:*",
27
+ "@types/dagre": "^0.7.53",
28
+ "@types/node": "^22.15.3",
29
+ "@types/react": "19.2.2",
30
+ "@types/react-dom": "19.2.2",
31
+ "autoprefixer": "^10.4.21",
32
+ "eslint": "^9.39.1",
33
+ "postcss": "^8.5.6",
34
+ "tailwindcss": "^3.4.17",
35
+ "typescript": "5.9.2"
36
+ }
37
+ }
38
+
@@ -0,0 +1,6 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
@@ -0,0 +1,11 @@
1
+ import type { Config } from "tailwindcss";
2
+
3
+ const config: Config = {
4
+ content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./lib/**/*.{ts,tsx}"],
5
+ theme: {
6
+ extend: {},
7
+ },
8
+ plugins: [],
9
+ };
10
+
11
+ export default config;
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "@repo/typescript-config/nextjs.json",
3
+ "compilerOptions": {
4
+ "plugins": [
5
+ {
6
+ "name": "next"
7
+ }
8
+ ],
9
+ "strictNullChecks": true
10
+ },
11
+ "include": [
12
+ "**/*.ts",
13
+ "**/*.tsx",
14
+ "**/*.d.ts",
15
+ "next-env.d.ts",
16
+ "next.config.js"
17
+ ],
18
+ "exclude": [
19
+ "node_modules"
20
+ ]
21
+ }
@@ -0,0 +1,4 @@
1
+ import { config } from "@repo/eslint-config/react-internal";
2
+
3
+ /** @type {import("eslint").Linter.Config} */
4
+ export default config;