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.
Files changed (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -0
  3. package/dist/agent/evidence.js +50 -0
  4. package/dist/agent/loop.js +124 -0
  5. package/dist/cli/args.js +70 -0
  6. package/dist/cli/dashboard.js +185 -0
  7. package/dist/cli/debt.js +41 -0
  8. package/dist/cli/doctor.js +68 -0
  9. package/dist/cli/errors.js +58 -0
  10. package/dist/cli/evidence-json.js +75 -0
  11. package/dist/cli/evidence.js +80 -0
  12. package/dist/cli/handoff.js +16 -0
  13. package/dist/cli/harness.js +57 -0
  14. package/dist/cli/hermes-json.js +31 -0
  15. package/dist/cli/hermes.js +28 -0
  16. package/dist/cli/init.js +142 -0
  17. package/dist/cli/install.js +34 -0
  18. package/dist/cli/main.js +216 -0
  19. package/dist/cli/mcp.js +15 -0
  20. package/dist/cli/package-smoke.js +37 -0
  21. package/dist/cli/policy-json.js +22 -0
  22. package/dist/cli/policy.js +43 -0
  23. package/dist/cli/release-artifact.js +47 -0
  24. package/dist/cli/release-dry-run.js +24 -0
  25. package/dist/cli/release-gate.js +28 -0
  26. package/dist/cli/release-publish.js +41 -0
  27. package/dist/cli/run-scaffold.js +68 -0
  28. package/dist/cli/run-state.js +41 -0
  29. package/dist/cli/run.js +191 -0
  30. package/dist/cli/smoke.js +58 -0
  31. package/dist/cli/status-json.js +6 -0
  32. package/dist/cli/status.js +26 -0
  33. package/dist/cli/task-json.js +8 -0
  34. package/dist/cli/task.js +64 -0
  35. package/dist/cli/tools.js +25 -0
  36. package/dist/cli/tui.js +72 -0
  37. package/dist/cli/write-preflight.js +27 -0
  38. package/dist/core/audit.js +41 -0
  39. package/dist/core/events.js +63 -0
  40. package/dist/core/fs.js +44 -0
  41. package/dist/core/paths.js +59 -0
  42. package/dist/core/redaction.js +178 -0
  43. package/dist/core/schema.js +253 -0
  44. package/dist/core/workspace.js +47 -0
  45. package/dist/evidence/evidence.js +170 -0
  46. package/dist/evidence/private-manifest.js +101 -0
  47. package/dist/handoff/handoff.js +49 -0
  48. package/dist/harness/replay.js +200 -0
  49. package/dist/harness/validate.js +465 -0
  50. package/dist/hermes/context-export.js +104 -0
  51. package/dist/index.js +29 -0
  52. package/dist/mcp/server.js +104 -0
  53. package/dist/mcp/tool-dispatch.js +159 -0
  54. package/dist/mcp/tool-registry.js +150 -0
  55. package/dist/mcp/tool-schemas.js +18 -0
  56. package/dist/policy/command-risk.js +39 -0
  57. package/dist/policy/permission-matrix.js +42 -0
  58. package/dist/policy/policy.js +20 -0
  59. package/dist/policy/preflight.js +47 -0
  60. package/dist/policy/presets.js +24 -0
  61. package/dist/policy/tokenizer.js +53 -0
  62. package/dist/providers/fallback-executor.js +46 -0
  63. package/dist/providers/mock-provider.js +49 -0
  64. package/dist/providers/provider-contract.js +2 -0
  65. package/dist/providers/provider-preparation.js +220 -0
  66. package/dist/providers/scripted-provider.js +69 -0
  67. package/dist/schemas/active-run-projection.schema.json +73 -0
  68. package/dist/schemas/active-run-resume.schema.json +68 -0
  69. package/dist/schemas/clean-checkout-smoke.schema.json +126 -0
  70. package/dist/schemas/context-export.schema.json +35 -0
  71. package/dist/schemas/event.schema.json +17 -0
  72. package/dist/schemas/evidence-list.schema.json +49 -0
  73. package/dist/schemas/feature-smoke.schema.json +67 -0
  74. package/dist/schemas/install-plan.schema.json +93 -0
  75. package/dist/schemas/package-smoke.schema.json +130 -0
  76. package/dist/schemas/private-evidence.schema.json +48 -0
  77. package/dist/schemas/provider-call.schema.json +42 -0
  78. package/dist/schemas/provider-config.schema.json +43 -0
  79. package/dist/schemas/release-artifact-manifest.schema.json +55 -0
  80. package/dist/schemas/release-artifact.schema.json +140 -0
  81. package/dist/schemas/release-dry-run.schema.json +141 -0
  82. package/dist/schemas/release-gate.schema.json +42 -0
  83. package/dist/schemas/release-publish.schema.json +114 -0
  84. package/dist/schemas/schema-index.json +145 -0
  85. package/dist/schemas/smoke-evidence-summary.schema.json +88 -0
  86. package/dist/schemas/tools-list.schema.json +78 -0
  87. package/dist/schemas/write-preflight.schema.json +47 -0
  88. package/dist/services/active-run-state.js +215 -0
  89. package/dist/services/capability-registry.js +540 -0
  90. package/dist/services/clean-checkout-smoke.js +393 -0
  91. package/dist/services/evidence-list.js +136 -0
  92. package/dist/services/feature-smoke.js +155 -0
  93. package/dist/services/harness-service.js +7 -0
  94. package/dist/services/install-plan.js +233 -0
  95. package/dist/services/operational-debt.js +767 -0
  96. package/dist/services/operations-status-service.js +195 -0
  97. package/dist/services/package-smoke.js +676 -0
  98. package/dist/services/policy-service.js +25 -0
  99. package/dist/services/project-read-model.js +101 -0
  100. package/dist/services/release-artifact-evidence.js +77 -0
  101. package/dist/services/release-artifact.js +351 -0
  102. package/dist/services/release-dry-run.js +253 -0
  103. package/dist/services/release-evidence.js +138 -0
  104. package/dist/services/release-publish.js +163 -0
  105. package/dist/services/smoke-evidence.js +104 -0
  106. package/dist/services/task-read-model.js +125 -0
  107. package/dist/services/tools-list.js +26 -0
  108. package/dist/services/write-preflight.js +240 -0
  109. package/dist/task/task-capsule.js +121 -0
  110. package/dist/tools/fake-shell.js +56 -0
  111. package/dist/tui/cache.js +341 -0
  112. package/dist/tui/constants.js +44 -0
  113. package/dist/tui/layout.js +140 -0
  114. package/dist/tui/markdown.js +238 -0
  115. package/dist/tui/read-model-worker.js +24 -0
  116. package/dist/tui/read-model.js +502 -0
  117. package/dist/tui/snapshot.js +434 -0
  118. package/dist/tui/state.js +229 -0
  119. package/dist/tui/terminal.js +475 -0
  120. package/dist/tui/theme.js +86 -0
  121. package/package.json +16 -0
@@ -0,0 +1,125 @@
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.createTaskListReport = createTaskListReport;
7
+ exports.createTaskShowReport = createTaskShowReport;
8
+ exports.createTaskReadReport = createTaskReadReport;
9
+ exports.formatTaskListReport = formatTaskListReport;
10
+ const node_fs_1 = __importDefault(require("node:fs"));
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const task_capsule_1 = require("../task/task-capsule");
13
+ const evidence_list_1 = require("./evidence-list");
14
+ const TASK_CAPSULE_FILES = [
15
+ 'TASK.md',
16
+ 'PLAN.md',
17
+ 'CONTEXT.md',
18
+ 'ACCEPTANCE.md',
19
+ 'FILES.md',
20
+ 'TESTS.md',
21
+ 'RISKS.md',
22
+ 'DECISIONS.md',
23
+ 'EVIDENCE.md',
24
+ 'evidence.jsonl',
25
+ 'HANDOFF.md'
26
+ ];
27
+ function createTaskListReport(projectRoot) {
28
+ const tasks = (0, task_capsule_1.listTaskCapsules)(projectRoot).map((task) => summarizeTask(projectRoot, task));
29
+ return {
30
+ schemaVersion: 'hadara.task.list.v1',
31
+ command: 'task.list',
32
+ ok: true,
33
+ count: tasks.length,
34
+ tasks
35
+ };
36
+ }
37
+ function createTaskShowReport(projectRoot, taskId) {
38
+ const task = (0, task_capsule_1.listTaskCapsules)(projectRoot).find((item) => item.id === taskId);
39
+ if (!task) {
40
+ return {
41
+ schemaVersion: 'hadara.task.show.v1',
42
+ command: 'task.show',
43
+ ok: false,
44
+ issues: [
45
+ {
46
+ severity: 'error',
47
+ code: 'TASK_NOT_FOUND',
48
+ message: `Task Capsule not found: ${taskId}`
49
+ }
50
+ ]
51
+ };
52
+ }
53
+ return {
54
+ schemaVersion: 'hadara.task.show.v1',
55
+ command: 'task.show',
56
+ ok: true,
57
+ task: {
58
+ ...summarizeTask(projectRoot, task),
59
+ taskMarkdown: node_fs_1.default.readFileSync(node_path_1.default.join(task.dir, 'TASK.md'), 'utf8')
60
+ },
61
+ issues: []
62
+ };
63
+ }
64
+ function createTaskReadReport(projectRoot, taskId, options = {}) {
65
+ const task = (0, task_capsule_1.listTaskCapsules)(projectRoot).find((item) => item.id === taskId);
66
+ if (!task) {
67
+ return {
68
+ schemaVersion: 'hadara.task.read.v1',
69
+ command: 'task.read',
70
+ ok: false,
71
+ issues: [
72
+ {
73
+ severity: 'error',
74
+ code: 'TASK_NOT_FOUND',
75
+ message: `Task Capsule not found: ${taskId}`
76
+ }
77
+ ]
78
+ };
79
+ }
80
+ const files = Object.fromEntries(TASK_CAPSULE_FILES.map((fileName) => {
81
+ const filePath = node_path_1.default.join(task.dir, fileName);
82
+ return [fileName, node_fs_1.default.existsSync(filePath) ? node_fs_1.default.readFileSync(filePath, 'utf8') : ''];
83
+ }));
84
+ const evidenceParse = (0, evidence_list_1.parseEvidenceIndexFile)(node_path_1.default.join(task.dir, 'evidence.jsonl'), task.id);
85
+ const includePrivate = options.includePrivate === true;
86
+ const evidenceRecords = evidenceParse.records.filter((record) => includePrivate || record.visibility !== 'private');
87
+ files['evidence.jsonl'] = formatEvidenceIndexFile(evidenceRecords);
88
+ return {
89
+ schemaVersion: 'hadara.task.read.v1',
90
+ command: 'task.read',
91
+ ok: !evidenceParse.issues.some((issue) => issue.severity === 'error'),
92
+ task: summarizeTask(projectRoot, task),
93
+ files,
94
+ evidenceIndex: evidenceRecords,
95
+ issues: evidenceParse.issues
96
+ };
97
+ }
98
+ function formatTaskListReport(report) {
99
+ return report.tasks.map((task) => `${task.id}\t${task.title}\t${task.capsule}`).join('\n');
100
+ }
101
+ function formatEvidenceIndexFile(records) {
102
+ if (records.length === 0)
103
+ return '';
104
+ return `${records.map((record) => JSON.stringify(record)).join('\n')}\n`;
105
+ }
106
+ function summarizeTask(projectRoot, task) {
107
+ return {
108
+ id: task.id,
109
+ title: task.title,
110
+ status: readTaskStatus(task),
111
+ slug: task.slug,
112
+ capsule: toPortablePath(node_path_1.default.relative(projectRoot, task.dir))
113
+ };
114
+ }
115
+ function readTaskStatus(task) {
116
+ const taskPath = node_path_1.default.join(task.dir, 'TASK.md');
117
+ if (!node_fs_1.default.existsSync(taskPath))
118
+ return 'Unknown';
119
+ const content = node_fs_1.default.readFileSync(taskPath, 'utf8');
120
+ const match = content.match(/^## Status\s*\n+([\s\S]*?)(?:\n## |\s*$)/m);
121
+ return match?.[1]?.trim().split(/\r?\n/)[0]?.trim() || 'Unknown';
122
+ }
123
+ function toPortablePath(value) {
124
+ return value.split(node_path_1.default.sep).join('/');
125
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createToolsListReport = createToolsListReport;
4
+ const capability_registry_1 = require("./capability-registry");
5
+ function createToolsListReport(options = {}) {
6
+ return {
7
+ schemaVersion: 'hadara.tools.list.v1',
8
+ command: 'tools.list',
9
+ ok: true,
10
+ surfaces: {
11
+ cli: capability_registry_1.HADARA_CLI_CAPABILITIES.map((surface) => ({ ...surface })),
12
+ mcp: createMcpSurfaces(options)
13
+ },
14
+ disabled: capability_registry_1.HADARA_DISABLED_CAPABILITIES.map((surface) => ({ ...surface })),
15
+ issues: []
16
+ };
17
+ }
18
+ function createMcpSurfaces(options) {
19
+ const readTools = capability_registry_1.HADARA_MCP_READ_CAPABILITIES.map((capability) => ({ ...capability.surface }));
20
+ const evidenceAttach = {
21
+ ...capability_registry_1.HADARA_MCP_EVIDENCE_ATTACH_CAPABILITY.surface,
22
+ enabledByDefault: options.enableEvidenceAttach === true,
23
+ availability: options.enableEvidenceAttach === true ? 'default' : capability_registry_1.HADARA_MCP_EVIDENCE_ATTACH_CAPABILITY.surface.availability
24
+ };
25
+ return [...readTools, evidenceAttach];
26
+ }
@@ -0,0 +1,240 @@
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.createWritePreflightReport = createWritePreflightReport;
7
+ exports.assertWritePreflightSchema = assertWritePreflightSchema;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const schema_1 = require("../core/schema");
11
+ const fs_1 = require("../core/fs");
12
+ const TASK_FILE_NAMES = [
13
+ 'TASK.md',
14
+ 'PLAN.md',
15
+ 'CONTEXT.md',
16
+ 'FILES.md',
17
+ 'ACCEPTANCE.md',
18
+ 'TESTS.md',
19
+ 'RISKS.md',
20
+ 'DECISIONS.md',
21
+ 'EVIDENCE.md',
22
+ 'evidence.jsonl',
23
+ 'HANDOFF.md'
24
+ ];
25
+ function createWritePreflightReport(projectRoot, targetArgs) {
26
+ const args = normalizeTargetArgs(targetArgs);
27
+ const command = identifyCommand(args);
28
+ let report;
29
+ switch (command) {
30
+ case 'task.create':
31
+ report = taskCreateReport(projectRoot, args);
32
+ break;
33
+ case 'evidence.collect':
34
+ report = evidenceCollectReport(projectRoot, args);
35
+ break;
36
+ case 'handoff.update':
37
+ report = simpleProjectWriteReport('handoff.update', ['docs/AGENT_HANDOFF.md']);
38
+ break;
39
+ case 'run-state.start':
40
+ case 'run-state.update':
41
+ case 'run-state.complete':
42
+ report = simpleProjectWriteReport(command, ['.hadara/local/state/active-run.json'], [
43
+ {
44
+ severity: 'warning',
45
+ code: 'WRITE_COMMAND_DEFERRED',
46
+ message: `${command} is a planned CLI-owned write boundary; the mutation command is not implemented yet.`
47
+ }
48
+ ]);
49
+ break;
50
+ case 'debt.add':
51
+ case 'debt.update':
52
+ report = {
53
+ ...simpleProjectWriteReport(command, ['docs/OPERATIONAL_DEBT.md'], [
54
+ {
55
+ severity: 'warning',
56
+ code: 'DEBT_WRITE_STORE_DEFERRED',
57
+ message: 'Operational debt mutation is deferred; the durable write store is not implemented yet.'
58
+ }
59
+ ]),
60
+ risk: 'medium',
61
+ requiresApproval: true
62
+ };
63
+ break;
64
+ default:
65
+ report = {
66
+ schemaVersion: 'hadara.write.preflight.v1',
67
+ ok: false,
68
+ command: 'unknown',
69
+ risk: 'low',
70
+ requiresApproval: false,
71
+ workspaceBoundary: 'project',
72
+ writes: [],
73
+ issues: [
74
+ {
75
+ severity: 'error',
76
+ code: 'UNSUPPORTED_WRITE_COMMAND',
77
+ message: `Unsupported write preflight target: ${args.join(' ') || '(empty)'}`
78
+ }
79
+ ]
80
+ };
81
+ }
82
+ assertWritePreflightSchema(report);
83
+ return report;
84
+ }
85
+ function assertWritePreflightSchema(report) {
86
+ (0, schema_1.assertSchema)('hadara.write.preflight.v1', report);
87
+ }
88
+ function taskCreateReport(projectRoot, args) {
89
+ const title = extractValueAfterPrefix(args, ['task', 'create']);
90
+ const issues = [];
91
+ const taskId = nextTaskIdForPreflight(node_path_1.default.join(projectRoot, 'tasks'));
92
+ const slug = (0, fs_1.slugify)(title || 'task');
93
+ const capsule = `tasks/${taskId}-${slug}`;
94
+ if (!title) {
95
+ issues.push({
96
+ severity: 'error',
97
+ code: 'TASK_TITLE_REQUIRED',
98
+ message: 'task create preflight requires a title.'
99
+ });
100
+ }
101
+ return {
102
+ schemaVersion: 'hadara.write.preflight.v1',
103
+ ok: issues.every((issue) => issue.severity !== 'error'),
104
+ command: 'task.create',
105
+ risk: 'low',
106
+ requiresApproval: false,
107
+ workspaceBoundary: 'project',
108
+ writes: [...TASK_FILE_NAMES.map((fileName) => `${capsule}/${fileName}`), 'docs/TASK_BOARD.md'],
109
+ issues
110
+ };
111
+ }
112
+ function evidenceCollectReport(projectRoot, args) {
113
+ const taskId = getOptionValue(args, '--task');
114
+ const kind = getOptionValue(args, '--kind') ?? 'note';
115
+ const artifactPath = getOptionValue(args, '--path');
116
+ const visibility = args.includes('--private') ? 'private' : getOptionValue(args, '--visibility') ?? 'public';
117
+ const issues = [];
118
+ if (!taskId) {
119
+ issues.push({
120
+ severity: 'error',
121
+ code: 'TASK_ID_REQUIRED',
122
+ message: 'evidence collect preflight requires --task <task-id>.'
123
+ });
124
+ }
125
+ const capsule = taskId ? findTaskCapsulePath(projectRoot, taskId) : null;
126
+ if (taskId && !capsule) {
127
+ issues.push({
128
+ severity: 'error',
129
+ code: 'TASK_CAPSULE_NOT_FOUND',
130
+ message: `Task Capsule not found: ${taskId}`
131
+ });
132
+ }
133
+ const writes = capsule ? [`${capsule}/EVIDENCE.md`, `${capsule}/evidence.jsonl`] : [];
134
+ if (capsule && artifactPath && visibility === 'public') {
135
+ writes.push(`${capsule}/artifacts/${kind}/<timestamp>-${safeFilePart(node_path_1.default.basename(artifactPath))}`);
136
+ }
137
+ if (taskId && artifactPath && visibility === 'private') {
138
+ writes.push(`.hadara/local/portable/data/private-evidence/${taskId}/<evidence-id>.bin`);
139
+ writes.push(`.hadara/local/portable/data/private-evidence/${taskId}/manifest.jsonl`);
140
+ writes.push('.hadara/local/portable/data/audit/audit.jsonl');
141
+ }
142
+ return {
143
+ schemaVersion: 'hadara.write.preflight.v1',
144
+ ok: issues.every((issue) => issue.severity !== 'error'),
145
+ command: 'evidence.collect',
146
+ risk: visibility === 'private' ? 'medium' : 'low',
147
+ requiresApproval: visibility === 'private',
148
+ workspaceBoundary: visibility === 'private' ? 'project+private-portable' : 'project',
149
+ writes,
150
+ issues
151
+ };
152
+ }
153
+ function simpleProjectWriteReport(command, writes, issues = []) {
154
+ return {
155
+ schemaVersion: 'hadara.write.preflight.v1',
156
+ ok: issues.every((issue) => issue.severity !== 'error'),
157
+ command,
158
+ risk: 'low',
159
+ requiresApproval: false,
160
+ workspaceBoundary: 'project',
161
+ writes,
162
+ issues
163
+ };
164
+ }
165
+ function identifyCommand(args) {
166
+ const [root, sub] = args;
167
+ if (root === 'task' && sub === 'create')
168
+ return 'task.create';
169
+ if (root === 'evidence' && sub === 'collect')
170
+ return 'evidence.collect';
171
+ if (root === 'handoff' && sub === 'update')
172
+ return 'handoff.update';
173
+ if (root === 'run-state' && (sub === 'start' || sub === 'update' || sub === 'complete'))
174
+ return `run-state.${sub}`;
175
+ if (root === 'debt' && (sub === 'add' || sub === 'update'))
176
+ return `debt.${sub}`;
177
+ return 'unknown';
178
+ }
179
+ function normalizeTargetArgs(args) {
180
+ const normalized = [];
181
+ for (let index = 0; index < args.length; index += 1) {
182
+ const value = args[index];
183
+ if (value === '--')
184
+ continue;
185
+ if (value === '--json')
186
+ continue;
187
+ if (value === '--project') {
188
+ index += 1;
189
+ continue;
190
+ }
191
+ normalized.push(value);
192
+ }
193
+ return normalized;
194
+ }
195
+ function extractValueAfterPrefix(args, prefix) {
196
+ return args
197
+ .slice(prefix.length)
198
+ .filter((value, index, values) => {
199
+ if (value.startsWith('--'))
200
+ return false;
201
+ const previous = values[index - 1];
202
+ return previous !== '--project';
203
+ })
204
+ .join(' ')
205
+ .trim();
206
+ }
207
+ function getOptionValue(args, option) {
208
+ const index = args.indexOf(option);
209
+ if (index === -1)
210
+ return undefined;
211
+ const value = args[index + 1];
212
+ if (!value || value.startsWith('--'))
213
+ return undefined;
214
+ return value;
215
+ }
216
+ function nextTaskIdForPreflight(tasksDir) {
217
+ if (!node_fs_1.default.existsSync(tasksDir))
218
+ return 'T-0001';
219
+ const max = node_fs_1.default
220
+ .readdirSync(tasksDir, { withFileTypes: true })
221
+ .filter((entry) => entry.isDirectory())
222
+ .map((entry) => entry.name.match(/^T-(\d{4})-/)?.[1])
223
+ .filter((value) => Boolean(value))
224
+ .map(Number)
225
+ .reduce((acc, value) => Math.max(acc, value), 0);
226
+ return `T-${String(max + 1).padStart(4, '0')}`;
227
+ }
228
+ function findTaskCapsulePath(projectRoot, taskId) {
229
+ const tasksDir = node_path_1.default.join(projectRoot, 'tasks');
230
+ if (!node_fs_1.default.existsSync(tasksDir))
231
+ return null;
232
+ const entry = node_fs_1.default.readdirSync(tasksDir).find((name) => name.startsWith(`${taskId}-`));
233
+ return entry ? toPortablePath(node_path_1.default.relative(projectRoot, node_path_1.default.join(tasksDir, entry))) : null;
234
+ }
235
+ function safeFilePart(value) {
236
+ return value.replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'artifact';
237
+ }
238
+ function toPortablePath(value) {
239
+ return value.split(node_path_1.default.sep).join('/');
240
+ }
@@ -0,0 +1,121 @@
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.TASK_FILES = void 0;
7
+ exports.isTaskCapsuleScaffoldContent = isTaskCapsuleScaffoldContent;
8
+ exports.nextTaskId = nextTaskId;
9
+ exports.createTaskCapsule = createTaskCapsule;
10
+ exports.listTaskCapsules = listTaskCapsules;
11
+ const node_fs_1 = __importDefault(require("node:fs"));
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const fs_1 = require("../core/fs");
14
+ exports.TASK_FILES = {
15
+ 'TASK.md': (task) => `# ${task.id} ${task.title}\n\n## Goal\n\nTBD.\n\n## Scope\n\nTBD.\n\n## Out of Scope\n\nTBD.\n\n## Status\n\nDraft\n`,
16
+ 'PLAN.md': () => `# Plan\n\n1. Read relevant docs.\n2. Implement the smallest useful slice.\n3. Run tests.\n4. Attach evidence.\n5. Update handoff.\n`,
17
+ 'CONTEXT.md': () => `# Context\n\nRelevant documents, files, assumptions, and constraints.\n`,
18
+ 'FILES.md': () => `# Files\n\n| Path | Action | Reason |\n|---|---|---|\n`,
19
+ 'ACCEPTANCE.md': () => `# Acceptance Criteria\n\n- [ ] Scope is implemented.\n- [ ] Tests or explicit constraints are recorded.\n- [ ] Evidence is attached.\n- [ ] Handoff is updated.\n`,
20
+ 'TESTS.md': () => `# Tests\n\n## Required\n\n- npm test\n\n## Optional\n\n- npm run check\n`,
21
+ 'RISKS.md': () => `# Risks\n\n| Risk | Mitigation |\n|---|---|\n`,
22
+ 'DECISIONS.md': () => `# Decisions\n\nRecord task-local design decisions here.\n`,
23
+ 'EVIDENCE.md': () => `# Evidence\n\n| Time | Kind | Summary | Result |\n|---|---|---|---|\n`,
24
+ 'evidence.jsonl': () => '',
25
+ 'HANDOFF.md': () => `# Handoff\n\n## Last Completed\n\nTBD.\n\n## Next Recommended Step\n\nTBD.\n`
26
+ };
27
+ function isTaskCapsuleScaffoldContent(task, fileName, content) {
28
+ if (fileName === 'TASK.md') {
29
+ return ['## Goal', '## Scope', '## Out of Scope'].some((heading) => isPlaceholderSection(readMarkdownSection(content, heading)));
30
+ }
31
+ if (fileName === 'ACCEPTANCE.md') {
32
+ return acceptanceChecklistText(content).join('\n') === [
33
+ 'Scope is implemented.',
34
+ 'Tests or explicit constraints are recorded.',
35
+ 'Evidence is attached.',
36
+ 'Handoff is updated.'
37
+ ].join('\n');
38
+ }
39
+ const factory = exports.TASK_FILES[fileName];
40
+ if (!factory)
41
+ return false;
42
+ return normalizeMarkdown(content) === normalizeMarkdown(factory(task));
43
+ }
44
+ function nextTaskId(tasksDir) {
45
+ (0, fs_1.ensureDir)(tasksDir);
46
+ const max = node_fs_1.default
47
+ .readdirSync(tasksDir, { withFileTypes: true })
48
+ .filter((entry) => entry.isDirectory())
49
+ .map((entry) => entry.name.match(/^T-(\d{4})-/)?.[1])
50
+ .filter((value) => Boolean(value))
51
+ .map(Number)
52
+ .reduce((acc, value) => Math.max(acc, value), 0);
53
+ return `T-${String(max + 1).padStart(4, '0')}`;
54
+ }
55
+ function createTaskCapsule(projectRoot, title) {
56
+ const tasksDir = node_path_1.default.join(projectRoot, 'tasks');
57
+ const id = nextTaskId(tasksDir);
58
+ const slug = (0, fs_1.slugify)(title);
59
+ const dir = node_path_1.default.join(tasksDir, `${id}-${slug}`);
60
+ const task = { id, title, slug, dir };
61
+ (0, fs_1.ensureDir)(dir);
62
+ for (const [fileName, factory] of Object.entries(exports.TASK_FILES)) {
63
+ (0, fs_1.writeFileIfMissing)(node_path_1.default.join(dir, fileName), factory(task));
64
+ }
65
+ const taskBoard = node_path_1.default.join(projectRoot, 'docs', 'TASK_BOARD.md');
66
+ (0, fs_1.ensureDir)(node_path_1.default.dirname(taskBoard));
67
+ const line = `| ${id} | ${title.replace(/\|/g, '/')} | Draft | ${node_path_1.default.relative(projectRoot, dir)} | |\n`;
68
+ if (!node_fs_1.default.existsSync(taskBoard)) {
69
+ node_fs_1.default.writeFileSync(taskBoard, `# TASK_BOARD\n\n| ID | Title | Status | Capsule | Notes |\n|---|---|---|---|---|\n${line}`, 'utf8');
70
+ }
71
+ else {
72
+ const current = node_fs_1.default.readFileSync(taskBoard, 'utf8');
73
+ if (!current.includes(`| ${id} |`)) {
74
+ node_fs_1.default.appendFileSync(taskBoard, line, 'utf8');
75
+ }
76
+ }
77
+ return task;
78
+ }
79
+ function listTaskCapsules(projectRoot) {
80
+ const tasksDir = node_path_1.default.join(projectRoot, 'tasks');
81
+ if (!node_fs_1.default.existsSync(tasksDir))
82
+ return [];
83
+ return node_fs_1.default
84
+ .readdirSync(tasksDir, { withFileTypes: true })
85
+ .filter((entry) => entry.isDirectory() && /^T-\d{4}-/.test(entry.name))
86
+ .map((entry) => {
87
+ const [id, ...slugParts] = entry.name.split('-');
88
+ const number = slugParts.shift();
89
+ const fullId = `${id}-${number}`;
90
+ const slug = slugParts.join('-');
91
+ const dir = node_path_1.default.join(tasksDir, entry.name);
92
+ const taskMd = node_path_1.default.join(dir, 'TASK.md');
93
+ const title = node_fs_1.default.existsSync(taskMd)
94
+ ? node_fs_1.default.readFileSync(taskMd, 'utf8').split('\n')[0].replace(/^#\s*T-\d{4}\s*/, '').trim()
95
+ : slug;
96
+ return { id: fullId, title, slug, dir };
97
+ })
98
+ .sort((a, b) => a.id.localeCompare(b.id));
99
+ }
100
+ function readMarkdownSection(content, heading) {
101
+ const start = content.indexOf(heading);
102
+ if (start < 0)
103
+ return '';
104
+ const afterHeading = content.slice(start + heading.length);
105
+ const nextHeading = afterHeading.search(/\n##\s+/);
106
+ return nextHeading >= 0 ? afterHeading.slice(0, nextHeading) : afterHeading;
107
+ }
108
+ function isPlaceholderSection(value) {
109
+ const normalized = value.trim();
110
+ return normalized.length === 0 || /^TBD\.?$/i.test(normalized);
111
+ }
112
+ function normalizeMarkdown(value) {
113
+ return value.replace(/\r\n/g, '\n').trim();
114
+ }
115
+ function acceptanceChecklistText(content) {
116
+ return content
117
+ .split(/\r?\n/)
118
+ .map((line) => line.trim())
119
+ .filter((line) => /^-\s+\[[ xX]\]/.test(line))
120
+ .map((line) => line.replace(/^-\s+\[[ xX]\]\s*/, '').trim());
121
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runFakeShellCommand = runFakeShellCommand;
4
+ const policy_1 = require("../policy/policy");
5
+ const preflight_1 = require("../policy/preflight");
6
+ function runFakeShellCommand(input) {
7
+ const mode = (0, policy_1.parsePermissionMode)(input.mode);
8
+ const preflight = (0, preflight_1.createShellExecutionPreflight)(input.command, mode);
9
+ if (preflight.execution.status === 'denied') {
10
+ return toObservation(input.command, mode, preflight, {
11
+ status: 'policy_denied',
12
+ exitCode: preflight.execution.exitCodeIfBlocked ?? 2,
13
+ stdout: '',
14
+ stderr: preflight.decision.reason,
15
+ reason: preflight.decision.reason
16
+ });
17
+ }
18
+ if (preflight.execution.status === 'requires_approval') {
19
+ return toObservation(input.command, mode, preflight, {
20
+ status: 'requires_approval',
21
+ exitCode: 0,
22
+ stdout: '',
23
+ stderr: '',
24
+ reason: preflight.decision.reason
25
+ });
26
+ }
27
+ const result = input.fixtures[input.command];
28
+ if (!result) {
29
+ return toObservation(input.command, mode, preflight, {
30
+ status: 'not_configured',
31
+ exitCode: 127,
32
+ stdout: '',
33
+ stderr: `No fake shell fixture configured for command: ${input.command}`,
34
+ reason: 'Fake shell fixtures must explicitly define allowed command outputs.'
35
+ });
36
+ }
37
+ return toObservation(input.command, mode, preflight, {
38
+ status: 'completed',
39
+ exitCode: result.exitCode,
40
+ stdout: result.stdout ?? '',
41
+ stderr: result.stderr ?? ''
42
+ });
43
+ }
44
+ function toObservation(commandText, mode, preflight, result) {
45
+ return {
46
+ schemaVersion: 'hadara.tools.fake-shell.v1',
47
+ command: 'tools.fake-shell.run',
48
+ ok: result.status === 'completed' && result.exitCode === 0,
49
+ input: {
50
+ mode,
51
+ command: commandText
52
+ },
53
+ preflight,
54
+ result
55
+ };
56
+ }