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,475 @@
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.TuiTerminalSession = void 0;
7
+ exports.createTuiTerminalSession = createTuiTerminalSession;
8
+ exports.decodeTuiInput = decodeTuiInput;
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const node_worker_threads_1 = require("node:worker_threads");
11
+ const read_model_1 = require("./read-model");
12
+ const snapshot_1 = require("./snapshot");
13
+ const cache_1 = require("./cache");
14
+ const constants_1 = require("./constants");
15
+ const state_1 = require("./state");
16
+ class TuiTerminalSession {
17
+ input;
18
+ output;
19
+ options;
20
+ model;
21
+ state;
22
+ running = false;
23
+ rawModeEnabled = false;
24
+ loadingTick = 0;
25
+ loaded = false;
26
+ loading = false;
27
+ activeLoadId = 0;
28
+ loadingTimer = null;
29
+ logLine = 'loading: reading work console';
30
+ hitboxes = [];
31
+ onResize = () => {
32
+ if (!this.running)
33
+ return;
34
+ this.render();
35
+ };
36
+ onData = (chunk) => {
37
+ for (const key of decodeTuiInput(chunk)) {
38
+ if (!this.running)
39
+ return;
40
+ this.applyKey(key);
41
+ }
42
+ };
43
+ constructor(options) {
44
+ this.options = options;
45
+ this.input = options.input ?? process.stdin;
46
+ this.output = options.output ?? process.stdout;
47
+ this.model = (0, read_model_1.createTuiLoadingReadModel)();
48
+ this.state = (0, state_1.createTuiInteractionState)(this.model);
49
+ }
50
+ start() {
51
+ if (!this.running) {
52
+ this.running = true;
53
+ if (this.shouldEnableRawMode()) {
54
+ this.input.setRawMode?.(true);
55
+ this.rawModeEnabled = true;
56
+ }
57
+ this.input.resume();
58
+ this.input.on('data', this.onData);
59
+ this.output.on?.('resize', this.onResize);
60
+ if (this.options.terminalControl !== false)
61
+ this.output.write('\x1b[?25l\x1b[?1000h\x1b[?1006h');
62
+ }
63
+ if (!this.loaded) {
64
+ if (this.options.asyncLoading) {
65
+ return this.beginInitialLoadAsync();
66
+ }
67
+ this.completeInitialLoad();
68
+ }
69
+ return this.render();
70
+ }
71
+ stop() {
72
+ if (!this.running)
73
+ return;
74
+ this.running = false;
75
+ this.input.off('data', this.onData);
76
+ if (this.rawModeEnabled) {
77
+ this.input.setRawMode?.(false);
78
+ this.rawModeEnabled = false;
79
+ }
80
+ this.input.pause();
81
+ this.output.off?.('resize', this.onResize);
82
+ this.stopLoadingPulse();
83
+ if (this.options.terminalControl !== false)
84
+ this.output.write('\x1b[?1000l\x1b[?1006l\x1b[?25h');
85
+ this.options.onStop?.();
86
+ }
87
+ isRunning() {
88
+ return this.running;
89
+ }
90
+ getState() {
91
+ return this.state;
92
+ }
93
+ getModel() {
94
+ return this.model;
95
+ }
96
+ handleKey(key) {
97
+ if (!this.running)
98
+ return null;
99
+ return this.applyKey(key);
100
+ }
101
+ render() {
102
+ const snapshot = (0, snapshot_1.renderTuiSnapshot)(this.model, this.snapshotOptions());
103
+ this.hitboxes = snapshot.hitboxes;
104
+ if (this.options.terminalControl !== false)
105
+ this.output.write('\x1b[H\x1b[2J');
106
+ this.output.write(snapshot.text);
107
+ return {
108
+ text: snapshot.text,
109
+ state: this.state,
110
+ model: this.model
111
+ };
112
+ }
113
+ applyKey(key) {
114
+ const mouseResult = this.applyMouseKey(key);
115
+ if (mouseResult !== undefined)
116
+ return mouseResult;
117
+ const requestedState = (0, state_1.reduceTuiInteractionState)(this.state, this.model, key, this.stateReduceOptions());
118
+ const nextState = this.options.asyncLoading ? this.completePendingEffectsAsync(requestedState) : this.completePendingEffects(requestedState);
119
+ this.state = nextState;
120
+ if (this.state.quitRequested) {
121
+ this.stop();
122
+ return null;
123
+ }
124
+ return this.render();
125
+ }
126
+ completePendingEffectsAsync(requestedState) {
127
+ if (this.loading)
128
+ return requestedState;
129
+ if (requestedState.detailRefreshRequested) {
130
+ this.beginReadModelLoad('detail', `loading ${requestedState.selectedTaskId ?? 'selected task'} detail`, {
131
+ ...(0, state_1.tuiStateToReadModelOptions)(requestedState),
132
+ profile: 'fast'
133
+ });
134
+ return requestedState;
135
+ }
136
+ if (requestedState.refreshRequested) {
137
+ this.beginReadModelLoad('full', 'refreshing read models', { ...(0, state_1.tuiStateToReadModelOptions)(requestedState), profile: 'fast' });
138
+ return requestedState;
139
+ }
140
+ return requestedState;
141
+ }
142
+ completePendingEffects(requestedState) {
143
+ let nextState = requestedState;
144
+ if (nextState.detailRefreshRequested) {
145
+ this.renderLoadingFrames(`loading ${nextState.selectedTaskId ?? 'selected task'} detail`);
146
+ this.model = this.loadModel('detail', { ...(0, state_1.tuiStateToReadModelOptions)(nextState), profile: 'fast' });
147
+ nextState = (0, state_1.reduceTuiInteractionState)(nextState, this.model, 'detail-refresh-complete');
148
+ this.logLine = `loaded ${this.model.selectedTaskId ?? 'selected task'} detail`;
149
+ }
150
+ if (nextState.refreshRequested) {
151
+ this.renderLoadingFrames('refreshing read models');
152
+ this.model = this.loadModel('full', { ...(0, state_1.tuiStateToReadModelOptions)(nextState), profile: 'fast' });
153
+ nextState = (0, state_1.reduceTuiInteractionState)(nextState, this.model, 'refresh-complete');
154
+ this.logLine = 'refreshed read models';
155
+ }
156
+ return nextState;
157
+ }
158
+ completeInitialLoad() {
159
+ this.renderLoadingFrames('initial load: reading read models');
160
+ this.model = this.loadModel('fast', { profile: 'fast' });
161
+ this.state = (0, state_1.createTuiInteractionState)(this.model, {
162
+ panel: this.state.activePanel,
163
+ selectedTaskId: this.model.selectedTaskId,
164
+ document: this.state.documentFile,
165
+ taskSearch: this.state.taskSearch,
166
+ searchActive: this.state.searchActive,
167
+ taskListVisibleRows: this.taskListVisibleRows()
168
+ });
169
+ this.loaded = true;
170
+ this.logLine = 'ready: work console loaded';
171
+ }
172
+ beginInitialLoadAsync() {
173
+ return this.beginReadModelLoad('fast', 'initial load: reading read models', { profile: 'fast' }, true);
174
+ }
175
+ snapshotOptions() {
176
+ return (0, state_1.tuiStateToSnapshotOptions)(this.state, {
177
+ width: this.options.width ?? this.output.columns,
178
+ height: this.options.height ?? this.output.rows,
179
+ widthPolicy: this.options.widthPolicy,
180
+ includeGeneratedAt: this.options.includeGeneratedAt,
181
+ theme: this.options.theme ?? 'none',
182
+ logLine: this.logLine
183
+ });
184
+ }
185
+ renderLoading(message) {
186
+ this.loadingTick += 1;
187
+ this.logLine = message;
188
+ const snapshot = (0, snapshot_1.renderTuiSnapshot)(this.model, {
189
+ ...this.snapshotOptions(),
190
+ loading: true,
191
+ loadingTick: this.loadingTick,
192
+ logLine: message
193
+ });
194
+ this.hitboxes = snapshot.hitboxes;
195
+ if (this.options.terminalControl !== false)
196
+ this.output.write('\x1b[H\x1b[2J');
197
+ this.output.write(snapshot.text);
198
+ return {
199
+ text: snapshot.text,
200
+ state: this.state,
201
+ model: this.model
202
+ };
203
+ }
204
+ renderLoadingFrames(message) {
205
+ for (let frame = 0; frame < 4; frame += 1) {
206
+ this.renderLoading(message);
207
+ }
208
+ }
209
+ beginReadModelLoad(refresh, message, readOptions, initial = false) {
210
+ if (this.loading)
211
+ return this.render();
212
+ this.loading = true;
213
+ const loadId = (this.activeLoadId += 1);
214
+ this.logLine = message;
215
+ const result = this.renderLoading(message);
216
+ this.startLoadingPulse(message);
217
+ this.loadModelAsync(refresh, readOptions)
218
+ .then((model) => {
219
+ if (!this.running || loadId !== this.activeLoadId)
220
+ return;
221
+ this.model = model;
222
+ if (initial) {
223
+ this.state = (0, state_1.createTuiInteractionState)(this.model, {
224
+ panel: this.state.activePanel,
225
+ selectedTaskId: this.model.selectedTaskId,
226
+ document: this.state.documentFile,
227
+ taskSearch: this.state.taskSearch,
228
+ searchActive: this.state.searchActive,
229
+ taskListVisibleRows: this.taskListVisibleRows()
230
+ });
231
+ this.loaded = true;
232
+ this.logLine = 'ready: work console loaded';
233
+ }
234
+ else if (this.state.detailRefreshRequested) {
235
+ this.state = (0, state_1.reduceTuiInteractionState)(this.state, this.model, 'detail-refresh-complete');
236
+ this.logLine = `loaded ${this.model.selectedTaskId ?? 'selected task'} detail`;
237
+ }
238
+ else if (this.state.refreshRequested) {
239
+ this.state = (0, state_1.reduceTuiInteractionState)(this.state, this.model, 'refresh-complete');
240
+ this.logLine = 'refreshed read models';
241
+ }
242
+ })
243
+ .catch((error) => {
244
+ if (!this.running || loadId !== this.activeLoadId)
245
+ return;
246
+ this.logLine = `read-model load failed: ${error instanceof Error ? error.message : String(error)}`;
247
+ if (initial)
248
+ this.loaded = true;
249
+ if (this.state.detailRefreshRequested)
250
+ this.state = (0, state_1.reduceTuiInteractionState)(this.state, this.model, 'detail-refresh-failed');
251
+ if (this.state.refreshRequested)
252
+ this.state = (0, state_1.reduceTuiInteractionState)(this.state, this.model, 'refresh-failed');
253
+ })
254
+ .finally(() => {
255
+ if (!this.running || loadId !== this.activeLoadId)
256
+ return;
257
+ this.loading = false;
258
+ this.stopLoadingPulse();
259
+ this.render();
260
+ });
261
+ return result;
262
+ }
263
+ startLoadingPulse(message) {
264
+ this.stopLoadingPulse();
265
+ const frameMs = Math.max(16, Math.floor(this.options.loadingFrameMs ?? 180));
266
+ this.loadingTimer = setInterval(() => {
267
+ if (!this.running || !this.loading)
268
+ return;
269
+ this.renderLoading(message);
270
+ }, frameMs);
271
+ this.loadingTimer.unref?.();
272
+ }
273
+ stopLoadingPulse() {
274
+ if (!this.loadingTimer)
275
+ return;
276
+ clearInterval(this.loadingTimer);
277
+ this.loadingTimer = null;
278
+ }
279
+ applyMouseKey(key) {
280
+ const parsed = parseMouseKey(key);
281
+ if (!parsed)
282
+ return undefined;
283
+ const hitbox = resolveHitbox(this.hitboxes, parsed.x, parsed.y);
284
+ if (!hitbox)
285
+ return this.render();
286
+ if (hitbox.action === 'panel') {
287
+ const activePanel = (0, constants_1.resolveTuiPanelId)(hitbox.payload, this.state.activePanel);
288
+ this.state = { ...this.state, activePanel, searchActive: activePanel === 'tasks' ? this.state.searchActive : false };
289
+ return this.render();
290
+ }
291
+ if (hitbox.action === 'task') {
292
+ const rows = (0, state_1.getTuiTaskRows)(this.model, this.state);
293
+ const selectedIndex = rows.findIndex((row) => row.id === hitbox.payload);
294
+ if (selectedIndex >= 0) {
295
+ const selected = rows[selectedIndex];
296
+ const selectedState = {
297
+ ...this.state,
298
+ selectedTaskId: selected?.id ?? null,
299
+ selectedTaskIndex: selectedIndex,
300
+ taskListScroll: this.state.taskListScroll,
301
+ documentScroll: 0
302
+ };
303
+ const requestedState = (0, state_1.reduceTuiInteractionState)(selectedState, this.model, 'enter', this.stateReduceOptions());
304
+ this.state = this.options.asyncLoading ? this.completePendingEffectsAsync(requestedState) : this.completePendingEffects(requestedState);
305
+ return this.render();
306
+ }
307
+ }
308
+ if (hitbox.action === 'document') {
309
+ this.state = (0, state_1.reduceTuiInteractionState)(this.state, this.model, hitbox.payload, this.stateReduceOptions());
310
+ return this.render();
311
+ }
312
+ return this.render();
313
+ }
314
+ shouldEnableRawMode() {
315
+ return this.options.enableRawMode !== false && Boolean(this.input.isTTY && this.input.setRawMode);
316
+ }
317
+ stateReduceOptions() {
318
+ return {
319
+ taskListVisibleRows: this.taskListVisibleRows(),
320
+ documentMaxScroll: (0, snapshot_1.getTuiDocumentScrollBounds)(this.model, this.snapshotOptions()).maxScroll
321
+ };
322
+ }
323
+ taskListVisibleRows() {
324
+ const terminalHeight = Math.max(10, Math.floor(this.options.height ?? this.output.rows ?? 32));
325
+ return (0, constants_1.tuiTaskVisibleRowsForAvailableRows)(Math.max(1, terminalHeight - 8));
326
+ }
327
+ loadModel(refresh, readOptions = {}) {
328
+ if (this.options.readModelLoader) {
329
+ const loaded = this.options.readModelLoader(refresh, readOptions);
330
+ if (loaded instanceof Promise)
331
+ throw new Error('Async readModelLoader requires asyncLoading mode.');
332
+ return loaded;
333
+ }
334
+ if (this.options.cache?.enabled) {
335
+ return (0, cache_1.createTuiReadModelWithCache)(this.options.projectRoot, {
336
+ ...readOptions,
337
+ cache: {
338
+ enabled: true,
339
+ root: this.options.cache.root,
340
+ refresh
341
+ }
342
+ }).model;
343
+ }
344
+ return (0, read_model_1.createTuiReadModel)(this.options.projectRoot, readOptions);
345
+ }
346
+ loadModelAsync(refresh, readOptions = {}) {
347
+ if (this.options.readModelLoader)
348
+ return Promise.resolve(this.options.readModelLoader(refresh, readOptions));
349
+ const workerPath = node_path_1.default.join(__dirname, 'read-model-worker.js');
350
+ return new Promise((resolve, reject) => {
351
+ const worker = new node_worker_threads_1.Worker(workerPath, {
352
+ workerData: {
353
+ projectRoot: this.options.projectRoot,
354
+ refresh,
355
+ readOptions,
356
+ cache: this.options.cache
357
+ }
358
+ });
359
+ worker.once('message', (message) => {
360
+ if (message.ok)
361
+ resolve(message.model);
362
+ else
363
+ reject(new Error(message.error));
364
+ });
365
+ worker.once('error', reject);
366
+ worker.once('exit', (code) => {
367
+ if (code !== 0)
368
+ reject(new Error(`TUI read-model worker exited with code ${code}`));
369
+ });
370
+ }).catch(() => Promise.resolve(this.loadModel(refresh, readOptions)));
371
+ }
372
+ }
373
+ exports.TuiTerminalSession = TuiTerminalSession;
374
+ function createTuiTerminalSession(options) {
375
+ return new TuiTerminalSession(options);
376
+ }
377
+ function decodeTuiInput(input) {
378
+ const text = Buffer.isBuffer(input) ? input.toString('utf8') : input;
379
+ const keys = [];
380
+ for (let index = 0; index < text.length; index += 1) {
381
+ const char = text[index] ?? '';
382
+ if (char === '\x03') {
383
+ keys.push('ctrl-c');
384
+ }
385
+ else if (char === '\t') {
386
+ keys.push('tab');
387
+ }
388
+ else if (char === '\r' || char === '\n') {
389
+ keys.push('enter');
390
+ }
391
+ else if (char === '\x7f' || char === '\b') {
392
+ keys.push('backspace');
393
+ }
394
+ else if (char === '\x1b') {
395
+ const mouse = matchMouseSequence(text.slice(index));
396
+ if (mouse) {
397
+ if (mouse.key)
398
+ keys.push(mouse.key);
399
+ index += mouse.length - 1;
400
+ continue;
401
+ }
402
+ const sequence = matchEscapeSequence(text.slice(index));
403
+ keys.push(sequence.key);
404
+ index += sequence.length - 1;
405
+ }
406
+ else {
407
+ keys.push(char);
408
+ }
409
+ }
410
+ return keys;
411
+ }
412
+ function parseMouseKey(key) {
413
+ if (typeof key !== 'string')
414
+ return null;
415
+ const match = key.match(/^mouse:(\d+):(\d+)$/);
416
+ if (!match)
417
+ return null;
418
+ return { x: Number(match[1]), y: Number(match[2]) };
419
+ }
420
+ function matchMouseSequence(text) {
421
+ const match = text.match(/^\x1b\[<(\d+);(\d+);(\d+)([mM])/);
422
+ if (!match)
423
+ return null;
424
+ if (match[4] !== 'M' || Number(match[1]) !== 0) {
425
+ return {
426
+ key: null,
427
+ length: match[0].length
428
+ };
429
+ }
430
+ return {
431
+ key: `mouse:${Number(match[2])}:${Number(match[3])}`,
432
+ length: match[0].length
433
+ };
434
+ }
435
+ function resolveHitbox(hitboxes, x, y) {
436
+ return hitboxes.find((box) => x >= box.x1 && x <= box.x2 && y >= box.y1 && y <= box.y2) ?? null;
437
+ }
438
+ function matchEscapeSequence(text) {
439
+ if (text.startsWith('\x1b[A'))
440
+ return { key: 'up', length: 3 };
441
+ if (text.startsWith('\x1b[B'))
442
+ return { key: 'down', length: 3 };
443
+ if (text.startsWith('\x1b[C'))
444
+ return { key: 'right', length: 3 };
445
+ if (text.startsWith('\x1b[D'))
446
+ return { key: 'left', length: 3 };
447
+ if (text.startsWith('\x1bOA'))
448
+ return { key: 'up', length: 3 };
449
+ if (text.startsWith('\x1bOB'))
450
+ return { key: 'down', length: 3 };
451
+ if (text.startsWith('\x1bOC'))
452
+ return { key: 'right', length: 3 };
453
+ if (text.startsWith('\x1bOD'))
454
+ return { key: 'left', length: 3 };
455
+ const modifiedArrow = text.match(/^\x1b\[(?:1|0)?(?:;\d+)+([ABCD])/);
456
+ if (modifiedArrow) {
457
+ const keyByCode = { A: 'up', B: 'down', C: 'right', D: 'left' };
458
+ return { key: keyByCode[modifiedArrow[1] ?? ''] ?? 'escape', length: modifiedArrow[0].length };
459
+ }
460
+ if (text.startsWith('\x1b[Z'))
461
+ return { key: 'shift-tab', length: 3 };
462
+ if (text.startsWith('\x1b[H'))
463
+ return { key: 'home', length: 3 };
464
+ if (text.startsWith('\x1b[F'))
465
+ return { key: 'end', length: 3 };
466
+ if (text.startsWith('\x1b[1~'))
467
+ return { key: 'home', length: 4 };
468
+ if (text.startsWith('\x1b[4~'))
469
+ return { key: 'end', length: 4 };
470
+ if (text.startsWith('\x1b[5~'))
471
+ return { key: 'pageup', length: 4 };
472
+ if (text.startsWith('\x1b[6~'))
473
+ return { key: 'pagedown', length: 4 };
474
+ return { key: 'escape', length: 1 };
475
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeTuiThemeName = normalizeTuiThemeName;
4
+ exports.tuiFg = tuiFg;
5
+ exports.tuiBg = tuiBg;
6
+ exports.tuiSwatch = tuiSwatch;
7
+ exports.tuiColorEnabled = tuiColorEnabled;
8
+ const ANSI_RESET = '\x1b[0m';
9
+ const THEME_HEX = {
10
+ hadara: {
11
+ canvas: '#0B1114',
12
+ panel: '#121D22',
13
+ panel2: '#17262C',
14
+ border: '#34434A',
15
+ text: '#DED6C2',
16
+ text2: '#BDB49D',
17
+ muted: '#848978',
18
+ dim: '#5D675F',
19
+ gold: '#C6A15F',
20
+ gold2: '#E0B96D',
21
+ teal: '#63AEB8',
22
+ teal2: '#82C7CE',
23
+ pass: '#82BE86',
24
+ warn: '#D0A45A',
25
+ fail: '#D97B73',
26
+ violet: '#A891DD',
27
+ white: '#F2EBD8',
28
+ black: '#081014'
29
+ },
30
+ contrast: {
31
+ canvas: '#050505',
32
+ panel: '#111111',
33
+ panel2: '#242424',
34
+ border: '#777777',
35
+ text: '#F3F3F3',
36
+ text2: '#D1D1D1',
37
+ muted: '#AAAAAA',
38
+ dim: '#777777',
39
+ gold: '#FFD166',
40
+ gold2: '#FFE08A',
41
+ teal: '#4ECDC4',
42
+ teal2: '#8CF5EA',
43
+ pass: '#74F174',
44
+ warn: '#FFD166',
45
+ fail: '#FF6B6B',
46
+ violet: '#C4A7FF',
47
+ white: '#FFFFFF',
48
+ black: '#000000'
49
+ }
50
+ };
51
+ function normalizeTuiThemeName(value, fallback = 'none') {
52
+ const normalized = String(value ?? '').toLowerCase();
53
+ if (normalized === 'hadara' || normalized === 'contrast' || normalized === 'none')
54
+ return normalized;
55
+ return fallback;
56
+ }
57
+ function tuiFg(theme, role, text) {
58
+ if (theme === 'none')
59
+ return text;
60
+ return `${ansiFg(theme, role)}${text}${ANSI_RESET}`;
61
+ }
62
+ function tuiBg(theme, role, text = '') {
63
+ if (theme === 'none')
64
+ return text;
65
+ return `${ansiBg(theme, role)}${text}${ANSI_RESET}`;
66
+ }
67
+ function tuiSwatch(theme, background, foreground, text) {
68
+ if (theme === 'none')
69
+ return text;
70
+ return `${ansiBg(theme, background)}${ansiFg(theme, foreground)}${text}${ANSI_RESET}`;
71
+ }
72
+ function tuiColorEnabled(theme) {
73
+ return theme !== 'none';
74
+ }
75
+ function ansiFg(theme, role) {
76
+ const [red, green, blue] = hexToRgb(THEME_HEX[theme][role]);
77
+ return `\x1b[38;2;${red};${green};${blue}m`;
78
+ }
79
+ function ansiBg(theme, role) {
80
+ const [red, green, blue] = hexToRgb(THEME_HEX[theme][role]);
81
+ return `\x1b[48;2;${red};${green};${blue}m`;
82
+ }
83
+ function hexToRgb(hex) {
84
+ const value = hex.replace('#', '');
85
+ return [Number.parseInt(value.slice(0, 2), 16), Number.parseInt(value.slice(2, 4), 16), Number.parseInt(value.slice(4, 6), 16)];
86
+ }
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "hadara",
3
+ "version": "0.1.0-rc.0",
4
+ "private": false,
5
+ "license": "MIT",
6
+ "description": "HADARA: portable agentic development workbench",
7
+ "bin": {
8
+ "hadara": "./dist/cli/main.js"
9
+ },
10
+ "files": [
11
+ "dist/",
12
+ "README.md",
13
+ "LICENSE",
14
+ "package.json"
15
+ ]
16
+ }