hadara 0.3.2 → 0.3.3

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 (52) hide show
  1. package/README.md +49 -34
  2. package/dist/cli/context.js +110 -0
  3. package/dist/cli/help.js +6 -7
  4. package/dist/cli/init.js +56 -58
  5. package/dist/cli/main.js +12 -0
  6. package/dist/cli/session.js +37 -0
  7. package/dist/cli/task.js +52 -0
  8. package/dist/context/code-graph-extractor.js +123 -0
  9. package/dist/context/code-index.js +1154 -0
  10. package/dist/context/context-cache-store.js +925 -0
  11. package/dist/context/context-graph-builder.js +341 -0
  12. package/dist/context/context-graph.js +42 -0
  13. package/dist/context/context-pack.js +457 -0
  14. package/dist/context/context-slice-boundary.js +37 -0
  15. package/dist/context/context-slice.js +487 -0
  16. package/dist/context/document-extractors.js +343 -0
  17. package/dist/context/evidence-extractors.js +179 -0
  18. package/dist/context/extractor-contract.js +166 -0
  19. package/dist/context/registry-extractors.js +177 -0
  20. package/dist/context/release-extractors.js +175 -0
  21. package/dist/context/session-start.js +297 -0
  22. package/dist/context/source-manifest.js +566 -0
  23. package/dist/context/state-projection.js +209 -0
  24. package/dist/context/task-extractors.js +168 -0
  25. package/dist/core/schema.js +26 -0
  26. package/dist/harness/validate.js +7 -8
  27. package/dist/schemas/code-index.schema.json +173 -0
  28. package/dist/schemas/context-cache-record.schema.json +56 -0
  29. package/dist/schemas/context-cache-status.schema.json +167 -0
  30. package/dist/schemas/context-cache-warm.schema.json +222 -0
  31. package/dist/schemas/context-graph.schema.json +286 -0
  32. package/dist/schemas/context-pack.schema.json +246 -0
  33. package/dist/schemas/context-slice.schema.json +94 -0
  34. package/dist/schemas/context-source-manifest.schema.json +147 -0
  35. package/dist/schemas/schema-index.json +91 -0
  36. package/dist/schemas/session-start.schema.json +158 -0
  37. package/dist/schemas/task-close-repair-plan.schema.json +67 -0
  38. package/dist/schemas/task-context.schema.json +125 -0
  39. package/dist/schemas/task-finalize.schema.json +98 -0
  40. package/dist/schemas/task-lifecycle.schema.json +84 -0
  41. package/dist/services/capability-registry.js +266 -9
  42. package/dist/services/lifecycle-guide.js +23 -29
  43. package/dist/services/protocol-consistency.js +6 -8
  44. package/dist/services/workbench-next-actions.js +2 -0
  45. package/dist/task/acceptance.js +171 -0
  46. package/dist/task/task-close-repair-plan.js +190 -0
  47. package/dist/task/task-close.js +34 -35
  48. package/dist/task/task-finalize.js +377 -0
  49. package/dist/task/task-lifecycle.js +210 -0
  50. package/dist/task/task-next.js +10 -1
  51. package/dist/task/task-ready.js +4 -0
  52. package/package.json +1 -1
@@ -0,0 +1,341 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectContextGraphExtractions = collectContextGraphExtractions;
4
+ exports.collectContextGraphExtractionsWithCache = collectContextGraphExtractionsWithCache;
5
+ exports.buildContextGraphReport = buildContextGraphReport;
6
+ exports.createTaskContextReport = createTaskContextReport;
7
+ const context_cache_store_1 = require("./context-cache-store");
8
+ const extractor_contract_1 = require("./extractor-contract");
9
+ const document_extractors_1 = require("./document-extractors");
10
+ const evidence_extractors_1 = require("./evidence-extractors");
11
+ const registry_extractors_1 = require("./registry-extractors");
12
+ const release_extractors_1 = require("./release-extractors");
13
+ const state_projection_1 = require("./state-projection");
14
+ const task_extractors_1 = require("./task-extractors");
15
+ const code_graph_extractor_1 = require("./code-graph-extractor");
16
+ function collectContextGraphExtractions(projectRoot, options = {}) {
17
+ const results = [
18
+ (0, task_extractors_1.extractTaskCapsules)(projectRoot),
19
+ (0, task_extractors_1.extractTaskBoard)(projectRoot),
20
+ (0, registry_extractors_1.extractDocsRegistry)(projectRoot),
21
+ (0, registry_extractors_1.extractCommandRegistry)(projectRoot),
22
+ (0, document_extractors_1.extractManagedSections)(projectRoot),
23
+ (0, document_extractors_1.extractProjectState)(projectRoot),
24
+ (0, document_extractors_1.extractDecisions)(projectRoot),
25
+ (0, document_extractors_1.extractAgentHandoff)(projectRoot),
26
+ (0, evidence_extractors_1.extractEvidence)(projectRoot),
27
+ (0, release_extractors_1.extractReleaseReadiness)(projectRoot)
28
+ ];
29
+ if (options.includeCode)
30
+ results.push((0, code_graph_extractor_1.extractCodeIndexGraph)(projectRoot, options.generatedAt));
31
+ return results;
32
+ }
33
+ function collectContextGraphExtractionsWithCache(projectRoot, options = {}) {
34
+ if (options.cacheStrategy === 'disabled') {
35
+ return {
36
+ extractionResults: collectContextGraphExtractions(projectRoot, options),
37
+ cache: { used: false, hit: false }
38
+ };
39
+ }
40
+ const generatedAt = options.generatedAt ?? new Date().toISOString();
41
+ const analysis = (0, context_cache_store_1.createSourceManifestCacheAnalysis)({
42
+ projectRoot,
43
+ generatedAt,
44
+ generatedByCommand: 'context.graph'
45
+ });
46
+ const graphCore = (0, context_cache_store_1.readContextGraphCoreShard)({ projectRoot, manifest: analysis.currentManifest });
47
+ if (graphCore.hit && graphCore.result) {
48
+ const results = [graphCore.result];
49
+ const code = options.includeCode
50
+ ? collectCodeGraphExtractionWithCache(projectRoot, {
51
+ manifest: analysis.currentManifest,
52
+ generatedAt
53
+ })
54
+ : undefined;
55
+ if (code)
56
+ results.push(code.result);
57
+ return {
58
+ extractionResults: results,
59
+ cache: {
60
+ used: true,
61
+ hit: true,
62
+ mode: options.includeCode ? code?.cache.mode ?? 'graph-core+live-code' : 'graph-core',
63
+ manifestHash: analysis.currentManifest.manifestHash,
64
+ readShardCount: 1 + (code?.cache.readShardCount ?? 0),
65
+ hitShardCount: 1 + (code?.cache.hitShardCount ?? 0),
66
+ missShardCount: code?.cache.missShardCount ?? 0,
67
+ staleShardCount: code?.cache.staleShardCount ?? 0,
68
+ corruptShardCount: code?.cache.corruptShardCount ?? 0,
69
+ schemaMismatchShardCount: code?.cache.schemaMismatchShardCount ?? 0,
70
+ shardPaths: [graphCore.path, ...(code?.cache.shardPaths ?? [])].sort(),
71
+ staleExtractorKeys: code?.cache.staleExtractorKeys ?? [],
72
+ ...(graphCore.record ? { createdAt: graphCore.record.createdAt, cachePath: graphCore.path } : {}),
73
+ sourceManifestCacheFresh: analysis.cacheFresh,
74
+ sourceManifestFastPath: analysis.fastPath,
75
+ ...(analysis.fastPathReason ? { sourceManifestFastPathReason: analysis.fastPathReason } : {}),
76
+ ...(analysis.fastPathStrategy ? { sourceManifestFastPathStrategy: analysis.fastPathStrategy } : {})
77
+ }
78
+ };
79
+ }
80
+ const shards = (0, context_cache_store_1.collectContextGraphExtractorShards)({ projectRoot, manifest: analysis.currentManifest });
81
+ const results = [
82
+ (0, task_extractors_1.extractTaskCapsules)(projectRoot),
83
+ shards.results.extractTaskBoard ?? (0, task_extractors_1.extractTaskBoard)(projectRoot),
84
+ shards.results.extractDocsRegistry ?? (0, registry_extractors_1.extractDocsRegistry)(projectRoot),
85
+ shards.results.extractCommandRegistry ?? (0, registry_extractors_1.extractCommandRegistry)(projectRoot),
86
+ (0, document_extractors_1.extractManagedSections)(projectRoot),
87
+ (0, document_extractors_1.extractProjectState)(projectRoot),
88
+ (0, document_extractors_1.extractDecisions)(projectRoot),
89
+ (0, document_extractors_1.extractAgentHandoff)(projectRoot),
90
+ (0, evidence_extractors_1.extractEvidence)(projectRoot),
91
+ (0, release_extractors_1.extractReleaseReadiness)(projectRoot)
92
+ ];
93
+ const code = options.includeCode
94
+ ? collectCodeGraphExtractionWithCache(projectRoot, {
95
+ manifest: analysis.currentManifest,
96
+ generatedAt
97
+ })
98
+ : undefined;
99
+ if (code)
100
+ results.push(code.result);
101
+ return {
102
+ extractionResults: results,
103
+ cache: {
104
+ ...mergeCodeCacheMetadata(shards.cache, code?.cache),
105
+ sourceManifestCacheFresh: analysis.cacheFresh,
106
+ sourceManifestFastPath: analysis.fastPath,
107
+ ...(analysis.fastPathReason ? { sourceManifestFastPathReason: analysis.fastPathReason } : {}),
108
+ ...(analysis.fastPathStrategy ? { sourceManifestFastPathStrategy: analysis.fastPathStrategy } : {})
109
+ }
110
+ };
111
+ }
112
+ function collectCodeGraphExtractionWithCache(projectRoot, input) {
113
+ const read = (0, context_cache_store_1.readContextCodeIndexShard)({ projectRoot, manifest: input.manifest });
114
+ if (read.hit && read.result) {
115
+ return {
116
+ result: (0, code_graph_extractor_1.codeIndexReportToGraphExtraction)(read.result),
117
+ cache: {
118
+ used: true,
119
+ hit: true,
120
+ mode: 'graph-core+code-index',
121
+ manifestHash: input.manifest.manifestHash,
122
+ readShardCount: 1,
123
+ hitShardCount: 1,
124
+ missShardCount: 0,
125
+ staleShardCount: 0,
126
+ corruptShardCount: 0,
127
+ schemaMismatchShardCount: 0,
128
+ shardPaths: [read.path],
129
+ staleExtractorKeys: [],
130
+ ...(read.record ? { createdAt: read.record.createdAt, cachePath: read.path } : {})
131
+ }
132
+ };
133
+ }
134
+ return {
135
+ result: (0, code_graph_extractor_1.extractCodeIndexGraph)(projectRoot, input.generatedAt),
136
+ cache: {
137
+ used: false,
138
+ hit: false,
139
+ mode: 'graph-core+live-code',
140
+ manifestHash: input.manifest.manifestHash,
141
+ readShardCount: 1,
142
+ hitShardCount: 0,
143
+ missShardCount: read.status === 'missing' ? 1 : 0,
144
+ staleShardCount: read.status === 'stale' ? 1 : 0,
145
+ corruptShardCount: read.status === 'corrupt' ? 1 : 0,
146
+ schemaMismatchShardCount: read.status === 'schema-mismatch' ? 1 : 0,
147
+ shardPaths: [read.path],
148
+ staleExtractorKeys: read.status === 'stale' ? ['codeIndex'] : []
149
+ }
150
+ };
151
+ }
152
+ function mergeCodeCacheMetadata(base, code) {
153
+ if (!code)
154
+ return base;
155
+ return {
156
+ ...base,
157
+ used: base.used || code.used,
158
+ hit: base.hit || code.hit,
159
+ mode: code.hit ? 'extractor-shards+code-index' : 'extractor-shards+live-code',
160
+ readShardCount: (base.readShardCount ?? 0) + (code.readShardCount ?? 0),
161
+ hitShardCount: (base.hitShardCount ?? 0) + (code.hitShardCount ?? 0),
162
+ missShardCount: (base.missShardCount ?? 0) + (code.missShardCount ?? 0),
163
+ staleShardCount: (base.staleShardCount ?? 0) + (code.staleShardCount ?? 0),
164
+ corruptShardCount: (base.corruptShardCount ?? 0) + (code.corruptShardCount ?? 0),
165
+ schemaMismatchShardCount: (base.schemaMismatchShardCount ?? 0) + (code.schemaMismatchShardCount ?? 0),
166
+ shardPaths: [...(base.shardPaths ?? []), ...(code.shardPaths ?? [])].sort(),
167
+ staleExtractorKeys: [...(base.staleExtractorKeys ?? []), ...(code.staleExtractorKeys ?? [])].sort(),
168
+ ...(code.hit && code.cachePath ? { cachePath: code.cachePath } : {}),
169
+ ...(code.hit && code.createdAt ? { createdAt: code.createdAt } : {})
170
+ };
171
+ }
172
+ function buildContextGraphReport(input) {
173
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
174
+ const collected = input.extractionResults ? undefined : collectContextGraphExtractionsWithCache(input.projectRoot, {
175
+ includeCode: input.includeCode,
176
+ generatedAt,
177
+ cacheStrategy: input.cacheStrategy
178
+ });
179
+ const extractionResults = input.extractionResults ?? collected?.extractionResults ?? [];
180
+ const merged = (0, extractor_contract_1.mergeGraphExtractionResults)(extractionResults);
181
+ const stateProjection = (0, state_projection_1.createContextStateProjectionReport)({ generatedAt, extractionResults });
182
+ const mode = input.mode ?? (input.taskId ? 'task' : 'full');
183
+ const taskContext = input.taskId
184
+ ? createTaskContextReport({
185
+ taskId: input.taskId,
186
+ nodes: merged.nodes,
187
+ edges: merged.edges,
188
+ stateProjection,
189
+ issues: merged.issues
190
+ })
191
+ : undefined;
192
+ return {
193
+ schemaVersion: 'hadara.contextGraph.v1',
194
+ command: 'context.graph',
195
+ ok: merged.issues.every((issue) => issue.severity !== 'error') && stateProjection.ok,
196
+ generatedAt,
197
+ projectRoot: input.projectRoot,
198
+ sourceHash: merged.source.sourceHash,
199
+ mode,
200
+ ...(input.taskId ? { taskId: input.taskId } : {}),
201
+ nodes: merged.nodes,
202
+ edges: merged.edges,
203
+ ...(taskContext ? { taskContext } : {}),
204
+ stateProjection,
205
+ summary: (0, extractor_contract_1.summarizeContextGraphExtraction)(merged.nodes, merged.edges, merged.issues),
206
+ cache: input.cache ?? collected?.cache ?? { used: false, hit: false },
207
+ issues: merged.issues
208
+ };
209
+ }
210
+ function createTaskContextReport(input) {
211
+ const task = findTaskNode(input.nodes, input.taskId);
212
+ const connectedNodes = connectedNodeIds(input.edges, (0, extractor_contract_1.createTaskNodeId)(input.taskId));
213
+ const readFirst = task ? [candidate(task, 'Active task capsule is the first read for task-scoped context routing.', 'explicit')] : [];
214
+ return {
215
+ schemaVersion: 'hadara.taskContext.v1',
216
+ taskId: input.taskId,
217
+ ...(task ? { task } : {}),
218
+ readFirst,
219
+ readIfNeeded: readIfNeededCandidates(input.nodes, input.edges, connectedNodes),
220
+ doNotReadByDefault: doNotReadByDefaultCandidates(input.nodes),
221
+ relatedEvidence: relatedEvidenceCandidates(input.nodes, input.edges, input.taskId),
222
+ relatedCommands: relatedCommandCandidates(input.nodes, input.edges, connectedNodes),
223
+ knownProblems: knownProblemCandidates(input.nodes),
224
+ validationSuggestions: validationSuggestions(input.taskId),
225
+ stateIssues: taskStateIssues(input.stateProjection.issues, input.taskId),
226
+ issues: input.issues
227
+ };
228
+ }
229
+ function findTaskNode(nodes, taskId) {
230
+ const nodeId = (0, extractor_contract_1.createTaskNodeId)(taskId);
231
+ const matches = nodes.filter((node) => node.id === nodeId && node.type === 'Task');
232
+ return matches.find((node) => node.kind === 'task-capsule') ?? matches[0];
233
+ }
234
+ function readIfNeededCandidates(nodes, edges, connectedIds) {
235
+ const candidates = new Map();
236
+ for (const node of nodes) {
237
+ if (node.type !== 'Document')
238
+ continue;
239
+ if (metadataBoolean(node, 'requiredReading')) {
240
+ candidates.set(node.id, candidate(node, 'Document registry marks this document as required reading.', 'explicit'));
241
+ continue;
242
+ }
243
+ if (connectedIds.has(node.id)) {
244
+ candidates.set(node.id, candidate(node, 'Document is directly connected to the task graph.', 'derived'));
245
+ }
246
+ }
247
+ for (const edge of edges) {
248
+ if (!connectedIds.has(edge.from) && !connectedIds.has(edge.to))
249
+ continue;
250
+ const from = nodes.find((node) => node.id === edge.from);
251
+ const to = nodes.find((node) => node.id === edge.to);
252
+ for (const node of [from, to]) {
253
+ if (node?.type !== 'Document' || candidates.has(node.id))
254
+ continue;
255
+ candidates.set(node.id, candidate(node, edge.reason, edge.confidence));
256
+ }
257
+ }
258
+ return sortCandidates(Array.from(candidates.values()));
259
+ }
260
+ function doNotReadByDefaultCandidates(nodes) {
261
+ return sortCandidates(nodes
262
+ .filter((node) => node.type === 'Document')
263
+ .filter((node) => {
264
+ const status = String(node.status ?? '').toLowerCase();
265
+ const kind = String(node.kind ?? '').toLowerCase();
266
+ const readWhen = Array.isArray(node.metadata?.readWhen) ? node.metadata.readWhen.map(String) : [];
267
+ return status === 'archived'
268
+ || status === 'superseded'
269
+ || kind.includes('historical')
270
+ || readWhen.includes('historical')
271
+ || metadataBoolean(node, 'requiredReading') === false && kind.includes('archive');
272
+ })
273
+ .map((node) => candidate(node, 'Historical, archived, or superseded document is excluded from default task routing.', 'derived')));
274
+ }
275
+ function relatedEvidenceCandidates(nodes, edges, taskId) {
276
+ const taskNodeId = (0, extractor_contract_1.createTaskNodeId)(taskId);
277
+ const evidenceIds = new Set(edges
278
+ .filter((edge) => edge.from === taskNodeId && (edge.type === 'HAS_EVIDENCE' || edge.type === 'CLOSES_WITH'))
279
+ .map((edge) => edge.to));
280
+ return sortCandidates(nodes
281
+ .filter((node) => node.type === 'Evidence' && evidenceIds.has(node.id))
282
+ .map((node) => candidate(node, 'Evidence is directly linked to this task.', 'explicit')));
283
+ }
284
+ function relatedCommandCandidates(nodes, edges, connectedIds) {
285
+ const commandIds = new Set();
286
+ for (const edge of edges) {
287
+ if (edge.type !== 'DESCRIBES_COMMAND' && edge.type !== 'CHECKS_COMMAND')
288
+ continue;
289
+ if (connectedIds.has(edge.from))
290
+ commandIds.add(edge.to);
291
+ if (connectedIds.has(edge.to))
292
+ commandIds.add(edge.from);
293
+ }
294
+ return sortCandidates(nodes
295
+ .filter((node) => node.type === 'Command' && commandIds.has(node.id))
296
+ .map((node) => candidate(node, 'Command is connected through task-relevant documentation or release checks.', 'derived')));
297
+ }
298
+ function knownProblemCandidates(nodes) {
299
+ return sortCandidates(nodes
300
+ .filter((node) => node.type === 'KnownProblem')
301
+ .map((node) => candidate(node, 'Current handoff records this known problem.', 'explicit')));
302
+ }
303
+ function taskStateIssues(issues, taskId) {
304
+ const relevant = issues.filter((issue) => issue.message.includes(taskId) || issue.paths.some((path) => path.includes(taskId)));
305
+ if (relevant.length > 0)
306
+ return relevant;
307
+ return issues.filter((issue) => issue.code === 'STATE_ACTIVE_TASK_MISMATCH' || issue.code === 'STATE_TASK_CAPSULE_MISSING');
308
+ }
309
+ function connectedNodeIds(edges, taskNodeId) {
310
+ const ids = new Set([taskNodeId]);
311
+ for (const edge of edges) {
312
+ if (edge.from === taskNodeId)
313
+ ids.add(edge.to);
314
+ if (edge.to === taskNodeId)
315
+ ids.add(edge.from);
316
+ }
317
+ return ids;
318
+ }
319
+ function candidate(node, reason, confidence) {
320
+ return {
321
+ id: node.id,
322
+ type: node.type,
323
+ ...(node.path ? { path: node.path } : {}),
324
+ reason,
325
+ confidence,
326
+ ...(node.source.hash ? { sourceHash: node.source.hash } : {})
327
+ };
328
+ }
329
+ function metadataBoolean(node, key) {
330
+ const value = node.metadata?.[key];
331
+ return typeof value === 'boolean' ? value : undefined;
332
+ }
333
+ function validationSuggestions(taskId) {
334
+ return [
335
+ 'npm run test:focused -- tests/unit/context-graph-builder.test.ts',
336
+ `node dist/cli/main.js task ready --task ${taskId} --level done --json`
337
+ ];
338
+ }
339
+ function sortCandidates(candidates) {
340
+ return candidates.sort((a, b) => a.id.localeCompare(b.id));
341
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONTEXT_CONFIDENCE_LEVELS = exports.CONTEXT_GRAPH_EDGE_TYPES = exports.CONTEXT_GRAPH_NODE_TYPES = exports.TASK_CONTEXT_SCHEMA_ID = exports.CONTEXT_GRAPH_SCHEMA_ID = void 0;
4
+ exports.CONTEXT_GRAPH_SCHEMA_ID = 'hadara.contextGraph.v1';
5
+ exports.TASK_CONTEXT_SCHEMA_ID = 'hadara.taskContext.v1';
6
+ exports.CONTEXT_GRAPH_NODE_TYPES = [
7
+ 'Task',
8
+ 'Document',
9
+ 'ManagedSection',
10
+ 'Evidence',
11
+ 'Command',
12
+ 'ReleaseCheck',
13
+ 'Decision',
14
+ 'KnownProblem',
15
+ 'SourceFile',
16
+ 'TestFile',
17
+ 'FixtureFile',
18
+ 'ConfigFile',
19
+ 'Symbol'
20
+ ];
21
+ exports.CONTEXT_GRAPH_EDGE_TYPES = [
22
+ 'HAS_EVIDENCE',
23
+ 'CLOSES_WITH',
24
+ 'REFERENCES_DOC',
25
+ 'REQUIRED_FOR',
26
+ 'SUPERSEDES',
27
+ 'DESCRIBES_COMMAND',
28
+ 'BELONGS_TO_DOCUMENT',
29
+ 'CHECKS_COMMAND',
30
+ 'AFFECTS_SURFACE',
31
+ 'DEPENDS_ON_EVIDENCE',
32
+ 'HAS_DECISION',
33
+ 'HAS_KNOWN_PROBLEM',
34
+ 'IMPORTS',
35
+ 'EXPORTS',
36
+ 'DEFINES_SYMBOL',
37
+ 'TESTS_FILE',
38
+ 'IMPLEMENTS_COMMAND',
39
+ 'REFERENCED_BY_DOC',
40
+ 'VALIDATED_BY_EVIDENCE'
41
+ ];
42
+ exports.CONTEXT_CONFIDENCE_LEVELS = ['explicit', 'derived', 'heuristic'];