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.
Files changed (226) hide show
  1. package/.github/workflows/ci.yml +23 -0
  2. package/CHANGELOG.md +96 -0
  3. package/README.md +193 -0
  4. package/bootstrap.ps1 +83 -0
  5. package/bootstrap.sh +71 -0
  6. package/dist/agent/loop.d.ts +68 -0
  7. package/dist/agent/loop.d.ts.map +1 -0
  8. package/dist/agent/loop.js +135 -0
  9. package/dist/agent/mcp.d.ts +33 -0
  10. package/dist/agent/mcp.d.ts.map +1 -0
  11. package/dist/agent/mcp.js +107 -0
  12. package/dist/agent/session.d.ts +16 -0
  13. package/dist/agent/session.d.ts.map +1 -0
  14. package/dist/agent/session.js +55 -0
  15. package/dist/agent/skills.d.ts +36 -0
  16. package/dist/agent/skills.d.ts.map +1 -0
  17. package/dist/agent/skills.js +153 -0
  18. package/dist/agent/stack.d.ts +21 -0
  19. package/dist/agent/stack.d.ts.map +1 -0
  20. package/dist/agent/stack.js +158 -0
  21. package/dist/agent/task.d.ts +21 -0
  22. package/dist/agent/task.d.ts.map +1 -0
  23. package/dist/agent/task.js +45 -0
  24. package/dist/agent/tools.d.ts +44 -0
  25. package/dist/agent/tools.d.ts.map +1 -0
  26. package/dist/agent/tools.js +262 -0
  27. package/dist/agent/trace.d.ts +34 -0
  28. package/dist/agent/trace.d.ts.map +1 -0
  29. package/dist/agent/trace.js +72 -0
  30. package/dist/agent.d.ts +46 -0
  31. package/dist/agent.d.ts.map +1 -0
  32. package/dist/agent.js +103 -0
  33. package/dist/auth.d.ts +74 -0
  34. package/dist/auth.d.ts.map +1 -0
  35. package/dist/auth.js +116 -0
  36. package/dist/brain/anthropic.d.ts +19 -0
  37. package/dist/brain/anthropic.d.ts.map +1 -0
  38. package/dist/brain/anthropic.js +74 -0
  39. package/dist/brain/claude-cli.d.ts +20 -0
  40. package/dist/brain/claude-cli.d.ts.map +1 -0
  41. package/dist/brain/claude-cli.js +79 -0
  42. package/dist/brain/ghost-ember.d.ts +28 -0
  43. package/dist/brain/ghost-ember.d.ts.map +1 -0
  44. package/dist/brain/ghost-ember.js +97 -0
  45. package/dist/brain/index.d.ts +22 -0
  46. package/dist/brain/index.d.ts.map +1 -0
  47. package/dist/brain/index.js +95 -0
  48. package/dist/brain/openai-compat.d.ts +21 -0
  49. package/dist/brain/openai-compat.d.ts.map +1 -0
  50. package/dist/brain/openai-compat.js +119 -0
  51. package/dist/brain/router/classify.d.ts +23 -0
  52. package/dist/brain/router/classify.d.ts.map +1 -0
  53. package/dist/brain/router/classify.js +160 -0
  54. package/dist/brain/router/execute.d.ts +23 -0
  55. package/dist/brain/router/execute.d.ts.map +1 -0
  56. package/dist/brain/router/execute.js +84 -0
  57. package/dist/brain/router/index.d.ts +26 -0
  58. package/dist/brain/router/index.d.ts.map +1 -0
  59. package/dist/brain/router/index.js +118 -0
  60. package/dist/brain/router/routing-memory.d.ts +27 -0
  61. package/dist/brain/router/routing-memory.d.ts.map +1 -0
  62. package/dist/brain/router/routing-memory.js +77 -0
  63. package/dist/brain/router/select.d.ts +32 -0
  64. package/dist/brain/router/select.d.ts.map +1 -0
  65. package/dist/brain/router/select.js +146 -0
  66. package/dist/brain/router/two-hop.d.ts +23 -0
  67. package/dist/brain/router/two-hop.d.ts.map +1 -0
  68. package/dist/brain/router/two-hop.js +39 -0
  69. package/dist/brain/router/verify.d.ts +37 -0
  70. package/dist/brain/router/verify.d.ts.map +1 -0
  71. package/dist/brain/router/verify.js +111 -0
  72. package/dist/brain/types.d.ts +55 -0
  73. package/dist/brain/types.d.ts.map +1 -0
  74. package/dist/brain/types.js +16 -0
  75. package/dist/brain/worker.d.ts +27 -0
  76. package/dist/brain/worker.d.ts.map +1 -0
  77. package/dist/brain/worker.js +71 -0
  78. package/dist/commands/ai.d.ts +24 -0
  79. package/dist/commands/ai.d.ts.map +1 -0
  80. package/dist/commands/ai.js +137 -0
  81. package/dist/commands/alerts.d.ts +19 -0
  82. package/dist/commands/alerts.d.ts.map +1 -0
  83. package/dist/commands/alerts.js +114 -0
  84. package/dist/commands/billing.d.ts +13 -0
  85. package/dist/commands/billing.d.ts.map +1 -0
  86. package/dist/commands/billing.js +55 -0
  87. package/dist/commands/chat.d.ts +22 -0
  88. package/dist/commands/chat.d.ts.map +1 -0
  89. package/dist/commands/chat.js +422 -0
  90. package/dist/commands/config.d.ts +18 -0
  91. package/dist/commands/config.d.ts.map +1 -0
  92. package/dist/commands/config.js +136 -0
  93. package/dist/commands/doctor.d.ts +11 -0
  94. package/dist/commands/doctor.d.ts.map +1 -0
  95. package/dist/commands/doctor.js +73 -0
  96. package/dist/commands/global.d.ts +11 -0
  97. package/dist/commands/global.d.ts.map +1 -0
  98. package/dist/commands/global.js +253 -0
  99. package/dist/commands/keep.d.ts +12 -0
  100. package/dist/commands/keep.d.ts.map +1 -0
  101. package/dist/commands/keep.js +58 -0
  102. package/dist/commands/lifecycle.d.ts +17 -0
  103. package/dist/commands/lifecycle.d.ts.map +1 -0
  104. package/dist/commands/lifecycle.js +267 -0
  105. package/dist/commands/login.d.ts +16 -0
  106. package/dist/commands/login.d.ts.map +1 -0
  107. package/dist/commands/login.js +234 -0
  108. package/dist/commands/maintenance.d.ts +12 -0
  109. package/dist/commands/maintenance.d.ts.map +1 -0
  110. package/dist/commands/maintenance.js +76 -0
  111. package/dist/commands/mcp.d.ts +16 -0
  112. package/dist/commands/mcp.d.ts.map +1 -0
  113. package/dist/commands/mcp.js +56 -0
  114. package/dist/commands/memory.d.ts +13 -0
  115. package/dist/commands/memory.d.ts.map +1 -0
  116. package/dist/commands/memory.js +218 -0
  117. package/dist/commands/osint.d.ts +14 -0
  118. package/dist/commands/osint.d.ts.map +1 -0
  119. package/dist/commands/osint.js +161 -0
  120. package/dist/commands/pentest.d.ts +13 -0
  121. package/dist/commands/pentest.d.ts.map +1 -0
  122. package/dist/commands/pentest.js +131 -0
  123. package/dist/commands/scale.d.ts +14 -0
  124. package/dist/commands/scale.d.ts.map +1 -0
  125. package/dist/commands/scale.js +191 -0
  126. package/dist/commands/serve.d.ts +16 -0
  127. package/dist/commands/serve.d.ts.map +1 -0
  128. package/dist/commands/serve.js +167 -0
  129. package/dist/commands/tui.d.ts +17 -0
  130. package/dist/commands/tui.d.ts.map +1 -0
  131. package/dist/commands/tui.js +138 -0
  132. package/dist/commands/wyrm.d.ts +20 -0
  133. package/dist/commands/wyrm.d.ts.map +1 -0
  134. package/dist/commands/wyrm.js +274 -0
  135. package/dist/config.d.ts +67 -0
  136. package/dist/config.d.ts.map +1 -0
  137. package/dist/config.js +54 -0
  138. package/dist/index.d.ts +16 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +85 -0
  141. package/dist/manifest.d.ts +31 -0
  142. package/dist/manifest.d.ts.map +1 -0
  143. package/dist/manifest.js +83 -0
  144. package/dist/ui.d.ts +57 -0
  145. package/dist/ui.d.ts.map +1 -0
  146. package/dist/ui.js +174 -0
  147. package/dist/utils.d.ts +33 -0
  148. package/dist/utils.d.ts.map +1 -0
  149. package/dist/utils.js +155 -0
  150. package/dist/wyrm/mcp.d.ts +37 -0
  151. package/dist/wyrm/mcp.d.ts.map +1 -0
  152. package/dist/wyrm/mcp.js +137 -0
  153. package/docs/SYSTEM-PREMORTEM.md +397 -0
  154. package/dragon-manifest.toml +241 -0
  155. package/dragon.py +177 -0
  156. package/install/launchd/lk.ghosts.dragonkeep.plist +57 -0
  157. package/install/systemd/dragonkeep.service +40 -0
  158. package/media/dragon-silver-lockup.svg +931 -0
  159. package/media/dragon-silver-mark.svg +931 -0
  160. package/media/dragon-silver.png +0 -0
  161. package/package.json +45 -0
  162. package/specs/001-godmode/constitution.md +54 -0
  163. package/specs/001-godmode/plan.md +30 -0
  164. package/specs/001-godmode/spec.md +64 -0
  165. package/specs/001-godmode/tasks.md +35 -0
  166. package/specs/002-premortem-positioning/premortem.md +211 -0
  167. package/src/agent/loop.ts +165 -0
  168. package/src/agent/mcp.ts +92 -0
  169. package/src/agent/session.ts +48 -0
  170. package/src/agent/skills.ts +138 -0
  171. package/src/agent/stack.ts +154 -0
  172. package/src/agent/task.ts +55 -0
  173. package/src/agent/tools.ts +255 -0
  174. package/src/agent/trace.ts +76 -0
  175. package/src/agent.ts +114 -0
  176. package/src/auth.ts +133 -0
  177. package/src/brain/anthropic.ts +83 -0
  178. package/src/brain/claude-cli.ts +78 -0
  179. package/src/brain/ghost-ember.ts +94 -0
  180. package/src/brain/index.ts +99 -0
  181. package/src/brain/openai-compat.ts +115 -0
  182. package/src/brain/router/classify.ts +167 -0
  183. package/src/brain/router/execute.ts +80 -0
  184. package/src/brain/router/index.ts +125 -0
  185. package/src/brain/router/routing-memory.ts +71 -0
  186. package/src/brain/router/select.ts +156 -0
  187. package/src/brain/router/two-hop.ts +62 -0
  188. package/src/brain/router/verify.ts +123 -0
  189. package/src/brain/types.ts +61 -0
  190. package/src/brain/worker.ts +72 -0
  191. package/src/commands/ai.ts +144 -0
  192. package/src/commands/alerts.ts +131 -0
  193. package/src/commands/billing.ts +59 -0
  194. package/src/commands/chat.ts +318 -0
  195. package/src/commands/config.ts +137 -0
  196. package/src/commands/doctor.ts +71 -0
  197. package/src/commands/global.ts +256 -0
  198. package/src/commands/keep.ts +67 -0
  199. package/src/commands/lifecycle.ts +273 -0
  200. package/src/commands/login.ts +184 -0
  201. package/src/commands/maintenance.ts +54 -0
  202. package/src/commands/mcp.ts +57 -0
  203. package/src/commands/memory.ts +229 -0
  204. package/src/commands/osint.ts +171 -0
  205. package/src/commands/pentest.ts +140 -0
  206. package/src/commands/scale.ts +185 -0
  207. package/src/commands/serve.ts +171 -0
  208. package/src/commands/tui.ts +126 -0
  209. package/src/commands/wyrm.ts +269 -0
  210. package/src/config.ts +93 -0
  211. package/src/index.ts +92 -0
  212. package/src/manifest.ts +104 -0
  213. package/src/ui.ts +188 -0
  214. package/src/utils.ts +153 -0
  215. package/src/wyrm/mcp.ts +130 -0
  216. package/test/auth.test.ts +70 -0
  217. package/test/brain.test.ts +39 -0
  218. package/test/security.test.ts +104 -0
  219. package/test/skills.test.ts +38 -0
  220. package/test/ui.test.ts +46 -0
  221. package/tsconfig.json +19 -0
  222. package/worker/package-lock.json +1527 -0
  223. package/worker/package.json +17 -0
  224. package/worker/src/index.ts +76 -0
  225. package/worker/tsconfig.json +15 -0
  226. package/worker/wrangler.toml +26 -0
@@ -0,0 +1,135 @@
1
+ /**
2
+ * The agent loop — Dragon's engine. Runs the model↔tool cycle entirely
3
+ * client-side so tools touch THIS machine:
4
+ *
5
+ * user → brain.turn(tools) → [tool calls?] → execute locally → feed back → repeat
6
+ * └ no calls → final answer, done
7
+ *
8
+ * Tools come from three sources, routed by name: local coding tools (read/edit/
9
+ * bash/grep…), the curated Wyrm memory tools (`wyrm_*` → MCP), and the optional
10
+ * hosted Dragon portal (`portal_ask`). Wyrm is wired by default and the system
11
+ * prompt tells the model to use it as long-term memory.
12
+ *
13
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
14
+ */
15
+ import { getLocalTool, localToolSpecs } from './tools.js';
16
+ const MAX_STEPS = 60; // hard stop against a runaway tool loop
17
+ export function buildSystemPrompt(opts) {
18
+ const lines = [
19
+ 'You are Dragon — Ghost Protocol\'s terminal coding agent (the `dragon` CLI). You operate like a senior engineer pair: precise, autonomous, and concise.',
20
+ `Working directory: ${opts.cwd}. Platform: ${process.platform}. Reasoning brain: ${opts.brainId}.`,
21
+ '',
22
+ 'TOOLS — you have real local tools (read_file, write_file, edit_file, list_dir, glob, grep, bash). Use them to DO the work, not describe it. Explore before you edit (grep/glob/read). Prefer edit_file over rewriting whole files. After code changes, build/test with bash to verify. Keep going until the task is genuinely done.',
23
+ 'Be surgical: match the surrounding code style, make the smallest change that works, and never invent file paths — find them.',
24
+ ];
25
+ lines.push('', 'STACK — you ARE the `dragon` operator console for the Ghost Protocol stack (scale · wyrm · pentest · keep · net). stack_status shows what is installed/running; stack_pentest runs a PhantomDragon web scan (authorized targets only) and stack_keep runs a DragonKeep system scan — both return STRUCTURED findings; stack_run drives any other dragon subcommand. You are a coding agent AND an operator — reach for the stack when the task is ops, not code.');
26
+ if (opts.skills) {
27
+ lines.push('', `SKILLS — you have ${opts.skills} reusable expert playbooks (design, security, infra, brand, project-specific). Before solving a non-trivial task from scratch, call skill_search for a relevant one and skill_read it, then apply its guidance. This institutional knowledge is your edge — reach for it first.`);
28
+ }
29
+ if (opts.wyrm) {
30
+ lines.push('', 'MEMORY (Wyrm) — you have persistent cross-session memory via the wyrm_* tools, and you should use it proactively:', '• Before non-trivial work, recall context with wyrm_recall / wyrm_search / wyrm_project_context.', '• Capture durable decisions, lessons, and gotchas with wyrm_remember / wyrm_capture so future sessions inherit them.', '• Track multi-step work as quests (wyrm_quest_add / wyrm_quest_complete). Record decisions with wyrm_decided_because.', '• Reach for an existing skill via wyrm_skill_search before solving something from scratch.', 'Read memory freely without asking. This is what makes you better than a stateless assistant.');
31
+ }
32
+ if (opts.portal) {
33
+ lines.push('', 'PORTAL — use portal_ask for the operator\'s Ghost Protocol account/licenses/catalog/services questions (it reaches the hosted account.ghosts.lk assistant). Not for coding.');
34
+ }
35
+ lines.push('', 'STYLE — terse, operator-grade, GitHub-flavored markdown. No filler, no emoji. Reference code as `path:line`. State what you did, not what you\'re "about to" do.');
36
+ if (opts.primed) {
37
+ lines.push('', '── Project context recalled from Wyrm (treat as background, verify before relying on specifics) ──', opts.primed);
38
+ }
39
+ return lines.join('\n');
40
+ }
41
+ /** Build the full tool surface handed to the brain this session. */
42
+ export function buildToolSpecs(deps) {
43
+ return [
44
+ ...localToolSpecs(),
45
+ ...(deps.skills?.toolSpecs() ?? []),
46
+ ...(deps.stack?.specs ?? []),
47
+ ...(deps.mcp?.toolSpecs() ?? []),
48
+ ...(deps.task ? [deps.task.spec] : []),
49
+ ...(deps.wyrm?.toolSpecs() ?? []),
50
+ ...(deps.portal ? [deps.portal.spec] : []),
51
+ ];
52
+ }
53
+ async function dispatch(call, deps) {
54
+ const { name, arguments: args } = call;
55
+ if (deps.skills?.handles(name))
56
+ return deps.skills.call(name, args);
57
+ if (deps.stack?.handles(name))
58
+ return deps.stack.call(name, args, deps.toolCtx);
59
+ if (deps.mcp?.handles(name))
60
+ return deps.mcp.call(name, args);
61
+ if (deps.task && name === deps.task.spec.name)
62
+ return deps.task.call(args);
63
+ if (deps.wyrm?.handles(name))
64
+ return deps.wyrm.call(name, args);
65
+ if (deps.portal && name === deps.portal.spec.name)
66
+ return deps.portal.call(args);
67
+ const tool = getLocalTool(name);
68
+ if (!tool)
69
+ return `error: unknown tool "${name}"`;
70
+ try {
71
+ return await tool.run(args, deps.toolCtx);
72
+ }
73
+ catch (e) {
74
+ return `error running ${name}: ${String(e instanceof Error ? e.message : e)}`;
75
+ }
76
+ }
77
+ function toolSummary(call, deps) {
78
+ const local = getLocalTool(call.name);
79
+ if (local)
80
+ return local.summary(call.arguments);
81
+ if (deps.skills?.handles(call.name))
82
+ return `${call.name}: ${String(call.arguments.query ?? call.arguments.name ?? '')}`;
83
+ if (deps.stack?.handles(call.name)) {
84
+ if (call.name === 'stack_pentest')
85
+ return `pentest: ${String(call.arguments.url ?? '')}`;
86
+ if (call.name === 'stack_keep')
87
+ return `keep: ${String(call.arguments.scan_type ?? 'quick')}`;
88
+ if (call.name === 'stack_status')
89
+ return 'stack_status';
90
+ return `stack: ${String(call.arguments.command ?? '')}`;
91
+ }
92
+ if (call.name === deps.task?.spec.name)
93
+ return `task: ${String(call.arguments.task ?? '').slice(0, 60)}`;
94
+ if (deps.mcp?.handles(call.name))
95
+ return call.name;
96
+ if (deps.wyrm?.handles(call.name))
97
+ return `${call.name}`;
98
+ if (call.name === deps.portal?.spec.name)
99
+ return `portal: ${String(call.arguments.question ?? '').slice(0, 60)}`;
100
+ return call.name;
101
+ }
102
+ /**
103
+ * Run one user message to completion (through any number of tool steps).
104
+ * Mutates deps.messages so the conversation persists across calls.
105
+ */
106
+ export async function runAgent(deps, userText, render, signal) {
107
+ const tools = buildToolSpecs(deps);
108
+ deps.messages.push({ role: 'user', content: userText });
109
+ for (let step = 0; step < MAX_STEPS; step++) {
110
+ let started = false;
111
+ const turn = await deps.brain.turn({
112
+ system: deps.system,
113
+ messages: deps.messages,
114
+ tools,
115
+ signal,
116
+ onDelta: (d) => { if (!started) {
117
+ render.onAssistantStart();
118
+ started = true;
119
+ } render.onDelta(d); },
120
+ });
121
+ deps.messages.push({ role: 'assistant', content: turn.text, toolCalls: turn.toolCalls.length ? turn.toolCalls : undefined });
122
+ if (!turn.toolCalls.length)
123
+ return; // settled — final answer streamed
124
+ for (const call of turn.toolCalls) {
125
+ const summary = toolSummary(call, deps);
126
+ render.onToolStart(summary);
127
+ const result = await dispatch(call, deps);
128
+ const ok = !result.startsWith('error');
129
+ render.onToolEnd(summary, result.split('\n')[0]?.slice(0, 120) ?? '', ok);
130
+ deps.messages.push({ role: 'tool', content: result, toolCallId: call.id, toolName: call.name });
131
+ }
132
+ }
133
+ render.onDelta(`\n[stopped after ${MAX_STEPS} tool steps]`);
134
+ }
135
+ //# sourceMappingURL=loop.js.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * MCP hub — connect ANY Model Context Protocol server (configured via `dragon mcp
3
+ * add`) and expose its tools to the agent. Dragon inherits the entire MCP ecosystem
4
+ * without forking: filesystem, GitHub, Postgres, Playwright, your own servers…
5
+ *
6
+ * Tools are namespaced `<server>__<tool>` to avoid collisions with built-in tools and
7
+ * across servers. Everything is bounded (connect + call timeouts) and best-effort — an
8
+ * unreachable server never blocks the agent.
9
+ *
10
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
11
+ */
12
+ import type { ToolSpec } from '../brain/types.js';
13
+ export interface McpServerConfig {
14
+ command: string;
15
+ args?: string[];
16
+ env?: Record<string, string>;
17
+ }
18
+ export declare class McpHub {
19
+ private clients;
20
+ private specs;
21
+ private route;
22
+ connected: boolean;
23
+ /** Connect every configured server (bounded). Never throws. */
24
+ connect(servers: Record<string, McpServerConfig>): Promise<void>;
25
+ get serverCount(): number;
26
+ toolSpecs(): ToolSpec[];
27
+ handles(name: string): boolean;
28
+ call(name: string, args: Record<string, unknown>): Promise<string>;
29
+ close(): Promise<void>;
30
+ }
31
+ /** Build + connect the hub from config (null if no servers configured). */
32
+ export declare function loadMcpHub(servers?: Record<string, McpServerConfig>): Promise<McpHub | null>;
33
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/agent/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAEjD,MAAM,WAAW,eAAe;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE;AAanG,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,KAAK,CAAsD;IACnE,SAAS,UAAQ;IAEjB,+DAA+D;IACzD,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BtE,IAAI,WAAW,IAAI,MAAM,CAA6B;IACtD,SAAS,IAAI,QAAQ,EAAE;IACvB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAExB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAWlE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B;AAED,2EAA2E;AAC3E,wBAAsB,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKlG"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * MCP hub — connect ANY Model Context Protocol server (configured via `dragon mcp
3
+ * add`) and expose its tools to the agent. Dragon inherits the entire MCP ecosystem
4
+ * without forking: filesystem, GitHub, Postgres, Playwright, your own servers…
5
+ *
6
+ * Tools are namespaced `<server>__<tool>` to avoid collisions with built-in tools and
7
+ * across servers. Everything is bounded (connect + call timeouts) and best-effort — an
8
+ * unreachable server never blocks the agent.
9
+ *
10
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
11
+ */
12
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
13
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
14
+ const CONNECT_TIMEOUT = 8_000;
15
+ const CALL_TIMEOUT = 30_000;
16
+ function withTimeout(p, ms, onTimeout) {
17
+ return new Promise((resolve) => {
18
+ let done = false;
19
+ const timer = setTimeout(() => { if (!done) {
20
+ done = true;
21
+ onTimeout?.();
22
+ resolve(null);
23
+ } }, ms);
24
+ p.then((v) => { if (!done) {
25
+ done = true;
26
+ clearTimeout(timer);
27
+ resolve(v);
28
+ } }, () => { if (!done) {
29
+ done = true;
30
+ clearTimeout(timer);
31
+ resolve(null);
32
+ } });
33
+ });
34
+ }
35
+ export class McpHub {
36
+ clients = new Map();
37
+ specs = [];
38
+ route = new Map();
39
+ connected = false;
40
+ /** Connect every configured server (bounded). Never throws. */
41
+ async connect(servers) {
42
+ for (const [name, cfg] of Object.entries(servers ?? {})) {
43
+ let transport = null;
44
+ try {
45
+ transport = new StdioClientTransport({ command: cfg.command, args: cfg.args ?? [], env: { ...process.env, ...(cfg.env ?? {}) }, stderr: 'ignore' });
46
+ const client = new Client({ name: 'dragon-cli', version: '3.6.0' }, { capabilities: {} });
47
+ const t = transport;
48
+ const ok = await withTimeout((async () => {
49
+ await client.connect(t);
50
+ const { tools } = await client.listTools();
51
+ for (const tool of tools) {
52
+ const ns = `${name}__${tool.name}`;
53
+ this.specs.push({ name: ns, description: `[${name}] ${tool.description ?? ''}`.slice(0, 1024), parameters: tool.inputSchema ?? { type: 'object', properties: {} } });
54
+ this.route.set(ns, { server: name, tool: tool.name });
55
+ }
56
+ return true;
57
+ })(), CONNECT_TIMEOUT, () => { try {
58
+ void transport?.close();
59
+ }
60
+ catch { /* ignore */ } });
61
+ if (ok)
62
+ this.clients.set(name, client);
63
+ else
64
+ try {
65
+ await client.close();
66
+ }
67
+ catch { /* ignore */ }
68
+ }
69
+ catch { /* skip this server */ }
70
+ }
71
+ this.connected = this.clients.size > 0;
72
+ }
73
+ get serverCount() { return this.clients.size; }
74
+ toolSpecs() { return this.specs; }
75
+ handles(name) { return this.route.has(name); }
76
+ async call(name, args) {
77
+ const r = this.route.get(name);
78
+ if (!r)
79
+ return 'error: unknown MCP tool';
80
+ const client = this.clients.get(r.server);
81
+ if (!client)
82
+ return `error: MCP server "${r.server}" not connected`;
83
+ const res = await withTimeout(client.callTool({ name: r.tool, arguments: args }), CALL_TIMEOUT);
84
+ if (res === null)
85
+ return `error: ${name} timed out`;
86
+ const text = (res.content ?? []).map((c) => (c.type === 'text' ? c.text ?? '' : `[${c.type}]`)).join('\n').trim();
87
+ return (res.isError ? 'error: ' : '') + (text || '(no result)');
88
+ }
89
+ async close() {
90
+ for (const c of this.clients.values()) {
91
+ try {
92
+ await c.close();
93
+ }
94
+ catch { /* ignore */ }
95
+ }
96
+ this.connected = false;
97
+ }
98
+ }
99
+ /** Build + connect the hub from config (null if no servers configured). */
100
+ export async function loadMcpHub(servers) {
101
+ if (!servers || !Object.keys(servers).length)
102
+ return null;
103
+ const hub = new McpHub();
104
+ await hub.connect(servers);
105
+ return hub.serverCount ? hub : null;
106
+ }
107
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Session persistence — save the conversation on exit (so `dragon chat --resume`
3
+ * picks up where you left off) and export a clean markdown transcript (`/save`).
4
+ * Stored under ~/.dragon/sessions, 0600 (may contain code/context).
5
+ *
6
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
7
+ */
8
+ import type { BrainMessage } from '../brain/types.js';
9
+ export declare function saveSession(messages: BrainMessage[], meta: {
10
+ cwd: string;
11
+ brain: string;
12
+ }): void;
13
+ export declare function loadLastSession(): BrainMessage[] | null;
14
+ /** Export the conversation as markdown (tool results omitted as noise). Returns the path. */
15
+ export declare function exportMarkdown(messages: BrainMessage[], file?: string): string;
16
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAOhG;AAED,wBAAgB,eAAe,IAAI,YAAY,EAAE,GAAG,IAAI,CAMvD;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAa9E"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Session persistence — save the conversation on exit (so `dragon chat --resume`
3
+ * picks up where you left off) and export a clean markdown transcript (`/save`).
4
+ * Stored under ~/.dragon/sessions, 0600 (may contain code/context).
5
+ *
6
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
7
+ */
8
+ import { mkdirSync, writeFileSync, readFileSync, existsSync, chmodSync } from 'node:fs';
9
+ import { homedir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ const DIR = join(homedir(), '.dragon', 'sessions');
12
+ const LAST = join(DIR, 'last.json');
13
+ export function saveSession(messages, meta) {
14
+ try {
15
+ if (!messages.length)
16
+ return;
17
+ mkdirSync(DIR, { recursive: true, mode: 0o700 });
18
+ writeFileSync(LAST, JSON.stringify({ ts: new Date().toISOString(), ...meta, messages }), { mode: 0o600 });
19
+ chmodSync(LAST, 0o600);
20
+ }
21
+ catch { /* never break exit over a save */ }
22
+ }
23
+ export function loadLastSession() {
24
+ try {
25
+ if (!existsSync(LAST))
26
+ return null;
27
+ const o = JSON.parse(readFileSync(LAST, 'utf-8'));
28
+ return Array.isArray(o.messages) && o.messages.length ? o.messages : null;
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ /** Export the conversation as markdown (tool results omitted as noise). Returns the path. */
35
+ export function exportMarkdown(messages, file) {
36
+ const md = [`# Dragon session — ${new Date().toISOString()}`, ''];
37
+ for (const m of messages) {
38
+ if (m.role === 'user')
39
+ md.push(`## You\n\n${m.content}\n`);
40
+ else if (m.role === 'assistant' && m.content)
41
+ md.push(`## Dragon\n\n${m.content}\n`);
42
+ else if (m.role === 'assistant' && m.toolCalls?.length)
43
+ md.push(`_ran: ${m.toolCalls.map((t) => t.name).join(', ')}_\n`);
44
+ }
45
+ const out = md.join('\n');
46
+ if (file) {
47
+ writeFileSync(file, out);
48
+ return file;
49
+ }
50
+ mkdirSync(DIR, { recursive: true, mode: 0o700 });
51
+ const f = join(DIR, `transcript-${new Date().toISOString().replace(/[:.]/g, '-')}.md`);
52
+ writeFileSync(f, out);
53
+ return f;
54
+ }
55
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Skills-as-tools — the moat. Ghost Protocol's skill library (200+ reusable expert
3
+ * playbooks under ~/.copilot/skills) becomes live agent capability: the agent can
4
+ * `skill_search` for a relevant playbook and `skill_read` it to apply its guidance
5
+ * mid-task, instead of solving everything from cold.
6
+ *
7
+ * A dedicated, READ-ONLY library reader — deliberately separate from the cwd-confined
8
+ * file tools (the skill dir lives outside any project cwd, but it's our own curated,
9
+ * non-secret content, so reading it is safe + intentional).
10
+ *
11
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
12
+ */
13
+ import type { ToolSpec } from '../brain/types.js';
14
+ export interface SkillMeta {
15
+ name: string;
16
+ description: string;
17
+ path: string;
18
+ }
19
+ /** Minimal YAML frontmatter parse (name + description, inline or folded) — no dep. */
20
+ export declare function parseFrontmatter(text: string): {
21
+ name?: string;
22
+ description?: string;
23
+ };
24
+ export declare class SkillLibrary {
25
+ private index;
26
+ load(): void;
27
+ get count(): number;
28
+ search(query: string, limit?: number): SkillMeta[];
29
+ read(name: string): string | null;
30
+ toolSpecs(): ToolSpec[];
31
+ handles(name: string): boolean;
32
+ call(name: string, args: Record<string, unknown>): string;
33
+ }
34
+ /** Build + load the library (empty + harmless if no skill dirs exist). */
35
+ export declare function loadSkillLibrary(): SkillLibrary;
36
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/agent/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAKjD,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAE9E,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAwBtF;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAkB;IAE/B,IAAI,IAAI,IAAI;IAmBZ,IAAI,KAAK,IAAI,MAAM,CAA6B;IAEhD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,SAAS,EAAE;IAgB7C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAUjC,SAAS,IAAI,QAAQ,EAAE;IAgBvB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAE9B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;CAY1D;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,IAAI,YAAY,CAI/C"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Skills-as-tools — the moat. Ghost Protocol's skill library (200+ reusable expert
3
+ * playbooks under ~/.copilot/skills) becomes live agent capability: the agent can
4
+ * `skill_search` for a relevant playbook and `skill_read` it to apply its guidance
5
+ * mid-task, instead of solving everything from cold.
6
+ *
7
+ * A dedicated, READ-ONLY library reader — deliberately separate from the cwd-confined
8
+ * file tools (the skill dir lives outside any project cwd, but it's our own curated,
9
+ * non-secret content, so reading it is safe + intentional).
10
+ *
11
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
12
+ */
13
+ import { readdirSync, readFileSync, existsSync } from 'node:fs';
14
+ import { homedir } from 'node:os';
15
+ import { join } from 'node:path';
16
+ const SKILL_DIRS = [process.env.DRAGON_SKILLS_DIR, join(homedir(), '.copilot/skills'), join(homedir(), '.claude/skills')].filter(Boolean);
17
+ const MAX_SKILL_CHARS = 14_000;
18
+ /** Minimal YAML frontmatter parse (name + description, inline or folded) — no dep. */
19
+ export function parseFrontmatter(text) {
20
+ const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
21
+ if (!m)
22
+ return {};
23
+ const lines = m[1].split(/\r?\n/);
24
+ let name;
25
+ let description;
26
+ const unquote = (s) => s.replace(/^["']|["']$/g, '').trim();
27
+ for (let i = 0; i < lines.length; i++) {
28
+ let mm;
29
+ if ((mm = lines[i].match(/^name:\s*(.+)$/)))
30
+ name = unquote(mm[1]);
31
+ else if ((mm = lines[i].match(/^description:\s*(.*)$/))) {
32
+ let d = mm[1].trim();
33
+ if (d === '' || d === '>' || d === '|' || d === '>-' || d === '|-') {
34
+ const cont = [];
35
+ for (let j = i + 1; j < lines.length; j++) {
36
+ if (/^\s+\S/.test(lines[j]))
37
+ cont.push(lines[j].trim());
38
+ else
39
+ break;
40
+ }
41
+ d = cont.join(' ');
42
+ }
43
+ description = unquote(d);
44
+ }
45
+ }
46
+ return { name, description };
47
+ }
48
+ export class SkillLibrary {
49
+ index = [];
50
+ load() {
51
+ const seen = new Set();
52
+ for (const dir of SKILL_DIRS) {
53
+ if (!existsSync(dir))
54
+ continue;
55
+ let entries;
56
+ try {
57
+ entries = readdirSync(dir);
58
+ }
59
+ catch {
60
+ continue;
61
+ }
62
+ for (const entry of entries) {
63
+ const skillFile = join(dir, entry, 'SKILL.md');
64
+ if (!existsSync(skillFile))
65
+ continue;
66
+ let fm;
67
+ try {
68
+ fm = parseFrontmatter(readFileSync(skillFile, 'utf-8'));
69
+ }
70
+ catch {
71
+ continue;
72
+ }
73
+ const name = fm.name || entry;
74
+ if (seen.has(name))
75
+ continue; // first dir wins (~/.copilot is canonical)
76
+ seen.add(name);
77
+ this.index.push({ name, description: fm.description ?? '', path: skillFile });
78
+ }
79
+ }
80
+ }
81
+ get count() { return this.index.length; }
82
+ search(query, limit = 8) {
83
+ const terms = query.toLowerCase().split(/\W+/).filter((t) => t.length > 2);
84
+ if (!terms.length)
85
+ return [];
86
+ const scored = this.index.map((s) => {
87
+ const name = s.name.toLowerCase();
88
+ const desc = s.description.toLowerCase();
89
+ let score = 0;
90
+ for (const t of terms) {
91
+ if (name.includes(t))
92
+ score += 5;
93
+ if (desc.includes(t))
94
+ score += 1;
95
+ }
96
+ return { s, score };
97
+ }).filter((x) => x.score > 0).sort((a, b) => b.score - a.score);
98
+ return scored.slice(0, limit).map((x) => x.s);
99
+ }
100
+ read(name) {
101
+ const want = name.toLowerCase().trim();
102
+ const hit = this.index.find((s) => s.name.toLowerCase() === want) ?? this.index.find((s) => s.name.toLowerCase().includes(want));
103
+ if (!hit)
104
+ return null;
105
+ try {
106
+ const body = readFileSync(hit.path, 'utf-8');
107
+ return body.length > MAX_SKILL_CHARS ? body.slice(0, MAX_SKILL_CHARS) + '\n… [truncated]' : body;
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ }
113
+ toolSpecs() {
114
+ if (!this.count)
115
+ return [];
116
+ return [
117
+ {
118
+ name: 'skill_search',
119
+ description: `Search Ghost Protocol's library of ${this.count} reusable expert skills (playbooks for design, security, infra, brand, specific projects). Use BEFORE solving a non-trivial task from scratch — there's likely a skill for it. Returns matching skill names + summaries; then skill_read one.`,
120
+ parameters: { type: 'object', properties: { query: { type: 'string', description: 'what you need help with, e.g. "harden a cli" or "premium dark web design"' } }, required: ['query'] },
121
+ },
122
+ {
123
+ name: 'skill_read',
124
+ description: 'Read the full content of a named skill (from skill_search) and apply its guidance to the task.',
125
+ parameters: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] },
126
+ },
127
+ ];
128
+ }
129
+ handles(name) { return name === 'skill_search' || name === 'skill_read'; }
130
+ call(name, args) {
131
+ if (name === 'skill_search') {
132
+ const hits = this.search(String(args.query ?? ''));
133
+ if (!hits.length)
134
+ return 'no matching skills';
135
+ return hits.map((s) => `• ${s.name} — ${s.description.slice(0, 180)}`).join('\n');
136
+ }
137
+ if (name === 'skill_read') {
138
+ const body = this.read(String(args.name ?? ''));
139
+ return body ?? `no skill named "${args.name}" (use skill_search first)`;
140
+ }
141
+ return 'error: unknown skill tool';
142
+ }
143
+ }
144
+ /** Build + load the library (empty + harmless if no skill dirs exist). */
145
+ export function loadSkillLibrary() {
146
+ const lib = new SkillLibrary();
147
+ try {
148
+ lib.load();
149
+ }
150
+ catch { /* never block the agent over skills */ }
151
+ return lib;
152
+ }
153
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Stack-fused tools — the agent IS the operator console. It can read the live Ghost
3
+ * Protocol stack state and drive `dragon` subcommands (scale/wyrm/pentest/keep/net…)
4
+ * as first-class tools, not just guess at bash.
5
+ *
6
+ * Safety: read-only verbs run freely; everything else (pentest, deploy, install…) is
7
+ * `dangerous` → always prompts + shows the full command (never covered by --auto).
8
+ * Interactive/recursive/long-lived verbs (chat/ask/login/serve/up) are refused.
9
+ * Each invocation is a fresh subprocess of THIS CLI, time-bounded + output-capped.
10
+ *
11
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
12
+ */
13
+ import type { ToolSpec } from '../brain/types.js';
14
+ import type { ToolContext } from './tools.js';
15
+ export interface StackTools {
16
+ specs: ToolSpec[];
17
+ handles(name: string): boolean;
18
+ call(name: string, args: Record<string, unknown>, ctx: ToolContext): Promise<string>;
19
+ }
20
+ export declare function makeStackTools(): StackTools;
21
+ //# sourceMappingURL=stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../src/agent/stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAyC7C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAC9B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACrF;AAED,wBAAgB,cAAc,IAAI,UAAU,CA8C3C"}