hadara 0.1.0-rc.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/LICENSE +21 -0
- package/README.md +109 -0
- package/dist/agent/evidence.js +50 -0
- package/dist/agent/loop.js +124 -0
- package/dist/cli/args.js +70 -0
- package/dist/cli/dashboard.js +185 -0
- package/dist/cli/debt.js +41 -0
- package/dist/cli/doctor.js +68 -0
- package/dist/cli/errors.js +58 -0
- package/dist/cli/evidence-json.js +75 -0
- package/dist/cli/evidence.js +80 -0
- package/dist/cli/handoff.js +16 -0
- package/dist/cli/harness.js +57 -0
- package/dist/cli/hermes-json.js +31 -0
- package/dist/cli/hermes.js +28 -0
- package/dist/cli/init.js +142 -0
- package/dist/cli/install.js +34 -0
- package/dist/cli/main.js +216 -0
- package/dist/cli/mcp.js +15 -0
- package/dist/cli/package-smoke.js +37 -0
- package/dist/cli/policy-json.js +22 -0
- package/dist/cli/policy.js +43 -0
- package/dist/cli/release-artifact.js +47 -0
- package/dist/cli/release-dry-run.js +24 -0
- package/dist/cli/release-gate.js +28 -0
- package/dist/cli/release-publish.js +41 -0
- package/dist/cli/run-scaffold.js +68 -0
- package/dist/cli/run-state.js +41 -0
- package/dist/cli/run.js +191 -0
- package/dist/cli/smoke.js +58 -0
- package/dist/cli/status-json.js +6 -0
- package/dist/cli/status.js +26 -0
- package/dist/cli/task-json.js +8 -0
- package/dist/cli/task.js +64 -0
- package/dist/cli/tools.js +25 -0
- package/dist/cli/tui.js +72 -0
- package/dist/cli/write-preflight.js +27 -0
- package/dist/core/audit.js +41 -0
- package/dist/core/events.js +63 -0
- package/dist/core/fs.js +44 -0
- package/dist/core/paths.js +59 -0
- package/dist/core/redaction.js +178 -0
- package/dist/core/schema.js +253 -0
- package/dist/core/workspace.js +47 -0
- package/dist/evidence/evidence.js +170 -0
- package/dist/evidence/private-manifest.js +101 -0
- package/dist/handoff/handoff.js +49 -0
- package/dist/harness/replay.js +200 -0
- package/dist/harness/validate.js +465 -0
- package/dist/hermes/context-export.js +104 -0
- package/dist/index.js +29 -0
- package/dist/mcp/server.js +104 -0
- package/dist/mcp/tool-dispatch.js +159 -0
- package/dist/mcp/tool-registry.js +150 -0
- package/dist/mcp/tool-schemas.js +18 -0
- package/dist/policy/command-risk.js +39 -0
- package/dist/policy/permission-matrix.js +42 -0
- package/dist/policy/policy.js +20 -0
- package/dist/policy/preflight.js +47 -0
- package/dist/policy/presets.js +24 -0
- package/dist/policy/tokenizer.js +53 -0
- package/dist/providers/fallback-executor.js +46 -0
- package/dist/providers/mock-provider.js +49 -0
- package/dist/providers/provider-contract.js +2 -0
- package/dist/providers/provider-preparation.js +220 -0
- package/dist/providers/scripted-provider.js +69 -0
- package/dist/schemas/active-run-projection.schema.json +73 -0
- package/dist/schemas/active-run-resume.schema.json +68 -0
- package/dist/schemas/clean-checkout-smoke.schema.json +126 -0
- package/dist/schemas/context-export.schema.json +35 -0
- package/dist/schemas/event.schema.json +17 -0
- package/dist/schemas/evidence-list.schema.json +49 -0
- package/dist/schemas/feature-smoke.schema.json +67 -0
- package/dist/schemas/install-plan.schema.json +93 -0
- package/dist/schemas/package-smoke.schema.json +130 -0
- package/dist/schemas/private-evidence.schema.json +48 -0
- package/dist/schemas/provider-call.schema.json +42 -0
- package/dist/schemas/provider-config.schema.json +43 -0
- package/dist/schemas/release-artifact-manifest.schema.json +55 -0
- package/dist/schemas/release-artifact.schema.json +140 -0
- package/dist/schemas/release-dry-run.schema.json +141 -0
- package/dist/schemas/release-gate.schema.json +42 -0
- package/dist/schemas/release-publish.schema.json +114 -0
- package/dist/schemas/schema-index.json +145 -0
- package/dist/schemas/smoke-evidence-summary.schema.json +88 -0
- package/dist/schemas/tools-list.schema.json +78 -0
- package/dist/schemas/write-preflight.schema.json +47 -0
- package/dist/services/active-run-state.js +215 -0
- package/dist/services/capability-registry.js +540 -0
- package/dist/services/clean-checkout-smoke.js +393 -0
- package/dist/services/evidence-list.js +136 -0
- package/dist/services/feature-smoke.js +155 -0
- package/dist/services/harness-service.js +7 -0
- package/dist/services/install-plan.js +233 -0
- package/dist/services/operational-debt.js +767 -0
- package/dist/services/operations-status-service.js +195 -0
- package/dist/services/package-smoke.js +676 -0
- package/dist/services/policy-service.js +25 -0
- package/dist/services/project-read-model.js +101 -0
- package/dist/services/release-artifact-evidence.js +77 -0
- package/dist/services/release-artifact.js +351 -0
- package/dist/services/release-dry-run.js +253 -0
- package/dist/services/release-evidence.js +138 -0
- package/dist/services/release-publish.js +163 -0
- package/dist/services/smoke-evidence.js +104 -0
- package/dist/services/task-read-model.js +125 -0
- package/dist/services/tools-list.js +26 -0
- package/dist/services/write-preflight.js +240 -0
- package/dist/task/task-capsule.js +121 -0
- package/dist/tools/fake-shell.js +56 -0
- package/dist/tui/cache.js +341 -0
- package/dist/tui/constants.js +44 -0
- package/dist/tui/layout.js +140 -0
- package/dist/tui/markdown.js +238 -0
- package/dist/tui/read-model-worker.js +24 -0
- package/dist/tui/read-model.js +502 -0
- package/dist/tui/snapshot.js +434 -0
- package/dist/tui/state.js +229 -0
- package/dist/tui/terminal.js +475 -0
- package/dist/tui/theme.js +86 -0
- package/package.json +16 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createTuiLoadingReadModel = createTuiLoadingReadModel;
|
|
7
|
+
exports.createTuiReadModel = createTuiReadModel;
|
|
8
|
+
exports.createTuiFastReadModel = createTuiFastReadModel;
|
|
9
|
+
const active_run_state_1 = require("../services/active-run-state");
|
|
10
|
+
const active_run_state_2 = require("../services/active-run-state");
|
|
11
|
+
const evidence_list_1 = require("../services/evidence-list");
|
|
12
|
+
const operational_debt_1 = require("../services/operational-debt");
|
|
13
|
+
const operations_status_service_1 = require("../services/operations-status-service");
|
|
14
|
+
const project_read_model_1 = require("../services/project-read-model");
|
|
15
|
+
const task_read_model_1 = require("../services/task-read-model");
|
|
16
|
+
const tools_list_1 = require("../services/tools-list");
|
|
17
|
+
const write_preflight_1 = require("../services/write-preflight");
|
|
18
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
19
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
20
|
+
const DEFAULT_EVIDENCE_LIMIT = 20;
|
|
21
|
+
const DEFAULT_WRITE_PREVIEW_TITLE = 'TUI Follow-up';
|
|
22
|
+
function createTuiLoadingReadModel() {
|
|
23
|
+
const generatedAt = new Date().toISOString();
|
|
24
|
+
return {
|
|
25
|
+
schemaVersion: 'hadara.tui.read_model.internal.v1',
|
|
26
|
+
command: 'tui.read-model',
|
|
27
|
+
ok: true,
|
|
28
|
+
generatedAt,
|
|
29
|
+
selectedTaskId: null,
|
|
30
|
+
overview: {
|
|
31
|
+
currentWork: null,
|
|
32
|
+
previousWork: null,
|
|
33
|
+
currentDetail: null,
|
|
34
|
+
previousDetail: null,
|
|
35
|
+
health: 'loading',
|
|
36
|
+
phase: 'loading read models',
|
|
37
|
+
branch: '...'
|
|
38
|
+
},
|
|
39
|
+
status: {
|
|
40
|
+
schemaVersion: 'hadara.ops.status.v1',
|
|
41
|
+
command: 'ops.status',
|
|
42
|
+
ok: true,
|
|
43
|
+
health: 'loading',
|
|
44
|
+
project: { branch: '...', phase: 'loading read models' },
|
|
45
|
+
tasks: {
|
|
46
|
+
counts: { done: 0, draft: 0, partial: 0, superseded: 0, inProgress: 0, unknown: 0 },
|
|
47
|
+
rawStatusCounts: {},
|
|
48
|
+
normalizedStatusCounts: {},
|
|
49
|
+
lastCompleted: [],
|
|
50
|
+
nextRecommended: 'Reading HADARA project state...'
|
|
51
|
+
},
|
|
52
|
+
handoff: { currentState: [], knownProblems: [], nextRecommendedStep: [] },
|
|
53
|
+
validation: { latestFullCheck: 'loading', latestDoneLevelValidation: null },
|
|
54
|
+
activeRun: {
|
|
55
|
+
schemaVersion: 'hadara.active_run.projection.v1',
|
|
56
|
+
command: 'active-run.projection',
|
|
57
|
+
ok: true,
|
|
58
|
+
path: '.hadara/local/state/active-run.json',
|
|
59
|
+
activeRun: null,
|
|
60
|
+
handoff: { fresh: true, staleReason: null },
|
|
61
|
+
resume: null,
|
|
62
|
+
issues: []
|
|
63
|
+
},
|
|
64
|
+
debt: { total: 0, open: 0, tracked: 0, mitigated: 0, candidate: 0, highOpen: 0, bySeverity: {} },
|
|
65
|
+
mcp: {
|
|
66
|
+
defaultMode: 'read-only',
|
|
67
|
+
evidenceAttach: { enabledByDefault: false, requiresFlag: '--enable-evidence-attach', requiresApproval: true, audited: true }
|
|
68
|
+
},
|
|
69
|
+
issues: []
|
|
70
|
+
},
|
|
71
|
+
tasks: {
|
|
72
|
+
schemaVersion: 'hadara.task.list.v1',
|
|
73
|
+
command: 'task.list',
|
|
74
|
+
ok: true,
|
|
75
|
+
count: 0,
|
|
76
|
+
tasks: [],
|
|
77
|
+
issues: []
|
|
78
|
+
},
|
|
79
|
+
selectedTask: null,
|
|
80
|
+
activeRun: {
|
|
81
|
+
projection: {
|
|
82
|
+
schemaVersion: 'hadara.active_run.projection.v1',
|
|
83
|
+
command: 'active-run.projection',
|
|
84
|
+
ok: true,
|
|
85
|
+
path: '.hadara/local/state/active-run.json',
|
|
86
|
+
activeRun: null,
|
|
87
|
+
handoff: { fresh: true, staleReason: null },
|
|
88
|
+
resume: null,
|
|
89
|
+
issues: []
|
|
90
|
+
},
|
|
91
|
+
resume: {
|
|
92
|
+
schemaVersion: 'hadara.active_run.resume.v1',
|
|
93
|
+
command: 'active-run.resume',
|
|
94
|
+
ok: true,
|
|
95
|
+
activeRun: null,
|
|
96
|
+
resumePrompt: {
|
|
97
|
+
summary: 'Reading active-run state...',
|
|
98
|
+
mustRead: [],
|
|
99
|
+
nextActions: [],
|
|
100
|
+
constraints: ['Read-only mode remains enforced.']
|
|
101
|
+
},
|
|
102
|
+
issues: []
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
debt: {
|
|
106
|
+
schemaVersion: 'hadara.operational_debt.list.v1',
|
|
107
|
+
command: 'debt.list',
|
|
108
|
+
ok: true,
|
|
109
|
+
aggregate: { total: 0, open: 0, tracked: 0, mitigated: 0, candidate: 0, highOpen: 0, bySeverity: {} },
|
|
110
|
+
records: [],
|
|
111
|
+
debts: [],
|
|
112
|
+
capsuleSizeIndicators: [],
|
|
113
|
+
issues: []
|
|
114
|
+
},
|
|
115
|
+
releaseGate: {
|
|
116
|
+
schemaVersion: 'hadara.releaseGate.v1',
|
|
117
|
+
command: 'release.gate',
|
|
118
|
+
ok: true,
|
|
119
|
+
mode: 'advisory',
|
|
120
|
+
checks: [],
|
|
121
|
+
issues: []
|
|
122
|
+
},
|
|
123
|
+
tools: {
|
|
124
|
+
schemaVersion: 'hadara.tools.list.v1',
|
|
125
|
+
command: 'tools.list',
|
|
126
|
+
ok: true,
|
|
127
|
+
tools: [],
|
|
128
|
+
surfaces: [],
|
|
129
|
+
disabled: [],
|
|
130
|
+
issues: []
|
|
131
|
+
},
|
|
132
|
+
writePreview: {
|
|
133
|
+
schemaVersion: 'hadara.write.preflight.v1',
|
|
134
|
+
command: 'unknown',
|
|
135
|
+
ok: true,
|
|
136
|
+
writes: [],
|
|
137
|
+
risk: 'low',
|
|
138
|
+
requiresApproval: false,
|
|
139
|
+
workspaceBoundary: 'project',
|
|
140
|
+
issues: []
|
|
141
|
+
},
|
|
142
|
+
issues: []
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function createTuiReadModel(projectRoot, options = {}) {
|
|
146
|
+
if (options.profile === 'fast')
|
|
147
|
+
return createTuiFastReadModel(projectRoot, options);
|
|
148
|
+
const status = (0, operations_status_service_1.createOpsStatusReport)(projectRoot);
|
|
149
|
+
const tasks = (0, task_read_model_1.createTaskListReport)(projectRoot);
|
|
150
|
+
const selectedTaskId = resolveSelectedTaskId(tasks.tasks, status, options.selectedTaskId);
|
|
151
|
+
const selectedSummary = selectedTaskId ? tasks.tasks.find((task) => task.id === selectedTaskId) ?? null : null;
|
|
152
|
+
const selectedTask = selectedSummary
|
|
153
|
+
? {
|
|
154
|
+
summary: selectedSummary,
|
|
155
|
+
detail: (0, task_read_model_1.createTaskReadReport)(projectRoot, selectedSummary.id, { includePrivate: options.includePrivateEvidence }),
|
|
156
|
+
evidence: (0, evidence_list_1.createEvidenceListReport)(projectRoot, {
|
|
157
|
+
taskId: selectedSummary.id,
|
|
158
|
+
limit: options.evidenceLimit ?? DEFAULT_EVIDENCE_LIMIT,
|
|
159
|
+
includePrivate: options.includePrivateEvidence
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
: null;
|
|
163
|
+
const activeRunResume = (0, active_run_state_1.createActiveRunResumeReport)(projectRoot);
|
|
164
|
+
const debt = (0, operational_debt_1.createOperationalDebtReport)(projectRoot);
|
|
165
|
+
const releaseGate = (0, operational_debt_1.createReleaseGateReport)(projectRoot, 'advisory');
|
|
166
|
+
const tools = (0, tools_list_1.createToolsListReport)();
|
|
167
|
+
const writePreview = (0, write_preflight_1.createWritePreflightReport)(projectRoot, ['task', 'create', options.writePreviewTitle ?? DEFAULT_WRITE_PREVIEW_TITLE]);
|
|
168
|
+
const overview = createOverview(projectRoot, tasks.tasks, selectedTask, status, options.includePrivateEvidence);
|
|
169
|
+
const issues = collectIssues({
|
|
170
|
+
status,
|
|
171
|
+
activeRunResume,
|
|
172
|
+
selectedTask,
|
|
173
|
+
releaseGate,
|
|
174
|
+
writePreview,
|
|
175
|
+
selectedTaskId,
|
|
176
|
+
explicitSelectedTaskId: options.selectedTaskId ?? null
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
schemaVersion: 'hadara.tui.read_model.internal.v1',
|
|
180
|
+
command: 'tui.read-model',
|
|
181
|
+
ok: !issues.some((issue) => issue.severity === 'error'),
|
|
182
|
+
generatedAt: new Date().toISOString(),
|
|
183
|
+
selectedTaskId,
|
|
184
|
+
overview,
|
|
185
|
+
status,
|
|
186
|
+
tasks,
|
|
187
|
+
selectedTask,
|
|
188
|
+
activeRun: {
|
|
189
|
+
projection: status.activeRun,
|
|
190
|
+
resume: activeRunResume
|
|
191
|
+
},
|
|
192
|
+
debt,
|
|
193
|
+
releaseGate,
|
|
194
|
+
tools,
|
|
195
|
+
writePreview,
|
|
196
|
+
issues
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function createTuiFastReadModel(projectRoot, options = {}) {
|
|
200
|
+
const sources = (0, project_read_model_1.readProjectSources)(projectRoot);
|
|
201
|
+
const tasks = (0, task_read_model_1.createTaskListReport)(projectRoot);
|
|
202
|
+
const activeRunProjection = (0, active_run_state_2.safeCreateActiveRunProjection)(projectRoot);
|
|
203
|
+
const status = createFastOpsStatusReport(projectRoot, sources, tasks, activeRunProjection);
|
|
204
|
+
const selectedTaskId = resolveSelectedTaskId(tasks.tasks, status, options.selectedTaskId);
|
|
205
|
+
const selectedSummary = selectedTaskId ? tasks.tasks.find((task) => task.id === selectedTaskId) ?? null : null;
|
|
206
|
+
const selectedTask = selectedSummary
|
|
207
|
+
? {
|
|
208
|
+
summary: selectedSummary,
|
|
209
|
+
detail: (0, task_read_model_1.createTaskReadReport)(projectRoot, selectedSummary.id, { includePrivate: options.includePrivateEvidence }),
|
|
210
|
+
evidence: (0, evidence_list_1.createEvidenceListReport)(projectRoot, {
|
|
211
|
+
taskId: selectedSummary.id,
|
|
212
|
+
limit: options.evidenceLimit ?? DEFAULT_EVIDENCE_LIMIT,
|
|
213
|
+
includePrivate: options.includePrivateEvidence
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
: null;
|
|
217
|
+
const activeRunResume = (0, active_run_state_1.createActiveRunResumeReport)(projectRoot);
|
|
218
|
+
const deferredIssue = {
|
|
219
|
+
source: 'tui-read-model',
|
|
220
|
+
severity: 'warning',
|
|
221
|
+
code: 'TUI_HEAVY_READS_DEFERRED',
|
|
222
|
+
message: 'TUI fast read model deferred debt, release-gate, tools, and write-preflight reads.'
|
|
223
|
+
};
|
|
224
|
+
const issues = [
|
|
225
|
+
...collectIssues({
|
|
226
|
+
status,
|
|
227
|
+
activeRunResume,
|
|
228
|
+
selectedTask,
|
|
229
|
+
releaseGate: createDeferredReleaseGateReport(),
|
|
230
|
+
writePreview: createDeferredWritePreflightReport(),
|
|
231
|
+
selectedTaskId,
|
|
232
|
+
explicitSelectedTaskId: options.selectedTaskId ?? null
|
|
233
|
+
}),
|
|
234
|
+
deferredIssue
|
|
235
|
+
];
|
|
236
|
+
return {
|
|
237
|
+
schemaVersion: 'hadara.tui.read_model.internal.v1',
|
|
238
|
+
command: 'tui.read-model',
|
|
239
|
+
ok: !issues.some((issue) => issue.severity === 'error'),
|
|
240
|
+
generatedAt: new Date().toISOString(),
|
|
241
|
+
selectedTaskId,
|
|
242
|
+
overview: createOverview(projectRoot, tasks.tasks, selectedTask, status, options.includePrivateEvidence),
|
|
243
|
+
status,
|
|
244
|
+
tasks,
|
|
245
|
+
selectedTask,
|
|
246
|
+
activeRun: {
|
|
247
|
+
projection: status.activeRun,
|
|
248
|
+
resume: activeRunResume
|
|
249
|
+
},
|
|
250
|
+
debt: createDeferredDebtReport(),
|
|
251
|
+
releaseGate: createDeferredReleaseGateReport(),
|
|
252
|
+
tools: createDeferredToolsListReport(),
|
|
253
|
+
writePreview: createDeferredWritePreflightReport(),
|
|
254
|
+
issues
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function createFastOpsStatusReport(projectRoot, sources, tasks, activeRun) {
|
|
258
|
+
const counts = countTaskStatuses(tasks.tasks);
|
|
259
|
+
const validation = {
|
|
260
|
+
latestFullCheck: extractValidationLine(sources.handoff.content, 'Latest full check') ?? extractValidationHistoryLine(sources.validationHistory.content, 'Docker check'),
|
|
261
|
+
latestDoneLevelValidation: extractValidationLine(sources.handoff.content, 'Latest done-level validation') ??
|
|
262
|
+
extractValidationHistoryLine(sources.validationHistory.content, 'harness validate')
|
|
263
|
+
};
|
|
264
|
+
const issues = [];
|
|
265
|
+
if (!sources.projectState.exists)
|
|
266
|
+
issues.push({ severity: 'warning', code: 'PROJECT_STATE_MISSING', message: 'docs/PROJECT_STATE.md is missing.' });
|
|
267
|
+
if (!sources.handoff.exists)
|
|
268
|
+
issues.push({ severity: 'warning', code: 'AGENT_HANDOFF_MISSING', message: 'docs/AGENT_HANDOFF.md is missing.' });
|
|
269
|
+
if (!sources.taskBoard.exists)
|
|
270
|
+
issues.push({ severity: 'warning', code: 'TASK_BOARD_MISSING', message: 'docs/TASK_BOARD.md is missing.' });
|
|
271
|
+
if (!sources.developmentSlices.exists)
|
|
272
|
+
issues.push({ severity: 'warning', code: 'DEVELOPMENT_SLICES_MISSING', message: 'docs/DEVELOPMENT_SLICES.md is missing.' });
|
|
273
|
+
if (!validation.latestFullCheck && !validation.latestDoneLevelValidation) {
|
|
274
|
+
issues.push({ severity: 'warning', code: 'VALIDATION_BASELINE_MISSING', message: 'No latest validation baseline was found in handoff or validation history.' });
|
|
275
|
+
}
|
|
276
|
+
issues.push(...activeRun.issues);
|
|
277
|
+
return {
|
|
278
|
+
schemaVersion: 'hadara.ops.status.v1',
|
|
279
|
+
command: 'ops.status',
|
|
280
|
+
ok: true,
|
|
281
|
+
health: issues.some((issue) => issue.severity === 'error') ? 'error' : issues.length > 0 ? 'degraded' : 'ok',
|
|
282
|
+
project: {
|
|
283
|
+
branch: readGitBranch(projectRoot),
|
|
284
|
+
phase: extractProjectPhase(sources.projectState.content)
|
|
285
|
+
},
|
|
286
|
+
tasks: {
|
|
287
|
+
counts: counts.counts,
|
|
288
|
+
rawStatusCounts: counts.rawStatusCounts,
|
|
289
|
+
normalizedStatusCounts: counts.normalizedStatusCounts,
|
|
290
|
+
lastCompleted: extractLastCompletedTaskIds(sources.handoff.content),
|
|
291
|
+
nextRecommended: extractListSection(sources.handoff.content, '## Next Recommended Step')[0] ?? null
|
|
292
|
+
},
|
|
293
|
+
handoff: {
|
|
294
|
+
currentState: extractListSection(sources.handoff.content, '## Current State'),
|
|
295
|
+
knownProblems: extractListSection(sources.handoff.content, '## Current Known Problems'),
|
|
296
|
+
nextRecommendedStep: extractListSection(sources.handoff.content, '## Next Recommended Step')
|
|
297
|
+
},
|
|
298
|
+
validation,
|
|
299
|
+
activeRun,
|
|
300
|
+
debt: { total: 0, open: 0, tracked: 0, mitigated: 0, candidate: 0, highOpen: 0, bySeverity: { high: 0, medium: 0, low: 0 } },
|
|
301
|
+
mcp: {
|
|
302
|
+
defaultMode: 'read-only',
|
|
303
|
+
evidenceAttach: {
|
|
304
|
+
enabledByDefault: false,
|
|
305
|
+
requiresFlag: '--enable-evidence-attach',
|
|
306
|
+
requiresApproval: true,
|
|
307
|
+
audited: true
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
issues
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function resolveSelectedTaskId(tasks, status, explicitSelectedTaskId) {
|
|
314
|
+
if (explicitSelectedTaskId)
|
|
315
|
+
return explicitSelectedTaskId;
|
|
316
|
+
const activeTaskId = status.activeRun.activeRun?.taskId;
|
|
317
|
+
if (activeTaskId && tasks.some((task) => task.id === activeTaskId))
|
|
318
|
+
return activeTaskId;
|
|
319
|
+
return tasks.at(-1)?.id ?? null;
|
|
320
|
+
}
|
|
321
|
+
function createOverview(projectRoot, tasks, selectedTask, status, includePrivateEvidence) {
|
|
322
|
+
const latestRows = [...tasks].reverse();
|
|
323
|
+
const currentWork = latestRows[0] ?? null;
|
|
324
|
+
const previousWork = latestRows[1] ?? null;
|
|
325
|
+
const readDetail = (task) => {
|
|
326
|
+
if (!task)
|
|
327
|
+
return null;
|
|
328
|
+
if (selectedTask?.summary.id === task.id)
|
|
329
|
+
return selectedTask.detail;
|
|
330
|
+
return (0, task_read_model_1.createTaskReadReport)(projectRoot, task.id, { includePrivate: includePrivateEvidence });
|
|
331
|
+
};
|
|
332
|
+
return {
|
|
333
|
+
currentWork,
|
|
334
|
+
previousWork,
|
|
335
|
+
currentDetail: readDetail(currentWork),
|
|
336
|
+
previousDetail: readDetail(previousWork),
|
|
337
|
+
health: status.health,
|
|
338
|
+
phase: status.project.phase,
|
|
339
|
+
branch: status.project.branch
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function collectIssues(input) {
|
|
343
|
+
const issues = [
|
|
344
|
+
...input.status.issues.map((issue) => ({ source: 'status', ...issue })),
|
|
345
|
+
...input.activeRunResume.issues.map((issue) => ({ source: 'active-run-resume', ...issue })),
|
|
346
|
+
...input.releaseGate.issues.map((issue) => ({ source: 'release-gate', ...issue })),
|
|
347
|
+
...input.writePreview.issues.map((issue) => ({ source: 'write-preflight', ...issue }))
|
|
348
|
+
];
|
|
349
|
+
if (input.selectedTask) {
|
|
350
|
+
issues.push(...input.selectedTask.detail.issues.map((issue) => ({ source: 'task-detail', ...issue })));
|
|
351
|
+
issues.push(...input.selectedTask.evidence.issues.map((issue) => ({ source: 'evidence', ...issue })));
|
|
352
|
+
}
|
|
353
|
+
else if (input.explicitSelectedTaskId || input.selectedTaskId) {
|
|
354
|
+
const taskId = input.explicitSelectedTaskId ?? input.selectedTaskId ?? '(unknown)';
|
|
355
|
+
issues.push({
|
|
356
|
+
source: 'tui-read-model',
|
|
357
|
+
severity: 'error',
|
|
358
|
+
code: 'TUI_SELECTED_TASK_NOT_FOUND',
|
|
359
|
+
message: `Selected Task Capsule not found: ${taskId}`
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return issues;
|
|
363
|
+
}
|
|
364
|
+
function countTaskStatuses(tasks) {
|
|
365
|
+
const counts = {
|
|
366
|
+
done: 0,
|
|
367
|
+
draft: 0,
|
|
368
|
+
partial: 0,
|
|
369
|
+
superseded: 0,
|
|
370
|
+
inProgress: 0,
|
|
371
|
+
unknown: 0
|
|
372
|
+
};
|
|
373
|
+
const rawStatusCounts = {};
|
|
374
|
+
const normalizedStatusCounts = {};
|
|
375
|
+
for (const task of tasks) {
|
|
376
|
+
const rawStatus = task.status || 'Unknown';
|
|
377
|
+
const normalizedStatus = normalizeStatus(rawStatus);
|
|
378
|
+
const aggregate = aggregateStatus(normalizedStatus);
|
|
379
|
+
counts[aggregate] += 1;
|
|
380
|
+
rawStatusCounts[rawStatus] = (rawStatusCounts[rawStatus] ?? 0) + 1;
|
|
381
|
+
normalizedStatusCounts[normalizedStatus] = (normalizedStatusCounts[normalizedStatus] ?? 0) + 1;
|
|
382
|
+
}
|
|
383
|
+
return { counts, rawStatusCounts, normalizedStatusCounts };
|
|
384
|
+
}
|
|
385
|
+
function readGitBranch(projectRoot) {
|
|
386
|
+
const headPath = node_path_1.default.join(projectRoot, '.git', 'HEAD');
|
|
387
|
+
if (!node_fs_1.default.existsSync(headPath))
|
|
388
|
+
return 'unknown';
|
|
389
|
+
const head = node_fs_1.default.readFileSync(headPath, 'utf8').trim();
|
|
390
|
+
const refPrefix = 'ref: refs/heads/';
|
|
391
|
+
if (head.startsWith(refPrefix))
|
|
392
|
+
return head.slice(refPrefix.length);
|
|
393
|
+
return head.length > 0 ? 'detached' : 'unknown';
|
|
394
|
+
}
|
|
395
|
+
function extractProjectPhase(projectState) {
|
|
396
|
+
const section = (0, project_read_model_1.extractSection)(projectState, '## Current Phase');
|
|
397
|
+
const line = section
|
|
398
|
+
.split(/\r?\n/)
|
|
399
|
+
.map((value) => value.trim())
|
|
400
|
+
.find(Boolean);
|
|
401
|
+
if (!line)
|
|
402
|
+
return 'unknown';
|
|
403
|
+
const explicit = line.match(/^Phase:\s*(.+)$/i);
|
|
404
|
+
if (explicit)
|
|
405
|
+
return explicit[1].trim();
|
|
406
|
+
if (/Phase 0\s*\/\s*Phase 1 boundary/i.test(line))
|
|
407
|
+
return 'bootstrap-development';
|
|
408
|
+
return line;
|
|
409
|
+
}
|
|
410
|
+
function extractListSection(content, heading) {
|
|
411
|
+
const section = (0, project_read_model_1.extractSection)(content, heading);
|
|
412
|
+
return section
|
|
413
|
+
.split(/\r?\n/)
|
|
414
|
+
.map((line) => line.trim().replace(/^[-*]\s+/, ''))
|
|
415
|
+
.filter(Boolean);
|
|
416
|
+
}
|
|
417
|
+
function extractLastCompletedTaskIds(handoff) {
|
|
418
|
+
return extractListSection(handoff, '## Last 3 Completed Tasks')
|
|
419
|
+
.map((line) => line.match(/^(T-\d{4})\b/)?.[1])
|
|
420
|
+
.filter((value) => Boolean(value));
|
|
421
|
+
}
|
|
422
|
+
function extractValidationLine(handoff, label) {
|
|
423
|
+
const validation = extractListSection(handoff, '## Validation Baseline');
|
|
424
|
+
const prefix = `${label}:`;
|
|
425
|
+
const line = validation.find((item) => item.startsWith(prefix));
|
|
426
|
+
return line ? line.slice(prefix.length).trim().replace(/\.$/, '') : null;
|
|
427
|
+
}
|
|
428
|
+
function extractValidationHistoryLine(validationHistory, pattern) {
|
|
429
|
+
const lines = validationHistory
|
|
430
|
+
.split(/\r?\n/)
|
|
431
|
+
.map((line) => line.trim().replace(/^[-*]\s+/, ''))
|
|
432
|
+
.filter((line) => line.includes(pattern));
|
|
433
|
+
return lines.at(-1)?.replace(/\.$/, '') ?? null;
|
|
434
|
+
}
|
|
435
|
+
function normalizeStatus(status) {
|
|
436
|
+
const value = status.trim().toLowerCase().replace(/[\s_-]+(.)/g, (_match, letter) => letter.toUpperCase());
|
|
437
|
+
return value || 'unknown';
|
|
438
|
+
}
|
|
439
|
+
function aggregateStatus(status) {
|
|
440
|
+
if (status === 'done')
|
|
441
|
+
return 'done';
|
|
442
|
+
if (status === 'draft')
|
|
443
|
+
return 'draft';
|
|
444
|
+
if (status === 'partial')
|
|
445
|
+
return 'partial';
|
|
446
|
+
if (status === 'superseded')
|
|
447
|
+
return 'superseded';
|
|
448
|
+
if (status === 'inProgress' || status === 'active' || status === 'doing')
|
|
449
|
+
return 'inProgress';
|
|
450
|
+
return 'unknown';
|
|
451
|
+
}
|
|
452
|
+
function createDeferredDebtReport() {
|
|
453
|
+
return {
|
|
454
|
+
schemaVersion: 'hadara.operational_debt.v1',
|
|
455
|
+
command: 'operational-debt.report',
|
|
456
|
+
ok: true,
|
|
457
|
+
records: [],
|
|
458
|
+
aggregate: { total: 0, open: 0, tracked: 0, mitigated: 0, candidate: 0, highOpen: 0, bySeverity: { high: 0, medium: 0, low: 0 } },
|
|
459
|
+
capsuleSizeIndicators: [],
|
|
460
|
+
issues: []
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function createDeferredReleaseGateReport() {
|
|
464
|
+
return {
|
|
465
|
+
schemaVersion: 'hadara.releaseGate.v1',
|
|
466
|
+
command: 'release.gate',
|
|
467
|
+
mode: 'advisory',
|
|
468
|
+
ok: true,
|
|
469
|
+
checks: [
|
|
470
|
+
{
|
|
471
|
+
code: 'TUI_FAST_RELEASE_GATE_DEFERRED',
|
|
472
|
+
name: 'Deferred release-gate check',
|
|
473
|
+
status: 'warning',
|
|
474
|
+
summary: 'Release-gate debt scan is deferred in the TUI fast read model.'
|
|
475
|
+
}
|
|
476
|
+
],
|
|
477
|
+
issues: []
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function createDeferredToolsListReport() {
|
|
481
|
+
return {
|
|
482
|
+
schemaVersion: 'hadara.tools.list.v1',
|
|
483
|
+
command: 'tools.list',
|
|
484
|
+
ok: true,
|
|
485
|
+
tools: [],
|
|
486
|
+
surfaces: [],
|
|
487
|
+
disabled: [],
|
|
488
|
+
issues: []
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function createDeferredWritePreflightReport() {
|
|
492
|
+
return {
|
|
493
|
+
schemaVersion: 'hadara.write.preflight.v1',
|
|
494
|
+
command: 'unknown',
|
|
495
|
+
ok: true,
|
|
496
|
+
writes: [],
|
|
497
|
+
risk: 'low',
|
|
498
|
+
requiresApproval: false,
|
|
499
|
+
workspaceBoundary: 'project',
|
|
500
|
+
issues: []
|
|
501
|
+
};
|
|
502
|
+
}
|