aura-code 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/dist/agent/context.d.ts +13 -0
  4. package/dist/agent/context.js +183 -0
  5. package/dist/agent/context.js.map +1 -0
  6. package/dist/agent/loop.d.ts +60 -0
  7. package/dist/agent/loop.js +228 -0
  8. package/dist/agent/loop.js.map +1 -0
  9. package/dist/agent/session-store.d.ts +30 -0
  10. package/dist/agent/session-store.js +178 -0
  11. package/dist/agent/session-store.js.map +1 -0
  12. package/dist/agent/spawner.d.ts +57 -0
  13. package/dist/agent/spawner.js +112 -0
  14. package/dist/agent/spawner.js.map +1 -0
  15. package/dist/agent/system-prompt.d.ts +3 -0
  16. package/dist/agent/system-prompt.js +100 -0
  17. package/dist/agent/system-prompt.js.map +1 -0
  18. package/dist/architect/engine.d.ts +47 -0
  19. package/dist/architect/engine.js +217 -0
  20. package/dist/architect/engine.js.map +1 -0
  21. package/dist/architect/types.d.ts +61 -0
  22. package/dist/architect/types.js +6 -0
  23. package/dist/architect/types.js.map +1 -0
  24. package/dist/cli/diamond.d.ts +1 -0
  25. package/dist/cli/diamond.js +23 -0
  26. package/dist/cli/diamond.js.map +1 -0
  27. package/dist/cli/display.d.ts +44 -0
  28. package/dist/cli/display.js +234 -0
  29. package/dist/cli/display.js.map +1 -0
  30. package/dist/cli/index.d.ts +2 -0
  31. package/dist/cli/index.js +1734 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/config/defaults.d.ts +46 -0
  34. package/dist/config/defaults.js +106 -0
  35. package/dist/config/defaults.js.map +1 -0
  36. package/dist/config/project-config.d.ts +90 -0
  37. package/dist/config/project-config.js +141 -0
  38. package/dist/config/project-config.js.map +1 -0
  39. package/dist/harness/proposer.d.ts +26 -0
  40. package/dist/harness/proposer.js +204 -0
  41. package/dist/harness/proposer.js.map +1 -0
  42. package/dist/harness/weakness-miner.d.ts +28 -0
  43. package/dist/harness/weakness-miner.js +359 -0
  44. package/dist/harness/weakness-miner.js.map +1 -0
  45. package/dist/integrations/harnesses.d.ts +23 -0
  46. package/dist/integrations/harnesses.js +161 -0
  47. package/dist/integrations/harnesses.js.map +1 -0
  48. package/dist/orchestration/competence.d.ts +53 -0
  49. package/dist/orchestration/competence.js +181 -0
  50. package/dist/orchestration/competence.js.map +1 -0
  51. package/dist/orchestration/executor.d.ts +42 -0
  52. package/dist/orchestration/executor.js +206 -0
  53. package/dist/orchestration/executor.js.map +1 -0
  54. package/dist/orchestration/index.d.ts +20 -0
  55. package/dist/orchestration/index.js +39 -0
  56. package/dist/orchestration/index.js.map +1 -0
  57. package/dist/orchestration/orchestrator-prompts.d.ts +11 -0
  58. package/dist/orchestration/orchestrator-prompts.js +153 -0
  59. package/dist/orchestration/orchestrator-prompts.js.map +1 -0
  60. package/dist/orchestration/orchestrator.d.ts +27 -0
  61. package/dist/orchestration/orchestrator.js +169 -0
  62. package/dist/orchestration/orchestrator.js.map +1 -0
  63. package/dist/orchestration/plan-store.d.ts +53 -0
  64. package/dist/orchestration/plan-store.js +176 -0
  65. package/dist/orchestration/plan-store.js.map +1 -0
  66. package/dist/orchestration/router-prompts.d.ts +6 -0
  67. package/dist/orchestration/router-prompts.js +55 -0
  68. package/dist/orchestration/router-prompts.js.map +1 -0
  69. package/dist/orchestration/router.d.ts +23 -0
  70. package/dist/orchestration/router.js +70 -0
  71. package/dist/orchestration/router.js.map +1 -0
  72. package/dist/orchestration/ruby-detect.d.ts +6 -0
  73. package/dist/orchestration/ruby-detect.js +130 -0
  74. package/dist/orchestration/ruby-detect.js.map +1 -0
  75. package/dist/orchestration/ruby-types.d.ts +44 -0
  76. package/dist/orchestration/ruby-types.js +6 -0
  77. package/dist/orchestration/ruby-types.js.map +1 -0
  78. package/dist/orchestration/specialist-prompts.d.ts +19 -0
  79. package/dist/orchestration/specialist-prompts.js +143 -0
  80. package/dist/orchestration/specialist-prompts.js.map +1 -0
  81. package/dist/orchestration/specialists.d.ts +66 -0
  82. package/dist/orchestration/specialists.js +175 -0
  83. package/dist/orchestration/specialists.js.map +1 -0
  84. package/dist/orchestration/types.d.ts +82 -0
  85. package/dist/orchestration/types.js +6 -0
  86. package/dist/orchestration/types.js.map +1 -0
  87. package/dist/perception/extractor.d.ts +7 -0
  88. package/dist/perception/extractor.js +521 -0
  89. package/dist/perception/extractor.js.map +1 -0
  90. package/dist/perception/graph-store.d.ts +25 -0
  91. package/dist/perception/graph-store.js +117 -0
  92. package/dist/perception/graph-store.js.map +1 -0
  93. package/dist/perception/index.d.ts +4 -0
  94. package/dist/perception/index.js +18 -0
  95. package/dist/perception/index.js.map +1 -0
  96. package/dist/perception/queries.d.ts +29 -0
  97. package/dist/perception/queries.js +354 -0
  98. package/dist/perception/queries.js.map +1 -0
  99. package/dist/perception/types.d.ts +103 -0
  100. package/dist/perception/types.js +6 -0
  101. package/dist/perception/types.js.map +1 -0
  102. package/dist/providers/anthropic.d.ts +11 -0
  103. package/dist/providers/anthropic.js +172 -0
  104. package/dist/providers/anthropic.js.map +1 -0
  105. package/dist/providers/factory.d.ts +58 -0
  106. package/dist/providers/factory.js +354 -0
  107. package/dist/providers/factory.js.map +1 -0
  108. package/dist/providers/fallback.d.ts +24 -0
  109. package/dist/providers/fallback.js +72 -0
  110. package/dist/providers/fallback.js.map +1 -0
  111. package/dist/providers/google.d.ts +11 -0
  112. package/dist/providers/google.js +129 -0
  113. package/dist/providers/google.js.map +1 -0
  114. package/dist/providers/openai-compatible.d.ts +24 -0
  115. package/dist/providers/openai-compatible.js +216 -0
  116. package/dist/providers/openai-compatible.js.map +1 -0
  117. package/dist/providers/resilient-factory.d.ts +24 -0
  118. package/dist/providers/resilient-factory.js +57 -0
  119. package/dist/providers/resilient-factory.js.map +1 -0
  120. package/dist/providers/resilient.d.ts +63 -0
  121. package/dist/providers/resilient.js +145 -0
  122. package/dist/providers/resilient.js.map +1 -0
  123. package/dist/providers/types.d.ts +84 -0
  124. package/dist/providers/types.js +6 -0
  125. package/dist/providers/types.js.map +1 -0
  126. package/dist/ruby/alternator.d.ts +40 -0
  127. package/dist/ruby/alternator.js +233 -0
  128. package/dist/ruby/alternator.js.map +1 -0
  129. package/dist/ruby/competence.d.ts +34 -0
  130. package/dist/ruby/competence.js +264 -0
  131. package/dist/ruby/competence.js.map +1 -0
  132. package/dist/ruby/episode-capture.d.ts +56 -0
  133. package/dist/ruby/episode-capture.js +193 -0
  134. package/dist/ruby/episode-capture.js.map +1 -0
  135. package/dist/ruby/fine-tune.d.ts +16 -0
  136. package/dist/ruby/fine-tune.js +253 -0
  137. package/dist/ruby/fine-tune.js.map +1 -0
  138. package/dist/ruby/index.d.ts +10 -0
  139. package/dist/ruby/index.js +26 -0
  140. package/dist/ruby/index.js.map +1 -0
  141. package/dist/ruby/ruby-model.d.ts +36 -0
  142. package/dist/ruby/ruby-model.js +81 -0
  143. package/dist/ruby/ruby-model.js.map +1 -0
  144. package/dist/ruby/training-data.d.ts +11 -0
  145. package/dist/ruby/training-data.js +107 -0
  146. package/dist/ruby/training-data.js.map +1 -0
  147. package/dist/ruby/types.d.ts +136 -0
  148. package/dist/ruby/types.js +20 -0
  149. package/dist/ruby/types.js.map +1 -0
  150. package/dist/safety/permissions.d.ts +32 -0
  151. package/dist/safety/permissions.js +304 -0
  152. package/dist/safety/permissions.js.map +1 -0
  153. package/dist/server/index.d.ts +9 -0
  154. package/dist/server/index.js +342 -0
  155. package/dist/server/index.js.map +1 -0
  156. package/dist/server/session.d.ts +22 -0
  157. package/dist/server/session.js +16 -0
  158. package/dist/server/session.js.map +1 -0
  159. package/dist/setup/first-run.d.ts +41 -0
  160. package/dist/setup/first-run.js +386 -0
  161. package/dist/setup/first-run.js.map +1 -0
  162. package/dist/setup/global-config.d.ts +19 -0
  163. package/dist/setup/global-config.js +97 -0
  164. package/dist/setup/global-config.js.map +1 -0
  165. package/dist/tools/audio-transcribe.d.ts +8 -0
  166. package/dist/tools/audio-transcribe.js +156 -0
  167. package/dist/tools/audio-transcribe.js.map +1 -0
  168. package/dist/tools/browser.d.ts +15 -0
  169. package/dist/tools/browser.js +241 -0
  170. package/dist/tools/browser.js.map +1 -0
  171. package/dist/tools/calendar.d.ts +12 -0
  172. package/dist/tools/calendar.js +196 -0
  173. package/dist/tools/calendar.js.map +1 -0
  174. package/dist/tools/clipboard.d.ts +7 -0
  175. package/dist/tools/clipboard.js +90 -0
  176. package/dist/tools/clipboard.js.map +1 -0
  177. package/dist/tools/cron.d.ts +10 -0
  178. package/dist/tools/cron.js +167 -0
  179. package/dist/tools/cron.js.map +1 -0
  180. package/dist/tools/edit-file.d.ts +6 -0
  181. package/dist/tools/edit-file.js +98 -0
  182. package/dist/tools/edit-file.js.map +1 -0
  183. package/dist/tools/email.d.ts +11 -0
  184. package/dist/tools/email.js +153 -0
  185. package/dist/tools/email.js.map +1 -0
  186. package/dist/tools/git.d.ts +1 -0
  187. package/dist/tools/git.js +7 -0
  188. package/dist/tools/git.js.map +1 -0
  189. package/dist/tools/http-request.d.ts +12 -0
  190. package/dist/tools/http-request.js +91 -0
  191. package/dist/tools/http-request.js.map +1 -0
  192. package/dist/tools/image-read.d.ts +7 -0
  193. package/dist/tools/image-read.js +125 -0
  194. package/dist/tools/image-read.js.map +1 -0
  195. package/dist/tools/index.d.ts +3 -0
  196. package/dist/tools/index.js +201 -0
  197. package/dist/tools/index.js.map +1 -0
  198. package/dist/tools/list-dir.d.ts +1 -0
  199. package/dist/tools/list-dir.js +6 -0
  200. package/dist/tools/list-dir.js.map +1 -0
  201. package/dist/tools/mcp.d.ts +11 -0
  202. package/dist/tools/mcp.js +356 -0
  203. package/dist/tools/mcp.js.map +1 -0
  204. package/dist/tools/memory.d.ts +9 -0
  205. package/dist/tools/memory.js +124 -0
  206. package/dist/tools/memory.js.map +1 -0
  207. package/dist/tools/notify.d.ts +9 -0
  208. package/dist/tools/notify.js +40 -0
  209. package/dist/tools/notify.js.map +1 -0
  210. package/dist/tools/read-file.d.ts +6 -0
  211. package/dist/tools/read-file.js +76 -0
  212. package/dist/tools/read-file.js.map +1 -0
  213. package/dist/tools/run-shell.d.ts +1 -0
  214. package/dist/tools/run-shell.js +6 -0
  215. package/dist/tools/run-shell.js.map +1 -0
  216. package/dist/tools/run-tests.d.ts +1 -0
  217. package/dist/tools/run-tests.js +6 -0
  218. package/dist/tools/run-tests.js.map +1 -0
  219. package/dist/tools/search-code.d.ts +1 -0
  220. package/dist/tools/search-code.js +6 -0
  221. package/dist/tools/search-code.js.map +1 -0
  222. package/dist/tools/telegram-bot.d.ts +2 -0
  223. package/dist/tools/telegram-bot.js +693 -0
  224. package/dist/tools/telegram-bot.js.map +1 -0
  225. package/dist/tools/telegram-safety.d.ts +52 -0
  226. package/dist/tools/telegram-safety.js +189 -0
  227. package/dist/tools/telegram-safety.js.map +1 -0
  228. package/dist/tools/telegram.d.ts +16 -0
  229. package/dist/tools/telegram.js +227 -0
  230. package/dist/tools/telegram.js.map +1 -0
  231. package/dist/tools/tools.d.ts +48 -0
  232. package/dist/tools/tools.js +328 -0
  233. package/dist/tools/tools.js.map +1 -0
  234. package/dist/tools/web-fetch.d.ts +11 -0
  235. package/dist/tools/web-fetch.js +119 -0
  236. package/dist/tools/web-fetch.js.map +1 -0
  237. package/dist/tools/web-search.d.ts +8 -0
  238. package/dist/tools/web-search.js +81 -0
  239. package/dist/tools/web-search.js.map +1 -0
  240. package/dist/tools/whatsapp.d.ts +10 -0
  241. package/dist/tools/whatsapp.js +222 -0
  242. package/dist/tools/whatsapp.js.map +1 -0
  243. package/dist/tools/write-file.d.ts +1 -0
  244. package/dist/tools/write-file.js +6 -0
  245. package/dist/tools/write-file.js.map +1 -0
  246. package/dist/tools/youtube-transcript.d.ts +9 -0
  247. package/dist/tools/youtube-transcript.js +200 -0
  248. package/dist/tools/youtube-transcript.js.map +1 -0
  249. package/dist/util/circuit-breaker.d.ts +44 -0
  250. package/dist/util/circuit-breaker.js +78 -0
  251. package/dist/util/circuit-breaker.js.map +1 -0
  252. package/dist/util/env.d.ts +18 -0
  253. package/dist/util/env.js +39 -0
  254. package/dist/util/env.js.map +1 -0
  255. package/dist/util/errors.d.ts +42 -0
  256. package/dist/util/errors.js +121 -0
  257. package/dist/util/errors.js.map +1 -0
  258. package/dist/util/rate-limiter.d.ts +71 -0
  259. package/dist/util/rate-limiter.js +141 -0
  260. package/dist/util/rate-limiter.js.map +1 -0
  261. package/dist/util/retry.d.ts +30 -0
  262. package/dist/util/retry.js +57 -0
  263. package/dist/util/retry.js.map +1 -0
  264. package/dist/util/sanitize.d.ts +16 -0
  265. package/dist/util/sanitize.js +255 -0
  266. package/dist/util/sanitize.js.map +1 -0
  267. package/dist/verify/checks.d.ts +21 -0
  268. package/dist/verify/checks.js +311 -0
  269. package/dist/verify/checks.js.map +1 -0
  270. package/dist/verify/index.d.ts +33 -0
  271. package/dist/verify/index.js +127 -0
  272. package/dist/verify/index.js.map +1 -0
  273. package/dist/verify/types.d.ts +20 -0
  274. package/dist/verify/types.js +3 -0
  275. package/dist/verify/types.js.map +1 -0
  276. package/dist/versioncheck.d.ts +22 -0
  277. package/dist/versioncheck.js +203 -0
  278. package/dist/versioncheck.js.map +1 -0
  279. package/dist/viz/index.d.ts +2 -0
  280. package/dist/viz/index.js +733 -0
  281. package/dist/viz/index.js.map +1 -0
  282. package/dist/workflows/engine.d.ts +46 -0
  283. package/dist/workflows/engine.js +320 -0
  284. package/dist/workflows/engine.js.map +1 -0
  285. package/dist/workflows/types.d.ts +92 -0
  286. package/dist/workflows/types.js +6 -0
  287. package/dist/workflows/types.js.map +1 -0
  288. package/package.json +86 -0
@@ -0,0 +1,733 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateDashboard = generateDashboard;
37
+ exports.openDashboard = openDashboard;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const child_process_1 = require("child_process");
41
+ // ─────────────────────────────────────────────────────────────────────────────
42
+ // Data loaders
43
+ // ─────────────────────────────────────────────────────────────────────────────
44
+ function loadGraph(projectRoot) {
45
+ const p = path.join(projectRoot, 'graphify-out', 'graph.json');
46
+ if (!fs.existsSync(p))
47
+ return null;
48
+ try {
49
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ function loadPlans(projectRoot) {
56
+ const base = path.join(process.env.HOME ?? '/tmp', '.aura', 'plans');
57
+ if (!fs.existsSync(base))
58
+ return [];
59
+ const safe = projectRoot.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 80);
60
+ const readDir = (d) => {
61
+ if (!fs.existsSync(d))
62
+ return [];
63
+ return fs.readdirSync(d)
64
+ .filter(f => f.endsWith('.json') && !f.endsWith('.tmp'))
65
+ .map(f => {
66
+ try {
67
+ return JSON.parse(fs.readFileSync(path.join(d, f), 'utf8'));
68
+ }
69
+ catch {
70
+ return null;
71
+ }
72
+ })
73
+ .filter((p) => p !== null);
74
+ };
75
+ // Plans from root level + project-specific subdir
76
+ const rootPlans = readDir(base);
77
+ const subPlans = readDir(path.join(base, safe));
78
+ const seen = new Set(rootPlans.map(p => p.id));
79
+ const merged = [...rootPlans];
80
+ for (const p of subPlans) {
81
+ if (!seen.has(p.id))
82
+ merged.push(p);
83
+ }
84
+ return merged.sort((a, b) => b.created - a.created);
85
+ }
86
+ function loadSessions(projectRoot) {
87
+ const base = path.join(process.env.HOME ?? '/tmp', '.aura', 'sessions');
88
+ const safe = projectRoot.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 80);
89
+ const readDir = (d) => {
90
+ if (!fs.existsSync(d))
91
+ return [];
92
+ return fs.readdirSync(d)
93
+ .filter(f => f.endsWith('.json') && !f.endsWith('.tmp'))
94
+ .map(f => {
95
+ try {
96
+ const parsed = JSON.parse(fs.readFileSync(path.join(d, f), 'utf8'));
97
+ if (!parsed.id)
98
+ return null;
99
+ return parsed;
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ })
105
+ .filter((s) => s !== null);
106
+ };
107
+ // Sessions from project-specific subdir + any .json files at root level
108
+ const subSessions = readDir(path.join(base, safe));
109
+ const rootSessions = readDir(base);
110
+ const seen = new Set(subSessions.map(s => s.id));
111
+ const merged = [...subSessions];
112
+ for (const s of rootSessions) {
113
+ if (!seen.has(s.id))
114
+ merged.push(s);
115
+ }
116
+ return merged.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
117
+ }
118
+ /**
119
+ * Strip a session down to metadata only — removes the full message history
120
+ * which can contain backticks, </script> tags, and other HTML-breaking content.
121
+ */
122
+ function stripSession(s) {
123
+ const history = s.history ?? [];
124
+ return {
125
+ id: s.id,
126
+ title: s.title,
127
+ createdAt: s.createdAt,
128
+ updatedAt: s.updatedAt,
129
+ messageCount: history.length,
130
+ toolCallCount: history.filter(m => m.role === 'tool_result').length,
131
+ };
132
+ }
133
+ /**
134
+ * Strip a plan down to metadata only — removes step result strings and
135
+ * plan outcome which can contain code with backticks or </script> sequences.
136
+ */
137
+ function stripPlan(p) {
138
+ return {
139
+ id: p.id,
140
+ goal: p.goal,
141
+ status: p.status,
142
+ created: p.created,
143
+ completed: p.completed,
144
+ steps: p.steps.map(s => ({
145
+ id: s.id,
146
+ specialist: s.specialist,
147
+ task: s.task,
148
+ status: s.status,
149
+ durationMs: s.durationMs,
150
+ dependsOn: s.dependsOn,
151
+ })),
152
+ };
153
+ }
154
+ function loadMemory(projectRoot) {
155
+ const base = path.join(process.env.HOME ?? '/tmp', '.aura', 'memory');
156
+ const safe = projectRoot.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 80);
157
+ const dir = path.join(base, safe);
158
+ if (!fs.existsSync(dir))
159
+ return [];
160
+ return fs.readdirSync(dir)
161
+ .filter(f => f.endsWith('.json') && !f.endsWith('.tmp'))
162
+ .map(f => {
163
+ try {
164
+ return JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8'));
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ })
170
+ .filter((m) => m !== null);
171
+ }
172
+ // ─────────────────────────────────────────────────────────────────────────────
173
+ // HTML template
174
+ // ─────────────────────────────────────────────────────────────────────────────
175
+ function buildHtml(data) {
176
+ // Escape </script> in JSON to prevent premature script-block closing in HTML
177
+ const json = JSON.stringify(data, null, 0).replace(/<\/script>/gi, '<\\/script>');
178
+ return `<!DOCTYPE html>
179
+ <html lang="en">
180
+ <head>
181
+ <meta charset="UTF-8">
182
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
183
+ <title>Aura — Memory Dashboard · ${data.projectName}</title>
184
+ <script src="https://d3js.org/d3.v7.min.js"></script>
185
+ <style>
186
+ :root {
187
+ --bg: #0d1117;
188
+ --surface: #161b22;
189
+ --canvas: #1c2128;
190
+ --card: #21262d;
191
+ --border: #30363d;
192
+ --border2: #484f58;
193
+ --primary: #f0883e;
194
+ --text: #e6edf3;
195
+ --muted: #8b949e;
196
+ --dim: #6e7681;
197
+ --success: #3fb950;
198
+ --error: #f85149;
199
+ --amber: #d29922;
200
+ --blue: #58a6ff;
201
+ --purple: #bc8cff;
202
+ }
203
+ * { box-sizing: border-box; margin: 0; padding: 0; }
204
+ body { background: var(--bg); color: var(--text); font-family: ui-monospace, 'JetBrains Mono', 'Fira Code', monospace; font-size: 13px; min-height: 100vh; }
205
+ header { background: var(--surface); border-bottom: 1px solid var(--border); padding: 12px 24px; display: flex; align-items: center; gap: 16px; }
206
+ header h1 { color: var(--primary); font-size: 14px; font-weight: 700; letter-spacing: 0.03em; }
207
+ header .meta { color: var(--muted); font-size: 11px; }
208
+ nav { background: var(--surface); border-bottom: 1px solid var(--border); display: flex; }
209
+ nav button { background: none; border: none; border-bottom: 2px solid transparent; color: var(--muted); cursor: pointer; font: inherit; font-size: 12px; padding: 9px 18px; transition: color .12s; }
210
+ nav button:hover { color: var(--text); }
211
+ nav button.active { border-bottom-color: var(--primary); color: var(--primary); }
212
+ .panel { display: none; padding: 20px; height: calc(100vh - 82px); overflow: auto; }
213
+ .panel.active { display: flex; flex-direction: column; gap: 14px; }
214
+
215
+ /* Overview */
216
+ .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; }
217
+ .stat-card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 16px 18px; }
218
+ .stat-card .num { color: var(--primary); font-size: 30px; font-weight: 700; line-height: 1; }
219
+ .stat-card .lbl { color: var(--muted); font-size: 10px; margin-top: 5px; text-transform: uppercase; letter-spacing: .08em; }
220
+
221
+ /* Graph panel */
222
+ #graph-svg { background: var(--canvas); border: 1px solid var(--border); border-radius: 8px; flex: 1; min-height: 0; cursor: grab; }
223
+ #graph-svg:active { cursor: grabbing; }
224
+ .graph-controls { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
225
+ .graph-controls input {
226
+ background: var(--card); border: 1px solid var(--border2); border-radius: 6px;
227
+ color: var(--text); font: inherit; font-size: 12px; padding: 6px 12px; width: 220px; outline: none;
228
+ }
229
+ .graph-controls input:focus { border-color: var(--primary); }
230
+ .legend { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
231
+ .legend-item { display: flex; align-items: center; gap: 5px; cursor: pointer; user-select: none; padding: 3px 9px; border-radius: 12px; border: 1.5px solid transparent; font-size: 11px; color: var(--muted); transition: all .12s; }
232
+ .legend-item.on { border-color: currentColor; color: var(--text); }
233
+ .legend-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
234
+ .hint { color: var(--dim); font-size: 10px; }
235
+
236
+ /* Tooltip */
237
+ .tooltip {
238
+ position: fixed; background: #161b22f0; border: 1px solid var(--border2);
239
+ border-radius: 7px; color: var(--text); font-size: 11px; max-width: 300px;
240
+ padding: 9px 13px; pointer-events: none; z-index: 999; line-height: 1.6;
241
+ box-shadow: 0 4px 20px #0008;
242
+ }
243
+ .tooltip strong { color: var(--primary); font-size: 12px; }
244
+ .tooltip .t-type { color: var(--muted); font-size: 10px; text-transform: uppercase; letter-spacing: .05em; }
245
+ .tooltip .t-file { color: var(--blue); font-size: 10px; }
246
+
247
+ /* Sessions */
248
+ .session-list { display: flex; flex-direction: column; gap: 8px; }
249
+ .session-card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer; padding: 12px 16px; transition: border-color .12s; }
250
+ .session-card:hover { border-color: var(--border2); }
251
+ .session-card.expanded { border-color: var(--primary); }
252
+ .s-header { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
253
+ .s-title { color: var(--text); font-size: 13px; font-weight: 600; }
254
+ .s-meta { color: var(--muted); font-size: 11px; white-space: nowrap; }
255
+ .s-id { color: var(--dim); font-size: 10px; margin-top: 3px; }
256
+ .session-messages { border-top: 1px solid var(--border); margin-top: 10px; padding-top: 10px; display: none; }
257
+ .session-card.expanded .session-messages { display: block; }
258
+ .msg { display: flex; gap: 10px; margin-bottom: 8px; }
259
+ .msg-role { font-size: 10px; min-width: 64px; padding-top: 1px; text-align: right; text-transform: uppercase; font-weight: 700; letter-spacing: .04em; flex-shrink: 0; }
260
+ .msg-role.user { color: var(--amber); }
261
+ .msg-role.assistant { color: var(--blue); }
262
+ .msg-role.tool_result { color: var(--success); }
263
+ .msg-content { color: var(--muted); font-size: 11px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; max-height: 100px; overflow: auto; }
264
+
265
+ /* Plans */
266
+ .plans-layout { display: flex; gap: 14px; flex: 1; min-height: 0; }
267
+ .plan-list-panel { display: flex; flex-direction: column; gap: 7px; width: 290px; flex-shrink: 0; overflow-y: auto; }
268
+ .plan-detail { flex: 1; display: flex; flex-direction: column; gap: 12px; min-height: 0; min-width: 0; }
269
+ .plan-card { background: var(--card); border: 1px solid var(--border); border-radius: 7px; cursor: pointer; padding: 10px 13px; transition: border-color .12s; }
270
+ .plan-card:hover { border-color: var(--border2); }
271
+ .plan-card.selected { border-color: var(--primary); background: #21262dcc; }
272
+ .p-goal { color: var(--text); font-size: 12px; line-height: 1.4; }
273
+ .p-meta { color: var(--muted); font-size: 10px; margin-top: 5px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
274
+ .status-badge { border-radius: 10px; font-size: 10px; font-weight: 700; padding: 2px 8px; display: inline-block; letter-spacing: .03em; }
275
+ .status-done { background: #1f3a2a; color: #3fb950; border: 1px solid #3fb95050; }
276
+ .status-failed { background: #3a1f1f; color: #f85149; border: 1px solid #f8514950; }
277
+ .status-running { background: #332a1a; color: #d29922; border: 1px solid #d2992250; }
278
+ .status-pending { background: #1a2233; color: #58a6ff; border: 1px solid #58a6ff50; }
279
+ .status-aborted { background: #252530; color: #8b949e; border: 1px solid #8b949e50; }
280
+ #dag-svg { background: var(--canvas); border: 1px solid var(--border); border-radius: 8px; flex: 1; min-height: 300px; cursor: grab; }
281
+ #dag-svg:active { cursor: grabbing; }
282
+ .step-result { background: var(--canvas); border: 1px solid var(--border); border-radius: 7px; color: var(--muted); font-size: 11px; line-height: 1.55; max-height: 180px; overflow-y: auto; padding: 12px 14px; white-space: pre-wrap; word-break: break-word; }
283
+
284
+ /* Memory table */
285
+ .memory-table { border-collapse: collapse; width: 100%; }
286
+ .memory-table th { background: var(--card); border-bottom: 2px solid var(--border); color: var(--muted); font-size: 10px; font-weight: 700; letter-spacing: .08em; padding: 9px 13px; text-align: left; text-transform: uppercase; }
287
+ .memory-table td { border-bottom: 1px solid var(--border); color: var(--muted); font-size: 11px; padding: 8px 13px; vertical-align: top; }
288
+ .memory-table td:first-child { color: var(--primary); white-space: nowrap; font-weight: 600; }
289
+ .memory-table tr:hover td { background: var(--card); }
290
+ .memory-val { max-width: 560px; white-space: pre-wrap; word-break: break-word; color: var(--text); }
291
+
292
+ .empty { color: var(--dim); font-size: 12px; padding: 32px; text-align: center; }
293
+ ::-webkit-scrollbar { width: 5px; height: 5px; }
294
+ ::-webkit-scrollbar-track { background: transparent; }
295
+ ::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; }
296
+ </style>
297
+ </head>
298
+ <body>
299
+ <header>
300
+ <h1>◈ Aura / memory dashboard</h1>
301
+ <span class="meta">project: <strong style="color:var(--primary)">${data.projectName}</strong> &nbsp;·&nbsp; ${data.generatedAt}</span>
302
+ </header>
303
+ <nav>
304
+ <button class="active" onclick="showPanel('overview',this)">Overview</button>
305
+ <button onclick="showPanel('graph',this)">Codebase Graph</button>
306
+ <button onclick="showPanel('sessions',this)">Sessions</button>
307
+ <button onclick="showPanel('plans',this)">Execution Plans</button>
308
+ <button onclick="showPanel('memory',this)">Agent Memory</button>
309
+ </nav>
310
+
311
+ <div id="overview" class="panel active"></div>
312
+ <div id="graph" class="panel"></div>
313
+ <div id="sessions" class="panel"></div>
314
+ <div id="plans" class="panel"></div>
315
+ <div id="memory" class="panel"></div>
316
+
317
+ <div class="tooltip" id="tooltip" style="display:none"></div>
318
+
319
+ <script>
320
+ const DATA = ` + json + `;
321
+
322
+ function showPanel(id, btn) {
323
+ document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
324
+ document.querySelectorAll('nav button').forEach(b => b.classList.remove('active'));
325
+ document.getElementById(id).classList.add('active');
326
+ if (btn) btn.classList.add('active');
327
+ if (id === 'graph' && !graphInit) initGraph();
328
+ if (id === 'plans' && !plansInit) initPlans();
329
+ }
330
+
331
+ // ── Overview ─────────────────────────────────────────────────────────────────
332
+ (function() {
333
+ const g = DATA.graph;
334
+ const nc = g ? g.nodes.length : 0, ec = g ? g.edges.length : 0;
335
+ const sc = DATA.sessions.length, pc = DATA.plans.length;
336
+ const mc = DATA.memory.length, dc = DATA.plans.filter(p=>p.status==='done').length;
337
+ const last = sc ? new Date(DATA.sessions[0].updatedAt).toLocaleString()
338
+ : pc ? new Date(DATA.plans[0].created).toLocaleString() : '—';
339
+
340
+ const NODE_C = {file:'#58a6ff',function:'#ff7b72',class:'#d2a8ff',interface:'#3fb950',const:'#ffa657',type:'#79c0ff',enum:'#f85149'};
341
+ const types = g ? g.nodes.reduce((a,n)=>{const t=n.type||'node';a[t]=(a[t]||0)+1;return a;},{}) : {};
342
+ const breakdown = Object.entries(types).sort((a,b)=>b[1]-a[1]).slice(0,8)
343
+ .map(([t,c])=>\`<span style="display:inline-flex;align-items:center;gap:5px;margin:3px 6px 3px 0">
344
+ <span style="width:9px;height:9px;border-radius:50%;background:\${NODE_C[t]||'#8b949e'};flex-shrink:0"></span>
345
+ <strong style="color:var(--text)">\${c}</strong>
346
+ <span style="color:var(--muted)">\${t}s</span>
347
+ </span>\`).join('');
348
+
349
+ document.getElementById('overview').innerHTML = \`
350
+ <div class="stats-grid">
351
+ <div class="stat-card"><div class="num">\${nc}</div><div class="lbl">Graph Nodes</div></div>
352
+ <div class="stat-card"><div class="num">\${ec}</div><div class="lbl">Graph Edges</div></div>
353
+ <div class="stat-card"><div class="num">\${sc}</div><div class="lbl">Chat Sessions</div></div>
354
+ <div class="stat-card"><div class="num">\${pc}</div><div class="lbl">Exec Plans</div></div>
355
+ <div class="stat-card"><div class="num">\${dc}</div><div class="lbl">Plans Done</div></div>
356
+ <div class="stat-card"><div class="num">\${mc}</div><div class="lbl">Memory Entries</div></div>
357
+ </div>
358
+ <div class="stat-card" style="max-width:640px">
359
+ <div class="lbl" style="margin-bottom:10px">Codebase Breakdown</div>
360
+ <div style="display:flex;flex-wrap:wrap">\${breakdown||'<span style="color:var(--dim)">no graph data</span>'}</div>
361
+ </div>
362
+ <div class="stat-card" style="max-width:360px">
363
+ <div class="lbl" style="margin-bottom:5px">Last Activity</div>
364
+ <div style="color:var(--blue);font-size:12px">\${last}</div>
365
+ </div>
366
+ \`;
367
+ })();
368
+
369
+ // ── Codebase Graph ────────────────────────────────────────────────────────────
370
+ let graphInit = false;
371
+ function initGraph() {
372
+ graphInit = true;
373
+ const panel = document.getElementById('graph');
374
+ if (!DATA.graph || !DATA.graph.nodes.length) {
375
+ panel.innerHTML = '<div class="empty">No graph.json found — run :graph refresh in the REPL first.</div>';
376
+ return;
377
+ }
378
+
379
+ const NODE_COLORS = {
380
+ file: '#58a6ff',
381
+ function: '#ff7b72',
382
+ class: '#d2a8ff',
383
+ interface: '#3fb950',
384
+ const: '#ffa657',
385
+ type: '#79c0ff',
386
+ enum: '#f85149',
387
+ node: '#8b949e',
388
+ };
389
+ const NODE_R = { file: 13, class: 11, interface: 10, function: 8, const: 7, type: 7, enum: 8, node: 7 };
390
+
391
+ const allTypes = [...new Set(DATA.graph.nodes.map(n => n.type || 'node'))];
392
+ const activeTypes = new Set(allTypes);
393
+
394
+ panel.innerHTML = \`
395
+ <div class="graph-controls">
396
+ <input id="graph-search" placeholder="🔍 Search nodes, files…" oninput="filterGraph()">
397
+ <div class="legend" id="legend"></div>
398
+ <span class="hint">scroll to zoom · drag to pan · drag nodes</span>
399
+ </div>
400
+ <svg id="graph-svg"></svg>
401
+ \`;
402
+
403
+ const legendEl = document.getElementById('legend');
404
+ allTypes.forEach(t => {
405
+ const item = document.createElement('span');
406
+ item.className = 'legend-item on';
407
+ item.style.color = NODE_COLORS[t] || '#8b949e';
408
+ item.innerHTML = \`<span class="legend-dot" style="background:\${NODE_COLORS[t]||'#8b949e'}"></span>\${t}\`;
409
+ item.onclick = () => {
410
+ if (activeTypes.has(t)) { activeTypes.delete(t); item.classList.remove('on'); }
411
+ else { activeTypes.add(t); item.classList.add('on'); }
412
+ filterGraph();
413
+ };
414
+ legendEl.appendChild(item);
415
+ });
416
+
417
+ const svgEl = document.getElementById('graph-svg');
418
+ const W = svgEl.clientWidth || 900, H = svgEl.clientHeight || 580;
419
+ const svg = d3.select('#graph-svg').attr('width', W).attr('height', H);
420
+ const g = svg.append('g');
421
+
422
+ svg.call(d3.zoom().scaleExtent([0.05, 6]).on('zoom', e => g.attr('transform', e.transform)));
423
+
424
+ // Arrow marker — bright color
425
+ svg.append('defs').append('marker')
426
+ .attr('id','arr').attr('viewBox','0 -5 10 10').attr('refX',2).attr('refY',0)
427
+ .attr('markerWidth',6).attr('markerHeight',6).attr('orient','auto')
428
+ .append('path').attr('d','M0,-5L10,0L0,5').attr('fill','#484f58');
429
+
430
+ const nodes = DATA.graph.nodes.map(n => ({...n}));
431
+ const edges = DATA.graph.edges.map(e => ({...e}));
432
+ const tooltip = document.getElementById('tooltip');
433
+
434
+ // Show labels for important node types always; others on hover
435
+ const ALWAYS_LABEL = new Set(['file','class','interface']);
436
+
437
+ function filterGraph() {
438
+ const term = document.getElementById('graph-search').value.toLowerCase();
439
+ const vis = new Set(
440
+ nodes.filter(n =>
441
+ activeTypes.has(n.type || 'node') &&
442
+ (!term || n.label.toLowerCase().includes(term) || (n.file||'').toLowerCase().includes(term))
443
+ ).map(n => n.id)
444
+ );
445
+ gNodes.style('opacity', d => vis.has(d.id) ? 1 : 0.06);
446
+ gLinks.style('opacity', d => {
447
+ const si = d.source.id || d.source, ti = d.target.id || d.target;
448
+ return vis.has(si) && vis.has(ti) ? 0.55 : 0.03;
449
+ });
450
+ gLabels.style('opacity', d => vis.has(d.id) ? 1 : 0.06);
451
+ }
452
+ window.filterGraph = filterGraph;
453
+
454
+ const sim = d3.forceSimulation(nodes)
455
+ .force('link', d3.forceLink(edges).id(d=>d.id).distance(d => {
456
+ const st = d.source.type || 'node', tt = d.target.type || 'node';
457
+ if (st==='file'||tt==='file') return 90;
458
+ return 60;
459
+ }).strength(0.6))
460
+ .force('charge', d3.forceManyBody().strength(d => d.type==='file' ? -300 : -160))
461
+ .force('center', d3.forceCenter(W/2, H/2))
462
+ .force('collision', d3.forceCollide(d => (NODE_R[d.type||'node']||7) + 6));
463
+
464
+ const gLinks = g.append('g').selectAll('line').data(edges).enter().append('line')
465
+ .attr('stroke','#484f58').attr('stroke-width', d => {
466
+ const r = d.relation || '';
467
+ return r === 'imports' ? 1.5 : 1;
468
+ })
469
+ .attr('opacity', 0.55)
470
+ .attr('marker-end','url(#arr)');
471
+
472
+ const gNodes = g.append('g').selectAll('circle').data(nodes).enter().append('circle')
473
+ .attr('r', d => NODE_R[d.type||'node'] || 7)
474
+ .attr('fill', d => NODE_COLORS[d.type||'node'] || '#8b949e')
475
+ .attr('stroke', '#0d1117').attr('stroke-width', 2)
476
+ .style('cursor','pointer')
477
+ .call(d3.drag()
478
+ .on('start', (e,d) => { if(!e.active) sim.alphaTarget(0.3).restart(); d.fx=d.x; d.fy=d.y; })
479
+ .on('drag', (e,d) => { d.fx=e.x; d.fy=e.y; })
480
+ .on('end', (e,d) => { if(!e.active) sim.alphaTarget(0); d.fx=null; d.fy=null; }))
481
+ .on('mouseover', (e,d) => {
482
+ tooltip.style.display='block';
483
+ tooltip.innerHTML = \`<strong>\${d.label}</strong><br>
484
+ <span class="t-type">\${d.type||'node'}</span>
485
+ \${d.file ? \`<br><span class="t-file">\${d.file}\${d.source_location?' · '+d.source_location:''}</span>\` : ''}\`;
486
+ })
487
+ .on('mousemove', e => { tooltip.style.left=(e.clientX+15)+'px'; tooltip.style.top=(e.clientY-8)+'px'; })
488
+ .on('mouseout', () => { tooltip.style.display='none'; });
489
+
490
+ const gLabels = g.append('g').selectAll('text')
491
+ .data(nodes.filter(n => ALWAYS_LABEL.has(n.type||'')))
492
+ .enter().append('text')
493
+ .text(d => d.label.length > 22 ? d.label.slice(0,20)+'…' : d.label)
494
+ .attr('fill', d => NODE_COLORS[d.type||'node'] || '#8b949e')
495
+ .attr('font-size', d => d.type==='file' ? '11px' : '10px')
496
+ .attr('font-weight', d => d.type==='file' ? '700' : '500')
497
+ .attr('pointer-events','none')
498
+ .attr('paint-order','stroke')
499
+ .attr('stroke','#0d1117').attr('stroke-width','3px')
500
+ .attr('dx', d => (NODE_R[d.type||'node']||7) + 4)
501
+ .attr('dy', '0.35em');
502
+
503
+ sim.on('tick', () => {
504
+ gLinks.attr('x1',d=>d.source.x).attr('y1',d=>d.source.y)
505
+ .attr('x2',d=>d.target.x).attr('y2',d=>d.target.y);
506
+ gNodes.attr('cx',d=>d.x).attr('cy',d=>d.y);
507
+ gLabels.attr('x',d=>d.x).attr('y',d=>d.y);
508
+ });
509
+ }
510
+
511
+ // ── Sessions ──────────────────────────────────────────────────────────────────
512
+ (function() {
513
+ const panel = document.getElementById('sessions');
514
+ if (!DATA.sessions.length) {
515
+ panel.innerHTML = '<div class="empty">No saved sessions found.</div>';
516
+ return;
517
+ }
518
+ const html = DATA.sessions.map(s => {
519
+ const turns = Math.floor((s.messageCount || 0) / 2);
520
+ const updated = new Date(s.updatedAt).toLocaleString();
521
+ return \`<div class="session-card">
522
+ <div class="s-header">
523
+ <span class="s-title">\${(s.title||'').replace(/</g,'&lt;')}</span>
524
+ <span class="s-meta">\${turns} turn\${turns!==1?'s':''} · \${updated}</span>
525
+ </div>
526
+ <div class="s-id">\${s.id} · \${s.messageCount||0} msgs · \${s.toolCallCount||0} tool calls</div>
527
+ </div>\`;
528
+ }).join('');
529
+ panel.innerHTML = \`<div class="session-list">\${html}</div>\`;
530
+ })();
531
+
532
+ // ── Execution Plans ───────────────────────────────────────────────────────────
533
+ let plansInit = false, dagSim = null;
534
+ function initPlans() {
535
+ plansInit = true;
536
+ const panel = document.getElementById('plans');
537
+ if (!DATA.plans.length) {
538
+ panel.innerHTML = '<div class="empty">No execution plans found. Run a multi-step orchestrated task first.</div>';
539
+ return;
540
+ }
541
+ panel.innerHTML = \`
542
+ <div class="plans-layout">
543
+ <div class="plan-list-panel" id="plan-list"></div>
544
+ <div class="plan-detail" id="plan-detail"><div class="empty">← Select a plan</div></div>
545
+ </div>
546
+ \`;
547
+ const listEl = document.getElementById('plan-list');
548
+ DATA.plans.forEach((plan, i) => {
549
+ const card = document.createElement('div');
550
+ card.className = 'plan-card' + (i===0?' selected':'');
551
+ const created = new Date(plan.created).toLocaleString();
552
+ const dur = plan.completed ? Math.round((plan.completed-plan.created)/1000)+'s' : '—';
553
+ card.innerHTML = \`
554
+ <div class="p-goal">\${plan.goal.slice(0,90).replace(/</g,'&lt;')}\${plan.goal.length>90?'…':''}</div>
555
+ <div class="p-meta">
556
+ <span class="status-badge status-\${plan.status}">\${plan.status}</span>
557
+ <span>\${plan.steps.length} steps</span>
558
+ <span>\${dur}</span>
559
+ <span style="color:var(--dim)">\${created}</span>
560
+ </div>\`;
561
+ card.onclick = () => {
562
+ document.querySelectorAll('.plan-card').forEach(c=>c.classList.remove('selected'));
563
+ card.classList.add('selected');
564
+ renderDag(plan);
565
+ };
566
+ listEl.appendChild(card);
567
+ });
568
+ if (DATA.plans.length) renderDag(DATA.plans[0]);
569
+ }
570
+
571
+ function renderDag(plan) {
572
+ const SPEC = { researcher:'#3fb950', coder:'#ff7b72', reviewer:'#58a6ff', planner:'#ffa657' };
573
+ const SPEC_BG = { researcher:'#1f3a2a', coder:'#3a1f1f', reviewer:'#1a2233', planner:'#332a1a' };
574
+ const S_ALPHA = { done:1, failed:0.85, skipped:0.3, running:1, waiting:0.55 };
575
+
576
+ const detail = document.getElementById('plan-detail');
577
+ const outcome = plan.outcome
578
+ ? \`<div class="step-result">\${plan.outcome.replace(/</g,'&lt;')}</div>\` : '';
579
+ detail.innerHTML = \`
580
+ <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap">
581
+ <span style="color:var(--text);font-size:13px;font-weight:600">\${plan.goal.replace(/</g,'&lt;')}</span>
582
+ <span class="status-badge status-\${plan.status}">\${plan.status}</span>
583
+ </div>
584
+ \${outcome}
585
+ <svg id="dag-svg"></svg>
586
+ \`;
587
+
588
+ const nodeData = plan.steps.map(s=>({...s}));
589
+ const edgeData = [];
590
+ plan.steps.forEach(s => s.dependsOn.forEach(dep => edgeData.push({source:dep,target:s.id})));
591
+
592
+ const svgEl = document.getElementById('dag-svg');
593
+ const W = svgEl.clientWidth || 640, H = Math.max(svgEl.clientHeight||320, 320);
594
+ const svg = d3.select('#dag-svg').attr('width',W).attr('height',H).selectAll('*').remove().select(function(){return this;});
595
+ const root = d3.select('#dag-svg');
596
+ const g = root.append('g');
597
+ root.call(d3.zoom().scaleExtent([0.2,4]).on('zoom',e=>g.attr('transform',e.transform)));
598
+
599
+ root.append('defs').append('marker')
600
+ .attr('id','darr').attr('viewBox','0 -5 10 10').attr('refX',68).attr('refY',0)
601
+ .attr('markerWidth',7).attr('markerHeight',7).attr('orient','auto')
602
+ .append('path').attr('d','M0,-5L10,0L0,5').attr('fill','#8b949e');
603
+
604
+ if (dagSim) dagSim.stop();
605
+ dagSim = d3.forceSimulation(nodeData)
606
+ .force('link', d3.forceLink(edgeData).id(d=>d.id).distance(180).strength(1))
607
+ .force('charge', d3.forceManyBody().strength(-500))
608
+ .force('center', d3.forceCenter(W/2,H/2))
609
+ .force('x', d3.forceX(W/2).strength(0.04))
610
+ .force('y', d3.forceY(H/2).strength(0.04));
611
+
612
+ const links = g.append('g').selectAll('line').data(edgeData).enter().append('line')
613
+ .attr('stroke','#8b949e').attr('stroke-width',2).attr('opacity',0.8)
614
+ .attr('marker-end','url(#darr)');
615
+
616
+ const nodeGs = g.append('g').selectAll('g').data(nodeData).enter().append('g')
617
+ .style('cursor','pointer')
618
+ .call(d3.drag()
619
+ .on('start',(e,d)=>{if(!e.active)dagSim.alphaTarget(0.3).restart();d.fx=d.x;d.fy=d.y;})
620
+ .on('drag', (e,d)=>{d.fx=e.x;d.fy=e.y;})
621
+ .on('end', (e,d)=>{if(!e.active)dagSim.alphaTarget(0);d.fx=null;d.fy=null;}));
622
+
623
+ // Background rect
624
+ nodeGs.append('rect').attr('width',140).attr('height',60).attr('rx',8)
625
+ .attr('x',-70).attr('y',-30)
626
+ .attr('fill', d=>SPEC_BG[d.specialist]||'#21262d')
627
+ .attr('stroke', d=>SPEC[d.specialist]||'#484f58')
628
+ .attr('stroke-width', d=>d.status==='done'?2.5:1.5)
629
+ .attr('opacity', d=>S_ALPHA[d.status]||0.6);
630
+
631
+ // Specialist label
632
+ nodeGs.append('text').text(d=>d.specialist.toUpperCase())
633
+ .attr('text-anchor','middle').attr('y',-11)
634
+ .attr('fill',d=>SPEC[d.specialist]||'#8b949e')
635
+ .attr('font-size','10px').attr('font-weight','800').attr('letter-spacing','.05em')
636
+ .attr('font-family','monospace');
637
+
638
+ // Step ID
639
+ nodeGs.append('text').text(d=>d.id)
640
+ .attr('text-anchor','middle').attr('y',4)
641
+ .attr('fill','#e6edf3').attr('font-size','11px').attr('font-weight','600')
642
+ .attr('font-family','monospace');
643
+
644
+ // Status icon
645
+ nodeGs.append('text')
646
+ .text(d=>({done:'✓',failed:'✗',skipped:'⊘',running:'⟳',waiting:'…'}[d.status]||'?'))
647
+ .attr('text-anchor','middle').attr('y',19)
648
+ .attr('fill',d=>({done:'#3fb950',failed:'#f85149',skipped:'#484f58',running:'#ffa657',waiting:'#8b949e'}[d.status]||'#8b949e'))
649
+ .attr('font-size','11px');
650
+
651
+ const tooltip = document.getElementById('tooltip');
652
+ nodeGs.on('mouseover',(e,d)=>{
653
+ const taskSnip = d.task.slice(0,160).replace(/</g,'&lt;');
654
+ const resultSnip = d.result ? \`<br><span style="color:var(--muted);font-size:10px">\${d.result.slice(0,200).replace(/</g,'&lt;')}\${d.result.length>200?'…':''}</span>\` : '';
655
+ tooltip.style.display='block';
656
+ tooltip.innerHTML=\`<strong>\${d.id}</strong> &nbsp;<span style="color:\${SPEC[d.specialist]||'#8b949e'}">\${d.specialist}</span><br>\${taskSnip}\${resultSnip}\`;
657
+ }).on('mousemove',e=>{
658
+ tooltip.style.left=(e.clientX+15)+'px'; tooltip.style.top=(e.clientY-8)+'px';
659
+ }).on('mouseout',()=>tooltip.style.display='none');
660
+
661
+ dagSim.on('tick',()=>{
662
+ links.attr('x1',d=>d.source.x).attr('y1',d=>d.source.y)
663
+ .attr('x2',d=>d.target.x).attr('y2',d=>d.target.y);
664
+ nodeGs.attr('transform',d=>\`translate(\${d.x},\${d.y})\`);
665
+ });
666
+ }
667
+
668
+ // ── Agent Memory ──────────────────────────────────────────────────────────────
669
+ (function() {
670
+ const panel = document.getElementById('memory');
671
+ if (!DATA.memory.length) {
672
+ panel.innerHTML = '<div class="empty">No orchestration memory entries found.</div>';
673
+ return;
674
+ }
675
+ const rows = DATA.memory.map(m => {
676
+ const ts = new Date(m.timestamp).toLocaleString();
677
+ const val = typeof m.value === 'string' ? m.value : JSON.stringify(m.value, null, 2);
678
+ return \`<tr>
679
+ <td>\${(m.key||'').replace(/</g,'&lt;')}</td>
680
+ <td style="color:var(--muted)">\${(m.stepId||'').replace(/</g,'&lt;')}</td>
681
+ <td style="color:var(--dim);white-space:nowrap">\${ts}</td>
682
+ <td class="memory-val">\${val.slice(0,400).replace(/</g,'&lt;')}\${val.length>400?'…':''}</td>
683
+ </tr>\`;
684
+ }).join('');
685
+ panel.innerHTML = \`
686
+ <table class="memory-table">
687
+ <thead><tr><th>Key</th><th>Step</th><th>Written</th><th>Value</th></tr></thead>
688
+ <tbody>\${rows}</tbody>
689
+ </table>
690
+ \`;
691
+ })();
692
+ </script>
693
+ </body>
694
+ </html>`;
695
+ }
696
+ // ─────────────────────────────────────────────────────────────────────────────
697
+ // Main export
698
+ // ─────────────────────────────────────────────────────────────────────────────
699
+ function generateDashboard(projectRoot) {
700
+ const graph = loadGraph(projectRoot);
701
+ const plans = loadPlans(projectRoot).map(stripPlan);
702
+ const sessions = loadSessions(projectRoot).map(stripSession);
703
+ const memory = loadMemory(projectRoot);
704
+ const pkgPath = path.join(projectRoot, 'package.json');
705
+ let projectName = path.basename(projectRoot);
706
+ try {
707
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
708
+ projectName = pkg.name ?? projectName;
709
+ }
710
+ catch { /* fallback to dir name */ }
711
+ const html = buildHtml({
712
+ graph,
713
+ plans,
714
+ sessions,
715
+ memory,
716
+ projectName,
717
+ generatedAt: new Date().toLocaleString(),
718
+ });
719
+ const outPath = path.join(projectRoot, 'graphify-out', 'dashboard.html');
720
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
721
+ fs.writeFileSync(outPath, html, 'utf8');
722
+ return outPath;
723
+ }
724
+ function openDashboard(filePath) {
725
+ try {
726
+ const opener = process.platform === 'darwin' ? 'open' :
727
+ process.platform === 'win32' ? 'start' :
728
+ 'xdg-open';
729
+ (0, child_process_1.execSync)(`${opener} "${filePath}"`, { stdio: 'ignore' });
730
+ }
731
+ catch { /* ignore if no browser */ }
732
+ }
733
+ //# sourceMappingURL=index.js.map