liteagents 2.5.2 → 2.6.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 (80) hide show
  1. package/CHANGELOG.md +76 -15
  2. package/README.md +58 -30
  3. package/docs/INSTALLATION_DEMO.md +1 -1
  4. package/docs/INSTALLATION_LOCATIONS.md +5 -5
  5. package/docs/INSTALLER_GUIDE.md +5 -5
  6. package/docs/KNOWLEDGE_BASE.md +5 -5
  7. package/docs/MIGRATION.md +1 -1
  8. package/docs/PASS_QUICK_START.md +1 -1
  9. package/docs/PRIVACY.md +1 -1
  10. package/docs/PUBLISHING.md +7 -7
  11. package/docs/RELEASE_NOTES_1.2.0.md +2 -2
  12. package/docs/SILENT_MODE_GUIDE.md +1 -1
  13. package/installer/cli.js +1 -1
  14. package/installer/installation-engine.js +82 -21
  15. package/installer/package-manager.js +34 -3
  16. package/package.json +7 -6
  17. package/packages/ampcode/AGENT.md +1 -0
  18. package/packages/ampcode/commands/friction/friction.js +19 -4
  19. package/packages/ampcode/commands/live-canvas/DESIGN_PRINCIPLES.md +2140 -0
  20. package/packages/ampcode/commands/live-canvas/README.md +273 -0
  21. package/packages/ampcode/commands/live-canvas/templates/.claude/settings.local.json +8 -0
  22. package/packages/ampcode/commands/live-canvas/templates/demo/post-variants.html +205 -0
  23. package/packages/ampcode/commands/live-canvas/templates/feedback-react/FeedbackOverlay.tsx +1299 -0
  24. package/packages/ampcode/commands/live-canvas/templates/feedback-react/format-utils.ts +283 -0
  25. package/packages/ampcode/commands/live-canvas/templates/feedback-react/index.ts +62 -0
  26. package/packages/ampcode/commands/live-canvas/templates/feedback-react/selector-utils.ts +423 -0
  27. package/packages/ampcode/commands/live-canvas/templates/feedback-react/types.ts +118 -0
  28. package/packages/ampcode/commands/live-canvas/templates/overlay-vanilla.js +477 -0
  29. package/packages/ampcode/commands/live-canvas.md +1104 -0
  30. package/packages/claude/CLAUDE.md +1 -0
  31. package/packages/claude/commands/friction/friction.js +19 -4
  32. package/packages/claude/plugins/live-canvas-marketplace/.claude-plugin/marketplace.json +14 -0
  33. package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/.claude-plugin/plugin.json +18 -0
  34. package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/README.md +89 -0
  35. package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/package-lock.json +1142 -0
  36. package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/package.json +17 -0
  37. package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/schema.json +37 -0
  38. package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/server.js +179 -0
  39. package/packages/claude/plugins/live-canvas-marketplace/setup.sh +61 -0
  40. package/packages/claude/skills/live-canvas/DESIGN_PRINCIPLES.md +2140 -0
  41. package/packages/claude/skills/live-canvas/INTEGRATION_NOTES.md +210 -0
  42. package/packages/claude/skills/live-canvas/README.md +273 -0
  43. package/packages/claude/skills/live-canvas/SKILL.md +1119 -0
  44. package/packages/claude/skills/live-canvas/templates/.claude/settings.local.json +8 -0
  45. package/packages/claude/skills/live-canvas/templates/demo/post-variants.html +205 -0
  46. package/packages/claude/skills/live-canvas/templates/feedback-react/FeedbackOverlay.tsx +1299 -0
  47. package/packages/claude/skills/live-canvas/templates/feedback-react/format-utils.ts +283 -0
  48. package/packages/claude/skills/live-canvas/templates/feedback-react/index.ts +62 -0
  49. package/packages/claude/skills/live-canvas/templates/feedback-react/selector-utils.ts +423 -0
  50. package/packages/claude/skills/live-canvas/templates/feedback-react/types.ts +118 -0
  51. package/packages/claude/skills/live-canvas/templates/overlay-vanilla.js +477 -0
  52. package/packages/claude/variants.json +2 -1
  53. package/packages/droid/AGENTS.md +1 -0
  54. package/packages/droid/commands/friction/friction.js +19 -4
  55. package/packages/droid/commands/live-canvas/DESIGN_PRINCIPLES.md +2140 -0
  56. package/packages/droid/commands/live-canvas/README.md +273 -0
  57. package/packages/droid/commands/live-canvas/templates/.claude/settings.local.json +8 -0
  58. package/packages/droid/commands/live-canvas/templates/demo/post-variants.html +205 -0
  59. package/packages/droid/commands/live-canvas/templates/feedback-react/FeedbackOverlay.tsx +1299 -0
  60. package/packages/droid/commands/live-canvas/templates/feedback-react/format-utils.ts +283 -0
  61. package/packages/droid/commands/live-canvas/templates/feedback-react/index.ts +62 -0
  62. package/packages/droid/commands/live-canvas/templates/feedback-react/selector-utils.ts +423 -0
  63. package/packages/droid/commands/live-canvas/templates/feedback-react/types.ts +118 -0
  64. package/packages/droid/commands/live-canvas/templates/overlay-vanilla.js +477 -0
  65. package/packages/droid/commands/live-canvas.md +1104 -0
  66. package/packages/opencode/AGENTS.md +1 -0
  67. package/packages/opencode/command/friction/friction.js +19 -4
  68. package/packages/opencode/command/live-canvas/DESIGN_PRINCIPLES.md +2140 -0
  69. package/packages/opencode/command/live-canvas/README.md +273 -0
  70. package/packages/opencode/command/live-canvas/templates/.claude/settings.local.json +8 -0
  71. package/packages/opencode/command/live-canvas/templates/demo/post-variants.html +205 -0
  72. package/packages/opencode/command/live-canvas/templates/feedback-react/FeedbackOverlay.tsx +1299 -0
  73. package/packages/opencode/command/live-canvas/templates/feedback-react/format-utils.ts +283 -0
  74. package/packages/opencode/command/live-canvas/templates/feedback-react/index.ts +62 -0
  75. package/packages/opencode/command/live-canvas/templates/feedback-react/selector-utils.ts +423 -0
  76. package/packages/opencode/command/live-canvas/templates/feedback-react/types.ts +118 -0
  77. package/packages/opencode/command/live-canvas/templates/overlay-vanilla.js +477 -0
  78. package/packages/opencode/command/live-canvas.md +1104 -0
  79. package/packages/opencode/opencode.jsonc +4 -0
  80. package/packages/subagentic-manual.md +18 -15
@@ -194,18 +194,23 @@ class InstallationEngine {
194
194
  }
195
195
  };
196
196
 
197
- // Helper function to recursively collect all files in a directory
198
- const collectDirectoryFiles = async (dirPath, relativePath = '') => {
197
+ // Helper function to recursively collect all files in a directory.
198
+ // Skips node_modules directories (callers may set skipNodeModules=true for plugins).
199
+ const collectDirectoryFiles = async (dirPath, relativePath = '', skipNodeModules = false) => {
199
200
  const items = await fs.promises.readdir(dirPath);
200
201
  const filesInDir = [];
201
202
 
202
203
  for (const item of items) {
204
+ if (skipNodeModules && item === 'node_modules') {
205
+ continue;
206
+ }
207
+
203
208
  const itemPath = path.join(dirPath, item);
204
209
  const itemRelativePath = path.join(relativePath, item);
205
210
  const stat = await fs.promises.stat(itemPath);
206
211
 
207
212
  if (stat.isDirectory()) {
208
- const subFiles = await collectDirectoryFiles(itemPath, itemRelativePath);
213
+ const subFiles = await collectDirectoryFiles(itemPath, itemRelativePath, skipNodeModules);
209
214
  filesInDir.push(...subFiles);
210
215
  } else {
211
216
  filesInDir.push({
@@ -310,6 +315,23 @@ class InstallationEngine {
310
315
  totalBytes += size;
311
316
  }
312
317
 
318
+ // Collect all plugin files (plugins are directories like skills; skip node_modules)
319
+ for (const pluginPath of (packageContents.plugins || [])) {
320
+ const baseRelativePath = path.relative(sourceBase, pluginPath);
321
+ const pluginFiles = await collectDirectoryFiles(pluginPath, baseRelativePath, true);
322
+
323
+ for (const file of pluginFiles) {
324
+ filesToCopy.push({
325
+ sourcePath: file.sourcePath,
326
+ relativePath: file.relativePath,
327
+ size: file.size,
328
+ type: 'plugin'
329
+ });
330
+ totalFiles++;
331
+ totalBytes += file.size;
332
+ }
333
+ }
334
+
313
335
  // Now copy all files with progress tracking
314
336
  let filesCompleted = 0;
315
337
  let bytesTransferred = 0;
@@ -364,6 +386,11 @@ class InstallationEngine {
364
386
  const items = await fs.promises.readdir(source);
365
387
 
366
388
  for (const item of items) {
389
+ // Skip node_modules - callers (plugins, backups) should never ship bundled deps
390
+ if (item === 'node_modules') {
391
+ continue;
392
+ }
393
+
367
394
  const sourcePath = path.join(source, item);
368
395
  const targetPath = path.join(target, item);
369
396
 
@@ -433,19 +460,22 @@ class InstallationEngine {
433
460
  agents: contents.agents.length,
434
461
  skills: contents.skills.length,
435
462
  resources: contents.resources.length,
436
- hooks: contents.hooks.length
463
+ hooks: contents.hooks.length,
464
+ plugins: (contents.plugins || []).length
437
465
  },
438
466
  installedFiles: {
439
467
  agents: contents.agents.map(extractAgentName),
440
468
  skills: contents.skills.map(extractSkillName),
441
469
  resources: contents.resources.map(p => path.basename(p)),
442
- hooks: contents.hooks.map(p => path.basename(p))
470
+ hooks: contents.hooks.map(p => path.basename(p)),
471
+ plugins: (contents.plugins || []).map(extractSkillName)
443
472
  },
444
473
  paths: {
445
474
  agents: path.join(targetPath, 'agents'),
446
475
  skills: path.join(targetPath, 'skills'),
447
476
  resources: path.join(targetPath, 'resources'),
448
- hooks: path.join(targetPath, 'hooks')
477
+ hooks: path.join(targetPath, 'hooks'),
478
+ plugins: path.join(targetPath, 'plugins')
449
479
  },
450
480
  files: {
451
481
  total: contents.totalFiles,
@@ -538,7 +568,7 @@ class InstallationEngine {
538
568
 
539
569
  // Remove files listed in manifest
540
570
  if (manifest.installedFiles) {
541
- for (const category of ['agents', 'skills', 'resources', 'hooks']) {
571
+ for (const category of ['agents', 'skills', 'resources', 'hooks', 'plugins']) {
542
572
  if (manifest.installedFiles[category] && manifest.paths && manifest.paths[category]) {
543
573
  const categoryPath = manifest.paths[category];
544
574
 
@@ -548,8 +578,8 @@ class InstallationEngine {
548
578
  if (category === 'agents') {
549
579
  // Agents have .md extension
550
580
  itemPath = path.join(categoryPath, `${item}.md`);
551
- } else if (category === 'skills') {
552
- // Skills are directories
581
+ } else if (category === 'skills' || category === 'plugins') {
582
+ // Skills and plugins are directories
553
583
  itemPath = path.join(categoryPath, item);
554
584
  } else {
555
585
  // Resources and hooks have their full names
@@ -650,7 +680,7 @@ class InstallationEngine {
650
680
  * @param {string} targetPath - Base installation path
651
681
  */
652
682
  async cleanupEmptyDirectories(targetPath) {
653
- const categories = ['agents', 'skills', 'resources', 'hooks'];
683
+ const categories = ['agents', 'skills', 'resources', 'hooks', 'plugins'];
654
684
 
655
685
  for (const category of categories) {
656
686
  const categoryPath = path.join(targetPath, category);
@@ -762,7 +792,8 @@ class InstallationEngine {
762
792
  agents: { expected: 0, found: 0, missing: [] },
763
793
  skills: { expected: 0, found: 0, missing: [] },
764
794
  resources: { expected: 0, found: 0, missing: [] },
765
- hooks: { expected: 0, found: 0, missing: [] }
795
+ hooks: { expected: 0, found: 0, missing: [] },
796
+ plugins: { expected: 0, found: 0, missing: [] }
766
797
  },
767
798
  timestamp: new Date().toISOString()
768
799
  };
@@ -785,8 +816,16 @@ class InstallationEngine {
785
816
  const manifest = JSON.parse(await fs.promises.readFile(manifestPath, 'utf8'));
786
817
  result.manifest = manifest;
787
818
 
788
- // Verify all required directories exist
819
+ // Verify all required directories exist (skip categories with 0 expected items)
789
820
  for (const [component, componentPath] of Object.entries(manifest.paths || {})) {
821
+ const expectedCount = (manifest.installedFiles && Array.isArray(manifest.installedFiles[component]))
822
+ ? manifest.installedFiles[component].length
823
+ : 0;
824
+
825
+ if (expectedCount === 0) {
826
+ continue;
827
+ }
828
+
790
829
  if (!fs.existsSync(componentPath)) {
791
830
  result.valid = false;
792
831
  result.issues.push({
@@ -799,7 +838,7 @@ class InstallationEngine {
799
838
  }
800
839
 
801
840
  // Verify installed files for each component category
802
- for (const category of ['agents', 'skills', 'resources', 'hooks']) {
841
+ for (const category of ['agents', 'skills', 'resources', 'hooks', 'plugins']) {
803
842
  if (manifest.installedFiles && manifest.installedFiles[category]) {
804
843
  const expectedFiles = manifest.installedFiles[category];
805
844
  result.components[category].expected = expectedFiles.length;
@@ -812,7 +851,7 @@ class InstallationEngine {
812
851
  // Construct full path based on category
813
852
  if (category === 'agents') {
814
853
  itemPath = path.join(categoryPath, `${item}.md`);
815
- } else if (category === 'skills') {
854
+ } else if (category === 'skills' || category === 'plugins') {
816
855
  itemPath = path.join(categoryPath, item);
817
856
  } else {
818
857
  itemPath = path.join(categoryPath, item);
@@ -847,7 +886,7 @@ class InstallationEngine {
847
886
 
848
887
  // Verify file counts match
849
888
  if (manifest.components) {
850
- for (const category of ['agents', 'skills', 'resources', 'hooks']) {
889
+ for (const category of ['agents', 'skills', 'resources', 'hooks', 'plugins']) {
851
890
  if (manifest.components[category] !== undefined) {
852
891
  const expectedCount = manifest.components[category];
853
892
  const foundCount = result.components[category].found;
@@ -967,7 +1006,7 @@ class InstallationEngine {
967
1006
 
968
1007
  // Collect all files from manifest
969
1008
  if (manifest.installedFiles) {
970
- for (const category of ['agents', 'skills', 'resources', 'hooks']) {
1009
+ for (const category of ['agents', 'skills', 'resources', 'hooks', 'plugins']) {
971
1010
  if (manifest.installedFiles[category] && manifest.paths && manifest.paths[category]) {
972
1011
  const categoryPath = manifest.paths[category];
973
1012
 
@@ -977,8 +1016,8 @@ class InstallationEngine {
977
1016
  if (category === 'agents') {
978
1017
  // Agents have .md extension
979
1018
  itemPath = path.join(categoryPath, `${item}.md`);
980
- } else if (category === 'skills') {
981
- // Skills are directories - need to count all files inside
1019
+ } else if (category === 'skills' || category === 'plugins') {
1020
+ // Skills and plugins are directories - need to count all files inside
982
1021
  itemPath = path.join(categoryPath, item);
983
1022
  } else {
984
1023
  // Resources and hooks have their full names
@@ -989,7 +1028,7 @@ class InstallationEngine {
989
1028
  path: itemPath,
990
1029
  category: category,
991
1030
  name: item,
992
- isDirectory: category === 'skills'
1031
+ isDirectory: category === 'skills' || category === 'plugins'
993
1032
  });
994
1033
  }
995
1034
  }
@@ -1443,6 +1482,26 @@ class InstallationEngine {
1443
1482
  }
1444
1483
  }
1445
1484
 
1485
+ // Compare plugins (directories, like skills)
1486
+ const currentPluginsList = currentContents.plugins || [];
1487
+ const newPluginsList = newContents.plugins || [];
1488
+ const currentPlugins = new Set(currentPluginsList.map(p => path.basename(p)));
1489
+ const newPlugins = new Set(newPluginsList.map(p => path.basename(p)));
1490
+
1491
+ for (const plugin of newPluginsList) {
1492
+ const basename = path.basename(plugin);
1493
+ if (!currentPlugins.has(basename)) {
1494
+ toAdd.push({ type: 'plugin', source: plugin, name: basename });
1495
+ }
1496
+ }
1497
+
1498
+ for (const plugin of currentPluginsList) {
1499
+ const basename = path.basename(plugin);
1500
+ if (!newPlugins.has(basename)) {
1501
+ toRemove.push({ type: 'plugin', name: basename, category: 'plugins' });
1502
+ }
1503
+ }
1504
+
1446
1505
  return { toAdd, toRemove };
1447
1506
  }
1448
1507
 
@@ -1456,7 +1515,8 @@ class InstallationEngine {
1456
1515
  agents: new Set(manifest.installedFiles.agents),
1457
1516
  skills: new Set(manifest.installedFiles.skills),
1458
1517
  resources: new Set(manifest.installedFiles.resources),
1459
- hooks: new Set(manifest.installedFiles.hooks)
1518
+ hooks: new Set(manifest.installedFiles.hooks),
1519
+ plugins: new Set(manifest.installedFiles.plugins || [])
1460
1520
  };
1461
1521
 
1462
1522
  for (const file of filesToRemove) {
@@ -1503,7 +1563,8 @@ class InstallationEngine {
1503
1563
  const sourcePath = file.source;
1504
1564
  const targetCategory = file.type === 'agent' ? 'agents' :
1505
1565
  file.type === 'skill' ? 'skills' :
1506
- file.type === 'resource' ? 'resources' : 'hooks';
1566
+ file.type === 'resource' ? 'resources' :
1567
+ file.type === 'plugin' ? 'plugins' : 'hooks';
1507
1568
 
1508
1569
  const targetDir = path.join(targetPath, targetCategory);
1509
1570
 
@@ -137,7 +137,8 @@ class PackageManager {
137
137
  skills: [],
138
138
  commands: [],
139
139
  resources: [],
140
- hooks: []
140
+ hooks: [],
141
+ plugins: []
141
142
  };
142
143
 
143
144
  // Helper function to process each content category
@@ -182,6 +183,7 @@ class PackageManager {
182
183
  selected.commands = selectItems('commands', variantConfig.commands, availableContent.commands || [], true);
183
184
  selected.resources = selectItems('resources', variantConfig.resources, availableContent.resources || []);
184
185
  selected.hooks = selectItems('hooks', variantConfig.hooks, availableContent.hooks || []);
186
+ selected.plugins = selectItems('plugins', variantConfig.plugins, availableContent.plugins || []);
185
187
 
186
188
  return selected;
187
189
  }
@@ -227,6 +229,7 @@ class PackageManager {
227
229
  commands: [],
228
230
  resources: [],
229
231
  hooks: [],
232
+ plugins: [],
230
233
  totalFiles: 0,
231
234
  totalSize: 0
232
235
  };
@@ -295,12 +298,24 @@ class PackageManager {
295
298
  }
296
299
  }
297
300
 
301
+ // Build directory paths for selected plugins (plugins are directories, like skills)
302
+ const pluginsDir = path.join(packageDir, 'plugins');
303
+ if (fs.existsSync(pluginsDir)) {
304
+ for (const plugin of (selectedContent.plugins || [])) {
305
+ const pluginPath = path.join(pluginsDir, plugin);
306
+ if (fs.existsSync(pluginPath)) {
307
+ contents.plugins.push(pluginPath);
308
+ }
309
+ }
310
+ }
311
+
298
312
  // Calculate total files
299
313
  contents.totalFiles = contents.agents.length +
300
314
  contents.skills.length +
301
315
  contents.commands.length +
302
316
  contents.resources.length +
303
- contents.hooks.length;
317
+ contents.hooks.length +
318
+ contents.plugins.length;
304
319
 
305
320
  return contents;
306
321
  }
@@ -349,6 +364,7 @@ class PackageManager {
349
364
  const commandsResult = await getItemsInDir(path.join(packageDir, commandsDir), true);
350
365
  const resourcesResult = await getItemsInDir(path.join(packageDir, 'resources'));
351
366
  const hooksResult = await getItemsInDir(path.join(packageDir, 'hooks'));
367
+ const pluginsResult = await getItemsInDir(path.join(packageDir, 'plugins'));
352
368
 
353
369
  return {
354
370
  agents: agentsResult.items,
@@ -357,7 +373,8 @@ class PackageManager {
357
373
  commands: commandsResult.items,
358
374
  commandsDir: commandsDir,
359
375
  resources: resourcesResult.items,
360
- hooks: hooksResult.items
376
+ hooks: hooksResult.items,
377
+ plugins: pluginsResult.items
361
378
  };
362
379
  }
363
380
 
@@ -399,6 +416,7 @@ class PackageManager {
399
416
  let totalSize = 0;
400
417
 
401
418
  // Helper function to calculate size of a file or directory recursively
419
+ // Skips node_modules directories (plugins may have local installs)
402
420
  const calculatePathSize = async (itemPath) => {
403
421
  try {
404
422
  const stat = await fs.promises.stat(itemPath);
@@ -406,6 +424,11 @@ class PackageManager {
406
424
  if (stat.isFile()) {
407
425
  return stat.size;
408
426
  } else if (stat.isDirectory()) {
427
+ // Skip node_modules to avoid bundling local dependencies
428
+ if (path.basename(itemPath) === 'node_modules') {
429
+ return 0;
430
+ }
431
+
409
432
  // Recursively calculate directory size
410
433
  let dirSize = 0;
411
434
  const items = await fs.promises.readdir(itemPath);
@@ -445,6 +468,11 @@ class PackageManager {
445
468
  totalSize += await calculatePathSize(hookPath);
446
469
  }
447
470
 
471
+ // Calculate size for all selected plugins (directories, excluding node_modules)
472
+ for (const pluginPath of (contents.plugins || [])) {
473
+ totalSize += await calculatePathSize(pluginPath);
474
+ }
475
+
448
476
  return {
449
477
  size: totalSize,
450
478
  formattedSize: this.formatBytes(totalSize)
@@ -601,6 +629,9 @@ class PackageManager {
601
629
  const commandsDirName = availableContent.commandsDir || 'commands';
602
630
  await checkContentExists('commands', selectedContent.commands || [], commandsDirName, true);
603
631
 
632
+ // Validate plugins (directories, like skills)
633
+ await checkContentExists('plugins', selectedContent.plugins || [], 'plugins', false);
634
+
604
635
  } catch (error) {
605
636
  return {
606
637
  valid: false,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "liteagents",
3
- "version": "2.5.2",
4
- "description": "AI development toolkit with 11 specialized agents and 22 commands. Simple one-question installer for Claude, Opencode, Ampcode, and Droid.",
3
+ "version": "2.6.0",
4
+ "description": "AI development toolkit with 11 specialized agents and 23 commands including live-canvas UI design with click-to-annotate feedback. Simple one-question installer for Claude, Opencode, Ampcode, and Droid.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "liteagents": "./installer/cli.js",
@@ -16,6 +16,7 @@
16
16
  "clean:git": "git clean -fdx tests/tmp/",
17
17
  "validate": "node ./scripts/validate-package.js",
18
18
  "validate-packages": "node scripts/validate-all-packages.js",
19
+ "prepack": "rm -rf packages/*/plugins/*/plugins/*/node_modules",
19
20
  "prepublishOnly": "npm run clean && npm run validate",
20
21
  "postinstall": "node postinstall.js"
21
22
  },
@@ -44,16 +45,16 @@
44
45
  "cli",
45
46
  "variant-system"
46
47
  ],
47
- "author": "amrhas82 <avoidaccess@msn.com>",
48
+ "author": "hamr0 <avoidaccess@msn.com>",
48
49
  "license": "MIT",
49
50
  "repository": {
50
51
  "type": "git",
51
- "url": "https://github.com/amrhas82/liteagents.git"
52
+ "url": "https://github.com/hamr0/liteagents.git"
52
53
  },
53
54
  "bugs": {
54
- "url": "https://github.com/amrhas82/liteagents/issues"
55
+ "url": "https://github.com/hamr0/liteagents/issues"
55
56
  },
56
- "homepage": "https://github.com/amrhas82/liteagents#readme",
57
+ "homepage": "https://github.com/hamr0/liteagents#readme",
57
58
  "publishConfig": {
58
59
  "registry": "https://registry.npmjs.org",
59
60
  "access": "public"
@@ -45,6 +45,7 @@ These subagents are available when using Ampcode CLI.
45
45
  | explain | Explain code for someone new to the codebase | /explain <code-section> |
46
46
  | friction | Analyze session logs for failure patterns and behavioral signals | /friction <sessions-path> |
47
47
  | git-commit | Analyze changes and create intelligent git commits | /git-commit |
48
+ | live-canvas | Design UI variations and collect click-to-annotate feedback from the browser (batch mode only on Amp) | /live-canvas |
48
49
  | optimize | Analyze and optimize performance issues | /optimize <target-area> |
49
50
  | refactor | Refactor code while maintaining behavior and tests | /refactor <code-section> |
50
51
  | remember | Consolidate stashes + friction into project memory | /remember |
@@ -1940,12 +1940,21 @@ function clusterCandidates(allCandidates) {
1940
1940
  const clusters = Object.values(clusterMap).map(cl => {
1941
1941
  const weight = signalWeights[cl.anchor_signal] || 1;
1942
1942
  const peaks = cl.peaks.sort((a, b) => a - b);
1943
+ // Session IDs follow "project/dateStr-shortId", so the first segment is
1944
+ // the project. Preserve both the ID list and the deduped project set so
1945
+ // downstream renderers can surface which repos produced the cluster.
1946
+ const sessionIds = Object.keys(cl.sessions);
1947
+ const projects = [...new Set(
1948
+ sessionIds.map(s => s.includes('/') ? s.split('/')[0] : 'unknown')
1949
+ )].sort();
1943
1950
  return {
1944
1951
  anchor_signal: cl.anchor_signal,
1945
1952
  tool_pattern: cl.tool_pattern,
1946
1953
  count: cl.count,
1947
1954
  score: cl.count * weight,
1948
- sessions: Object.keys(cl.sessions).length,
1955
+ sessions: sessionIds.length,
1956
+ session_ids: sessionIds,
1957
+ projects: projects,
1949
1958
  median_peak: peaks[Math.floor(peaks.length / 2)],
1950
1959
  max_peak: peaks[peaks.length - 1],
1951
1960
  contexts: cl.contexts,
@@ -2048,10 +2057,13 @@ function extractMain(sessionsDir) {
2048
2057
 
2049
2058
  // Summary table
2050
2059
  reviewLines.push('## Cluster Summary\n\n');
2051
- reviewLines.push('| # | Signal | Tool Pattern | Count | Sessions | Score | Median Peak |\n');
2052
- reviewLines.push('|---|--------|-------------|-------|----------|-------|-------------|\n');
2060
+ reviewLines.push('| # | Signal | Tool Pattern | Count | Sessions | Projects | Score | Median Peak |\n');
2061
+ reviewLines.push('|---|--------|-------------|-------|----------|----------|-------|-------------|\n');
2053
2062
  reviewClusters.forEach((cl, idx) => {
2054
- reviewLines.push(`| ${idx + 1} | ${cl.anchor_signal} | ${cl.tool_pattern} | ${cl.count} | ${cl.sessions} | ${cl.score} | ${cl.median_peak} |\n`);
2063
+ const projs = cl.projects || [];
2064
+ const projectsShort = projs.slice(0, 3).join(', ') +
2065
+ (projs.length > 3 ? `, +${projs.length - 3}` : '');
2066
+ reviewLines.push(`| ${idx + 1} | ${cl.anchor_signal} | ${cl.tool_pattern} | ${cl.count} | ${cl.sessions} | ${projectsShort || '-'} | ${cl.score} | ${cl.median_peak} |\n`);
2055
2067
  });
2056
2068
  reviewLines.push('\n---\n\n');
2057
2069
 
@@ -2059,6 +2071,9 @@ function extractMain(sessionsDir) {
2059
2071
  reviewClusters.forEach((cl, idx) => {
2060
2072
  reviewLines.push(`## Cluster ${idx + 1}: ${cl.anchor_signal} | ${cl.tool_pattern}\n\n`);
2061
2073
  reviewLines.push(`**Occurrences:** ${cl.count} across ${cl.sessions} sessions | **Score:** ${cl.score} | **Median peak:** ${cl.median_peak} | **Max peak:** ${cl.max_peak}\n\n`);
2074
+ if (cl.projects && cl.projects.length > 0) {
2075
+ reviewLines.push(`**Projects:** ${cl.projects.join(', ')}\n\n`);
2076
+ }
2062
2077
 
2063
2078
  if (cl.contexts.length > 0) {
2064
2079
  reviewLines.push('### User Context (what the user said)\n\n');