hadara 0.3.4-rc.0 → 0.4.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/README.md +29 -29
- package/dist/cli/dashboard.js +16 -4
- package/dist/cli/docs.js +50 -0
- package/dist/cli/doctor.js +74 -3
- package/dist/cli/evidence-json.js +3 -0
- package/dist/cli/evidence.js +72 -0
- package/dist/cli/help.js +4 -3
- package/dist/cli/init.js +608 -122
- package/dist/cli/legacy-boundary.js +77 -0
- package/dist/cli/main.js +28 -0
- package/dist/cli/package-smoke.js +1 -0
- package/dist/cli/release-artifact.js +10 -1
- package/dist/cli/release-publish.js +9 -0
- package/dist/cli/task.js +142 -24
- package/dist/cli/validation.js +81 -0
- package/dist/context/context-pack.js +113 -19
- package/dist/context/session-start.js +61 -21
- package/dist/context/task-extractors.js +3 -2
- package/dist/core/schema.js +14 -0
- package/dist/core/timing.js +12 -0
- package/dist/dev/docker-check.js +3 -2
- package/dist/evidence/evidence.js +134 -11
- package/dist/evidence/semantics.js +3 -1
- package/dist/handoff/handoff-stale-problems.js +6 -1
- package/dist/handoff/handoff-suggestion.js +7 -1
- package/dist/handoff/handoff.js +1 -0
- package/dist/harness/validate.js +434 -72
- package/dist/schemas/close-source.schema.json +33 -0
- package/dist/schemas/docs-inbox.schema.json +50 -0
- package/dist/schemas/docs-read-map.schema.json +83 -0
- package/dist/schemas/docs-register.schema.json +70 -0
- package/dist/schemas/evidence-projection.schema.json +35 -0
- package/dist/schemas/schema-index.json +51 -2
- package/dist/schemas/task-audit-close.schema.json +7 -0
- package/dist/schemas/task-close.schema.json +4 -0
- package/dist/schemas/task-finalize.schema.json +40 -1
- package/dist/schemas/task-status.schema.json +117 -0
- package/dist/schemas/task-workbench.schema.json +97 -2
- package/dist/schemas/validation-run.schema.json +103 -0
- package/dist/services/capability-registry.js +216 -51
- package/dist/services/ci-gate.js +5 -2
- package/dist/services/clean-checkout-smoke.js +4 -2
- package/dist/services/dashboard-bootstrap.js +19 -4
- package/dist/services/dashboard-cache.js +2 -0
- package/dist/services/dashboard-task-detail.js +43 -13
- package/dist/services/dashboard-timeline.js +2 -2
- package/dist/services/docs-registry.js +485 -30
- package/dist/services/evidence-lint.js +9 -3
- package/dist/services/lifecycle-guide.js +11 -12
- package/dist/services/operational-debt.js +8 -14
- package/dist/services/operations-status-service.js +7 -1
- package/dist/services/package-recycle.js +101 -45
- package/dist/services/package-smoke.js +7 -2
- package/dist/services/proof-status.js +2 -0
- package/dist/services/protocol-consistency.js +13 -6
- package/dist/services/protocol-migration.js +2 -1
- package/dist/services/release-artifact.js +3 -2
- package/dist/services/release-closeout.js +2 -1
- package/dist/services/release-diagnostics.js +5 -4
- package/dist/services/release-dry-run.js +3 -2
- package/dist/services/state-projection.js +31 -16
- package/dist/services/task-read-model.js +7 -7
- package/dist/services/task-workbench.js +598 -29
- package/dist/services/validation-run.js +333 -0
- package/dist/services/workbench-next-actions.js +31 -20
- package/dist/services/write-preflight.js +2 -20
- package/dist/task/acceptance.js +27 -5
- package/dist/task/authoring-guidance.js +76 -0
- package/dist/task/lifecycle-next-actions.js +0 -1
- package/dist/task/task-capsule.js +24 -18
- package/dist/task/task-close-repair-plan.js +3 -2
- package/dist/task/task-close.js +319 -39
- package/dist/task/task-complete-flow.js +4 -3
- package/dist/task/task-create.js +1 -0
- package/dist/task/task-finalize.js +161 -28
- package/dist/task/task-finish.js +36 -8
- package/dist/task/task-lifecycle.js +8 -3
- package/dist/task/task-templates.js +5 -24
- package/dist/task/task-upgrade-scaffold.js +11 -60
- package/dist/tui/constants.js +1 -7
- package/dist/tui/read-model.js +6 -4
- package/dist/tui/snapshot.js +14 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<img alt="Stable npm release" src="https://img.shields.io/badge/npm-0.
|
|
9
|
-
<img alt="
|
|
8
|
+
<img alt="Stable npm release" src="https://img.shields.io/badge/npm-0.4.0-blue">
|
|
9
|
+
<img alt="Next npm release" src="https://img.shields.io/badge/next-0.4.0--rc.0-blue">
|
|
10
10
|
<img alt="Node.js" src="https://img.shields.io/badge/node-%3E%3D22-brightgreen">
|
|
11
11
|
<img alt="License" src="https://img.shields.io/badge/license-MIT-lightgrey">
|
|
12
12
|
</p>
|
|
@@ -24,30 +24,30 @@ This repository is both the HADARA source checkout and the HADARA protocol works
|
|
|
24
24
|
Current stable npm release:
|
|
25
25
|
|
|
26
26
|
```text
|
|
27
|
-
hadara@0.
|
|
27
|
+
hadara@0.4.0
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Previous npm release candidate:
|
|
31
31
|
|
|
32
32
|
```text
|
|
33
|
-
hadara@0.
|
|
33
|
+
hadara@0.4.0-rc.0
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Previous
|
|
36
|
+
Previous agent UX release candidate:
|
|
37
37
|
|
|
38
38
|
```text
|
|
39
|
-
hadara@0.3.
|
|
39
|
+
hadara@0.3.4-rc.0
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
The 0.
|
|
42
|
+
The 0.4.0 source line is a breaking productization release after stable 0.3.3 and the published 0.3.4-rc.0 agent UX line. It keeps the context-routing and finalize-first lifecycle baseline, then simplifies new projects around a productized 0.4 scaffold, registry-backed docs read maps, a four-file Task Capsule, generated evidence projection, a normalized close-source contract, and fail-closed legacy mutation boundaries.
|
|
43
43
|
|
|
44
|
-
Phase labels are internal implementation phases, not npm release-candidate labels. The stable `0.
|
|
44
|
+
Phase labels are internal implementation phases, not npm release-candidate labels. The stable `0.4.0` package is the default install target through the `latest` dist-tag after the approval-gated publish completes; `0.4.0-rc.0` remains the previous release candidate on npm under the `next` dist-tag.
|
|
45
45
|
|
|
46
46
|
| Surface | Status |
|
|
47
47
|
|---|---|
|
|
48
|
-
| Current stable | [`hadara@0.
|
|
49
|
-
|
|
|
50
|
-
| Previous RC | [`hadara@0.3.
|
|
48
|
+
| Current stable | [`hadara@0.4.0`](docs/RELEASE_NOTES.md#040) |
|
|
49
|
+
| Previous RC | [`hadara@0.4.0-rc.0`](docs/RELEASE_NOTES.md#040-rc0) on `next` |
|
|
50
|
+
| Previous agent UX RC | [`hadara@0.3.4-rc.0`](docs/RELEASE_NOTES.md#034-rc0) |
|
|
51
51
|
| Previous stable | [`hadara@0.3.2`](docs/RELEASE_NOTES.md#032) |
|
|
52
52
|
| Historical RCs | See [Release Notes](docs/RELEASE_NOTES.md). |
|
|
53
53
|
| GitHub Release | Secondary target, approval-gated. |
|
|
@@ -55,7 +55,7 @@ Phase labels are internal implementation phases, not npm release-candidate label
|
|
|
55
55
|
| PyPI/Python package | `hadara==0.2.0rc1` published preview bridge. |
|
|
56
56
|
| Installer scripts / USB launchers | Deferred. |
|
|
57
57
|
|
|
58
|
-
No release command should publish, create a GitHub Release, build Docker images, upload artifacts, or load token values unless an operator explicitly approves the mutation path for the active release capsule. Stable `0.
|
|
58
|
+
No release command should publish, create a GitHub Release, build Docker images, upload artifacts, or load token values unless an operator explicitly approves the mutation path for the active release capsule. Stable `0.4.0` is the default npm package line after the T-0490 approval-gated publish completes; `0.4.0-rc.0` remains available for explicit RC evaluation through `hadara@next` or the exact version. The `v0.4.0-rc.0` GitHub Release exists as a draft prerelease; Docker/PyPI publish, installer execution, and MCP release/package execution remain separate explicit mutations.
|
|
59
59
|
|
|
60
60
|
## Install
|
|
61
61
|
|
|
@@ -64,7 +64,7 @@ Requires Node.js 22.
|
|
|
64
64
|
Install the stable release:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
npm install -g hadara@0.
|
|
67
|
+
npm install -g hadara@0.4.0
|
|
68
68
|
hadara help
|
|
69
69
|
hadara doctor --json
|
|
70
70
|
```
|
|
@@ -72,26 +72,26 @@ hadara doctor --json
|
|
|
72
72
|
Run without a global install:
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
npx hadara@0.
|
|
76
|
-
npx hadara@0.
|
|
75
|
+
npx hadara@0.4.0 help
|
|
76
|
+
npx hadara@0.4.0 doctor --json
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
Evaluate the previous
|
|
79
|
+
Evaluate the previous `0.4.0-rc.0` explicitly:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
npm install -g hadara@0.
|
|
83
|
-
npx hadara@0.
|
|
82
|
+
npm install -g hadara@0.4.0-rc.0
|
|
83
|
+
npx hadara@0.4.0-rc.0 help
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
For release or recycle evidence, prefer an isolated prefix install when PATH, global installs, or `npx` cache behavior may be stale:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
tmp="$(mktemp -d)"
|
|
90
|
-
npm --prefix "$tmp" install hadara@0.
|
|
90
|
+
npm --prefix "$tmp" install hadara@0.4.0
|
|
91
91
|
"$tmp/node_modules/.bin/hadara" version --json
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
`npx hadara@0.
|
|
94
|
+
`npx hadara@0.4.0 ...` remains convenient for normal use. The isolated installed-bin path is stronger proof that the published package installed and executed from the intended package tree.
|
|
95
95
|
|
|
96
96
|
## What HADARA Gives You
|
|
97
97
|
|
|
@@ -116,7 +116,7 @@ Ask the CLI for the workflow before choosing commands:
|
|
|
116
116
|
```bash
|
|
117
117
|
hadara help
|
|
118
118
|
hadara help lifecycle
|
|
119
|
-
hadara task
|
|
119
|
+
hadara task status --json
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
Use structured discovery when an agent or tool needs machine-readable command metadata:
|
|
@@ -129,16 +129,15 @@ hadara help command task.close
|
|
|
129
129
|
|
|
130
130
|
## Primary Capsule Lifecycle
|
|
131
131
|
|
|
132
|
-
The 0.
|
|
132
|
+
The 0.4 primary path is intentionally small. Agents should start with compact context and use `task finalize` as the default lifecycle close path:
|
|
133
133
|
|
|
134
134
|
```bash
|
|
135
|
-
hadara task
|
|
135
|
+
hadara task status --json
|
|
136
136
|
hadara session start --task T-XXXX --json
|
|
137
137
|
hadara task create "implement a focused change" --json
|
|
138
138
|
hadara task status --task T-XXXX --json
|
|
139
139
|
hadara evidence add-command --task T-XXXX --summary "..." --result passed --category validation --idempotency-key "command:T-XXXX:check" --json
|
|
140
140
|
# Finalize Task Capsule docs and tracked state docs before closing.
|
|
141
|
-
hadara task lifecycle --task T-XXXX --json
|
|
142
141
|
hadara task finalize --task T-XXXX --json
|
|
143
142
|
hadara task finalize --task T-XXXX --execute --plan-hash sha256:... --json
|
|
144
143
|
hadara handoff suggest --task T-XXXX --json
|
|
@@ -162,8 +161,9 @@ Deferred Evidence v2 scope is explicit: rebuild preview/execute, `check-id`, `su
|
|
|
162
161
|
Use read-only lifecycle diagnostics when you want a compact current-stage report, next recommended action, or close-proof repair explanation:
|
|
163
162
|
|
|
164
163
|
```bash
|
|
164
|
+
hadara task status --task T-XXXX --json
|
|
165
165
|
hadara task complete --task T-XXXX --json
|
|
166
|
-
hadara task lifecycle --task T-XXXX --json
|
|
166
|
+
hadara task lifecycle --task T-XXXX --json # compatibility
|
|
167
167
|
hadara task close-repair-plan --task T-XXXX --json
|
|
168
168
|
```
|
|
169
169
|
|
|
@@ -268,7 +268,7 @@ Dashboard, TUI, Hermes, MCP, installer, package, release, and run commands stay
|
|
|
268
268
|
|
|
269
269
|
## Safety Boundaries
|
|
270
270
|
|
|
271
|
-
HADARA 0.
|
|
271
|
+
HADARA 0.4.0 is not:
|
|
272
272
|
|
|
273
273
|
- a full agent runtime;
|
|
274
274
|
- Rack/enterprise behavior;
|
|
@@ -308,7 +308,7 @@ Portable/local state is not committed. Project docs, Task Capsules, and reduced
|
|
|
308
308
|
|
|
309
309
|
`evidence.jsonl` is the canonical Task Capsule evidence source. `EVIDENCE.md` is a non-canonical human summary that can help review validation history, but it must not be treated as the source of truth for rebuild, migration, or resolution logic.
|
|
310
310
|
|
|
311
|
-
0.
|
|
311
|
+
0.4.0 does not implement `hadara evidence rebuild --json` or an execute mode. Future rebuild work must first define whether a difference is formatting regeneration, managed-section drift, or data inconsistency before reporting `wouldChange`. Any later write-capable rebuild flow must be dry-run-first, reviewed, and before-hash guarded before it rewrites derived Markdown.
|
|
312
312
|
|
|
313
313
|
## Development / Contributing
|
|
314
314
|
|
|
@@ -321,7 +321,7 @@ hadara init --profile standard
|
|
|
321
321
|
hadara init --profile governed
|
|
322
322
|
```
|
|
323
323
|
|
|
324
|
-
Every profile generates
|
|
324
|
+
Every profile generates the 0.4 project scaffold with compact agent entry docs, a docs registry, a workflow guide, and the current context-aware finalize-first lifecycle loop plus low-level proof-boundary reference commands.
|
|
325
325
|
|
|
326
326
|
| Profile | Use When |
|
|
327
327
|
|---|---|
|
package/dist/cli/dashboard.js
CHANGED
|
@@ -95,7 +95,7 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
|
|
|
95
95
|
if (url.pathname === '/api/status') {
|
|
96
96
|
// Dashboard clients fetch operational debt separately; keep the status
|
|
97
97
|
// route cheap so a single slow debt scan cannot block first API paint.
|
|
98
|
-
return jsonResponse((
|
|
98
|
+
return jsonResponse(readCachedOpsStatus(projectRoot, false, url.searchParams.get('cache') === 'bypass'), headOnly);
|
|
99
99
|
}
|
|
100
100
|
if (url.pathname === '/api/dashboard/core') {
|
|
101
101
|
const bypassProjection = url.searchParams.get('cache') === 'bypass';
|
|
@@ -125,7 +125,12 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
|
|
|
125
125
|
if (selectedTaskId)
|
|
126
126
|
keyParts.push('selected', selectedTaskId);
|
|
127
127
|
const key = (0, dashboard_cache_1.createDashboardCacheKey)(projectRoot, ...keyParts);
|
|
128
|
-
const cached = (0, dashboard_cache_1.getOrCreateCachedReport)(key, { ttlMs: dashboard_cache_1.DASHBOARD_CACHE_TTLS.bootstrap, bypass: url.searchParams.get('cache') === 'bypass' }, () => (0, dashboard_bootstrap_1.createDashboardBootstrapReport)(projectRoot, {
|
|
128
|
+
const cached = (0, dashboard_cache_1.getOrCreateCachedReport)(key, { ttlMs: dashboard_cache_1.DASHBOARD_CACHE_TTLS.bootstrap, bypass: url.searchParams.get('cache') === 'bypass' }, () => (0, dashboard_bootstrap_1.createDashboardBootstrapReport)(projectRoot, {
|
|
129
|
+
tier,
|
|
130
|
+
...(selectedTaskId ? { selectedTaskId } : {}),
|
|
131
|
+
status: readCachedOpsStatus(projectRoot, tier === 'full', url.searchParams.get('cache') === 'bypass'),
|
|
132
|
+
tasks: readCachedTaskList(projectRoot, url.searchParams.get('cache') === 'bypass')
|
|
133
|
+
}));
|
|
129
134
|
return jsonResponse((0, dashboard_cache_1.withDashboardCacheMetadata)(cached.value, cached.cache), headOnly);
|
|
130
135
|
}
|
|
131
136
|
if (url.pathname === '/api/dashboard/task-detail') {
|
|
@@ -139,7 +144,7 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
|
|
|
139
144
|
if (url.pathname === '/api/dashboard/cache/status')
|
|
140
145
|
return jsonResponse((0, dashboard_cache_1.createDashboardCacheStatusReport)(), headOnly);
|
|
141
146
|
if (url.pathname === '/api/tasks')
|
|
142
|
-
return jsonResponse((
|
|
147
|
+
return jsonResponse(readCachedTaskList(projectRoot, url.searchParams.get('cache') === 'bypass'), headOnly);
|
|
143
148
|
if (url.pathname === '/api/active-run')
|
|
144
149
|
return jsonResponse((0, active_run_state_1.safeCreateActiveRunProjection)(projectRoot), headOnly);
|
|
145
150
|
if (url.pathname === '/api/debt') {
|
|
@@ -159,7 +164,8 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
|
|
|
159
164
|
const taskId = url.searchParams.get('taskId')?.trim();
|
|
160
165
|
if (!taskId)
|
|
161
166
|
return missingTaskId(headOnly);
|
|
162
|
-
|
|
167
|
+
const detail = url.searchParams.get('detail') === 'full' ? 'full' : 'fast';
|
|
168
|
+
return jsonResponse((0, task_workbench_1.createTaskWorkbenchReport)(projectRoot, taskId, new Date(), { detail }), headOnly);
|
|
163
169
|
}
|
|
164
170
|
if (url.pathname === '/api/evidence-lint') {
|
|
165
171
|
const taskId = url.searchParams.get('taskId')?.trim();
|
|
@@ -175,6 +181,12 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
|
|
|
175
181
|
}
|
|
176
182
|
return notFound();
|
|
177
183
|
}
|
|
184
|
+
function readCachedOpsStatus(projectRoot, includeDebt, bypass) {
|
|
185
|
+
return (0, dashboard_cache_1.getOrCreateCachedReport)((0, dashboard_cache_1.createDashboardCacheKey)(projectRoot, 'status', includeDebt ? 'full' : 'core'), { ttlMs: dashboard_cache_1.DASHBOARD_CACHE_TTLS.status, bypass }, () => (0, operations_status_service_1.createOpsStatusReport)(projectRoot, { includeDebt })).value;
|
|
186
|
+
}
|
|
187
|
+
function readCachedTaskList(projectRoot, bypass) {
|
|
188
|
+
return (0, dashboard_cache_1.getOrCreateCachedReport)((0, dashboard_cache_1.createDashboardCacheKey)(projectRoot, 'tasks'), { ttlMs: dashboard_cache_1.DASHBOARD_CACHE_TTLS.tasks, bypass }, () => (0, task_read_model_1.createTaskListReport)(projectRoot)).value;
|
|
189
|
+
}
|
|
178
190
|
function missingTaskId(headOnly) {
|
|
179
191
|
return jsonResponse({
|
|
180
192
|
schemaVersion: 'hadara.dashboard.api.error.v1',
|
package/dist/cli/docs.js
CHANGED
|
@@ -5,6 +5,7 @@ const args_1 = require("./args");
|
|
|
5
5
|
const docs_cleanup_1 = require("../services/docs-cleanup");
|
|
6
6
|
const docs_registry_1 = require("../services/docs-registry");
|
|
7
7
|
const managed_sections_1 = require("../services/managed-sections");
|
|
8
|
+
const legacy_boundary_1 = require("./legacy-boundary");
|
|
8
9
|
function handleDocsCommand(input) {
|
|
9
10
|
const sub = input.args[1];
|
|
10
11
|
if (sub === 'list') {
|
|
@@ -26,6 +27,39 @@ function handleDocsCommand(input) {
|
|
|
26
27
|
printReport(report, input.jsonOutput);
|
|
27
28
|
return true;
|
|
28
29
|
}
|
|
30
|
+
if (sub === 'read-map') {
|
|
31
|
+
const report = (0, docs_registry_1.createDocsReadMapReport)(input.projectRoot, (0, args_1.getRequiredStringOption)(input.args, '--task'));
|
|
32
|
+
printReport(report, input.jsonOutput);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (sub === 'inbox') {
|
|
36
|
+
const report = (0, docs_registry_1.createDocsInboxReport)(input.projectRoot);
|
|
37
|
+
printReport(report, input.jsonOutput);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (sub === 'register') {
|
|
41
|
+
if ((0, args_1.getFlag)(input.args, '--execute') && blockLegacyMutation(input, 'docs.register'))
|
|
42
|
+
return true;
|
|
43
|
+
const report = (0, docs_registry_1.createDocsRegisterReport)(input.projectRoot, {
|
|
44
|
+
documentPath: (0, args_1.getRequiredStringOption)(input.args, '--path'),
|
|
45
|
+
title: (0, args_1.getStringOption)(input.args, '--title'),
|
|
46
|
+
kind: (0, args_1.getStringOption)(input.args, '--kind'),
|
|
47
|
+
status: (0, args_1.getStringOption)(input.args, '--status'),
|
|
48
|
+
readWhen: (0, args_1.getStringOption)(input.args, '--read-when'),
|
|
49
|
+
readTier: (0, args_1.getStringOption)(input.args, '--read-tier'),
|
|
50
|
+
authority: (0, args_1.getStringOption)(input.args, '--authority'),
|
|
51
|
+
editPolicy: (0, args_1.getStringOption)(input.args, '--edit-policy'),
|
|
52
|
+
activeForTasks: splitCsv((0, args_1.getStringOption)(input.args, '--active-for-task')),
|
|
53
|
+
driftRisk: (0, args_1.getStringOption)(input.args, '--drift'),
|
|
54
|
+
driftReviewRequired: (0, args_1.getFlag)(input.args, '--drift-review-required'),
|
|
55
|
+
driftReason: (0, args_1.getStringOption)(input.args, '--drift-reason'),
|
|
56
|
+
requiredReading: (0, args_1.getFlag)(input.args, '--required-reading'),
|
|
57
|
+
requireExists: (0, args_1.getFlag)(input.args, '--require-exists'),
|
|
58
|
+
mode: (0, args_1.getFlag)(input.args, '--execute') ? 'execute' : 'dry-run'
|
|
59
|
+
});
|
|
60
|
+
printReport(report, input.jsonOutput);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
29
63
|
if (sub === 'managed') {
|
|
30
64
|
const managedSub = input.args[2];
|
|
31
65
|
if (managedSub === 'list') {
|
|
@@ -38,6 +72,8 @@ function handleDocsCommand(input) {
|
|
|
38
72
|
}
|
|
39
73
|
}
|
|
40
74
|
if (sub === 'patch') {
|
|
75
|
+
if ((0, args_1.getFlag)(input.args, '--execute') && blockLegacyMutation(input, 'docs.patch'))
|
|
76
|
+
return true;
|
|
41
77
|
const report = (0, managed_sections_1.createDocsPatchPlanReport)(input.projectRoot, {
|
|
42
78
|
targetPath: (0, args_1.getRequiredStringOption)(input.args, '--path'),
|
|
43
79
|
sectionId: (0, args_1.getRequiredStringOption)(input.args, '--section'),
|
|
@@ -72,6 +108,14 @@ function handleDocsCommand(input) {
|
|
|
72
108
|
}
|
|
73
109
|
return false;
|
|
74
110
|
}
|
|
111
|
+
function blockLegacyMutation(input, command) {
|
|
112
|
+
const report = (0, legacy_boundary_1.createLegacyMutationBlockedReport)(input.projectRoot, command);
|
|
113
|
+
if (!report)
|
|
114
|
+
return false;
|
|
115
|
+
(0, legacy_boundary_1.printLegacyMutationBlockedReport)(report, input.jsonOutput);
|
|
116
|
+
process.exitCode = 6;
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
75
119
|
function printReport(report, jsonOutput) {
|
|
76
120
|
if (jsonOutput) {
|
|
77
121
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -79,3 +123,9 @@ function printReport(report, jsonOutput) {
|
|
|
79
123
|
}
|
|
80
124
|
console.log(JSON.stringify(report, null, 2));
|
|
81
125
|
}
|
|
126
|
+
function splitCsv(value) {
|
|
127
|
+
if (value === undefined)
|
|
128
|
+
return undefined;
|
|
129
|
+
const items = value.split(',').map((item) => item.trim()).filter(Boolean);
|
|
130
|
+
return items.length > 0 ? items : undefined;
|
|
131
|
+
}
|
package/dist/cli/doctor.js
CHANGED
|
@@ -7,18 +7,33 @@ exports.createDoctorReport = createDoctorReport;
|
|
|
7
7
|
exports.formatDoctorReport = formatDoctorReport;
|
|
8
8
|
exports.handleDoctorCommand = handleDoctorCommand;
|
|
9
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
-
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
12
|
+
function createDoctorReport(paths, nodeVersion = process.version, options = {}) {
|
|
11
13
|
const checks = [
|
|
12
14
|
pathCheck('docs', paths.projectDocsDir),
|
|
13
15
|
pathCheck('tasks', paths.projectTasksDir),
|
|
14
16
|
pathCheck('project-context', `${paths.projectContextDir}/HADARA_CONTEXT.md`)
|
|
15
17
|
];
|
|
18
|
+
const executablePath = normalizeOptionalPath(options.cliEntry ?? process.argv[1] ?? null);
|
|
19
|
+
const resolvedExecutablePath = resolveExecutablePath(executablePath);
|
|
20
|
+
const packageRoot = findPackageRoot([resolvedExecutablePath, executablePath]);
|
|
16
21
|
return {
|
|
17
22
|
schemaVersion: 'hadara.doctor.v1',
|
|
18
23
|
command: 'doctor',
|
|
19
24
|
ok: checks.every((check) => check.status === 'ok'),
|
|
20
25
|
runtime: {
|
|
21
|
-
node: nodeVersion
|
|
26
|
+
node: nodeVersion,
|
|
27
|
+
nodePath: normalizePath(options.nodePath ?? process.execPath)
|
|
28
|
+
},
|
|
29
|
+
installation: {
|
|
30
|
+
executablePath,
|
|
31
|
+
resolvedExecutablePath,
|
|
32
|
+
packageRoot,
|
|
33
|
+
packageVersion: package_json_1.default.version,
|
|
34
|
+
registry: 'https://registry.npmjs.org',
|
|
35
|
+
installCommand: `npm install -g ${package_json_1.default.name}@${package_json_1.default.version}`,
|
|
36
|
+
latestInstallCommand: `npm install -g ${package_json_1.default.name}`
|
|
22
37
|
},
|
|
23
38
|
paths: {
|
|
24
39
|
portableRoot: paths.portableRoot,
|
|
@@ -35,11 +50,18 @@ function formatDoctorReport(report) {
|
|
|
35
50
|
`dataRoot: ${report.paths.dataRoot}`,
|
|
36
51
|
`projectRoot: ${report.paths.projectRoot}`,
|
|
37
52
|
`Node: ${report.runtime.node}`,
|
|
53
|
+
`Node path: ${report.runtime.nodePath}`,
|
|
54
|
+
'Install:',
|
|
55
|
+
` executable: ${report.installation.executablePath ?? 'unknown'}`,
|
|
56
|
+
` resolved: ${report.installation.resolvedExecutablePath ?? 'unknown'}`,
|
|
57
|
+
` package: ${report.installation.packageVersion}${report.installation.packageRoot ? ` at ${report.installation.packageRoot}` : ' (package root unknown)'}`,
|
|
58
|
+
` registry: ${report.installation.registry}`,
|
|
59
|
+
` install: ${report.installation.installCommand}`,
|
|
38
60
|
...report.checks.map((check) => `${formatCheckLabel(check.id)}${check.status}`)
|
|
39
61
|
].join('\n');
|
|
40
62
|
}
|
|
41
63
|
function handleDoctorCommand(input) {
|
|
42
|
-
const report = createDoctorReport(input.paths);
|
|
64
|
+
const report = createDoctorReport(input.paths, process.version, { cliEntry: process.argv[1] });
|
|
43
65
|
if (input.jsonOutput) {
|
|
44
66
|
console.log(JSON.stringify(report, null, 2));
|
|
45
67
|
}
|
|
@@ -57,6 +79,55 @@ function pathCheck(id, targetPath) {
|
|
|
57
79
|
path: targetPath
|
|
58
80
|
};
|
|
59
81
|
}
|
|
82
|
+
function normalizeOptionalPath(value) {
|
|
83
|
+
if (!value)
|
|
84
|
+
return null;
|
|
85
|
+
return normalizePath(value);
|
|
86
|
+
}
|
|
87
|
+
function normalizePath(value) {
|
|
88
|
+
return node_path_1.default.normalize(value);
|
|
89
|
+
}
|
|
90
|
+
function resolveExecutablePath(executablePath) {
|
|
91
|
+
if (!executablePath)
|
|
92
|
+
return null;
|
|
93
|
+
try {
|
|
94
|
+
return normalizePath(node_fs_1.default.realpathSync.native(executablePath));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function findPackageRoot(candidates) {
|
|
101
|
+
for (const candidate of candidates) {
|
|
102
|
+
const found = candidate ? findPackageRootFromFile(candidate) : null;
|
|
103
|
+
if (found)
|
|
104
|
+
return found;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
function findPackageRootFromFile(filePath) {
|
|
109
|
+
let current = node_fs_1.default.existsSync(filePath) && node_fs_1.default.statSync(filePath).isDirectory()
|
|
110
|
+
? filePath
|
|
111
|
+
: node_path_1.default.dirname(filePath);
|
|
112
|
+
while (true) {
|
|
113
|
+
const packagePath = node_path_1.default.join(current, 'package.json');
|
|
114
|
+
if (isHadaraPackageJson(packagePath))
|
|
115
|
+
return normalizePath(current);
|
|
116
|
+
const parent = node_path_1.default.dirname(current);
|
|
117
|
+
if (parent === current)
|
|
118
|
+
return null;
|
|
119
|
+
current = parent;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function isHadaraPackageJson(packagePath) {
|
|
123
|
+
try {
|
|
124
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(packagePath, 'utf8'));
|
|
125
|
+
return parsed.name === package_json_1.default.name;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
60
131
|
function formatCheckLabel(id) {
|
|
61
132
|
if (id === 'docs')
|
|
62
133
|
return 'docs/: ';
|
|
@@ -15,6 +15,7 @@ function createEvidenceCollectReport(projectRoot, input) {
|
|
|
15
15
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
16
16
|
command: 'evidence.collect',
|
|
17
17
|
ok: false,
|
|
18
|
+
taskId: input.taskId,
|
|
18
19
|
issues: [
|
|
19
20
|
{
|
|
20
21
|
severity: 'error',
|
|
@@ -49,6 +50,7 @@ function createEvidenceCollectReport(projectRoot, input) {
|
|
|
49
50
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
50
51
|
command: 'evidence.collect',
|
|
51
52
|
ok: false,
|
|
53
|
+
taskId: input.taskId,
|
|
52
54
|
issues: [
|
|
53
55
|
{
|
|
54
56
|
severity: 'error',
|
|
@@ -64,6 +66,7 @@ function createEvidenceCollectReport(projectRoot, input) {
|
|
|
64
66
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
65
67
|
command: 'evidence.collect',
|
|
66
68
|
ok: true,
|
|
69
|
+
taskId: input.taskId,
|
|
67
70
|
evidence: {
|
|
68
71
|
...appendResult.evidence,
|
|
69
72
|
markdownPath: toPortablePath(node_path_1.default.relative(projectRoot, appendResult.markdownPath)),
|
package/dist/cli/evidence.js
CHANGED
|
@@ -13,8 +13,13 @@ const evidence_list_1 = require("../services/evidence-list");
|
|
|
13
13
|
const evidence_migration_1 = require("../services/evidence-migration");
|
|
14
14
|
const evidence_summary_1 = require("../services/evidence-summary");
|
|
15
15
|
const args_1 = require("./args");
|
|
16
|
+
const legacy_boundary_1 = require("./legacy-boundary");
|
|
16
17
|
function handleEvidenceCommand(input) {
|
|
17
18
|
const sub = input.args[1];
|
|
19
|
+
if (hasHelpFlag(input.args)) {
|
|
20
|
+
console.log(renderEvidenceCommandHelp(sub));
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
18
23
|
if (sub === 'list') {
|
|
19
24
|
const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
|
|
20
25
|
const report = (0, evidence_list_1.createEvidenceListReport)(input.projectRoot, {
|
|
@@ -79,7 +84,24 @@ function handleEvidenceCommand(input) {
|
|
|
79
84
|
process.exitCode = 6;
|
|
80
85
|
return true;
|
|
81
86
|
}
|
|
87
|
+
if (sub === 'project') {
|
|
88
|
+
const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
|
|
89
|
+
const report = (0, evidence_1.createEvidenceProjectionReport)(input.projectRoot, taskId, (0, args_1.getFlag)(input.args, '--execute'));
|
|
90
|
+
if (input.jsonOutput) {
|
|
91
|
+
console.log(JSON.stringify(report, null, 2));
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
console.log(`[HADARA] evidence project ${taskId}: ${report.ok ? 'ok' : 'issues'} | mode ${report.mode} | wouldChange ${report.wouldChange}`);
|
|
95
|
+
for (const issue of report.issues)
|
|
96
|
+
console.log(`[${issue.severity}] ${issue.code}: ${issue.message}`);
|
|
97
|
+
}
|
|
98
|
+
if (!report.ok)
|
|
99
|
+
process.exitCode = 6;
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
82
102
|
if (sub === 'migrate') {
|
|
103
|
+
if ((0, args_1.getFlag)(input.args, '--execute') && blockLegacyMutation(input, 'evidence.migrate'))
|
|
104
|
+
return true;
|
|
83
105
|
const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
|
|
84
106
|
const report = (0, evidence_migration_1.createEvidenceMigrationPreviewReport)({
|
|
85
107
|
projectRoot: input.projectRoot,
|
|
@@ -103,6 +125,8 @@ function handleEvidenceCommand(input) {
|
|
|
103
125
|
return true;
|
|
104
126
|
}
|
|
105
127
|
if (sub === 'add-command') {
|
|
128
|
+
if (blockLegacyMutation(input, 'evidence.add-command'))
|
|
129
|
+
return true;
|
|
106
130
|
const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
|
|
107
131
|
const summary = (0, args_1.getStringOption)(input.args, '--summary') ?? 'Command completed.';
|
|
108
132
|
const outcome = parseOptionalEvidenceOutcome((0, args_1.getStringOption)(input.args, '--outcome'));
|
|
@@ -114,6 +138,7 @@ function handleEvidenceCommand(input) {
|
|
|
114
138
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
115
139
|
command: 'evidence.add-command',
|
|
116
140
|
ok: false,
|
|
141
|
+
taskId,
|
|
117
142
|
issues: [resultOutcomeIssue]
|
|
118
143
|
};
|
|
119
144
|
if (input.jsonOutput) {
|
|
@@ -158,6 +183,8 @@ function handleEvidenceCommand(input) {
|
|
|
158
183
|
}
|
|
159
184
|
if (sub !== 'collect')
|
|
160
185
|
return false;
|
|
186
|
+
if (blockLegacyMutation(input, 'evidence.collect'))
|
|
187
|
+
return true;
|
|
161
188
|
const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
|
|
162
189
|
const kind = parseEvidenceKind((0, args_1.getStringOption)(input.args, '--kind', 'note') ?? 'note');
|
|
163
190
|
const summary = (0, args_1.getStringOption)(input.args, '--summary') ?? 'Manual evidence collection placeholder.';
|
|
@@ -190,6 +217,51 @@ function handleEvidenceCommand(input) {
|
|
|
190
217
|
}
|
|
191
218
|
return true;
|
|
192
219
|
}
|
|
220
|
+
function hasHelpFlag(args) {
|
|
221
|
+
return args.includes('--help') || args.includes('-h');
|
|
222
|
+
}
|
|
223
|
+
function renderEvidenceCommandHelp(sub) {
|
|
224
|
+
if (sub === 'add-command') {
|
|
225
|
+
return [
|
|
226
|
+
'[HADARA] evidence add-command',
|
|
227
|
+
'',
|
|
228
|
+
'Usage:',
|
|
229
|
+
' hadara evidence add-command --task T-XXXX --summary "..." --result passed --category validation --json',
|
|
230
|
+
'',
|
|
231
|
+
'Records an already-run command result. It does not execute shell commands.',
|
|
232
|
+
'Options:',
|
|
233
|
+
' --task <id> Task Capsule id.',
|
|
234
|
+
' --summary <text> Human-readable result summary.',
|
|
235
|
+
' --result <value> Legacy-compatible result: passed, failed, blocked, unknown.',
|
|
236
|
+
' --outcome <value> Evidence v2 outcome.',
|
|
237
|
+
' --category <value> Evidence v2 category.',
|
|
238
|
+
' --resolves <id> Add a resolves:<id> tag. Repeatable.',
|
|
239
|
+
' --supersedes <id> Add a supersedes:<id> tag. Repeatable.',
|
|
240
|
+
' --idempotency-key <key> Reuse an existing keyed record instead of appending duplicates.',
|
|
241
|
+
' --private Store as private evidence metadata.'
|
|
242
|
+
].join('\n');
|
|
243
|
+
}
|
|
244
|
+
return [
|
|
245
|
+
'[HADARA] evidence',
|
|
246
|
+
'',
|
|
247
|
+
'Usage:',
|
|
248
|
+
' hadara evidence list --task T-XXXX --json',
|
|
249
|
+
' hadara evidence lint --task T-XXXX --json',
|
|
250
|
+
' hadara evidence summary --task T-XXXX --json',
|
|
251
|
+
' hadara evidence project --task T-XXXX [--execute] --json',
|
|
252
|
+
' hadara evidence add-command --task T-XXXX --summary "..." --result passed --json',
|
|
253
|
+
'',
|
|
254
|
+
'Use `hadara help command evidence.add-command` for registry metadata.'
|
|
255
|
+
].join('\n');
|
|
256
|
+
}
|
|
257
|
+
function blockLegacyMutation(input, command) {
|
|
258
|
+
const report = (0, legacy_boundary_1.createLegacyMutationBlockedReport)(input.projectRoot, command);
|
|
259
|
+
if (!report)
|
|
260
|
+
return false;
|
|
261
|
+
(0, legacy_boundary_1.printLegacyMutationBlockedReport)(report, input.jsonOutput);
|
|
262
|
+
process.exitCode = 6;
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
193
265
|
function persistedEvidenceId(record) {
|
|
194
266
|
return record.schemaVersion === 'hadara.evidence.v2' && record.id ? record.id : 'evidence.jsonl';
|
|
195
267
|
}
|
package/dist/cli/help.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.renderCommandHelp = renderCommandHelp;
|
|
|
7
7
|
exports.renderFamilyHelp = renderFamilyHelp;
|
|
8
8
|
const capability_registry_1 = require("../services/capability-registry");
|
|
9
9
|
const lifecycle_guide_1 = require("../services/lifecycle-guide");
|
|
10
|
-
const LIFECYCLE_ORDER = ['
|
|
10
|
+
const LIFECYCLE_ORDER = ['inspect', 'create', 'evidence', 'finalize', 'handoff'];
|
|
11
11
|
function handleHelpCommand(input) {
|
|
12
12
|
const topic = input.args[1];
|
|
13
13
|
if (!topic) {
|
|
@@ -51,7 +51,7 @@ function renderDefaultHelp() {
|
|
|
51
51
|
'',
|
|
52
52
|
'Start:',
|
|
53
53
|
' hadara help lifecycle',
|
|
54
|
-
' hadara task
|
|
54
|
+
' hadara task status --json',
|
|
55
55
|
' hadara task status --task T-XXXX --json',
|
|
56
56
|
'',
|
|
57
57
|
'Primary capsule lifecycle:',
|
|
@@ -74,7 +74,7 @@ function renderLifecycleHelp() {
|
|
|
74
74
|
const report = (0, lifecycle_guide_1.createLifecycleGuideReport)();
|
|
75
75
|
const diagnostics = (0, capability_registry_1.listCommandRegistryEntries)({ family: 'proof-diagnostics' }).filter((entry) => entry.requiredness === 'diagnostic');
|
|
76
76
|
const lines = [
|
|
77
|
-
'HADARA 0.
|
|
77
|
+
'HADARA 0.4 primary task lifecycle',
|
|
78
78
|
'',
|
|
79
79
|
'Primary capsule lifecycle:'
|
|
80
80
|
];
|
|
@@ -104,6 +104,7 @@ function renderCommandHelp(id) {
|
|
|
104
104
|
`Scope: ${entry.scope}`,
|
|
105
105
|
`Lifecycle stage: ${entry.lifecycleStage}`,
|
|
106
106
|
`Requiredness: ${entry.requiredness}`,
|
|
107
|
+
`Status: ${entry.status}`,
|
|
107
108
|
`Write boundary: ${entry.writeBoundary}`,
|
|
108
109
|
`Read-only: ${entry.readOnly ? 'yes' : 'no'}`,
|
|
109
110
|
`Risk: ${entry.risk}`,
|