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.
Files changed (66) hide show
  1. package/.forge.json +6 -5
  2. package/README.md +7 -7
  3. package/dist/cli.js +193 -131
  4. package/dist/cli.js.map +1 -1
  5. package/dist/codex-poll.d.ts +38 -0
  6. package/dist/codex-poll.js +70 -0
  7. package/dist/codex-poll.js.map +1 -0
  8. package/dist/config/schema.d.ts +0 -28
  9. package/dist/config/schema.js +0 -7
  10. package/dist/config/schema.js.map +1 -1
  11. package/dist/graph/index.d.ts +6 -0
  12. package/dist/graph/index.js +11 -0
  13. package/dist/graph/index.js.map +1 -0
  14. package/dist/graph/query.d.ts +52 -0
  15. package/dist/graph/query.js +319 -0
  16. package/dist/graph/query.js.map +1 -0
  17. package/dist/graph/reader.d.ts +13 -0
  18. package/dist/graph/reader.js +140 -0
  19. package/dist/graph/reader.js.map +1 -0
  20. package/dist/graph/schemas.d.ts +215 -0
  21. package/dist/graph/schemas.js +54 -0
  22. package/dist/graph/schemas.js.map +1 -0
  23. package/dist/graph/types.d.ts +109 -0
  24. package/dist/graph/types.js +2 -0
  25. package/dist/graph/types.js.map +1 -0
  26. package/dist/graph/validator.d.ts +32 -0
  27. package/dist/graph/validator.js +253 -0
  28. package/dist/graph/validator.js.map +1 -0
  29. package/dist/graph/writer.d.ts +18 -0
  30. package/dist/graph/writer.js +159 -0
  31. package/dist/graph/writer.js.map +1 -0
  32. package/dist/linear/client.d.ts +94 -4
  33. package/dist/linear/client.js +203 -20
  34. package/dist/linear/client.js.map +1 -1
  35. package/dist/linear/sync.d.ts +11 -14
  36. package/dist/linear/sync.js +63 -83
  37. package/dist/linear/sync.js.map +1 -1
  38. package/dist/runner/loop.d.ts +1 -1
  39. package/dist/runner/loop.js +93 -100
  40. package/dist/runner/loop.js.map +1 -1
  41. package/dist/runner/prompt.d.ts +7 -9
  42. package/dist/runner/prompt.js +27 -30
  43. package/dist/runner/prompt.js.map +1 -1
  44. package/dist/setup.js +14 -0
  45. package/dist/setup.js.map +1 -1
  46. package/dist/types.d.ts +0 -23
  47. package/package.json +3 -5
  48. package/skills/README.md +35 -33
  49. package/skills/forge-build.md +344 -0
  50. package/skills/forge-capture.md +204 -0
  51. package/skills/forge-fix.md +207 -0
  52. package/skills/forge-plan.md +335 -0
  53. package/skills/forge-quick.md +154 -0
  54. package/skills/forge-setup.md +2 -2
  55. package/skills/ref/adversarial-review.md +117 -0
  56. package/skills/ref/graph-correction.md +88 -0
  57. package/skills/ref/requirement-sizing.md +146 -0
  58. package/dist/server.d.ts +0 -6
  59. package/dist/server.js +0 -51
  60. package/dist/server.js.map +0 -1
  61. package/dist/state/status.d.ts +0 -66
  62. package/dist/state/status.js +0 -96
  63. package/dist/state/status.js.map +0 -1
  64. package/skills/forge-go.md +0 -583
  65. package/skills/forge-spec.md +0 -367
  66. package/skills/forge-triage.md +0 -179
@@ -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,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC;IAC7C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;CACjC,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,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,YAAY,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5C,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"}
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":"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"}
@@ -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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"}