ghost-dragon 4.2.1
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/.github/workflows/ci.yml +23 -0
- package/CHANGELOG.md +96 -0
- package/README.md +193 -0
- package/bootstrap.ps1 +83 -0
- package/bootstrap.sh +71 -0
- package/dist/agent/loop.d.ts +68 -0
- package/dist/agent/loop.d.ts.map +1 -0
- package/dist/agent/loop.js +135 -0
- package/dist/agent/mcp.d.ts +33 -0
- package/dist/agent/mcp.d.ts.map +1 -0
- package/dist/agent/mcp.js +107 -0
- package/dist/agent/session.d.ts +16 -0
- package/dist/agent/session.d.ts.map +1 -0
- package/dist/agent/session.js +55 -0
- package/dist/agent/skills.d.ts +36 -0
- package/dist/agent/skills.d.ts.map +1 -0
- package/dist/agent/skills.js +153 -0
- package/dist/agent/stack.d.ts +21 -0
- package/dist/agent/stack.d.ts.map +1 -0
- package/dist/agent/stack.js +158 -0
- package/dist/agent/task.d.ts +21 -0
- package/dist/agent/task.d.ts.map +1 -0
- package/dist/agent/task.js +45 -0
- package/dist/agent/tools.d.ts +44 -0
- package/dist/agent/tools.d.ts.map +1 -0
- package/dist/agent/tools.js +262 -0
- package/dist/agent/trace.d.ts +34 -0
- package/dist/agent/trace.d.ts.map +1 -0
- package/dist/agent/trace.js +72 -0
- package/dist/agent.d.ts +46 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +103 -0
- package/dist/auth.d.ts +74 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +116 -0
- package/dist/brain/anthropic.d.ts +19 -0
- package/dist/brain/anthropic.d.ts.map +1 -0
- package/dist/brain/anthropic.js +74 -0
- package/dist/brain/claude-cli.d.ts +20 -0
- package/dist/brain/claude-cli.d.ts.map +1 -0
- package/dist/brain/claude-cli.js +79 -0
- package/dist/brain/ghost-ember.d.ts +28 -0
- package/dist/brain/ghost-ember.d.ts.map +1 -0
- package/dist/brain/ghost-ember.js +97 -0
- package/dist/brain/index.d.ts +22 -0
- package/dist/brain/index.d.ts.map +1 -0
- package/dist/brain/index.js +95 -0
- package/dist/brain/openai-compat.d.ts +21 -0
- package/dist/brain/openai-compat.d.ts.map +1 -0
- package/dist/brain/openai-compat.js +119 -0
- package/dist/brain/router/classify.d.ts +23 -0
- package/dist/brain/router/classify.d.ts.map +1 -0
- package/dist/brain/router/classify.js +160 -0
- package/dist/brain/router/execute.d.ts +23 -0
- package/dist/brain/router/execute.d.ts.map +1 -0
- package/dist/brain/router/execute.js +84 -0
- package/dist/brain/router/index.d.ts +26 -0
- package/dist/brain/router/index.d.ts.map +1 -0
- package/dist/brain/router/index.js +118 -0
- package/dist/brain/router/routing-memory.d.ts +27 -0
- package/dist/brain/router/routing-memory.d.ts.map +1 -0
- package/dist/brain/router/routing-memory.js +77 -0
- package/dist/brain/router/select.d.ts +32 -0
- package/dist/brain/router/select.d.ts.map +1 -0
- package/dist/brain/router/select.js +146 -0
- package/dist/brain/router/two-hop.d.ts +23 -0
- package/dist/brain/router/two-hop.d.ts.map +1 -0
- package/dist/brain/router/two-hop.js +39 -0
- package/dist/brain/router/verify.d.ts +37 -0
- package/dist/brain/router/verify.d.ts.map +1 -0
- package/dist/brain/router/verify.js +111 -0
- package/dist/brain/types.d.ts +55 -0
- package/dist/brain/types.d.ts.map +1 -0
- package/dist/brain/types.js +16 -0
- package/dist/brain/worker.d.ts +27 -0
- package/dist/brain/worker.d.ts.map +1 -0
- package/dist/brain/worker.js +71 -0
- package/dist/commands/ai.d.ts +24 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +137 -0
- package/dist/commands/alerts.d.ts +19 -0
- package/dist/commands/alerts.d.ts.map +1 -0
- package/dist/commands/alerts.js +114 -0
- package/dist/commands/billing.d.ts +13 -0
- package/dist/commands/billing.d.ts.map +1 -0
- package/dist/commands/billing.js +55 -0
- package/dist/commands/chat.d.ts +22 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +422 -0
- package/dist/commands/config.d.ts +18 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +136 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +73 -0
- package/dist/commands/global.d.ts +11 -0
- package/dist/commands/global.d.ts.map +1 -0
- package/dist/commands/global.js +253 -0
- package/dist/commands/keep.d.ts +12 -0
- package/dist/commands/keep.d.ts.map +1 -0
- package/dist/commands/keep.js +58 -0
- package/dist/commands/lifecycle.d.ts +17 -0
- package/dist/commands/lifecycle.d.ts.map +1 -0
- package/dist/commands/lifecycle.js +267 -0
- package/dist/commands/login.d.ts +16 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +234 -0
- package/dist/commands/maintenance.d.ts +12 -0
- package/dist/commands/maintenance.d.ts.map +1 -0
- package/dist/commands/maintenance.js +76 -0
- package/dist/commands/mcp.d.ts +16 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +56 -0
- package/dist/commands/memory.d.ts +13 -0
- package/dist/commands/memory.d.ts.map +1 -0
- package/dist/commands/memory.js +218 -0
- package/dist/commands/osint.d.ts +14 -0
- package/dist/commands/osint.d.ts.map +1 -0
- package/dist/commands/osint.js +161 -0
- package/dist/commands/pentest.d.ts +13 -0
- package/dist/commands/pentest.d.ts.map +1 -0
- package/dist/commands/pentest.js +131 -0
- package/dist/commands/scale.d.ts +14 -0
- package/dist/commands/scale.d.ts.map +1 -0
- package/dist/commands/scale.js +191 -0
- package/dist/commands/serve.d.ts +16 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +167 -0
- package/dist/commands/tui.d.ts +17 -0
- package/dist/commands/tui.d.ts.map +1 -0
- package/dist/commands/tui.js +138 -0
- package/dist/commands/wyrm.d.ts +20 -0
- package/dist/commands/wyrm.d.ts.map +1 -0
- package/dist/commands/wyrm.js +274 -0
- package/dist/config.d.ts +67 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +54 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +85 -0
- package/dist/manifest.d.ts +31 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +83 -0
- package/dist/ui.d.ts +57 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +174 -0
- package/dist/utils.d.ts +33 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +155 -0
- package/dist/wyrm/mcp.d.ts +37 -0
- package/dist/wyrm/mcp.d.ts.map +1 -0
- package/dist/wyrm/mcp.js +137 -0
- package/docs/SYSTEM-PREMORTEM.md +397 -0
- package/dragon-manifest.toml +241 -0
- package/dragon.py +177 -0
- package/install/launchd/lk.ghosts.dragonkeep.plist +57 -0
- package/install/systemd/dragonkeep.service +40 -0
- package/media/dragon-silver-lockup.svg +931 -0
- package/media/dragon-silver-mark.svg +931 -0
- package/media/dragon-silver.png +0 -0
- package/package.json +45 -0
- package/specs/001-godmode/constitution.md +54 -0
- package/specs/001-godmode/plan.md +30 -0
- package/specs/001-godmode/spec.md +64 -0
- package/specs/001-godmode/tasks.md +35 -0
- package/specs/002-premortem-positioning/premortem.md +211 -0
- package/src/agent/loop.ts +165 -0
- package/src/agent/mcp.ts +92 -0
- package/src/agent/session.ts +48 -0
- package/src/agent/skills.ts +138 -0
- package/src/agent/stack.ts +154 -0
- package/src/agent/task.ts +55 -0
- package/src/agent/tools.ts +255 -0
- package/src/agent/trace.ts +76 -0
- package/src/agent.ts +114 -0
- package/src/auth.ts +133 -0
- package/src/brain/anthropic.ts +83 -0
- package/src/brain/claude-cli.ts +78 -0
- package/src/brain/ghost-ember.ts +94 -0
- package/src/brain/index.ts +99 -0
- package/src/brain/openai-compat.ts +115 -0
- package/src/brain/router/classify.ts +167 -0
- package/src/brain/router/execute.ts +80 -0
- package/src/brain/router/index.ts +125 -0
- package/src/brain/router/routing-memory.ts +71 -0
- package/src/brain/router/select.ts +156 -0
- package/src/brain/router/two-hop.ts +62 -0
- package/src/brain/router/verify.ts +123 -0
- package/src/brain/types.ts +61 -0
- package/src/brain/worker.ts +72 -0
- package/src/commands/ai.ts +144 -0
- package/src/commands/alerts.ts +131 -0
- package/src/commands/billing.ts +59 -0
- package/src/commands/chat.ts +318 -0
- package/src/commands/config.ts +137 -0
- package/src/commands/doctor.ts +71 -0
- package/src/commands/global.ts +256 -0
- package/src/commands/keep.ts +67 -0
- package/src/commands/lifecycle.ts +273 -0
- package/src/commands/login.ts +184 -0
- package/src/commands/maintenance.ts +54 -0
- package/src/commands/mcp.ts +57 -0
- package/src/commands/memory.ts +229 -0
- package/src/commands/osint.ts +171 -0
- package/src/commands/pentest.ts +140 -0
- package/src/commands/scale.ts +185 -0
- package/src/commands/serve.ts +171 -0
- package/src/commands/tui.ts +126 -0
- package/src/commands/wyrm.ts +269 -0
- package/src/config.ts +93 -0
- package/src/index.ts +92 -0
- package/src/manifest.ts +104 -0
- package/src/ui.ts +188 -0
- package/src/utils.ts +153 -0
- package/src/wyrm/mcp.ts +130 -0
- package/test/auth.test.ts +70 -0
- package/test/brain.test.ts +39 -0
- package/test/security.test.ts +104 -0
- package/test/skills.test.ts +38 -0
- package/test/ui.test.ts +46 -0
- package/tsconfig.json +19 -0
- package/worker/package-lock.json +1527 -0
- package/worker/package.json +17 -0
- package/worker/src/index.ts +76 -0
- package/worker/tsconfig.json +15 -0
- package/worker/wrangler.toml +26 -0
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI design system — the "ops console" premium look (stealth silver).
|
|
3
|
+
*
|
|
4
|
+
* One place for the chrome: framed panels (boxen, ANSI-width-aware), a chrome
|
|
5
|
+
* gradient wordmark, status dots, key/value rows, rules + section headers. Every
|
|
6
|
+
* command composes these so the whole CLI reads as one flagship tool.
|
|
7
|
+
*
|
|
8
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
9
|
+
*/
|
|
10
|
+
/** Brushed-chrome gradient across a string — the premium wordmark treatment. */
|
|
11
|
+
export declare function chrome(s: string): string;
|
|
12
|
+
/** A framed panel with an optional left-aligned title (rendered in the border). */
|
|
13
|
+
export declare function panel(lines: string[], opts?: {
|
|
14
|
+
title?: string;
|
|
15
|
+
borderColor?: string;
|
|
16
|
+
width?: number;
|
|
17
|
+
}): string;
|
|
18
|
+
export declare const emerald: import("chalk").ChalkInstance;
|
|
19
|
+
export declare const emeraldDim: import("chalk").ChalkInstance;
|
|
20
|
+
/** Zip two rectangular blocks side-by-side. `leftWidth` = the left block's visible
|
|
21
|
+
* width (set a fixed `width` on the left panel so padding lines up). */
|
|
22
|
+
export declare function joinColumns(left: string, right: string, leftWidth: number, gap?: number): string;
|
|
23
|
+
/** A brushed-chrome horizontal rule. */
|
|
24
|
+
export declare function chromeRule(width: number): string;
|
|
25
|
+
/** A unicode sparkline (8 vertical levels). Emerald where there's signal, faint for zero. */
|
|
26
|
+
export declare function sparkline(values: number[]): string;
|
|
27
|
+
/** A fractional gauge bar (eighth-cell precision). Colour ramps green→amber→red. */
|
|
28
|
+
export declare function gauge(frac: number, width: number): string;
|
|
29
|
+
/**
|
|
30
|
+
* A multi-row Braille area/line chart — 4× the vertical resolution of a
|
|
31
|
+
* sparkline and 2× horizontal (values are interpolated across dot columns).
|
|
32
|
+
* Returns `height` colored rows of `width` cells. Emerald signal by default.
|
|
33
|
+
*/
|
|
34
|
+
export declare function brailleChart(values: number[], opts?: {
|
|
35
|
+
width?: number;
|
|
36
|
+
height?: number;
|
|
37
|
+
max?: number;
|
|
38
|
+
color?: (s: string) => string;
|
|
39
|
+
area?: boolean;
|
|
40
|
+
}): string[];
|
|
41
|
+
export declare const dot: {
|
|
42
|
+
on: string;
|
|
43
|
+
off: string;
|
|
44
|
+
live: string;
|
|
45
|
+
warn: string;
|
|
46
|
+
crit: string;
|
|
47
|
+
};
|
|
48
|
+
export declare const statusDot: (on: boolean) => string;
|
|
49
|
+
/** "label value" with the label dim + padded to a column. */
|
|
50
|
+
export declare function kv(label: string, value: string, pad?: number): string;
|
|
51
|
+
/** A hairline rule, optionally with a section label on the left. */
|
|
52
|
+
export declare function rule(width?: number, label?: string): string;
|
|
53
|
+
/** Collapse $HOME → ~ for tidy paths. */
|
|
54
|
+
export declare function tidyPath(p: string): string;
|
|
55
|
+
/** The ops-console brand splash (global header). */
|
|
56
|
+
export declare function brandHeader(version: string): string;
|
|
57
|
+
//# sourceMappingURL=ui.d.ts.map
|
package/dist/ui.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiBH,gFAAgF;AAChF,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAaxC;AAED,mFAAmF;AACnF,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CASlH;AAGD,eAAO,MAAM,OAAO,+BAAuB,CAAA;AAC3C,eAAO,MAAM,UAAU,+BAAuB,CAAA;AAE9C;yEACyE;AACzE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,CAS3F;AAED,wCAAwC;AACxC,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAMD,6FAA6F;AAC7F,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAIlD;AAED,oFAAoF;AACpF,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CASzD;AAWD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1G,MAAM,EAAE,CAkCV;AAED,eAAO,MAAM,GAAG;;;;;;CAMf,CAAA;AACD,eAAO,MAAM,SAAS,GAAI,IAAI,OAAO,WAA4B,CAAA;AAEjE,8DAA8D;AAC9D,wBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,CAEhE;AAED,oEAAoE;AACpE,wBAAgB,IAAI,CAAC,KAAK,SAAK,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,yCAAyC;AACzC,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAG1C;AAED,oDAAoD;AACpD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQnD"}
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI design system — the "ops console" premium look (stealth silver).
|
|
3
|
+
*
|
|
4
|
+
* One place for the chrome: framed panels (boxen, ANSI-width-aware), a chrome
|
|
5
|
+
* gradient wordmark, status dots, key/value rows, rules + section headers. Every
|
|
6
|
+
* command composes these so the whole CLI reads as one flagship tool.
|
|
7
|
+
*
|
|
8
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
9
|
+
*/
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import boxen from 'boxen';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { C } from './utils.js';
|
|
14
|
+
const BORDER = '#454e57'; // dim slate — the frame
|
|
15
|
+
const SILVER_STOPS = ['#dfe6ec', '#99a3ac', '#eef3f7', '#99a3ac', '#dfe6ec'];
|
|
16
|
+
function hexLerp(a, b, t) {
|
|
17
|
+
const A = [1, 3, 5].map((i) => parseInt(a.slice(i, i + 2), 16));
|
|
18
|
+
const B = [1, 3, 5].map((i) => parseInt(b.slice(i, i + 2), 16));
|
|
19
|
+
const c = A.map((v, i) => Math.round(v + (B[i] - v) * t));
|
|
20
|
+
return '#' + c.map((v) => v.toString(16).padStart(2, '0')).join('');
|
|
21
|
+
}
|
|
22
|
+
/** Brushed-chrome gradient across a string — the premium wordmark treatment. */
|
|
23
|
+
export function chrome(s) {
|
|
24
|
+
const chars = [...s];
|
|
25
|
+
const n = chars.length;
|
|
26
|
+
if (n === 0)
|
|
27
|
+
return s;
|
|
28
|
+
return chars
|
|
29
|
+
.map((ch, i) => {
|
|
30
|
+
if (ch === ' ')
|
|
31
|
+
return ch;
|
|
32
|
+
const p = (n === 1 ? 0 : i / (n - 1)) * (SILVER_STOPS.length - 1);
|
|
33
|
+
const lo = Math.floor(p);
|
|
34
|
+
const hi = Math.min(lo + 1, SILVER_STOPS.length - 1);
|
|
35
|
+
return chalk.hex(hexLerp(SILVER_STOPS[lo], SILVER_STOPS[hi], p - lo)).bold(ch);
|
|
36
|
+
})
|
|
37
|
+
.join('');
|
|
38
|
+
}
|
|
39
|
+
/** A framed panel with an optional left-aligned title (rendered in the border). */
|
|
40
|
+
export function panel(lines, opts = {}) {
|
|
41
|
+
return boxen(lines.join('\n'), {
|
|
42
|
+
title: opts.title,
|
|
43
|
+
titleAlignment: 'left',
|
|
44
|
+
padding: { top: 0, bottom: 0, left: 1, right: 2 },
|
|
45
|
+
borderStyle: 'round',
|
|
46
|
+
borderColor: opts.borderColor ?? BORDER,
|
|
47
|
+
...(opts.width ? { width: opts.width } : {}),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// ghosts.lk "live/online" accent — a whisper of emerald (oklch 0.55 0.15 155).
|
|
51
|
+
export const emerald = chalk.hex('#2bb673');
|
|
52
|
+
export const emeraldDim = chalk.hex('#1c6e49');
|
|
53
|
+
/** Zip two rectangular blocks side-by-side. `leftWidth` = the left block's visible
|
|
54
|
+
* width (set a fixed `width` on the left panel so padding lines up). */
|
|
55
|
+
export function joinColumns(left, right, leftWidth, gap = 3) {
|
|
56
|
+
const L = left.split('\n');
|
|
57
|
+
const R = right.split('\n');
|
|
58
|
+
const n = Math.max(L.length, R.length);
|
|
59
|
+
const blank = ' '.repeat(leftWidth);
|
|
60
|
+
const sep = ' '.repeat(gap);
|
|
61
|
+
const out = [];
|
|
62
|
+
for (let i = 0; i < n; i++)
|
|
63
|
+
out.push((L[i] ?? blank) + sep + (R[i] ?? ''));
|
|
64
|
+
return out.join('\n');
|
|
65
|
+
}
|
|
66
|
+
/** A brushed-chrome horizontal rule. */
|
|
67
|
+
export function chromeRule(width) {
|
|
68
|
+
return chrome('─'.repeat(Math.max(4, width)));
|
|
69
|
+
}
|
|
70
|
+
// ── sub-cell data viz (the obscure terminal-graphics craft) ──
|
|
71
|
+
const SPARK = '▁▂▃▄▅▆▇█';
|
|
72
|
+
const PARTIAL = [' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█']; // eighth-cell fills
|
|
73
|
+
/** A unicode sparkline (8 vertical levels). Emerald where there's signal, faint for zero. */
|
|
74
|
+
export function sparkline(values) {
|
|
75
|
+
if (!values.length)
|
|
76
|
+
return '';
|
|
77
|
+
const max = Math.max(...values, 1);
|
|
78
|
+
return values.map((v) => (v <= 0 ? C.faint('▁') : emerald(SPARK[Math.min(7, Math.max(0, Math.round((v / max) * 7)))]))).join('');
|
|
79
|
+
}
|
|
80
|
+
/** A fractional gauge bar (eighth-cell precision). Colour ramps green→amber→red. */
|
|
81
|
+
export function gauge(frac, width) {
|
|
82
|
+
const f = Math.max(0, Math.min(1, frac));
|
|
83
|
+
const cells = f * width;
|
|
84
|
+
const full = Math.floor(cells);
|
|
85
|
+
const rem = Math.round((cells - full) * 8);
|
|
86
|
+
const bar = '█'.repeat(full) + (rem > 0 ? PARTIAL[rem] : '');
|
|
87
|
+
const used = full + (rem > 0 ? 1 : 0);
|
|
88
|
+
const col = f < 0.7 ? emerald : f < 0.9 ? C.high : C.critical;
|
|
89
|
+
return col(bar) + C.faint('·'.repeat(Math.max(0, width - used)));
|
|
90
|
+
}
|
|
91
|
+
// Braille (U+2800 + 8-bit mask) = a 2x4 dot grid per cell → 8 sub-pixels, the
|
|
92
|
+
// densest text-only renderer (see the terminal-subcell-graphics skill).
|
|
93
|
+
const BR_DOTS = [
|
|
94
|
+
[0x01, 0x08],
|
|
95
|
+
[0x02, 0x10],
|
|
96
|
+
[0x04, 0x20],
|
|
97
|
+
[0x40, 0x80],
|
|
98
|
+
];
|
|
99
|
+
/**
|
|
100
|
+
* A multi-row Braille area/line chart — 4× the vertical resolution of a
|
|
101
|
+
* sparkline and 2× horizontal (values are interpolated across dot columns).
|
|
102
|
+
* Returns `height` colored rows of `width` cells. Emerald signal by default.
|
|
103
|
+
*/
|
|
104
|
+
export function brailleChart(values, opts = {}) {
|
|
105
|
+
const width = Math.max(2, opts.width ?? 32);
|
|
106
|
+
const height = Math.max(1, opts.height ?? 3);
|
|
107
|
+
const W = width * 2;
|
|
108
|
+
const H = height * 4;
|
|
109
|
+
const max = opts.max ?? Math.max(...values, 1);
|
|
110
|
+
const color = opts.color ?? emerald;
|
|
111
|
+
const area = opts.area ?? true;
|
|
112
|
+
const bits = new Uint8Array(width * height);
|
|
113
|
+
const set = (x, y) => {
|
|
114
|
+
if (x < 0 || x >= W || y < 0 || y >= H)
|
|
115
|
+
return;
|
|
116
|
+
bits[(y >> 2) * width + (x >> 1)] |= BR_DOTS[y & 3][x & 1];
|
|
117
|
+
};
|
|
118
|
+
const n = values.length;
|
|
119
|
+
for (let x = 0; x < W; x++) {
|
|
120
|
+
const idx = n <= 1 ? 0 : (x / (W - 1)) * (n - 1);
|
|
121
|
+
const lo = Math.floor(idx);
|
|
122
|
+
const hi = Math.min(lo + 1, n - 1);
|
|
123
|
+
const v = (values[lo] ?? 0) + ((values[hi] ?? 0) - (values[lo] ?? 0)) * (idx - lo);
|
|
124
|
+
const lvl = Math.max(0, Math.min(H - 1, Math.round((v / max) * (H - 1))));
|
|
125
|
+
const top = H - 1 - lvl;
|
|
126
|
+
if (area)
|
|
127
|
+
for (let y = H - 1; y >= top; y--)
|
|
128
|
+
set(x, y);
|
|
129
|
+
else
|
|
130
|
+
set(x, top);
|
|
131
|
+
}
|
|
132
|
+
const rows = [];
|
|
133
|
+
for (let cy = 0; cy < height; cy++) {
|
|
134
|
+
let s = '';
|
|
135
|
+
for (let cx = 0; cx < width; cx++) {
|
|
136
|
+
const b = bits[cy * width + cx];
|
|
137
|
+
s += b ? String.fromCharCode(0x2800 + b) : ' ';
|
|
138
|
+
}
|
|
139
|
+
rows.push(color(s));
|
|
140
|
+
}
|
|
141
|
+
return rows;
|
|
142
|
+
}
|
|
143
|
+
export const dot = {
|
|
144
|
+
on: C.accent('●'),
|
|
145
|
+
off: C.faint('○'),
|
|
146
|
+
live: chalk.hex('#46e0b0')('●'),
|
|
147
|
+
warn: C.high('●'),
|
|
148
|
+
crit: C.critical('●'),
|
|
149
|
+
};
|
|
150
|
+
export const statusDot = (on) => (on ? dot.on : dot.off);
|
|
151
|
+
/** "label value" with the label dim + padded to a column. */
|
|
152
|
+
export function kv(label, value, pad = 7) {
|
|
153
|
+
return `${C.faint(label.padEnd(pad))} ${value}`;
|
|
154
|
+
}
|
|
155
|
+
/** A hairline rule, optionally with a section label on the left. */
|
|
156
|
+
export function rule(width = 50, label) {
|
|
157
|
+
if (!label)
|
|
158
|
+
return C.faint('─'.repeat(width));
|
|
159
|
+
const tag = ` ${label.toUpperCase()} `;
|
|
160
|
+
return C.faint('──') + C.info(tag) + C.faint('─'.repeat(Math.max(0, width - tag.length - 2)));
|
|
161
|
+
}
|
|
162
|
+
/** Collapse $HOME → ~ for tidy paths. */
|
|
163
|
+
export function tidyPath(p) {
|
|
164
|
+
const h = homedir();
|
|
165
|
+
return p.startsWith(h) ? '~' + p.slice(h.length) : p;
|
|
166
|
+
}
|
|
167
|
+
/** The ops-console brand splash (global header). */
|
|
168
|
+
export function brandHeader(version) {
|
|
169
|
+
return panel([
|
|
170
|
+
`${C.accent('▟█▙')} ${chrome('DRAGON')} ${C.faint('· stack control')}`,
|
|
171
|
+
`${C.accent('▜█▛')} ${C.faint('account.ghosts.lk · v' + version)}`,
|
|
172
|
+
], { title: chrome('GHOST PROTOCOL') });
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=ui.js.map
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for Dragon CLI.
|
|
3
|
+
*
|
|
4
|
+
* Output palette intentionally narrow to match the Dragon Console
|
|
5
|
+
* web UI (one teal/green accent · severity by intensity, not hue).
|
|
6
|
+
*/
|
|
7
|
+
import { type SpawnOptions } from 'child_process';
|
|
8
|
+
export declare function exec(cmd: string, cwd?: string): string;
|
|
9
|
+
export declare function run(cmd: string, args: string[], cwd: string, opts?: SpawnOptions): Promise<number>;
|
|
10
|
+
export declare function fetchJSON<T = any>(url: string, timeoutMs?: number): Promise<T>;
|
|
11
|
+
export declare const C: {
|
|
12
|
+
accent: import("chalk").ChalkInstance;
|
|
13
|
+
hot: import("chalk").ChalkInstance;
|
|
14
|
+
critical: import("chalk").ChalkInstance;
|
|
15
|
+
high: import("chalk").ChalkInstance;
|
|
16
|
+
medium: import("chalk").ChalkInstance;
|
|
17
|
+
low: import("chalk").ChalkInstance;
|
|
18
|
+
info: import("chalk").ChalkInstance;
|
|
19
|
+
faint: import("chalk").ChalkInstance;
|
|
20
|
+
};
|
|
21
|
+
export declare function label(text: string): string;
|
|
22
|
+
export declare function eyebrow(num: string | number, text: string): string;
|
|
23
|
+
export declare function dim(text: string): string;
|
|
24
|
+
export declare function error(msg: string): void;
|
|
25
|
+
export declare function warn(msg: string): void;
|
|
26
|
+
export declare function success(msg: string): void;
|
|
27
|
+
export declare function info(msg: string): void;
|
|
28
|
+
export declare function table(rows: Record<string, string>[]): void;
|
|
29
|
+
export declare function isInstalled(dir: string): boolean;
|
|
30
|
+
export declare function isRunning(port?: number, host?: string, timeoutMs?: number): Promise<boolean>;
|
|
31
|
+
export declare function readVersion(dir: string): string | null;
|
|
32
|
+
export declare function lastUpdated(dir: string): string | null;
|
|
33
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAMlE,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAUlG;AAED,wBAAsB,SAAS,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,SAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAUlF;AASD,eAAO,MAAM,CAAC;;;;;;;;;CASb,CAAA;AAID,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExC;AAKD,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAiD;AACzF,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAkD;AACzF,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAA+C;AACzF,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAkD;AAEzF,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAa1D;AAMD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,SAAc,EAAE,SAAS,SAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAS9F;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4BtD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtD"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for Dragon CLI.
|
|
3
|
+
*
|
|
4
|
+
* Output palette intentionally narrow to match the Dragon Console
|
|
5
|
+
* web UI (one teal/green accent · severity by intensity, not hue).
|
|
6
|
+
*/
|
|
7
|
+
import { execSync, spawn } from 'child_process';
|
|
8
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { createConnection } from 'node:net';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
export function exec(cmd, cwd) {
|
|
13
|
+
try {
|
|
14
|
+
return execSync(cmd, { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
throw new Error(e.stderr?.trim() || e.message);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function run(cmd, args, cwd, opts) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const child = spawn(cmd, args, {
|
|
23
|
+
cwd,
|
|
24
|
+
stdio: 'inherit',
|
|
25
|
+
...opts,
|
|
26
|
+
});
|
|
27
|
+
child.on('close', code => resolve(code ?? 1));
|
|
28
|
+
child.on('error', reject);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export async function fetchJSON(url, timeoutMs = 5000) {
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
38
|
+
return res.json();
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// ─── brand palette: STEALTH SILVER ─────────────────────────────
|
|
45
|
+
// Mirrors account.ghosts.lk's monochrome scheme (near-black canvas,
|
|
46
|
+
// silver/white accent, zinc muted) — the operator-stealth look. The teal
|
|
47
|
+
// Dragon-Console accent was retired here. Severity stays in hue (function
|
|
48
|
+
// over stealth — a CRITICAL must read as red). Truecolor assumed; legacy
|
|
49
|
+
// 16-color terms fall back to the nearest chalk basic colour.
|
|
50
|
+
export const C = {
|
|
51
|
+
accent: chalk.hex('#c8d0d8'), // stealth silver · primary
|
|
52
|
+
hot: chalk.hex('#eef3f7'), // bright silver/white · live / running
|
|
53
|
+
critical: chalk.hex('#ff5a55'), // red · severity
|
|
54
|
+
high: chalk.hex('#fb923c'), // orange · severity
|
|
55
|
+
medium: chalk.hex('#fbbf24'), // amber · severity
|
|
56
|
+
low: chalk.hex('#8ab4e8'), // soft blue
|
|
57
|
+
info: chalk.hex('#9aa4ad'), // zinc grey
|
|
58
|
+
faint: chalk.hex('#5b6570'), // dim slate
|
|
59
|
+
};
|
|
60
|
+
// ─── output discipline ─────────────────────────────────────────
|
|
61
|
+
export function label(text) {
|
|
62
|
+
return C.accent.bold(`[ ${text.toUpperCase()} ]`);
|
|
63
|
+
}
|
|
64
|
+
export function eyebrow(num, text) {
|
|
65
|
+
return C.accent.bold(`[ ${String(num).padStart(2, '0')} // ${text.toUpperCase()} ]`);
|
|
66
|
+
}
|
|
67
|
+
export function dim(text) {
|
|
68
|
+
return chalk.dim(text);
|
|
69
|
+
}
|
|
70
|
+
// Severity glyphs + hues match the web UI sev pills:
|
|
71
|
+
// CRITICAL ⚠ red · HIGH ! orange · MEDIUM amber · LOW blue · INFO grey
|
|
72
|
+
// CLI helpers use the matching colour so error/warn/info read at a glance.
|
|
73
|
+
export function error(msg) { console.error(C.critical.bold(' ⚠'), msg); }
|
|
74
|
+
export function warn(msg) { console.warn(C.high(' !'), msg); }
|
|
75
|
+
export function success(msg) { console.log(C.accent(' ✓'), msg); }
|
|
76
|
+
export function info(msg) { console.log(C.info(' ·'), msg); }
|
|
77
|
+
export function table(rows) {
|
|
78
|
+
if (rows.length === 0)
|
|
79
|
+
return;
|
|
80
|
+
const keys = Object.keys(rows[0]);
|
|
81
|
+
const widths = keys.map(k => Math.max(k.length, ...rows.map(r => (r[k] || '').length)));
|
|
82
|
+
const header = keys.map((k, i) => k.padEnd(widths[i])).join(' ');
|
|
83
|
+
const sep = widths.map(w => '─'.repeat(w)).join('──');
|
|
84
|
+
console.log(chalk.dim(header));
|
|
85
|
+
console.log(chalk.dim(sep));
|
|
86
|
+
rows.forEach(row => {
|
|
87
|
+
console.log(keys.map((k, i) => (row[k] || '').padEnd(widths[i])).join(' '));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// ─── module state probes ──────────────────────────────────────
|
|
91
|
+
// Used by `dragon list` + `dragon doctor` so output reflects reality,
|
|
92
|
+
// not "the repo dir exists therefore ✓".
|
|
93
|
+
export function isInstalled(dir) {
|
|
94
|
+
return existsSync(join(dir, '.git'));
|
|
95
|
+
}
|
|
96
|
+
export function isRunning(port, host = '127.0.0.1', timeoutMs = 200) {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
if (!port)
|
|
99
|
+
return resolve(false);
|
|
100
|
+
const sock = createConnection({ host, port });
|
|
101
|
+
const done = (ok) => { try {
|
|
102
|
+
sock.destroy();
|
|
103
|
+
}
|
|
104
|
+
catch { } resolve(ok); };
|
|
105
|
+
sock.once('connect', () => done(true));
|
|
106
|
+
sock.once('error', () => done(false));
|
|
107
|
+
setTimeout(() => done(false), timeoutMs);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
export function readVersion(dir) {
|
|
111
|
+
const parsers = {
|
|
112
|
+
'package.json': (raw) => { try {
|
|
113
|
+
return JSON.parse(raw).version ?? null;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
} },
|
|
118
|
+
'Cargo.toml': (raw) => raw.match(/^version\s*=\s*"([^"]+)"/m)?.[1] ?? null,
|
|
119
|
+
'pyproject.toml': (raw) => raw.match(/^version\s*=\s*"([^"]+)"/m)?.[1] ?? null,
|
|
120
|
+
};
|
|
121
|
+
// Search the repo root + a handful of conventional monorepo layouts so
|
|
122
|
+
// packages like Wyrm (no root manifest, version in packages/mcp-server)
|
|
123
|
+
// still report a version on `dragon list`.
|
|
124
|
+
const candidates = [
|
|
125
|
+
dir,
|
|
126
|
+
join(dir, 'packages', 'mcp-server'),
|
|
127
|
+
join(dir, 'packages', 'core'),
|
|
128
|
+
join(dir, 'packages', 'cli'),
|
|
129
|
+
join(dir, 'apps', 'web'),
|
|
130
|
+
join(dir, 'apps', 'server'),
|
|
131
|
+
];
|
|
132
|
+
for (const c of candidates) {
|
|
133
|
+
for (const [fname, parse] of Object.entries(parsers)) {
|
|
134
|
+
const f = join(c, fname);
|
|
135
|
+
if (!existsSync(f))
|
|
136
|
+
continue;
|
|
137
|
+
try {
|
|
138
|
+
const v = parse(readFileSync(f, 'utf-8'));
|
|
139
|
+
if (v)
|
|
140
|
+
return v;
|
|
141
|
+
}
|
|
142
|
+
catch { /* keep trying */ }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
export function lastUpdated(dir) {
|
|
148
|
+
try {
|
|
149
|
+
return new Date(statSync(dir).mtime).toISOString().slice(0, 10);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep Wyrm integration — the agent's memory spine, on by default.
|
|
3
|
+
*
|
|
4
|
+
* Spawns the Wyrm MCP server (`wyrm-mcp`) over stdio and:
|
|
5
|
+
* - exposes a CURATED subset of Wyrm's tools to the brain (recall, remember,
|
|
6
|
+
* capture, search, project/global context, quests, skills, entities, truths)
|
|
7
|
+
* so the model can read + write long-term memory mid-task;
|
|
8
|
+
* - PRIMES each session with the current project's context, folded into the
|
|
9
|
+
* system prompt, so Dragon starts already knowing the project.
|
|
10
|
+
*
|
|
11
|
+
* Everything is best-effort: if Wyrm isn't reachable the agent still runs (just
|
|
12
|
+
* without memory). Tool routing in the loop sends any `wyrm_*` call here.
|
|
13
|
+
*
|
|
14
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
15
|
+
*/
|
|
16
|
+
import type { ToolSpec } from '../brain/types.js';
|
|
17
|
+
export interface WyrmTool {
|
|
18
|
+
spec: ToolSpec;
|
|
19
|
+
}
|
|
20
|
+
export declare class Wyrm {
|
|
21
|
+
private client;
|
|
22
|
+
private transport;
|
|
23
|
+
private toolNames;
|
|
24
|
+
connected: boolean;
|
|
25
|
+
/** Connect + discover tools (bounded by CONNECT_TIMEOUT). Never throws / hangs. */
|
|
26
|
+
connect(): Promise<boolean>;
|
|
27
|
+
private exposed;
|
|
28
|
+
/** Curated Wyrm tool specs to hand the brain (empty if not connected). */
|
|
29
|
+
toolSpecs(): ToolSpec[];
|
|
30
|
+
handles(name: string): boolean;
|
|
31
|
+
/** Invoke a Wyrm tool; returns a string result for the model (bounded). */
|
|
32
|
+
call(name: string, args: Record<string, unknown>): Promise<string>;
|
|
33
|
+
/** Best-effort project context for the system prompt. Never throws. */
|
|
34
|
+
prime(cwd: string): Promise<string | null>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/wyrm/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAgCjD,MAAM,WAAW,QAAQ;IAAG,IAAI,EAAE,QAAQ,CAAA;CAAE;AAE5C,qBAAa,IAAI;IACf,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,SAAS,CAAoB;IACrC,SAAS,UAAQ;IAEjB,mFAAmF;IAC7E,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IA0BjC,OAAO,CAAC,OAAO,CAAiB;IAEhC,0EAA0E;IAC1E,SAAS,IAAI,QAAQ,EAAE;IAIvB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI9B,2EAA2E;IACrE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAWxE,uEAAuE;IACjE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAa1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
|
package/dist/wyrm/mcp.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep Wyrm integration — the agent's memory spine, on by default.
|
|
3
|
+
*
|
|
4
|
+
* Spawns the Wyrm MCP server (`wyrm-mcp`) over stdio and:
|
|
5
|
+
* - exposes a CURATED subset of Wyrm's tools to the brain (recall, remember,
|
|
6
|
+
* capture, search, project/global context, quests, skills, entities, truths)
|
|
7
|
+
* so the model can read + write long-term memory mid-task;
|
|
8
|
+
* - PRIMES each session with the current project's context, folded into the
|
|
9
|
+
* system prompt, so Dragon starts already knowing the project.
|
|
10
|
+
*
|
|
11
|
+
* Everything is best-effort: if Wyrm isn't reachable the agent still runs (just
|
|
12
|
+
* without memory). Tool routing in the loop sends any `wyrm_*` call here.
|
|
13
|
+
*
|
|
14
|
+
* Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
|
|
15
|
+
*/
|
|
16
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
17
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
18
|
+
import { existsSync } from 'node:fs';
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
/** High-value Wyrm tools we surface to the model (the full surface is ~116). */
|
|
22
|
+
const EXPOSED = new Set([
|
|
23
|
+
'wyrm_recall', 'wyrm_remember', 'wyrm_capture', 'wyrm_search',
|
|
24
|
+
'wyrm_project_context', 'wyrm_global_context', 'wyrm_context_build',
|
|
25
|
+
'wyrm_all_quests', 'wyrm_quest_add', 'wyrm_quest_complete',
|
|
26
|
+
'wyrm_skill_search', 'wyrm_skill_get',
|
|
27
|
+
'wyrm_entity_search', 'wyrm_truth_get', 'wyrm_truth_set', 'wyrm_decided_because',
|
|
28
|
+
]);
|
|
29
|
+
function wyrmBin() {
|
|
30
|
+
const candidates = [process.env.WYRM_MCP_BIN, join(homedir(), '.npm-global/bin/wyrm-mcp')].filter(Boolean);
|
|
31
|
+
for (const c of candidates)
|
|
32
|
+
if (existsSync(c))
|
|
33
|
+
return c;
|
|
34
|
+
return 'wyrm-mcp'; // fall back to PATH
|
|
35
|
+
}
|
|
36
|
+
const CONNECT_TIMEOUT = 8_000; // a slow/hanging Wyrm must never freeze the agent
|
|
37
|
+
const CALL_TIMEOUT = 25_000;
|
|
38
|
+
/** Resolve to `null` if the promise doesn't settle in `ms` (running `onTimeout`). */
|
|
39
|
+
function withTimeout(p, ms, onTimeout) {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
let done = false;
|
|
42
|
+
const timer = setTimeout(() => { if (!done) {
|
|
43
|
+
done = true;
|
|
44
|
+
onTimeout?.();
|
|
45
|
+
resolve(null);
|
|
46
|
+
} }, ms);
|
|
47
|
+
p.then((v) => { if (!done) {
|
|
48
|
+
done = true;
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
resolve(v);
|
|
51
|
+
} }, () => { if (!done) {
|
|
52
|
+
done = true;
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
resolve(null);
|
|
55
|
+
} });
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export class Wyrm {
|
|
59
|
+
client = null;
|
|
60
|
+
transport = null;
|
|
61
|
+
toolNames = new Set();
|
|
62
|
+
connected = false;
|
|
63
|
+
/** Connect + discover tools (bounded by CONNECT_TIMEOUT). Never throws / hangs. */
|
|
64
|
+
async connect() {
|
|
65
|
+
try {
|
|
66
|
+
this.transport = new StdioClientTransport({ command: wyrmBin(), args: [], stderr: 'ignore' });
|
|
67
|
+
this.client = new Client({ name: 'dragon-cli', version: '3.7.0' }, { capabilities: {} });
|
|
68
|
+
const ok = await withTimeout((async () => {
|
|
69
|
+
await this.client.connect(this.transport);
|
|
70
|
+
const { tools } = await this.client.listTools();
|
|
71
|
+
this.exposed = tools
|
|
72
|
+
.filter((t) => EXPOSED.has(t.name))
|
|
73
|
+
.map((t) => ({ name: t.name, description: t.description ?? '', parameters: t.inputSchema ?? { type: 'object', properties: {} } }));
|
|
74
|
+
this.exposed.forEach((t) => this.toolNames.add(t.name));
|
|
75
|
+
return true;
|
|
76
|
+
})(), CONNECT_TIMEOUT, () => { try {
|
|
77
|
+
void this.transport?.close();
|
|
78
|
+
}
|
|
79
|
+
catch { /* ignore */ } });
|
|
80
|
+
this.connected = ok === true;
|
|
81
|
+
if (!this.connected) {
|
|
82
|
+
try {
|
|
83
|
+
await this.client?.close();
|
|
84
|
+
}
|
|
85
|
+
catch { /* ignore */ }
|
|
86
|
+
this.client = null;
|
|
87
|
+
}
|
|
88
|
+
return this.connected;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
this.connected = false;
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exposed = [];
|
|
96
|
+
/** Curated Wyrm tool specs to hand the brain (empty if not connected). */
|
|
97
|
+
toolSpecs() {
|
|
98
|
+
return this.exposed;
|
|
99
|
+
}
|
|
100
|
+
handles(name) {
|
|
101
|
+
return this.toolNames.has(name);
|
|
102
|
+
}
|
|
103
|
+
/** Invoke a Wyrm tool; returns a string result for the model (bounded). */
|
|
104
|
+
async call(name, args) {
|
|
105
|
+
if (!this.client)
|
|
106
|
+
return 'error: Wyrm not connected';
|
|
107
|
+
const res = await withTimeout(this.client.callTool({ name, arguments: args }), CALL_TIMEOUT);
|
|
108
|
+
if (res === null)
|
|
109
|
+
return `error: ${name} timed out`;
|
|
110
|
+
const text = (res.content ?? []).map((c) => (c.type === 'text' ? c.text ?? '' : `[${c.type}]`)).join('\n').trim();
|
|
111
|
+
return (res.isError ? 'error: ' : '') + (text || '(no result)');
|
|
112
|
+
}
|
|
113
|
+
/** Best-effort project context for the system prompt. Never throws. */
|
|
114
|
+
async prime(cwd) {
|
|
115
|
+
if (!this.client)
|
|
116
|
+
return null;
|
|
117
|
+
for (const [name, args] of [
|
|
118
|
+
['wyrm_project_context', { projectPath: cwd }],
|
|
119
|
+
['wyrm_project_context', { project: cwd }],
|
|
120
|
+
]) {
|
|
121
|
+
if (!this.toolNames.has(name))
|
|
122
|
+
continue;
|
|
123
|
+
const out = await this.call(name, args);
|
|
124
|
+
if (out && !out.startsWith('error'))
|
|
125
|
+
return out.slice(0, 4000);
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
async close() {
|
|
130
|
+
try {
|
|
131
|
+
await this.client?.close();
|
|
132
|
+
}
|
|
133
|
+
catch { /* ignore */ }
|
|
134
|
+
this.connected = false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=mcp.js.map
|