forge-cc 1.0.3 → 2.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.
- package/.forge.json +6 -5
- package/README.md +7 -7
- package/dist/cli.js +193 -131
- package/dist/cli.js.map +1 -1
- package/dist/codex-poll.d.ts +38 -0
- package/dist/codex-poll.js +70 -0
- package/dist/codex-poll.js.map +1 -0
- package/dist/config/schema.d.ts +0 -28
- package/dist/config/schema.js +0 -7
- package/dist/config/schema.js.map +1 -1
- package/dist/graph/index.d.ts +6 -0
- package/dist/graph/index.js +11 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/query.d.ts +52 -0
- package/dist/graph/query.js +319 -0
- package/dist/graph/query.js.map +1 -0
- package/dist/graph/reader.d.ts +13 -0
- package/dist/graph/reader.js +140 -0
- package/dist/graph/reader.js.map +1 -0
- package/dist/graph/schemas.d.ts +215 -0
- package/dist/graph/schemas.js +54 -0
- package/dist/graph/schemas.js.map +1 -0
- package/dist/graph/types.d.ts +109 -0
- package/dist/graph/types.js +2 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/graph/validator.d.ts +32 -0
- package/dist/graph/validator.js +253 -0
- package/dist/graph/validator.js.map +1 -0
- package/dist/graph/writer.d.ts +18 -0
- package/dist/graph/writer.js +159 -0
- package/dist/graph/writer.js.map +1 -0
- package/dist/linear/client.d.ts +94 -4
- package/dist/linear/client.js +203 -20
- package/dist/linear/client.js.map +1 -1
- package/dist/linear/sync.d.ts +11 -14
- package/dist/linear/sync.js +63 -83
- package/dist/linear/sync.js.map +1 -1
- package/dist/runner/loop.d.ts +1 -1
- package/dist/runner/loop.js +93 -100
- package/dist/runner/loop.js.map +1 -1
- package/dist/runner/prompt.d.ts +7 -9
- package/dist/runner/prompt.js +27 -30
- package/dist/runner/prompt.js.map +1 -1
- package/dist/setup.js +14 -0
- package/dist/setup.js.map +1 -1
- package/dist/types.d.ts +0 -23
- package/package.json +3 -5
- package/skills/README.md +35 -33
- package/skills/forge-build.md +344 -0
- package/skills/forge-capture.md +204 -0
- package/skills/forge-fix.md +207 -0
- package/skills/forge-plan.md +335 -0
- package/skills/forge-quick.md +154 -0
- package/skills/forge-setup.md +2 -2
- package/skills/ref/adversarial-review.md +117 -0
- package/skills/ref/graph-correction.md +88 -0
- package/skills/ref/requirement-sizing.md +146 -0
- package/dist/server.d.ts +0 -6
- package/dist/server.js +0 -51
- package/dist/server.js.map +0 -1
- package/dist/state/status.d.ts +0 -66
- package/dist/state/status.js +0 -96
- package/dist/state/status.js.map +0 -1
- package/skills/forge-go.md +0 -583
- package/skills/forge-spec.md +0 -367
- package/skills/forge-triage.md +0 -179
package/dist/config/schema.js
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
const linearStatesSchema = z.object({
|
|
3
|
-
planned: z.string().default("Planned"),
|
|
4
|
-
inProgress: z.string().default("In Progress"),
|
|
5
|
-
inReview: z.string().default("In Review"),
|
|
6
|
-
done: z.string().default("Done"),
|
|
7
|
-
}).strict();
|
|
8
2
|
export const forgeConfigSchema = z.object({
|
|
9
3
|
gates: z.array(z.string()).default(["types", "lint", "tests"]),
|
|
10
4
|
gateTimeouts: z.record(z.string(), z.number()).default({}),
|
|
11
5
|
maxIterations: z.number().default(5),
|
|
12
6
|
linearTeam: z.string().default(""),
|
|
13
|
-
linearStates: linearStatesSchema.default({}),
|
|
14
7
|
verifyFreshness: z.number().default(600000),
|
|
15
8
|
forgeVersion: z.string().default("1.0.0"),
|
|
16
9
|
}).strict();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9D,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC1D,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAClC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;CAC1C,CAAC,CAAC,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { RequirementStatus, RequirementFiles, Requirement, RequirementMeta, GroupDef, LinearConfig, GraphIndex, ResolvedRequirement, ProjectGraph, ValidationError, GroupStatus, } from "./types.js";
|
|
2
|
+
export { requirementStatusEnum, requirementFilesSchema, requirementFrontmatterSchema, requirementMetaSchema, groupDefSchema, linearConfigSchema, graphIndexSchema, } from "./schemas.js";
|
|
3
|
+
export { loadGraph, loadIndex, loadRequirement, loadRequirements, loadOverview, discoverGraphs, } from "./reader.js";
|
|
4
|
+
export { writeIndex, writeRequirement, writeOverview, initGraph, updateRequirementStatus, batchUpdateStatus, addDiscoveredRequirement, } from "./writer.js";
|
|
5
|
+
export { findReady, findBlocked, getTransitiveDeps, computeWaves, groupStatus, findDiscovered, isProjectComplete, buildRequirementContext, } from "./query.js";
|
|
6
|
+
export { validateGraph, detectCycles, findDanglingEdges, findOrphans, findFileConflicts, } from "./validator.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Schemas (re-export values)
|
|
2
|
+
export { requirementStatusEnum, requirementFilesSchema, requirementFrontmatterSchema, requirementMetaSchema, groupDefSchema, linearConfigSchema, graphIndexSchema, } from "./schemas.js";
|
|
3
|
+
// Reader
|
|
4
|
+
export { loadGraph, loadIndex, loadRequirement, loadRequirements, loadOverview, discoverGraphs, } from "./reader.js";
|
|
5
|
+
// Writer
|
|
6
|
+
export { writeIndex, writeRequirement, writeOverview, initGraph, updateRequirementStatus, batchUpdateStatus, addDiscoveredRequirement, } from "./writer.js";
|
|
7
|
+
// Query
|
|
8
|
+
export { findReady, findBlocked, getTransitiveDeps, computeWaves, groupStatus, findDiscovered, isProjectComplete, buildRequirementContext, } from "./query.js";
|
|
9
|
+
// Validator
|
|
10
|
+
export { validateGraph, detectCycles, findDanglingEdges, findOrphans, findFileConflicts, } from "./validator.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/graph/index.ts"],"names":[],"mappings":"AAeA,6BAA6B;AAC7B,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,4BAA4B,EAC5B,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,SAAS;AACT,OAAO,EACL,SAAS,EACT,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,SAAS;AACT,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,uBAAuB,EACvB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AAErB,QAAQ;AACR,OAAO,EACL,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AAEpB,YAAY;AACZ,OAAO,EACL,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,iBAAiB,GAClB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { GraphIndex, Requirement, RequirementFiles, GroupStatus } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns requirement IDs that are ready to start:
|
|
4
|
+
* - status === "pending"
|
|
5
|
+
* - all requirement-level dependsOn are "complete"
|
|
6
|
+
* - all group-level dependsOn groups are fully complete
|
|
7
|
+
*
|
|
8
|
+
* Sorted by: priority desc, group order (topo), insertion order within group.
|
|
9
|
+
*/
|
|
10
|
+
export declare function findReady(index: GraphIndex): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Returns pending requirements where at least one dependsOn is NOT "complete"
|
|
13
|
+
* or group-level deps are not met. Includes both requirement-level and group-level blockers.
|
|
14
|
+
*/
|
|
15
|
+
export declare function findBlocked(index: GraphIndex): Array<{
|
|
16
|
+
id: string;
|
|
17
|
+
blockedBy: string[];
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* DFS traversal of dependsOn graph. Returns IDs in topological order
|
|
21
|
+
* (dependencies first, target last). Throws on cycle detection.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getTransitiveDeps(index: GraphIndex, id: string): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Group ready requirements into parallel waves. Two requirements cannot
|
|
26
|
+
* share a wave if they have overlapping files (creates or modifies).
|
|
27
|
+
*
|
|
28
|
+
* Uses fileOverrides if provided for a given ID, otherwise falls back to
|
|
29
|
+
* the requirement's files field.
|
|
30
|
+
*
|
|
31
|
+
* Algorithm: greedy — for each requirement in order, assign to first wave
|
|
32
|
+
* with no file conflicts. If none, create new wave.
|
|
33
|
+
*/
|
|
34
|
+
export declare function computeWaves(readyIds: string[], requirements: Map<string, Requirement>, fileOverrides?: Map<string, RequirementFiles>): string[][];
|
|
35
|
+
/**
|
|
36
|
+
* Count requirements by status per group. `isComplete` = every non-rejected
|
|
37
|
+
* requirement is "complete".
|
|
38
|
+
*/
|
|
39
|
+
export declare function groupStatus(index: GraphIndex): Record<string, GroupStatus>;
|
|
40
|
+
/**
|
|
41
|
+
* Return IDs where status === "discovered".
|
|
42
|
+
*/
|
|
43
|
+
export declare function findDiscovered(index: GraphIndex): string[];
|
|
44
|
+
/**
|
|
45
|
+
* True when every requirement with status other than "rejected" has status "complete".
|
|
46
|
+
*/
|
|
47
|
+
export declare function isProjectComplete(index: GraphIndex): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Get the target requirement + all transitive deps. Return in topological order
|
|
50
|
+
* (deps first, target last). Skips IDs not found in the requirements Map.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildRequirementContext(index: GraphIndex, requirements: Map<string, Requirement>, targetId: string): Requirement[];
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
2
|
+
/** Check if every non-rejected requirement in a group is "complete". */
|
|
3
|
+
function isGroupComplete(index, groupKey) {
|
|
4
|
+
for (const meta of Object.values(index.requirements)) {
|
|
5
|
+
if (meta.group !== groupKey)
|
|
6
|
+
continue;
|
|
7
|
+
if (meta.status === "rejected")
|
|
8
|
+
continue;
|
|
9
|
+
if (meta.status !== "complete")
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
/** Topological sort of group keys using their dependsOn edges. */
|
|
15
|
+
function topoSortGroups(index) {
|
|
16
|
+
const keys = Object.keys(index.groups);
|
|
17
|
+
const visited = new Set();
|
|
18
|
+
const sorted = [];
|
|
19
|
+
const visiting = new Set();
|
|
20
|
+
function visit(key) {
|
|
21
|
+
if (visited.has(key))
|
|
22
|
+
return;
|
|
23
|
+
if (visiting.has(key))
|
|
24
|
+
return; // cycle — skip gracefully
|
|
25
|
+
visiting.add(key);
|
|
26
|
+
const deps = index.groups[key]?.dependsOn ?? [];
|
|
27
|
+
for (const dep of deps) {
|
|
28
|
+
if (index.groups[dep])
|
|
29
|
+
visit(dep);
|
|
30
|
+
}
|
|
31
|
+
visiting.delete(key);
|
|
32
|
+
visited.add(key);
|
|
33
|
+
sorted.push(key);
|
|
34
|
+
}
|
|
35
|
+
// Sort keys by order field (ascending), then alphabetically for stability
|
|
36
|
+
const orderedKeys = [...keys].sort((a, b) => {
|
|
37
|
+
const oa = index.groups[a].order ?? Infinity;
|
|
38
|
+
const ob = index.groups[b].order ?? Infinity;
|
|
39
|
+
if (oa !== ob)
|
|
40
|
+
return oa - ob;
|
|
41
|
+
return a.localeCompare(b);
|
|
42
|
+
});
|
|
43
|
+
for (const key of orderedKeys) {
|
|
44
|
+
visit(key);
|
|
45
|
+
}
|
|
46
|
+
return sorted;
|
|
47
|
+
}
|
|
48
|
+
/** Build a map from group key to its topological position. */
|
|
49
|
+
function groupOrderMap(index) {
|
|
50
|
+
const sorted = topoSortGroups(index);
|
|
51
|
+
const map = new Map();
|
|
52
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
53
|
+
map.set(sorted[i], i);
|
|
54
|
+
}
|
|
55
|
+
return map;
|
|
56
|
+
}
|
|
57
|
+
/** Check if all group-level dependsOn are fully complete for a requirement's group. */
|
|
58
|
+
function areGroupDepsComplete(index, groupKey) {
|
|
59
|
+
const groupDef = index.groups[groupKey];
|
|
60
|
+
if (!groupDef?.dependsOn?.length)
|
|
61
|
+
return true;
|
|
62
|
+
for (const depGroup of groupDef.dependsOn) {
|
|
63
|
+
if (!isGroupComplete(index, depGroup))
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
/** Get group-level blocker group keys for a requirement's group. */
|
|
69
|
+
function getGroupBlockers(index, groupKey) {
|
|
70
|
+
const groupDef = index.groups[groupKey];
|
|
71
|
+
if (!groupDef?.dependsOn?.length)
|
|
72
|
+
return [];
|
|
73
|
+
const blockers = [];
|
|
74
|
+
for (const depGroup of groupDef.dependsOn) {
|
|
75
|
+
if (!isGroupComplete(index, depGroup)) {
|
|
76
|
+
blockers.push(depGroup);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return blockers;
|
|
80
|
+
}
|
|
81
|
+
// ── Query Functions ──────────────────────────────────────────────────
|
|
82
|
+
/**
|
|
83
|
+
* Returns requirement IDs that are ready to start:
|
|
84
|
+
* - status === "pending"
|
|
85
|
+
* - all requirement-level dependsOn are "complete"
|
|
86
|
+
* - all group-level dependsOn groups are fully complete
|
|
87
|
+
*
|
|
88
|
+
* Sorted by: priority desc, group order (topo), insertion order within group.
|
|
89
|
+
*/
|
|
90
|
+
export function findReady(index) {
|
|
91
|
+
const gOrder = groupOrderMap(index);
|
|
92
|
+
const reqEntries = Object.entries(index.requirements);
|
|
93
|
+
const ready = [];
|
|
94
|
+
for (const [id, meta] of reqEntries) {
|
|
95
|
+
if (meta.status !== "pending")
|
|
96
|
+
continue;
|
|
97
|
+
// Check requirement-level deps
|
|
98
|
+
const allReqDepsComplete = meta.dependsOn.every((depId) => {
|
|
99
|
+
const depMeta = index.requirements[depId];
|
|
100
|
+
return depMeta?.status === "complete";
|
|
101
|
+
});
|
|
102
|
+
if (!allReqDepsComplete)
|
|
103
|
+
continue;
|
|
104
|
+
// Check group-level deps
|
|
105
|
+
if (!areGroupDepsComplete(index, meta.group))
|
|
106
|
+
continue;
|
|
107
|
+
ready.push(id);
|
|
108
|
+
}
|
|
109
|
+
// Sort: priority desc, then group order asc, then insertion order (stable)
|
|
110
|
+
const insertionOrder = new Map();
|
|
111
|
+
reqEntries.forEach(([id], i) => insertionOrder.set(id, i));
|
|
112
|
+
ready.sort((a, b) => {
|
|
113
|
+
const metaA = index.requirements[a];
|
|
114
|
+
const metaB = index.requirements[b];
|
|
115
|
+
// Priority descending (higher first)
|
|
116
|
+
const pa = metaA.priority ?? 0;
|
|
117
|
+
const pb = metaB.priority ?? 0;
|
|
118
|
+
if (pa !== pb)
|
|
119
|
+
return pb - pa;
|
|
120
|
+
// Group order ascending
|
|
121
|
+
const ga = gOrder.get(metaA.group) ?? Infinity;
|
|
122
|
+
const gb = gOrder.get(metaB.group) ?? Infinity;
|
|
123
|
+
if (ga !== gb)
|
|
124
|
+
return ga - gb;
|
|
125
|
+
// Insertion order (original order in index)
|
|
126
|
+
return (insertionOrder.get(a) ?? 0) - (insertionOrder.get(b) ?? 0);
|
|
127
|
+
});
|
|
128
|
+
return ready;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Returns pending requirements where at least one dependsOn is NOT "complete"
|
|
132
|
+
* or group-level deps are not met. Includes both requirement-level and group-level blockers.
|
|
133
|
+
*/
|
|
134
|
+
export function findBlocked(index) {
|
|
135
|
+
const result = [];
|
|
136
|
+
for (const [id, meta] of Object.entries(index.requirements)) {
|
|
137
|
+
if (meta.status !== "pending")
|
|
138
|
+
continue;
|
|
139
|
+
const blockers = [];
|
|
140
|
+
// Requirement-level blockers
|
|
141
|
+
for (const depId of meta.dependsOn) {
|
|
142
|
+
const depMeta = index.requirements[depId];
|
|
143
|
+
if (!depMeta || depMeta.status !== "complete") {
|
|
144
|
+
blockers.push(depId);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Group-level blockers
|
|
148
|
+
const groupBlockers = getGroupBlockers(index, meta.group);
|
|
149
|
+
for (const gb of groupBlockers) {
|
|
150
|
+
blockers.push(`group:${gb}`);
|
|
151
|
+
}
|
|
152
|
+
if (blockers.length > 0) {
|
|
153
|
+
result.push({ id, blockedBy: blockers });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* DFS traversal of dependsOn graph. Returns IDs in topological order
|
|
160
|
+
* (dependencies first, target last). Throws on cycle detection.
|
|
161
|
+
*/
|
|
162
|
+
export function getTransitiveDeps(index, id) {
|
|
163
|
+
const result = [];
|
|
164
|
+
const visited = new Set();
|
|
165
|
+
const visiting = new Set();
|
|
166
|
+
function visit(currentId) {
|
|
167
|
+
if (visited.has(currentId))
|
|
168
|
+
return;
|
|
169
|
+
if (visiting.has(currentId)) {
|
|
170
|
+
throw new Error(`Cycle detected: ${currentId} is part of a dependency cycle`);
|
|
171
|
+
}
|
|
172
|
+
visiting.add(currentId);
|
|
173
|
+
const meta = index.requirements[currentId];
|
|
174
|
+
if (meta) {
|
|
175
|
+
for (const depId of meta.dependsOn) {
|
|
176
|
+
visit(depId);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
visiting.delete(currentId);
|
|
180
|
+
visited.add(currentId);
|
|
181
|
+
result.push(currentId);
|
|
182
|
+
}
|
|
183
|
+
visit(id);
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Group ready requirements into parallel waves. Two requirements cannot
|
|
188
|
+
* share a wave if they have overlapping files (creates or modifies).
|
|
189
|
+
*
|
|
190
|
+
* Uses fileOverrides if provided for a given ID, otherwise falls back to
|
|
191
|
+
* the requirement's files field.
|
|
192
|
+
*
|
|
193
|
+
* Algorithm: greedy — for each requirement in order, assign to first wave
|
|
194
|
+
* with no file conflicts. If none, create new wave.
|
|
195
|
+
*/
|
|
196
|
+
export function computeWaves(readyIds, requirements, fileOverrides) {
|
|
197
|
+
const waves = [];
|
|
198
|
+
const waveFiles = [];
|
|
199
|
+
function getFiles(id) {
|
|
200
|
+
const override = fileOverrides?.get(id);
|
|
201
|
+
if (override) {
|
|
202
|
+
return [...override.creates, ...override.modifies];
|
|
203
|
+
}
|
|
204
|
+
const req = requirements.get(id);
|
|
205
|
+
if (!req)
|
|
206
|
+
return [];
|
|
207
|
+
return [...req.files.creates, ...req.files.modifies];
|
|
208
|
+
}
|
|
209
|
+
for (const id of readyIds) {
|
|
210
|
+
const files = getFiles(id);
|
|
211
|
+
let assigned = false;
|
|
212
|
+
for (let w = 0; w < waves.length; w++) {
|
|
213
|
+
const hasConflict = files.some((f) => waveFiles[w].has(f));
|
|
214
|
+
if (!hasConflict) {
|
|
215
|
+
waves[w].push(id);
|
|
216
|
+
for (const f of files)
|
|
217
|
+
waveFiles[w].add(f);
|
|
218
|
+
assigned = true;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (!assigned) {
|
|
223
|
+
waves.push([id]);
|
|
224
|
+
waveFiles.push(new Set(files));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return waves;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Count requirements by status per group. `isComplete` = every non-rejected
|
|
231
|
+
* requirement is "complete".
|
|
232
|
+
*/
|
|
233
|
+
export function groupStatus(index) {
|
|
234
|
+
const result = {};
|
|
235
|
+
// Initialize all groups
|
|
236
|
+
for (const groupKey of Object.keys(index.groups)) {
|
|
237
|
+
result[groupKey] = {
|
|
238
|
+
total: 0,
|
|
239
|
+
complete: 0,
|
|
240
|
+
inProgress: 0,
|
|
241
|
+
pending: 0,
|
|
242
|
+
discovered: 0,
|
|
243
|
+
rejected: 0,
|
|
244
|
+
isComplete: true,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
// Count requirements per group
|
|
248
|
+
for (const meta of Object.values(index.requirements)) {
|
|
249
|
+
const gs = result[meta.group];
|
|
250
|
+
if (!gs)
|
|
251
|
+
continue;
|
|
252
|
+
gs.total++;
|
|
253
|
+
switch (meta.status) {
|
|
254
|
+
case "complete":
|
|
255
|
+
gs.complete++;
|
|
256
|
+
break;
|
|
257
|
+
case "in_progress":
|
|
258
|
+
gs.inProgress++;
|
|
259
|
+
break;
|
|
260
|
+
case "pending":
|
|
261
|
+
gs.pending++;
|
|
262
|
+
break;
|
|
263
|
+
case "discovered":
|
|
264
|
+
gs.discovered++;
|
|
265
|
+
break;
|
|
266
|
+
case "rejected":
|
|
267
|
+
gs.rejected++;
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Compute isComplete: every non-rejected requirement is "complete"
|
|
272
|
+
for (const groupKey of Object.keys(result)) {
|
|
273
|
+
const gs = result[groupKey];
|
|
274
|
+
const nonRejected = gs.total - gs.rejected;
|
|
275
|
+
gs.isComplete = nonRejected > 0
|
|
276
|
+
? gs.complete === nonRejected
|
|
277
|
+
: true; // empty group is complete
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Return IDs where status === "discovered".
|
|
283
|
+
*/
|
|
284
|
+
export function findDiscovered(index) {
|
|
285
|
+
const result = [];
|
|
286
|
+
for (const [id, meta] of Object.entries(index.requirements)) {
|
|
287
|
+
if (meta.status === "discovered") {
|
|
288
|
+
result.push(id);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* True when every requirement with status other than "rejected" has status "complete".
|
|
295
|
+
*/
|
|
296
|
+
export function isProjectComplete(index) {
|
|
297
|
+
for (const meta of Object.values(index.requirements)) {
|
|
298
|
+
if (meta.status === "rejected")
|
|
299
|
+
continue;
|
|
300
|
+
if (meta.status !== "complete")
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Get the target requirement + all transitive deps. Return in topological order
|
|
307
|
+
* (deps first, target last). Skips IDs not found in the requirements Map.
|
|
308
|
+
*/
|
|
309
|
+
export function buildRequirementContext(index, requirements, targetId) {
|
|
310
|
+
const depIds = getTransitiveDeps(index, targetId);
|
|
311
|
+
const result = [];
|
|
312
|
+
for (const depId of depIds) {
|
|
313
|
+
const req = requirements.get(depId);
|
|
314
|
+
if (req)
|
|
315
|
+
result.push(req);
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/graph/query.ts"],"names":[],"mappings":"AAOA,wEAAwE;AAExE,wEAAwE;AACxE,SAAS,eAAe,CACtB,KAAiB,EACjB,QAAgB;IAEhB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACrD,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,SAAS;QACtC,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;YAAE,SAAS;QACzC,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kEAAkE;AAClE,SAAS,cAAc,CAAC,KAAiB;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,SAAS,KAAK,CAAC,GAAW;QACxB,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO;QAC7B,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,0BAA0B;QACzD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;QAChD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;QACD,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,0EAA0E;IAC1E,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC1C,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,QAAQ,CAAC;QAC7C,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,QAAQ,CAAC;QAC7C,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAC9B,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,KAAK,CAAC,GAAG,CAAC,CAAC;IACb,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,8DAA8D;AAC9D,SAAS,aAAa,CAAC,KAAiB;IACtC,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uFAAuF;AACvF,SAAS,oBAAoB,CAC3B,KAAiB,EACjB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM;QAAE,OAAO,IAAI,CAAC;IAC9C,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAC;IACtD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oEAAoE;AACpE,SAAS,gBAAgB,CACvB,KAAiB,EACjB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM;QAAE,OAAO,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;YACtC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,wEAAwE;AAExE;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,KAAiB;IACzC,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,SAAS;QAExC,+BAA+B;QAC/B,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACxD,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAC1C,OAAO,OAAO,EAAE,MAAM,KAAK,UAAU,CAAC;QACxC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,kBAAkB;YAAE,SAAS;QAElC,yBAAyB;QACzB,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS;QAEvD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,2EAA2E;IAC3E,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjD,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAE3D,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAClB,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAEpC,qCAAqC;QACrC,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;QAC/B,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;QAC/B,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAE9B,wBAAwB;QACxB,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC;QAC/C,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC;QAC/C,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAE9B,4CAA4C;QAC5C,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,KAAiB;IAEjB,MAAM,MAAM,GAA+C,EAAE,CAAC;IAE9D,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QAC5D,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,SAAS;QAExC,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,6BAA6B;QAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAC1C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBAC9C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QAED,uBAAuB;QACvB,MAAM,aAAa,GAAG,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC/B,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAiB,EACjB,EAAU;IAEV,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,SAAS,KAAK,CAAC,SAAiB;QAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO;QACnC,IAAI,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CACb,mBAAmB,SAAS,gCAAgC,CAC7D,CAAC;QACJ,CAAC;QAED,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnC,KAAK,CAAC,KAAK,CAAC,CAAC;YACf,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAkB,EAClB,YAAsC,EACtC,aAA6C;IAE7C,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,MAAM,SAAS,GAAkB,EAAE,CAAC;IAEpC,SAAS,QAAQ,CAAC,EAAU;QAC1B,MAAM,QAAQ,GAAG,aAAa,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC3B,IAAI,QAAQ,GAAG,KAAK,CAAC;QAErB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3D,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClB,KAAK,MAAM,CAAC,IAAI,KAAK;oBAAE,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC3C,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACjB,SAAS,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,KAAiB;IAEjB,MAAM,MAAM,GAAgC,EAAE,CAAC;IAE/C,wBAAwB;IACxB,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,QAAQ,CAAC,GAAG;YACjB,KAAK,EAAE,CAAC;YACR,QAAQ,EAAE,CAAC;YACX,UAAU,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;YACV,UAAU,EAAE,CAAC;YACb,QAAQ,EAAE,CAAC;YACX,UAAU,EAAE,IAAI;SACjB,CAAC;IACJ,CAAC;IAED,+BAA+B;IAC/B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACrD,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,EAAE;YAAE,SAAS;QAElB,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,KAAK,UAAU;gBACb,EAAE,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM;YACR,KAAK,aAAa;gBAChB,EAAE,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM;YACR,KAAK,SAAS;gBACZ,EAAE,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM;YACR,KAAK,YAAY;gBACf,EAAE,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM;YACR,KAAK,UAAU;gBACb,EAAE,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM;QACV,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5B,MAAM,WAAW,GAAG,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC;QAC3C,EAAE,CAAC,UAAU,GAAG,WAAW,GAAG,CAAC;YAC7B,CAAC,CAAC,EAAE,CAAC,QAAQ,KAAK,WAAW;YAC7B,CAAC,CAAC,IAAI,CAAC,CAAC,0BAA0B;IACtC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAiB;IAC9C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QAC5D,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAiB;IACjD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACrD,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;YAAE,SAAS;QACzC,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAiB,EACjB,YAAsC,EACtC,QAAgB;IAEhB,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAClD,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { GraphIndex, ProjectGraph, Requirement } from "./types.js";
|
|
2
|
+
/** Load complete project graph: index, overview, and all requirements. */
|
|
3
|
+
export declare function loadGraph(projectDir: string, slug: string): Promise<ProjectGraph>;
|
|
4
|
+
/** Fast path: load and validate _index.yaml only. */
|
|
5
|
+
export declare function loadIndex(projectDir: string, slug: string): Promise<GraphIndex>;
|
|
6
|
+
/** Load a single requirement by ID. Returns null if not found. */
|
|
7
|
+
export declare function loadRequirement(projectDir: string, slug: string, id: string): Promise<Requirement | null>;
|
|
8
|
+
/** Load multiple requirements by ID in a single directory scan. */
|
|
9
|
+
export declare function loadRequirements(projectDir: string, slug: string, ids: string[]): Promise<Map<string, Requirement>>;
|
|
10
|
+
/** Read overview.md as a string. Throws if missing. */
|
|
11
|
+
export declare function loadOverview(projectDir: string, slug: string): Promise<string>;
|
|
12
|
+
/** Discover all valid graph slugs in .planning/graph/. */
|
|
13
|
+
export declare function discoverGraphs(projectDir: string): Promise<string[]>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse as yamlParse } from "yaml";
|
|
4
|
+
import { graphIndexSchema, requirementFrontmatterSchema } from "./schemas.js";
|
|
5
|
+
function graphDir(projectDir, slug) {
|
|
6
|
+
return join(projectDir, ".planning", "graph", slug);
|
|
7
|
+
}
|
|
8
|
+
function indexPath(projectDir, slug) {
|
|
9
|
+
return join(graphDir(projectDir, slug), "_index.yaml");
|
|
10
|
+
}
|
|
11
|
+
function overviewPath(projectDir, slug) {
|
|
12
|
+
return join(graphDir(projectDir, slug), "overview.md");
|
|
13
|
+
}
|
|
14
|
+
function requirementsDir(projectDir, slug) {
|
|
15
|
+
return join(graphDir(projectDir, slug), "requirements");
|
|
16
|
+
}
|
|
17
|
+
/** Parse YAML frontmatter from a markdown file. Returns frontmatter object and body string. */
|
|
18
|
+
function parseFrontmatter(rawContent) {
|
|
19
|
+
// Normalize CRLF → LF so Windows-authored files parse correctly
|
|
20
|
+
const content = rawContent.replace(/\r\n/g, "\n");
|
|
21
|
+
if (!content.startsWith("---\n")) {
|
|
22
|
+
throw new Error("Missing YAML frontmatter opening ---");
|
|
23
|
+
}
|
|
24
|
+
const end = content.indexOf("\n---\n", 4);
|
|
25
|
+
if (end === -1) {
|
|
26
|
+
throw new Error("Missing YAML frontmatter closing ---");
|
|
27
|
+
}
|
|
28
|
+
const yamlStr = content.slice(4, end);
|
|
29
|
+
const body = content.slice(end + 5).trim();
|
|
30
|
+
return { frontmatter: yamlParse(yamlStr), body };
|
|
31
|
+
}
|
|
32
|
+
/** Parse a requirement .md file into a Requirement. */
|
|
33
|
+
function parseRequirementFile(content) {
|
|
34
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
35
|
+
const parsed = requirementFrontmatterSchema.parse(frontmatter);
|
|
36
|
+
return { ...parsed, body };
|
|
37
|
+
}
|
|
38
|
+
/** Load complete project graph: index, overview, and all requirements. */
|
|
39
|
+
export async function loadGraph(projectDir, slug) {
|
|
40
|
+
const index = await loadIndex(projectDir, slug);
|
|
41
|
+
const overview = await loadOverview(projectDir, slug);
|
|
42
|
+
const requirements = new Map();
|
|
43
|
+
let entries;
|
|
44
|
+
try {
|
|
45
|
+
entries = await readdir(requirementsDir(projectDir, slug));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
entries = [];
|
|
49
|
+
}
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (!entry.endsWith(".md"))
|
|
52
|
+
continue;
|
|
53
|
+
const raw = await readFile(join(requirementsDir(projectDir, slug), entry), "utf-8");
|
|
54
|
+
const req = parseRequirementFile(raw);
|
|
55
|
+
if (requirements.has(req.id)) {
|
|
56
|
+
throw new Error(`Duplicate requirement ID "${req.id}" found in file "${entry}" — another file already defines this ID`);
|
|
57
|
+
}
|
|
58
|
+
requirements.set(req.id, req);
|
|
59
|
+
}
|
|
60
|
+
return { index, overview, requirements };
|
|
61
|
+
}
|
|
62
|
+
/** Fast path: load and validate _index.yaml only. */
|
|
63
|
+
export async function loadIndex(projectDir, slug) {
|
|
64
|
+
const raw = await readFile(indexPath(projectDir, slug), "utf-8");
|
|
65
|
+
const data = yamlParse(raw);
|
|
66
|
+
return graphIndexSchema.parse(data);
|
|
67
|
+
}
|
|
68
|
+
/** Load a single requirement by ID. Returns null if not found. */
|
|
69
|
+
export async function loadRequirement(projectDir, slug, id) {
|
|
70
|
+
let entries;
|
|
71
|
+
try {
|
|
72
|
+
entries = await readdir(requirementsDir(projectDir, slug));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
if (!entry.endsWith(".md"))
|
|
79
|
+
continue;
|
|
80
|
+
const raw = await readFile(join(requirementsDir(projectDir, slug), entry), "utf-8");
|
|
81
|
+
const req = parseRequirementFile(raw);
|
|
82
|
+
if (req.id === id)
|
|
83
|
+
return req;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/** Load multiple requirements by ID in a single directory scan. */
|
|
88
|
+
export async function loadRequirements(projectDir, slug, ids) {
|
|
89
|
+
const wanted = new Set(ids);
|
|
90
|
+
const result = new Map();
|
|
91
|
+
let entries;
|
|
92
|
+
try {
|
|
93
|
+
entries = await readdir(requirementsDir(projectDir, slug));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
if (!entry.endsWith(".md"))
|
|
100
|
+
continue;
|
|
101
|
+
const raw = await readFile(join(requirementsDir(projectDir, slug), entry), "utf-8");
|
|
102
|
+
const req = parseRequirementFile(raw);
|
|
103
|
+
if (wanted.has(req.id)) {
|
|
104
|
+
result.set(req.id, req);
|
|
105
|
+
if (result.size === wanted.size)
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
/** Read overview.md as a string. Throws if missing. */
|
|
112
|
+
export async function loadOverview(projectDir, slug) {
|
|
113
|
+
return readFile(overviewPath(projectDir, slug), "utf-8");
|
|
114
|
+
}
|
|
115
|
+
/** Discover all valid graph slugs in .planning/graph/. */
|
|
116
|
+
export async function discoverGraphs(projectDir) {
|
|
117
|
+
const baseDir = join(projectDir, ".planning", "graph");
|
|
118
|
+
let entries;
|
|
119
|
+
try {
|
|
120
|
+
entries = await readdir(baseDir);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
const slugs = [];
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
try {
|
|
128
|
+
const idx = join(baseDir, entry, "_index.yaml");
|
|
129
|
+
const raw = await readFile(idx, "utf-8");
|
|
130
|
+
const data = yamlParse(raw);
|
|
131
|
+
graphIndexSchema.parse(data);
|
|
132
|
+
slugs.push(entry);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// skip invalid entries
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return slugs;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/graph/reader.ts"],"names":[],"mappings":"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"}
|