martty 0.2.11

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/bin/dsh-tui.js +68 -0
  4. package/cordis.patch.yml +30 -0
  5. package/creator/cordis.patch.yml +6 -0
  6. package/creator/package.json +10 -0
  7. package/lib/acp-client-events.js +65 -0
  8. package/lib/acp-client.js +114 -0
  9. package/lib/acp-host.js +24 -0
  10. package/lib/acp-session-config.js +376 -0
  11. package/lib/acp-session-plan.js +196 -0
  12. package/lib/acp-session-stats.js +239 -0
  13. package/lib/agent.js +64 -0
  14. package/lib/boot.js +119 -0
  15. package/lib/client-process.js +11 -0
  16. package/lib/client-run.js +379 -0
  17. package/lib/cordis-protocol.js +51 -0
  18. package/lib/creator-overlay.js +77 -0
  19. package/lib/demo-skin.js +79 -0
  20. package/lib/ember.js +20 -0
  21. package/lib/index.js +226 -0
  22. package/lib/inspect.js +971 -0
  23. package/lib/jsonrpc-line-transport.js +155 -0
  24. package/lib/mux.js +281 -0
  25. package/lib/palettes/default.json +44 -0
  26. package/lib/palettes/ember.json +44 -0
  27. package/lib/plan-view.js +92 -0
  28. package/lib/profile-acp-client.js +11 -0
  29. package/lib/right-demo.js +55 -0
  30. package/lib/runner.js +94 -0
  31. package/lib/spawn-tui.js +179 -0
  32. package/lib/stats-view.js +90 -0
  33. package/lib/tui-commands.js +144 -0
  34. package/lib/tui-overlay.js +252 -0
  35. package/lib/tui-slots.js +351 -0
  36. package/lib/tui-theme.js +463 -0
  37. package/package.json +83 -0
  38. package/skills/tui-plugin-development/SKILL.md +172 -0
  39. package/vendor/darwin-arm64/dsh-tui +0 -0
  40. package/vendor/darwin-x64/dsh-tui +0 -0
  41. package/vendor/linux-arm64/dsh-tui +0 -0
  42. package/vendor/linux-x64/dsh-tui +0 -0
  43. package/vendor/win32-x64/dsh-tui.exe +0 -0
@@ -0,0 +1,463 @@
1
+ /**
2
+ * Plugin ABI for TUI palette packs: `ctx.tuiTheme.register(palette)`.
3
+ *
4
+ * Transport method `_dsh/cordis/tui/theme/update` is compositor-private.
5
+ * Plugins never call it.
6
+ * Registrations that happen before JSON-RPC `notify` exists are queued and
7
+ * flushed from `bindNotify`.
8
+ */
9
+
10
+ import { readFileSync } from 'node:fs'
11
+ import { isAbsolute } from 'node:path'
12
+ import { CORDIS_METHODS } from './cordis-protocol.js'
13
+
14
+ export const name = 'tui-theme'
15
+ export const inject = []
16
+
17
+ const defaultPalette = JSON.parse(
18
+ readFileSync(new URL('./palettes/default.json', import.meta.url), 'utf8'),
19
+ )
20
+
21
+ export const TOKEN_NAMES = Object.freeze([
22
+ 'bg',
23
+ 'surface',
24
+ 'panel',
25
+ 'fg',
26
+ 'fg_secondary',
27
+ 'fg_tertiary',
28
+ 'caption',
29
+ 'brand',
30
+ 'brand_soft',
31
+ 'bubble_bg',
32
+ 'bubble_fg',
33
+ 'border',
34
+ 'code_bg',
35
+ 'ok',
36
+ 'warn',
37
+ 'err',
38
+ 'hint',
39
+ 'chip_bg',
40
+ ])
41
+
42
+ const TOKEN_DESCRIPTIONS = {
43
+ bg: 'Full-screen background.',
44
+ surface: 'Raised transcript surface.',
45
+ panel: 'Composer, status, and chrome panels.',
46
+ fg: 'Primary text.',
47
+ fg_secondary: 'Secondary text.',
48
+ fg_tertiary: 'Tertiary text.',
49
+ caption: 'Captions and timestamps.',
50
+ brand: 'Brand accent.',
51
+ brand_soft: 'Soft brand fill.',
52
+ bubble_bg: 'User bubble background.',
53
+ bubble_fg: 'User bubble text.',
54
+ border: 'Borders and rules.',
55
+ code_bg: 'Code block background.',
56
+ ok: 'Success state.',
57
+ warn: 'Warning state.',
58
+ err: 'Error state.',
59
+ hint: 'Hint and prompt-row accent.',
60
+ chip_bg: 'Chip fill.',
61
+ }
62
+
63
+ /** Closed token directory for Cordis Client inspect (`Theme.listTokens`). */
64
+ export const TOKEN_INSPECT = Object.freeze(
65
+ TOKEN_NAMES.map((name) => Object.freeze({
66
+ name,
67
+ description: TOKEN_DESCRIPTIONS[name],
68
+ valueType: '#RRGGBB',
69
+ requiresLightAndDark: true,
70
+ })),
71
+ )
72
+
73
+ const HEX = /^#[0-9A-Fa-f]{6}$/
74
+ const PALETTE_FIELDS = new Set(['id', 'label', 'dark', 'light', 'background'])
75
+ const BACKGROUND_FIELDS = new Set(['source', 'fit', 'anchor', 'opacity'])
76
+ const BACKGROUND_SOURCE_FIELDS = new Set(['kind', 'path', 'mediaType', 'base64'])
77
+ const BACKGROUND_ANCHOR_FIELDS = new Set(['x', 'y'])
78
+ const PROTOCOL = 0
79
+
80
+ /**
81
+ * Validate a palette against tui-palette protocol 0 (closed token names).
82
+ * @param {unknown} palette
83
+ * @returns {{ id: string, label: string, dark: Record<string, string>, light: Record<string, string>, background?: object }}
84
+ */
85
+ export function validatePalette(palette) {
86
+ if (palette === null || typeof palette !== 'object' || Array.isArray(palette)) {
87
+ throw new Error('tuiTheme.register: palette must be an object')
88
+ }
89
+ const extraFields = Object.keys(palette).filter((key) => !PALETTE_FIELDS.has(key))
90
+ if (extraFields.length > 0) {
91
+ throw new Error(`tuiTheme.register: unknown field(s) ${extraFields.join(', ')}`)
92
+ }
93
+ if (typeof palette.id !== 'string' || palette.id.length < 1) {
94
+ throw new Error('tuiTheme.register: id must be a non-empty string')
95
+ }
96
+ if (typeof palette.label !== 'string' || palette.label.length < 1) {
97
+ throw new Error('tuiTheme.register: label must be a non-empty string')
98
+ }
99
+ const validated = {
100
+ id: palette.id,
101
+ label: palette.label,
102
+ dark: validateTokenMap(palette.dark, 'dark'),
103
+ light: validateTokenMap(palette.light, 'light'),
104
+ }
105
+ if (palette.background !== undefined) {
106
+ validated.background = validateBackground(palette.background)
107
+ }
108
+ return validated
109
+ }
110
+
111
+ function exactFields(value, allowed, at) {
112
+ const extra = Object.keys(value).filter((key) => !allowed.has(key))
113
+ if (extra.length > 0) {
114
+ throw new Error(`tuiTheme.register: ${at} unknown field(s) ${extra.join(', ')}`)
115
+ }
116
+ }
117
+
118
+ function finiteUnit(value, fallback, at) {
119
+ const selected = value === undefined ? fallback : value
120
+ if (typeof selected !== 'number' || !Number.isFinite(selected)
121
+ || selected < 0 || selected > 1) {
122
+ throw new Error(`tuiTheme.register: ${at} must be a finite number from 0 to 1`)
123
+ }
124
+ return selected
125
+ }
126
+
127
+ function validateBackground(background) {
128
+ if (background === null || typeof background !== 'object' || Array.isArray(background)) {
129
+ throw new Error('tuiTheme.register: background must be an object')
130
+ }
131
+ exactFields(background, BACKGROUND_FIELDS, 'background')
132
+ const source = background.source
133
+ if (source === null || typeof source !== 'object' || Array.isArray(source)) {
134
+ throw new Error('tuiTheme.register: background.source must be an object')
135
+ }
136
+ exactFields(source, BACKGROUND_SOURCE_FIELDS, 'background.source')
137
+ let validatedSource
138
+ if (source.kind === 'file') {
139
+ if (typeof source.path !== 'string' || !isAbsolute(source.path)) {
140
+ throw new Error('tuiTheme.register: background.source.path must be absolute')
141
+ }
142
+ if (source.mediaType !== undefined || source.base64 !== undefined) {
143
+ throw new Error('tuiTheme.register: file background source only accepts kind and path')
144
+ }
145
+ validatedSource = { kind: 'file', path: source.path }
146
+ } else if (source.kind === 'data') {
147
+ if (source.path !== undefined || source.mediaType !== 'image/png'
148
+ || typeof source.base64 !== 'string' || source.base64.length === 0) {
149
+ throw new Error(
150
+ 'tuiTheme.register: data background source needs image/png mediaType and base64',
151
+ )
152
+ }
153
+ validatedSource = {
154
+ kind: 'data',
155
+ mediaType: 'image/png',
156
+ base64: source.base64,
157
+ }
158
+ } else {
159
+ throw new Error('tuiTheme.register: background.source.kind must be file or data')
160
+ }
161
+
162
+ const fit = background.fit ?? 'cover'
163
+ if (!['cover', 'contain', 'stretch'].includes(fit)) {
164
+ throw new Error('tuiTheme.register: background.fit must be cover, contain, or stretch')
165
+ }
166
+ const anchor = background.anchor ?? {}
167
+ if (anchor === null || typeof anchor !== 'object' || Array.isArray(anchor)) {
168
+ throw new Error('tuiTheme.register: background.anchor must be an object')
169
+ }
170
+ exactFields(anchor, BACKGROUND_ANCHOR_FIELDS, 'background.anchor')
171
+ return {
172
+ source: validatedSource,
173
+ fit,
174
+ anchor: {
175
+ x: finiteUnit(anchor.x, 0.5, 'background.anchor.x'),
176
+ y: finiteUnit(anchor.y, 0.5, 'background.anchor.y'),
177
+ },
178
+ opacity: finiteUnit(background.opacity, 1, 'background.opacity'),
179
+ }
180
+ }
181
+
182
+ function validateTokenMap(map, which) {
183
+ if (map === null || typeof map !== 'object' || Array.isArray(map)) {
184
+ throw new Error(`tuiTheme.register: ${which} must be an object`)
185
+ }
186
+ const missing = TOKEN_NAMES.filter((name) => !Object.hasOwn(map, name))
187
+ if (missing.length > 0) {
188
+ throw new Error(`tuiTheme.register: ${which} missing token(s) ${missing.join(', ')}`)
189
+ }
190
+ const extra = Object.keys(map).filter((name) => !TOKEN_NAMES.includes(name))
191
+ if (extra.length > 0) {
192
+ throw new Error(`tuiTheme.register: ${which} unknown token(s) ${extra.join(', ')}`)
193
+ }
194
+ const out = {}
195
+ for (const name of TOKEN_NAMES) {
196
+ const value = map[name]
197
+ if (typeof value !== 'string' || !HEX.test(value)) {
198
+ throw new Error(`tuiTheme.register: ${which}.${name} must be a #RRGGBB hex color`)
199
+ }
200
+ out[name] = value
201
+ }
202
+ return out
203
+ }
204
+
205
+ /**
206
+ * Install `ctx.tuiTheme`. The service catalog starts with builtin `default`.
207
+ * `register` grows the catalog (like adding an agent preset). `/theme` and
208
+ * `activate` switch which pack covers. Builtin `default` is never mutated.
209
+ * @param {object} ctx
210
+ * @param {{ notify?: (method: string, params: object) => void }} [options]
211
+ * @returns {{ register: Function, activate: Function, list: Function, active: Function, subscribe: Function, observeSelected: Function, exportInspectTokens: Function, bindNotify: Function, flush: Function }}
212
+ */
213
+ export function installTuiTheme(ctx, options = {}) {
214
+ const palettes = new Map()
215
+ const queue = []
216
+ const listeners = new Set()
217
+ let send = typeof options.notify === 'function' ? options.notify : undefined
218
+ let activeId = 'default'
219
+ let activationRevision = 0
220
+
221
+ const builtin = validatePalette(defaultPalette)
222
+ palettes.set(builtin.id, { palette: builtin, owner: undefined, loaded: true })
223
+
224
+ function emit(method, params) {
225
+ if (typeof send === 'function') {
226
+ send(method, params)
227
+ return
228
+ }
229
+ queue.push({ method, params })
230
+ }
231
+
232
+ function flush() {
233
+ if (typeof send !== 'function') return
234
+ const pending = queue.splice(0, queue.length)
235
+ for (const item of pending) send(item.method, item.params)
236
+ }
237
+
238
+ function bindNotify(fn) {
239
+ send = fn
240
+ flush()
241
+ }
242
+
243
+ function select(id, notifyPainter) {
244
+ if (typeof id !== 'string' || id.length < 1) {
245
+ throw new Error('tuiTheme.activate: id must be a non-empty string')
246
+ }
247
+ const entry = palettes.get(id)
248
+ if (entry === undefined) {
249
+ throw new Error(`tuiTheme.activate: palette "${id}" is not registered`)
250
+ }
251
+ if (!entry.loaded) {
252
+ throw new Error(`tuiTheme.activate: theme Plugin for palette "${id}" is not loaded`)
253
+ }
254
+ const { palette } = entry
255
+ const previousId = activeId
256
+ activeId = id
257
+ activationRevision += 1
258
+ if (notifyPainter) {
259
+ emit(CORDIS_METHODS.themeUpdate, { protocol: PROTOCOL, palette, activate: true })
260
+ }
261
+ if (previousId !== id) {
262
+ for (const listener of [...listeners]) {
263
+ try {
264
+ listener(id)
265
+ } catch {
266
+ // A broken Plugin subscriber must not block the registry commit.
267
+ }
268
+ }
269
+ }
270
+ }
271
+
272
+ function activate(id) {
273
+ select(id, true)
274
+ }
275
+
276
+ function observeSelected(params) {
277
+ if (params === null || typeof params !== 'object' || Array.isArray(params)) {
278
+ throw new Error('tuiTheme.observeSelected: params must be an object')
279
+ }
280
+ if (params.protocol !== PROTOCOL) return false
281
+ select(params.id, false)
282
+ return true
283
+ }
284
+
285
+ function register(palette, registerOptions = {}) {
286
+ return registerOwned(undefined, palette, registerOptions)
287
+ }
288
+
289
+ /** Dynamic Client runner entry: pin a palette to its owning Plugin id. */
290
+ function registerOwned(owner, palette, registerOptions = {}) {
291
+ if (owner !== undefined && (typeof owner !== 'string' || owner.length === 0)) {
292
+ throw new Error('tuiTheme.registerOwned: owner must be a non-empty string')
293
+ }
294
+ const validated = validatePalette(palette)
295
+ if (validated.id === 'default') {
296
+ throw new Error('tuiTheme.register: cannot replace builtin palette "default"')
297
+ }
298
+ const replace = registerOptions.replace === true
299
+ const retained = palettes.get(validated.id)
300
+ const resumesOwner = owner !== undefined
301
+ && retained?.owner === owner
302
+ && retained.loaded === false
303
+ if (retained !== undefined && !replace && !resumesOwner) {
304
+ throw new Error(`tuiTheme.register: palette "${validated.id}" is already registered`)
305
+ }
306
+ const cover = owner !== undefined || registerOptions.activate === true
307
+ let entry
308
+ let released = false
309
+ const setup = () => {
310
+ const previousId = cover ? activeId : undefined
311
+ let leaseRevision
312
+ entry = { palette: validated, owner, loaded: true }
313
+ palettes.set(validated.id, entry)
314
+ emit(CORDIS_METHODS.themeUpdate, {
315
+ protocol: PROTOCOL,
316
+ palette: validated,
317
+ activate: false,
318
+ loaded: true,
319
+ ...(owner === undefined ? {} : { owner: { pluginId: owner } }),
320
+ })
321
+ if (cover) {
322
+ activate(validated.id)
323
+ leaseRevision = activationRevision
324
+ }
325
+ return () => {
326
+ const wasActive = activeId === validated.id
327
+ const ownsActivation = cover && wasActive && activationRevision === leaseRevision
328
+ const removed = palettes.get(validated.id) === entry
329
+ if (!removed) return
330
+ if (ownsActivation) {
331
+ const restoreId = previousId !== undefined && palettes.get(previousId)?.loaded === true
332
+ ? previousId
333
+ : 'default'
334
+ activate(restoreId)
335
+ } else if (wasActive) {
336
+ activate('default')
337
+ }
338
+ if (owner === undefined) {
339
+ palettes.delete(validated.id)
340
+ emit(CORDIS_METHODS.themeRemove, { protocol: PROTOCOL, id: validated.id })
341
+ } else {
342
+ entry.loaded = false
343
+ emit(CORDIS_METHODS.themeUpdate, {
344
+ protocol: PROTOCOL,
345
+ palette: validated,
346
+ activate: false,
347
+ loaded: false,
348
+ owner: { pluginId: owner },
349
+ })
350
+ }
351
+ }
352
+ }
353
+ const update = (patch) => {
354
+ if (released || entry === undefined || palettes.get(validated.id) !== entry) {
355
+ throw new Error(`tuiTheme.register: palette "${validated.id}" is no longer registered`)
356
+ }
357
+ if (patch === null || typeof patch !== 'object' || Array.isArray(patch)) {
358
+ throw new Error('tuiTheme.register: update patch must be an object')
359
+ }
360
+ const extra = Object.keys(patch).filter((key) => key !== 'background')
361
+ if (extra.length > 0) {
362
+ throw new Error(`tuiTheme.register: update unknown field(s) ${extra.join(', ')}`)
363
+ }
364
+ if (!Object.hasOwn(patch, 'background')) {
365
+ throw new Error('tuiTheme.register: update needs background')
366
+ }
367
+ const next = { ...entry.palette }
368
+ if (patch.background === null) delete next.background
369
+ else next.background = validateBackground(patch.background)
370
+ entry.palette = next
371
+ emit(CORDIS_METHODS.themeUpdate, {
372
+ protocol: PROTOCOL,
373
+ palette: next,
374
+ activate: false,
375
+ loaded: true,
376
+ ...(owner === undefined ? {} : { owner: { pluginId: owner } }),
377
+ })
378
+ }
379
+ let release
380
+ if (typeof ctx.effect === 'function') {
381
+ release = ctx.effect(setup, `tuiTheme.register(${JSON.stringify(validated.id)})`)
382
+ } else {
383
+ release = setup()
384
+ }
385
+ const dispose = () => {
386
+ if (released) return
387
+ released = true
388
+ return release?.()
389
+ }
390
+ dispose.update = update
391
+ dispose.dispose = dispose
392
+ return dispose
393
+ }
394
+
395
+ function list() {
396
+ return [...palettes.values()].map(({ palette }) => ({
397
+ id: palette.id,
398
+ label: palette.label,
399
+ }))
400
+ }
401
+
402
+ function active() {
403
+ return activeId
404
+ }
405
+
406
+ function owner(id) {
407
+ return palettes.get(id)?.owner
408
+ }
409
+
410
+ function isLoaded(id) {
411
+ return palettes.get(id)?.loaded === true
412
+ }
413
+
414
+ function subscribe(listener) {
415
+ if (typeof listener !== 'function') {
416
+ throw new Error('tuiTheme.subscribe: listener must be a function')
417
+ }
418
+ const setup = () => {
419
+ listeners.add(listener)
420
+ return () => listeners.delete(listener)
421
+ }
422
+ const release = typeof ctx.effect === 'function'
423
+ ? ctx.effect(setup, 'tuiTheme.subscribe')
424
+ : setup()
425
+ let disposed = false
426
+ return () => {
427
+ if (disposed) return
428
+ disposed = true
429
+ return release?.()
430
+ }
431
+ }
432
+
433
+ function exportInspectTokens() {
434
+ return TOKEN_INSPECT.map((token) => ({ ...token }))
435
+ }
436
+
437
+ const service = {
438
+ register,
439
+ registerOwned,
440
+ activate,
441
+ list,
442
+ active,
443
+ owner,
444
+ isLoaded,
445
+ subscribe,
446
+ observeSelected,
447
+ exportInspectTokens,
448
+ bindNotify,
449
+ flush,
450
+ }
451
+ if (typeof ctx.provide === 'function') {
452
+ ctx.provide('tuiTheme', service)
453
+ } else {
454
+ ctx.tuiTheme = service
455
+ }
456
+
457
+ return service
458
+ }
459
+
460
+ /** Cordis plugin entry: provide the client-tree theme registry. */
461
+ export function apply(ctx) {
462
+ installTuiTheme(ctx)
463
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "martty",
3
+ "version": "0.2.11",
4
+ "description": "Terminal-native ACP client UI; Cordis client tree, any ACP agent",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/openma-ai/Martty.git"
9
+ },
10
+ "homepage": "https://dshsuite.dev",
11
+ "bugs": {
12
+ "url": "https://github.com/openma-ai/Martty/issues"
13
+ },
14
+ "type": "module",
15
+ "main": "lib/index.js",
16
+ "scripts": {
17
+ "pretest": "node --test ../scripts/workflow-release.test.mjs ../scripts/package-alias.test.mjs",
18
+ "test": "node --test ../scripts/package-native.test.mjs ../scripts/check-release-tag.test.mjs ../scripts/check-static-elf.test.mjs ../scripts/smoke-old-linux.test.mjs ../scripts/cargo-guard.test.mjs ../scripts/build-npm.test.mjs ../scripts/client-profile.test.mjs ../scripts/plugin-runner.test.mjs ../scripts/profile-link-resolution.test.mjs ../scripts/release.test.mjs ../scripts/jsonrpc-line-transport.test.mjs ../scripts/tui-theme.test.mjs ../scripts/tui-slots.test.mjs ../scripts/tui-commands.test.mjs ../scripts/tui-overlay.test.mjs ../scripts/mux.test.mjs ../scripts/acp-client.test.mjs ../scripts/acp-client-events.test.mjs ../scripts/acp-session-config.test.mjs ../scripts/acp-session-plan.test.mjs ../scripts/acp-session-stats.test.mjs ../scripts/plan-view.test.mjs ../scripts/stats-view.test.mjs ../scripts/runner.test.mjs ../scripts/inspect.test.mjs ../scripts/creator-overlay.test.mjs ../scripts/real-agent-e2e.test.mjs"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public",
22
+ "tag": "latest"
23
+ },
24
+ "exports": {
25
+ ".": "./lib/index.js",
26
+ "./ember": "./lib/ember.js",
27
+ "./theme": "./lib/tui-theme.js",
28
+ "./slots": "./lib/tui-slots.js",
29
+ "./commands": "./lib/tui-commands.js",
30
+ "./overlay": "./lib/tui-overlay.js",
31
+ "./session-plan": "./lib/acp-session-plan.js",
32
+ "./session-stats": "./lib/acp-session-stats.js",
33
+ "./plan-view": "./lib/plan-view.js",
34
+ "./stats-view": "./lib/stats-view.js",
35
+ "./right-demo": "./lib/right-demo.js",
36
+ "./acp-client": "./lib/acp-client.js",
37
+ "./acp-client-events": "./lib/acp-client-events.js",
38
+ "./profile-acp-client": "./lib/profile-acp-client.js",
39
+ "./acp-host": "./lib/acp-host.js",
40
+ "./creator-overlay": "./lib/creator-overlay.js",
41
+ "./cordis-client-runner": "./lib/inspect.js",
42
+ "./runner": "./lib/runner.js",
43
+ "./cordis.patch.yml": "./cordis.patch.yml",
44
+ "./package.json": "./package.json"
45
+ },
46
+ "bin": {
47
+ "dsh-tui": "bin/dsh-tui.js",
48
+ "dsb": "bin/dsh-tui.js"
49
+ },
50
+ "files": [
51
+ "bin",
52
+ "lib",
53
+ "creator",
54
+ "skills/tui-plugin-development/SKILL.md",
55
+ "vendor",
56
+ "cordis.patch.yml",
57
+ "README.md",
58
+ "LICENSE"
59
+ ],
60
+ "dsh": {
61
+ "bundle": {
62
+ "patch": "./cordis.patch.yml"
63
+ }
64
+ },
65
+ "dependencies": {
66
+ "@deepseek-ai/cordis": "^4.0.1",
67
+ "@openma/deepseek-harness-acp": "0.4.13"
68
+ },
69
+ "devDependencies": {
70
+ "@deepseek-ai/dsh": "0.1.0-rc.6"
71
+ },
72
+ "engines": {
73
+ "node": ">=18"
74
+ },
75
+ "keywords": [
76
+ "deepseek",
77
+ "harness",
78
+ "tui",
79
+ "agent",
80
+ "acp",
81
+ "agent-client-protocol"
82
+ ]
83
+ }