@bastani/atomic 0.9.40-alpha.1 → 0.9.40-alpha.2

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 (33) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/package.json +1 -1
  5. package/dist/builtin/web-access/CHANGELOG.md +6 -0
  6. package/dist/builtin/web-access/README.md +1 -1
  7. package/dist/builtin/web-access/index.bundle.mjs +2 -5
  8. package/dist/builtin/web-access/package.json +1 -1
  9. package/dist/builtin/workflows/CHANGELOG.md +6 -0
  10. package/dist/builtin/workflows/package.json +1 -1
  11. package/dist/builtin/workflows/src/extension/index.bundle.mjs +11 -2
  12. package/dist/cli/args.js +1 -1
  13. package/dist/cli/args.js.map +1 -1
  14. package/dist/core/export-html/index.d.ts.map +1 -1
  15. package/dist/core/export-html/index.js +3 -2
  16. package/dist/core/export-html/index.js.map +1 -1
  17. package/dist/core/export-html/template.html +1 -0
  18. package/dist/core/export-html/template.js +37 -20
  19. package/dist/core/export-html/vendor/marked-cjk-friendly.min.js +6 -0
  20. package/dist/core/export-html/vendor/marked.min.js +76 -3
  21. package/docs/providers.md +1 -1
  22. package/docs/settings.md +1 -1
  23. package/npm-shrinkwrap.json +35 -35
  24. package/package.json +5 -5
  25. package/dist/core/export-html/template-js/data-tree.js +0 -287
  26. package/dist/core/export-html/template-js/entries-navigation.js +0 -434
  27. package/dist/core/export-html/template-js/initialization.js +0 -335
  28. package/dist/core/export-html/template-js/message-tools.js +0 -428
  29. package/dist/core/export-html/template-js/tree-filter-render.js +0 -419
  30. package/dist/core/export-html/template-script.d.ts +0 -3
  31. package/dist/core/export-html/template-script.d.ts.map +0 -1
  32. package/dist/core/export-html/template-script.js +0 -13
  33. package/dist/core/export-html/template-script.js.map +0 -1
@@ -1,419 +0,0 @@
1
- // ============================================================
2
- // FILTERING (pure data)
3
- // ============================================================
4
-
5
- let filterMode = 'default';
6
- let searchQuery = '';
7
-
8
- function hasTextContent(content) {
9
- if (typeof content === 'string') return content.trim().length > 0;
10
- if (Array.isArray(content)) {
11
- for (const c of content) {
12
- if (c.type === 'text' && c.text && c.text.trim().length > 0) return true;
13
- }
14
- }
15
- return false;
16
- }
17
-
18
- function extractContent(content) {
19
- if (typeof content === 'string') return content;
20
- if (Array.isArray(content)) {
21
- return content
22
- .filter(c => c.type === 'text' && c.text)
23
- .map(c => c.text)
24
- .join('');
25
- }
26
- return '';
27
- }
28
-
29
- /**
30
- * Parse a skill block from message text.
31
- * Returns null if the text doesn't contain a skill block.
32
- * Matches the format: <skill name="..." location="...">\n...\n</skill>\n\nuser message
33
- */
34
- function parseSkillBlock(text) {
35
- const match = text.match(/^<skill name="([^"]+)" location="([^"]+)"(?: candidate="([^"]+)")?>\n([\s\S]*?)\n<\/skill>(?:\n\n([\s\S]+))?$/);
36
- if (!match) return null;
37
- return {
38
- name: match[1],
39
- location: match[2],
40
- candidateId: match[3] || undefined,
41
- content: match[4],
42
- userMessage: match[5]?.trim() || undefined,
43
- };
44
- }
45
-
46
- function getSearchableText(entry, label) {
47
- const parts = [];
48
- if (label) parts.push(label);
49
-
50
- switch (entry.type) {
51
- case 'message': {
52
- const msg = entry.message;
53
- parts.push(msg.role);
54
- if (msg.content) parts.push(extractContent(msg.content));
55
- if (msg.role === 'bashExecution' && msg.command) parts.push(msg.command);
56
- break;
57
- }
58
- case 'custom_message':
59
- parts.push(entry.customType);
60
- parts.push(typeof entry.content === 'string' ? entry.content : extractContent(entry.content));
61
- break;
62
- case 'compaction':
63
- parts.push('compaction');
64
- break;
65
- case 'branch_summary':
66
- parts.push('branch summary', entry.summary);
67
- break;
68
- case 'model_change':
69
- parts.push('model', entry.modelId);
70
- break;
71
- case 'thinking_level_change':
72
- parts.push('thinking', entry.thinkingLevel);
73
- break;
74
- case 'context_window_change':
75
- parts.push('context window', String(entry.contextWindow));
76
- break;
77
- case 'context_edit':
78
- parts.push('context edit', entry.replacement === null ? 'omit' : 'replace', entry.targetId);
79
- break;
80
- }
81
-
82
- return parts.join(' ').toLowerCase();
83
- }
84
-
85
- /**
86
- * Filter flat nodes based on current filterMode and searchQuery.
87
- */
88
- function filterNodes(flatNodes, currentLeafId) {
89
- const searchTokens = searchQuery.toLowerCase().split(/\s+/).filter(Boolean);
90
-
91
- const filtered = flatNodes.filter(flatNode => {
92
- const entry = flatNode.node.entry;
93
- const label = flatNode.node.label;
94
- const isCurrentLeaf = entry.id === currentLeafId;
95
-
96
- // Always show current leaf
97
- if (isCurrentLeaf) return true;
98
-
99
- // Hide assistant messages with only tool calls (no text) unless error/aborted
100
- if (entry.type === 'message' && entry.message.role === 'assistant') {
101
- const msg = entry.message;
102
- const hasText = hasTextContent(msg.content);
103
- const isErrorOrAborted = msg.stopReason && msg.stopReason !== 'stop' && msg.stopReason !== 'toolUse';
104
- if (!hasText && !isErrorOrAborted) return false;
105
- }
106
-
107
- // Apply filter mode
108
- const isSettingsEntry = ['label', 'custom', 'context_edit', 'model_change', 'thinking_level_change', 'context_window_change'].includes(entry.type);
109
- let passesFilter;
110
-
111
- switch (filterMode) {
112
- case 'user-only':
113
- passesFilter = entry.type === 'message' && entry.message.role === 'user';
114
- break;
115
- case 'no-tools':
116
- passesFilter = !isSettingsEntry && !(entry.type === 'message' && entry.message.role === 'toolResult');
117
- break;
118
- case 'labeled-only':
119
- passesFilter = label !== undefined;
120
- break;
121
- case 'all':
122
- passesFilter = true;
123
- break;
124
- default: // 'default'
125
- passesFilter = !isSettingsEntry;
126
- break;
127
- }
128
-
129
- if (!passesFilter) return false;
130
-
131
- // Apply search filter
132
- if (searchTokens.length > 0) {
133
- const nodeText = getSearchableText(entry, label);
134
- if (!searchTokens.every(t => nodeText.includes(t))) return false;
135
- }
136
-
137
- return true;
138
- });
139
-
140
- // Recalculate visual structure based on visible tree
141
- recalculateVisualStructure(filtered, flatNodes);
142
-
143
- return filtered;
144
- }
145
-
146
- /**
147
- * Recompute indentation/connectors for the filtered view
148
- *
149
- * Filtering can hide intermediate entries; descendants attach to the nearest visible ancestor.
150
- * Keep indentation semantics aligned with flattenTree() so single-child chains don't drift right.
151
- */
152
- function recalculateVisualStructure(filteredNodes, allFlatNodes) {
153
- if (filteredNodes.length === 0) return;
154
-
155
- const visibleIds = new Set(filteredNodes.map(n => n.node.entry.id));
156
-
157
- // Build entry map for parent lookup (using full tree)
158
- const entryMap = new Map();
159
- for (const flatNode of allFlatNodes) {
160
- entryMap.set(flatNode.node.entry.id, flatNode);
161
- }
162
-
163
- // Find nearest visible ancestor for a node
164
- function findVisibleAncestor(nodeId) {
165
- let currentId = entryMap.get(nodeId)?.node.entry.parentId;
166
- while (currentId != null) {
167
- if (visibleIds.has(currentId)) {
168
- return currentId;
169
- }
170
- currentId = entryMap.get(currentId)?.node.entry.parentId;
171
- }
172
- return null;
173
- }
174
-
175
- // Build visible tree structure
176
- const visibleParent = new Map();
177
- const visibleChildren = new Map();
178
- visibleChildren.set(null, []); // root-level nodes
179
-
180
- for (const flatNode of filteredNodes) {
181
- const nodeId = flatNode.node.entry.id;
182
- const ancestorId = findVisibleAncestor(nodeId);
183
- visibleParent.set(nodeId, ancestorId);
184
-
185
- if (!visibleChildren.has(ancestorId)) {
186
- visibleChildren.set(ancestorId, []);
187
- }
188
- visibleChildren.get(ancestorId).push(nodeId);
189
- }
190
-
191
- // Update multipleRoots based on visible roots
192
- const visibleRootIds = visibleChildren.get(null);
193
- const multipleRoots = visibleRootIds.length > 1;
194
-
195
- // Build a map for quick lookup: nodeId → FlatNode
196
- const filteredNodeMap = new Map();
197
- for (const flatNode of filteredNodes) {
198
- filteredNodeMap.set(flatNode.node.entry.id, flatNode);
199
- }
200
-
201
- // DFS traversal of visible tree, applying same indentation rules as flattenTree()
202
- // Stack items: [nodeId, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild]
203
- const stack = [];
204
-
205
- // Add visible roots in reverse order (to process in forward order via stack)
206
- for (let i = visibleRootIds.length - 1; i >= 0; i--) {
207
- const isLast = i === visibleRootIds.length - 1;
208
- stack.push([
209
- visibleRootIds[i],
210
- multipleRoots ? 1 : 0,
211
- multipleRoots,
212
- multipleRoots,
213
- isLast,
214
- [],
215
- multipleRoots
216
- ]);
217
- }
218
-
219
- while (stack.length > 0) {
220
- const [nodeId, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop();
221
-
222
- const flatNode = filteredNodeMap.get(nodeId);
223
- if (!flatNode) continue;
224
-
225
- // Update this node's visual properties
226
- flatNode.indent = indent;
227
- flatNode.showConnector = showConnector;
228
- flatNode.isLast = isLast;
229
- flatNode.gutters = gutters;
230
- flatNode.isVirtualRootChild = isVirtualRootChild;
231
- flatNode.multipleRoots = multipleRoots;
232
-
233
- // Get visible children of this node
234
- const children = visibleChildren.get(nodeId) || [];
235
- const multipleChildren = children.length > 1;
236
-
237
- // Calculate child indent using same rules as flattenTree():
238
- // - Parent branches (multiple children): children get +1
239
- // - Just branched and indent > 0: children get +1 for visual grouping
240
- // - Single-child chain: stay flat
241
- let childIndent;
242
- if (multipleChildren) {
243
- childIndent = indent + 1;
244
- } else if (justBranched && indent > 0) {
245
- childIndent = indent + 1;
246
- } else {
247
- childIndent = indent;
248
- }
249
-
250
- // Build gutters for children (same logic as flattenTree)
251
- const connectorDisplayed = showConnector && !isVirtualRootChild;
252
- const currentDisplayIndent = multipleRoots ? Math.max(0, indent - 1) : indent;
253
- const connectorPosition = Math.max(0, currentDisplayIndent - 1);
254
- const childGutters = connectorDisplayed
255
- ? [...gutters, { position: connectorPosition, show: !isLast }]
256
- : gutters;
257
-
258
- // Add children in reverse order (to process in forward order via stack)
259
- for (let i = children.length - 1; i >= 0; i--) {
260
- const childIsLast = i === children.length - 1;
261
- stack.push([
262
- children[i],
263
- childIndent,
264
- multipleChildren,
265
- multipleChildren,
266
- childIsLast,
267
- childGutters,
268
- false
269
- ]);
270
- }
271
- }
272
- }
273
-
274
- // ============================================================
275
- // TREE DISPLAY TEXT (pure data -> string)
276
- // ============================================================
277
-
278
- function shortenPath(p) {
279
- if (typeof p !== 'string') return '';
280
- if (p.startsWith('/Users/')) {
281
- const parts = p.split('/');
282
- if (parts.length > 2) return '~' + p.slice(('/Users/' + parts[2]).length);
283
- }
284
- if (p.startsWith('/home/')) {
285
- const parts = p.split('/');
286
- if (parts.length > 2) return '~' + p.slice(('/home/' + parts[2]).length);
287
- }
288
- return p;
289
- }
290
-
291
- function formatToolCall(name, args) {
292
- switch (name) {
293
- case 'read': {
294
- const path = shortenPath(String(args.path || args.file_path || ''));
295
- return `[read: ${path}${formatReadLineRange(args.offset, args.limit)}]`;
296
- }
297
- case 'write':
298
- return `[write: ${shortenPath(String(args.path || args.file_path || ''))}]`;
299
- case 'edit':
300
- return `[edit: ${shortenPath(String(args.path || args.file_path || ''))}]`;
301
- case 'bash': {
302
- const rawCmd = String(args.command || '');
303
- const cmd = rawCmd.replace(/[\n\t]/g, ' ').trim().slice(0, 50);
304
- return `[bash: ${cmd}${rawCmd.length > 50 ? '...' : ''}]`;
305
- }
306
- case 'grep':
307
- return `[grep: /${args.pattern || ''}/ in ${shortenPath(String(args.path || '.'))}]`;
308
- case 'find':
309
- return `[find: ${args.pattern || ''} in ${shortenPath(String(args.path || '.'))}]`;
310
- case 'ls':
311
- return `[ls: ${shortenPath(String(args.path || '.'))}]`;
312
- default: {
313
- const argsStr = JSON.stringify(args).slice(0, 40);
314
- return `[${name}: ${argsStr}${JSON.stringify(args).length > 40 ? '...' : ''}]`;
315
- }
316
- }
317
- }
318
-
319
- function escapeHtml(text) {
320
- return String(text)
321
- .replace(/&/g, '&amp;')
322
- .replace(/</g, '&lt;')
323
- .replace(/>/g, '&gt;')
324
- .replace(/"/g, '&quot;')
325
- .replace(/'/g, '&#39;');
326
- }
327
-
328
- function sanitizeMarkdownUrl(value) {
329
- const href = String(value || '').trim().replace(/[\x00-\x1f\x7f]/g, '');
330
- if (!href) return href;
331
-
332
- const scheme = href.match(/^([A-Za-z][A-Za-z0-9+.-]*):/);
333
- if (scheme && !/^(https?|mailto|tel|ftp)$/i.test(scheme[1])) {
334
- return null;
335
- }
336
-
337
- return href;
338
- }
339
-
340
- /**
341
- * Truncate string to maxLen chars, append "..." if truncated.
342
- */
343
- function truncate(s, maxLen = 100) {
344
- if (s.length <= maxLen) return s;
345
- return s.slice(0, maxLen) + '...';
346
- }
347
-
348
- /**
349
- * Get display text for tree node (returns HTML string).
350
- */
351
- function getTreeNodeDisplayHtml(entry, label) {
352
- const normalize = s => s.replace(/[\n\t]/g, ' ').trim();
353
- const labelHtml = label ? `<span class="tree-label">[${escapeHtml(label)}]</span> ` : '';
354
-
355
- switch (entry.type) {
356
- case 'message': {
357
- const msg = entry.message;
358
- if (msg.role === 'user') {
359
- const rawContent = extractContent(msg.content);
360
- const skillBlock = parseSkillBlock(rawContent);
361
- if (skillBlock) {
362
- let treeHtml = labelHtml + `<span class="tree-role-skill">skill:</span> ${escapeHtml(skillBlock.name)}`;
363
- if (skillBlock.userMessage) {
364
- treeHtml += ` · <span class="tree-role-user">user:</span> ${escapeHtml(truncate(normalize(skillBlock.userMessage)))}`;
365
- }
366
- return treeHtml;
367
- }
368
- const content = truncate(normalize(rawContent));
369
- return labelHtml + `<span class="tree-role-user">user:</span> ${escapeHtml(content)}`;
370
- }
371
- if (msg.role === 'assistant') {
372
- const textContent = truncate(normalize(extractContent(msg.content)));
373
- if (textContent) {
374
- return labelHtml + `<span class="tree-role-assistant">assistant:</span> ${escapeHtml(textContent)}`;
375
- }
376
- if (msg.stopReason === 'aborted') {
377
- return labelHtml + `<span class="tree-role-assistant">assistant:</span> <span class="tree-muted">(aborted)</span>`;
378
- }
379
- if (msg.errorMessage) {
380
- return labelHtml + `<span class="tree-role-assistant">assistant:</span> <span class="tree-error">${escapeHtml(truncate(msg.errorMessage))}</span>`;
381
- }
382
- return labelHtml + `<span class="tree-role-assistant">assistant:</span> <span class="tree-muted">(no text)</span>`;
383
- }
384
- if (msg.role === 'toolResult') {
385
- const toolCall = msg.toolCallId ? toolCallMap.get(msg.toolCallId) : null;
386
- if (toolCall) {
387
- return labelHtml + `<span class="tree-role-tool">${escapeHtml(formatToolCall(toolCall.name, toolCall.arguments))}</span>`;
388
- }
389
- return labelHtml + `<span class="tree-role-tool">[${escapeHtml(msg.toolName || 'tool')}]</span>`;
390
- }
391
- if (msg.role === 'bashExecution') {
392
- const cmd = truncate(normalize(msg.command || ''));
393
- return labelHtml + `<span class="tree-role-tool">[bash]:</span> ${escapeHtml(cmd)}`;
394
- }
395
- return labelHtml + `<span class="tree-muted">[${escapeHtml(msg.role)}]</span>`;
396
- }
397
- case 'compaction':
398
- return labelHtml + `<span class="tree-compaction">[compaction: ${Math.round(entry.tokensBefore/1000)}k tokens]</span>`;
399
- case 'branch_summary': {
400
- const summary = truncate(normalize(entry.summary || ''));
401
- return labelHtml + `<span class="tree-branch-summary">[branch summary]:</span> ${escapeHtml(summary)}`;
402
- }
403
- case 'custom_message': {
404
- const content = typeof entry.content === 'string' ? entry.content : extractContent(entry.content);
405
- return labelHtml + `<span class="tree-custom">[${escapeHtml(entry.customType)}]:</span> ${escapeHtml(truncate(normalize(content)))}`;
406
- }
407
- case 'model_change':
408
- return labelHtml + `<span class="tree-muted">[model: ${escapeHtml(entry.modelId)}]</span>`;
409
- case 'thinking_level_change':
410
- return labelHtml + `<span class="tree-muted">[thinking: ${escapeHtml(entry.thinkingLevel)}]</span>`;
411
- case 'context_window_change':
412
- return labelHtml + `<span class="tree-muted">[context window: ${escapeHtml(String(entry.contextWindow))}]</span>`;
413
- case 'context_edit':
414
- return labelHtml + `<span class="tree-muted">[context ${entry.replacement === null ? 'omit' : 'replace'}: ${escapeHtml(entry.targetId)}]</span>`;
415
- default:
416
- return labelHtml + `<span class="tree-muted">[${escapeHtml(entry.type)}]</span>`;
417
- }
418
- }
419
-
@@ -1,3 +0,0 @@
1
- export declare const EXPORT_HTML_TEMPLATE_SCRIPT_CHUNKS: readonly ["data-tree.js", "tree-filter-render.js", "message-tools.js", "entries-navigation.js", "initialization.js"];
2
- export declare function readExportHtmlTemplateScript(templateDir: string): string;
3
- //# sourceMappingURL=template-script.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"template-script.d.ts","sourceRoot":"","sources":["../../../src/core/export-html/template-script.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kCAAkC,YAC9C,cAAc,EACd,uBAAuB,EACvB,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,CACV,CAAC;AAEX,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAIxE"}
@@ -1,13 +0,0 @@
1
- import { readFileSync } from "fs";
2
- import { join } from "path";
3
- export const EXPORT_HTML_TEMPLATE_SCRIPT_CHUNKS = [
4
- "data-tree.js",
5
- "tree-filter-render.js",
6
- "message-tools.js",
7
- "entries-navigation.js",
8
- "initialization.js",
9
- ];
10
- export function readExportHtmlTemplateScript(templateDir) {
11
- return EXPORT_HTML_TEMPLATE_SCRIPT_CHUNKS.map((fileName) => readFileSync(join(templateDir, "template-js", fileName), "utf-8")).join("");
12
- }
13
- //# sourceMappingURL=template-script.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"template-script.js","sourceRoot":"","sources":["../../../src/core/export-html/template-script.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,CAAC,MAAM,kCAAkC,GAAG;IACjD,cAAc;IACd,uBAAuB;IACvB,kBAAkB;IAClB,uBAAuB;IACvB,mBAAmB;CACV,CAAC;AAEX,MAAM,UAAU,4BAA4B,CAAC,WAAmB;IAC/D,OAAO,kCAAkC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAC1D,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,CACjE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACZ,CAAC","sourcesContent":["import { readFileSync } from \"fs\";\nimport { join } from \"path\";\n\nexport const EXPORT_HTML_TEMPLATE_SCRIPT_CHUNKS = [\n\t\"data-tree.js\",\n\t\"tree-filter-render.js\",\n\t\"message-tools.js\",\n\t\"entries-navigation.js\",\n\t\"initialization.js\",\n] as const;\n\nexport function readExportHtmlTemplateScript(templateDir: string): string {\n\treturn EXPORT_HTML_TEMPLATE_SCRIPT_CHUNKS.map((fileName) =>\n\t\treadFileSync(join(templateDir, \"template-js\", fileName), \"utf-8\"),\n\t).join(\"\");\n}\n"]}