liteagents 2.5.3 → 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.
- package/CHANGELOG.md +31 -0
- package/README.md +51 -45
- package/installer/cli.js +1 -1
- package/installer/installation-engine.js +82 -21
- package/installer/package-manager.js +34 -3
- package/package.json +3 -2
- package/packages/ampcode/AGENT.md +1 -0
- package/packages/ampcode/commands/friction/friction.js +19 -4
- package/packages/ampcode/commands/live-canvas/DESIGN_PRINCIPLES.md +2140 -0
- package/packages/ampcode/commands/live-canvas/README.md +273 -0
- package/packages/ampcode/commands/live-canvas/templates/.claude/settings.local.json +8 -0
- package/packages/ampcode/commands/live-canvas/templates/demo/post-variants.html +205 -0
- package/packages/ampcode/commands/live-canvas/templates/feedback-react/FeedbackOverlay.tsx +1299 -0
- package/packages/ampcode/commands/live-canvas/templates/feedback-react/format-utils.ts +283 -0
- package/packages/ampcode/commands/live-canvas/templates/feedback-react/index.ts +62 -0
- package/packages/ampcode/commands/live-canvas/templates/feedback-react/selector-utils.ts +423 -0
- package/packages/ampcode/commands/live-canvas/templates/feedback-react/types.ts +118 -0
- package/packages/ampcode/commands/live-canvas/templates/overlay-vanilla.js +477 -0
- package/packages/ampcode/commands/live-canvas.md +1104 -0
- package/packages/claude/CLAUDE.md +1 -0
- package/packages/claude/commands/friction/friction.js +19 -4
- package/packages/claude/plugins/live-canvas-marketplace/.claude-plugin/marketplace.json +14 -0
- package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/.claude-plugin/plugin.json +18 -0
- package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/README.md +89 -0
- package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/package-lock.json +1142 -0
- package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/package.json +17 -0
- package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/schema.json +37 -0
- package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/server.js +179 -0
- package/packages/claude/plugins/live-canvas-marketplace/setup.sh +61 -0
- package/packages/claude/skills/live-canvas/DESIGN_PRINCIPLES.md +2140 -0
- package/packages/claude/skills/live-canvas/INTEGRATION_NOTES.md +210 -0
- package/packages/claude/skills/live-canvas/README.md +273 -0
- package/packages/claude/skills/live-canvas/SKILL.md +1119 -0
- package/packages/claude/skills/live-canvas/templates/.claude/settings.local.json +8 -0
- package/packages/claude/skills/live-canvas/templates/demo/post-variants.html +205 -0
- package/packages/claude/skills/live-canvas/templates/feedback-react/FeedbackOverlay.tsx +1299 -0
- package/packages/claude/skills/live-canvas/templates/feedback-react/format-utils.ts +283 -0
- package/packages/claude/skills/live-canvas/templates/feedback-react/index.ts +62 -0
- package/packages/claude/skills/live-canvas/templates/feedback-react/selector-utils.ts +423 -0
- package/packages/claude/skills/live-canvas/templates/feedback-react/types.ts +118 -0
- package/packages/claude/skills/live-canvas/templates/overlay-vanilla.js +477 -0
- package/packages/claude/variants.json +2 -1
- package/packages/droid/AGENTS.md +1 -0
- package/packages/droid/commands/friction/friction.js +19 -4
- package/packages/droid/commands/live-canvas/DESIGN_PRINCIPLES.md +2140 -0
- package/packages/droid/commands/live-canvas/README.md +273 -0
- package/packages/droid/commands/live-canvas/templates/.claude/settings.local.json +8 -0
- package/packages/droid/commands/live-canvas/templates/demo/post-variants.html +205 -0
- package/packages/droid/commands/live-canvas/templates/feedback-react/FeedbackOverlay.tsx +1299 -0
- package/packages/droid/commands/live-canvas/templates/feedback-react/format-utils.ts +283 -0
- package/packages/droid/commands/live-canvas/templates/feedback-react/index.ts +62 -0
- package/packages/droid/commands/live-canvas/templates/feedback-react/selector-utils.ts +423 -0
- package/packages/droid/commands/live-canvas/templates/feedback-react/types.ts +118 -0
- package/packages/droid/commands/live-canvas/templates/overlay-vanilla.js +477 -0
- package/packages/droid/commands/live-canvas.md +1104 -0
- package/packages/opencode/AGENTS.md +1 -0
- package/packages/opencode/command/friction/friction.js +19 -4
- package/packages/opencode/command/live-canvas/DESIGN_PRINCIPLES.md +2140 -0
- package/packages/opencode/command/live-canvas/README.md +273 -0
- package/packages/opencode/command/live-canvas/templates/.claude/settings.local.json +8 -0
- package/packages/opencode/command/live-canvas/templates/demo/post-variants.html +205 -0
- package/packages/opencode/command/live-canvas/templates/feedback-react/FeedbackOverlay.tsx +1299 -0
- package/packages/opencode/command/live-canvas/templates/feedback-react/format-utils.ts +283 -0
- package/packages/opencode/command/live-canvas/templates/feedback-react/index.ts +62 -0
- package/packages/opencode/command/live-canvas/templates/feedback-react/selector-utils.ts +423 -0
- package/packages/opencode/command/live-canvas/templates/feedback-react/types.ts +118 -0
- package/packages/opencode/command/live-canvas/templates/overlay-vanilla.js +477 -0
- package/packages/opencode/command/live-canvas.md +1104 -0
- package/packages/opencode/opencode.jsonc +4 -0
- package/packages/subagentic-manual.md +15 -12
package/CHANGELOG.md
CHANGED
|
@@ -17,6 +17,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
+
## [2.6.0] - 2026-04-19
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- **live-canvas skill** — Design interview, generates 5 UI variations, collects click-to-annotate feedback from the browser, produces a final implementation plan. Available as a skill in Claude Code and as a command in Droid/Ampcode/Opencode.
|
|
24
|
+
- **Vanilla overlay** (`overlay-vanilla.js`, ~400 lines, zero deps) — framework-agnostic click-to-annotate HUD with pin placement, selector inference (`data-testid` > `id` > class chain, CSS-in-JS hashes filtered), variant detection via `data-variant` attribute, and a v1.0 schema wire-compatible with the upstream React template.
|
|
25
|
+
- **React overlay** (`FeedbackOverlay.tsx`) kept for React/Next/Vite projects.
|
|
26
|
+
- **Demo** (`templates/demo/post-variants.html`) — standalone 5-variant post card playground for review without starting a dev server.
|
|
27
|
+
- **Handholding activation flow** — Phase 0 probes the channel on every invocation and offers `AskUserQuestion` choices: first-time users get the full 4-step setup; returning users who forgot the dev flag get the restart command; channel-up sessions proceed silently.
|
|
28
|
+
- **live-canvas-channel plugin** (Claude Code only) — MCP channel server that bridges the overlay's HTTP POSTs into the live session as `notifications/claude/channel` events. Packaged as a Claude Code marketplace under `packages/claude/plugins/live-canvas-marketplace/`.
|
|
29
|
+
- HTTP listener on `127.0.0.1:8788` with `/health` probe and `/feedback` POST endpoint
|
|
30
|
+
- MCP server using `@modelcontextprotocol/sdk` with `experimental: {'claude/channel': {}}` capability
|
|
31
|
+
- `instructions` field added to tell Claude how to act on incoming `<channel source="live-canvas">` tags: acknowledge in chat → locate variant file → edit → confirm
|
|
32
|
+
- `setup.sh` helper that runs `npm install` and prints the 3 remaining manual steps
|
|
33
|
+
- **Installer: plugins as first-class category** — `packages/<tool>/plugins/` now discovered, selected per variant, and copied to `<target>/plugins/`, parallel to skills. `node_modules/` excluded during copy and size computation. Manifest generation includes a `plugins` component count, installed-files list, and path entry.
|
|
34
|
+
- **Friction report: project attribution in cluster output** — `antigen_review.md` now shows which projects each cluster spans (new "Projects" column in the summary table; new `**Projects:**` line per cluster). Data was always in `session_id`; the previous version dropped it during clustering.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
- **README restructured for Hot Memory visibility** — new top-level `🧠 Hot Memory` section between Quick Start and What's Included, with pipeline diagram and sample friction output. Manual Skills/Commands list regrouped into named sub-sections (Hot Memory Pipeline, Design, Workflow & analysis). Old duplicated Hot Memory section removed.
|
|
38
|
+
- **Command/skill count 22 → 23** per tool across README, subagentic-manual, and per-package `AGENTS.md` / `AGENT.md` / `CLAUDE.md`.
|
|
39
|
+
- **opencode.jsonc** — added `live-canvas` entry under `"command"` block.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- **Friction clustering dropped project names** — cluster object converted `{sessionId: true}` dict to a count before rendering, so `antigen_review.md` never surfaced which repos contributed to each pattern. Fix preserves `session_ids[]` and `projects[]` on every cluster.
|
|
43
|
+
- **Live Canvas overlay counter flicker in live mode** — counter went 0→1→0 during successful push roundtrip. Rewrote so successful live pushes never enter the pending-batch state; counter stays at 0 unless a push actually fails.
|
|
44
|
+
|
|
45
|
+
### Notes
|
|
46
|
+
- The Claude Code channel plugin is subject to Claude Code's research preview: custom channels require `--dangerously-load-development-channels` at session start, and steps 2-4 of setup (`/plugin marketplace add`, `/plugin install`, session restart) cannot be automated. The skill prints copyable commands and an alias suggestion.
|
|
47
|
+
- Droid, Ampcode, and Opencode run live-canvas in batch mode only — channels are Claude-Code-specific.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
20
51
|
## [2.5.2] - 2026-02-11
|
|
21
52
|
|
|
22
53
|
### Added
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
╚══════╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
**AI development toolkit with 11 specialized agents and
|
|
12
|
+
**AI development toolkit with 11 specialized agents and 23 commands per tool**
|
|
13
13
|
|
|
14
14
|
[](https://www.npmjs.com/package/liteagents)
|
|
15
15
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -43,10 +43,10 @@ liteagents
|
|
|
43
43
|
|
|
44
44
|
### Supported Tools
|
|
45
45
|
|
|
46
|
-
- **Claude Code** - 11 subagents +
|
|
47
|
-
- **Opencode** - 11 agent references +
|
|
48
|
-
- **Ampcode** - 11 subagents +
|
|
49
|
-
- **Droid** - 11 agent references +
|
|
46
|
+
- **Claude Code** - 11 subagents + 11 skills + 12 commands (+ optional live-canvas channel plugin)
|
|
47
|
+
- **Opencode** - 11 agent references + 23 commands
|
|
48
|
+
- **Ampcode** - 11 subagents + 23 commands
|
|
49
|
+
- **Droid** - 11 agent references + 23 commands
|
|
50
50
|
|
|
51
51
|
**Key Difference:**
|
|
52
52
|
- **Claude Code**: Full subagent system with orchestrator + skills (auto-triggering)
|
|
@@ -69,6 +69,39 @@ liteagents
|
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
72
|
+
## 🧠 Hot Memory — project-local learning from your own sessions
|
|
73
|
+
|
|
74
|
+
Liteagents ships a three-command pipeline that turns Claude Code's session logs into project-local memory. No databases, no external services, just markdown files the assistant reads via `@MEMORY.md`.
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
/stash → /friction → /remember
|
|
78
|
+
capture analyze consolidate
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- **`/stash`** — snapshot the current session's context before compaction or handoff
|
|
82
|
+
- **`/friction`** — mine JSONL session logs for frustration signals, failed flows, and abandonment patterns; cluster them into antigen candidates per project
|
|
83
|
+
- **`/remember`** — consolidate stashes + friction antigens into `.claude/memory/MEMORY.md`; auto-injected into `CLAUDE.md` via `@MEMORY.md` so every future session in the project benefits
|
|
84
|
+
|
|
85
|
+
What you get is a memory that *learns from your own mistakes and interventions*, grows quietly in your repo, and works anywhere Claude Code runs. The friction analyzer alone scans all your projects and gives you a per-repo reliability verdict:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Per-Project:
|
|
89
|
+
my-app 56% BAD (40/72) median: 16.0 🔴
|
|
90
|
+
api-service 40% BAD (2/5) median: 0.5 🟡
|
|
91
|
+
web-client 0% BAD (0/1) median: 0.0 ✅
|
|
92
|
+
|
|
93
|
+
WORST: my-app/0203-1630-11eb903a peak=225 turns=127
|
|
94
|
+
BEST: web-client/0202-2121-8d8608e1 peak=0 turns=4
|
|
95
|
+
|
|
96
|
+
Verdict: ✓ USEFUL Intervention predictability: 93%
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Results land in `.claude/friction/antigen_review.md` with projects, error patterns, and offending tool sequences called out per cluster — so `/remember` can pick them up and encode them as rules the next session sees.
|
|
100
|
+
|
|
101
|
+
> This is the thing in liteagents that nothing else ships. Normal skill bundles give you instructions. The hot-memory pipeline gives you instructions the assistant wrote for itself, from your own logs.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
72
105
|
## 🤖 What's Included
|
|
73
106
|
|
|
74
107
|
### 11 Agents
|
|
@@ -88,14 +121,24 @@ liteagents
|
|
|
88
121
|
- **system-architect** - System design, technology selection, API design, scalability planning
|
|
89
122
|
- **ui-designer** - UI/UX design, wireframes, prototypes, accessibility, design systems
|
|
90
123
|
|
|
91
|
-
###
|
|
124
|
+
### 23 Commands/Skills
|
|
92
125
|
|
|
93
126
|
**Auto-Triggering Skills (3)** - Claude Code only:
|
|
94
127
|
- **test-driven-development** - Write test first, watch fail, minimal passing code
|
|
95
128
|
- **testing-anti-patterns** - Prevent mocking anti-patterns
|
|
96
129
|
- **verification-before-completion** - Verify before claiming done
|
|
97
130
|
|
|
98
|
-
**Manual Skills/Commands (
|
|
131
|
+
**Manual Skills/Commands (20):**
|
|
132
|
+
|
|
133
|
+
*Hot Memory Pipeline (3)* — see the [🧠 Hot Memory](#-hot-memory--project-local-learning-from-your-own-sessions) section above for the full walkthrough:
|
|
134
|
+
- **stash** - Snapshot session context to `.claude/stash/` before compaction, handoff, or ending complex work
|
|
135
|
+
- **friction** - Analyze all JSONL sessions for frustration signals, cluster failures per project, surface antigens
|
|
136
|
+
- **remember** - Consolidate stashes + friction antigens into `.claude/memory/MEMORY.md`; auto-injected via `@MEMORY.md`
|
|
137
|
+
|
|
138
|
+
*Design*:
|
|
139
|
+
- **live-canvas** - Design UI variations with click-to-annotate feedback in the browser; ships a companion MCP channel plugin for Claude Code so Saves stream into the session in real time. Other tools use batch mode.
|
|
140
|
+
|
|
141
|
+
*Workflow & analysis*:
|
|
99
142
|
- **brainstorming** - Structured brainstorming sessions
|
|
100
143
|
- **code-review** - Implementation review against requirements
|
|
101
144
|
- **condition-based-waiting** - Replace timeouts with condition polling
|
|
@@ -105,52 +148,15 @@ liteagents
|
|
|
105
148
|
- **systematic-debugging** - Four-phase debugging framework
|
|
106
149
|
- **debug** - Systematic issue investigation
|
|
107
150
|
- **explain** - Explain code for newcomers
|
|
108
|
-
- **friction** - Analyze session logs for failure patterns and behavioral signals
|
|
109
151
|
- **git-commit** - Intelligent commit creation
|
|
110
152
|
- **optimize** - Performance analysis
|
|
111
153
|
- **refactor** - Safe refactoring with behavior preservation
|
|
112
|
-
- **remember** - Consolidate stashes + friction into project memory
|
|
113
154
|
- **review** - Comprehensive code review
|
|
114
155
|
- **security** - Vulnerability scanning
|
|
115
156
|
- **ship** - Pre-deployment checklist
|
|
116
|
-
- **stash** - Save session context for compaction recovery or handoffs
|
|
117
157
|
- **test-generate** - Generate test suites
|
|
118
158
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Lightweight session memory that learns from your usage patterns.
|
|
122
|
-
|
|
123
|
-
```
|
|
124
|
-
/stash → /friction → /remember
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
1. **`/stash`** - Snapshot current session context to `.claude/stash/`. Use before compaction, handoffs, or ending complex work.
|
|
128
|
-
2. **`/friction`** - Analyze session logs for failure patterns. Scores sessions, clusters failures, outputs actionable antigens.
|
|
129
|
-
3. **`/remember`** - Consolidate stashes + friction into `.claude/memory/MEMORY.md`. Extracts facts, episodes, and behavioral preferences. Injects into CLAUDE.md via `@MEMORY.md`.
|
|
130
|
-
|
|
131
|
-
**Result:** Project-local memory that accumulates across sessions — no external dependencies, no databases, just markdown.
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
# Standalone CLI — analyze all projects
|
|
135
|
-
liteagents friction ~/.claude/projects
|
|
136
|
-
|
|
137
|
-
Per-Project:
|
|
138
|
-
my-app 56% BAD (40/72) median: 16.0 🔴
|
|
139
|
-
api-service 40% BAD (2/5) median: 0.5 🟡
|
|
140
|
-
web-client 0% BAD (0/1) median: 0.0 ✅
|
|
141
|
-
|
|
142
|
-
Session Extremes:
|
|
143
|
-
WORST: my-app/0203-1630-11eb903a peak=225 turns=127
|
|
144
|
-
BEST: web-client/0202-2121-8d8608e1 peak=0 turns=4
|
|
145
|
-
|
|
146
|
-
Last 2 Weeks:
|
|
147
|
-
2026-02-02 15 sessions 10 BAD ██████░░░░ 67%
|
|
148
|
-
2026-02-03 29 sessions 12 BAD ████░░░░░░ 41%
|
|
149
|
-
2026-02-04 6 sessions 2 BAD ███░░░░░░░ 33%
|
|
150
|
-
|
|
151
|
-
Verdict: ✓ USEFUL
|
|
152
|
-
Intervention predictability: 93%
|
|
153
|
-
```
|
|
159
|
+
> **Claude-only plugin:** `live-canvas-channel` is a bundled Claude Code MCP channel plugin that ships under `~/.claude/plugins/live-canvas-marketplace/`. One-time `/plugin install` + a session started with `--dangerously-load-development-channels` unlocks live mode. Skill probes for the channel on each invocation and handholds setup when missing. See [`packages/claude/skills/live-canvas/README.md`](packages/claude/skills/live-canvas/README.md) for the full walkthrough.
|
|
154
160
|
|
|
155
161
|
---
|
|
156
162
|
|
package/installer/cli.js
CHANGED
|
@@ -456,7 +456,7 @@ ${colors.bright}${colors.cyan}██╔══██║██║ ██║█
|
|
|
456
456
|
${colors.bright}${colors.cyan}██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ██║╚██████╗ ██║ ██╗██║ ██║${colors.reset}
|
|
457
457
|
${colors.bright}${colors.cyan}╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝${colors.reset}
|
|
458
458
|
|
|
459
|
-
${colors.bright}v2.
|
|
459
|
+
${colors.bright}v2.6.0 | 11 agents + 23 commands per tool${colors.reset}
|
|
460
460
|
`);
|
|
461
461
|
}
|
|
462
462
|
|
|
@@ -194,18 +194,23 @@ class InstallationEngine {
|
|
|
194
194
|
}
|
|
195
195
|
};
|
|
196
196
|
|
|
197
|
-
// Helper function to recursively collect all files in a directory
|
|
198
|
-
|
|
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' :
|
|
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.
|
|
4
|
-
"description": "AI development toolkit with 11 specialized agents and
|
|
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
|
},
|
|
@@ -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:
|
|
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('
|
|
2060
|
+
reviewLines.push('| # | Signal | Tool Pattern | Count | Sessions | Projects | Score | Median Peak |\n');
|
|
2061
|
+
reviewLines.push('|---|--------|-------------|-------|----------|----------|-------|-------------|\n');
|
|
2053
2062
|
reviewClusters.forEach((cl, idx) => {
|
|
2054
|
-
|
|
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');
|