@ghl-ai/aw 0.1.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/apply.mjs +49 -0
- package/bin.js +3 -0
- package/cli.mjs +141 -0
- package/commands/drop.mjs +88 -0
- package/commands/init.mjs +75 -0
- package/commands/nuke.mjs +95 -0
- package/commands/pull.mjs +252 -0
- package/commands/push.mjs +214 -0
- package/commands/search.mjs +183 -0
- package/commands/status.mjs +108 -0
- package/config.mjs +70 -0
- package/constants.mjs +7 -0
- package/fmt.mjs +99 -0
- package/git.mjs +61 -0
- package/glob.mjs +22 -0
- package/integrate.mjs +466 -0
- package/link.mjs +209 -0
- package/manifest.mjs +62 -0
- package/mcp.mjs +166 -0
- package/package.json +47 -0
- package/paths.mjs +139 -0
- package/plan.mjs +133 -0
- package/registry.mjs +138 -0
package/integrate.mjs
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
// integrate.mjs — Generate commands for all IDEs, instructions (CLAUDE.md, AGENTS.md)
|
|
2
|
+
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import * as fmt from './fmt.mjs';
|
|
6
|
+
|
|
7
|
+
// AW CLI commands to generate
|
|
8
|
+
const AW_COMMANDS = [
|
|
9
|
+
{ name: 'pull', description: 'Pull agents & skills from registry', hint: '<path>' },
|
|
10
|
+
{ name: 'push', description: 'Push local changes to registry', hint: '<path>' },
|
|
11
|
+
{ name: 'status', description: 'Show workspace sync status', hint: '' },
|
|
12
|
+
{ name: 'drop', description: 'Stop syncing a path', hint: '<path>' },
|
|
13
|
+
{ name: 'search', description: 'Search local and remote registry', hint: '<query>' },
|
|
14
|
+
{ name: 'nuke', description: 'Remove entire .aw_registry/', hint: '' },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generate /aw:* commands directly into namespace commands/ dirs.
|
|
19
|
+
* Writes CLI commands + agent invoke commands alongside hand-written commands.
|
|
20
|
+
* link.mjs symlinks everything from commands/ → .claude/commands/aw/ for discovery.
|
|
21
|
+
*
|
|
22
|
+
* A .generated-manifest.json tracks which files were generated so they can be
|
|
23
|
+
* cleaned on rebuild without needing filename prefixes.
|
|
24
|
+
*/
|
|
25
|
+
export function generateCommands(cwd) {
|
|
26
|
+
const awDir = join(cwd, '.aw_registry');
|
|
27
|
+
|
|
28
|
+
// Clean old .generated-commands if it exists (migration)
|
|
29
|
+
const oldGenDir = join(awDir, '.generated-commands');
|
|
30
|
+
if (existsSync(oldGenDir)) rmSync(oldGenDir, { recursive: true, force: true });
|
|
31
|
+
|
|
32
|
+
let count = 0;
|
|
33
|
+
const namespaces = listNamespaceDirs(awDir);
|
|
34
|
+
|
|
35
|
+
for (const ns of namespaces) {
|
|
36
|
+
const commandsDir = join(awDir, ns, 'commands');
|
|
37
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
38
|
+
|
|
39
|
+
// 1. CLI commands
|
|
40
|
+
for (const cmd of AW_COMMANDS) {
|
|
41
|
+
const fileName = `${cmd.name}.md`;
|
|
42
|
+
// Skip if a hand-written command with same name exists
|
|
43
|
+
if (existsSync(join(commandsDir, fileName))) continue;
|
|
44
|
+
|
|
45
|
+
const content = [
|
|
46
|
+
'---',
|
|
47
|
+
`name: aw:${cmd.name}`,
|
|
48
|
+
`description: ${cmd.description}`,
|
|
49
|
+
cmd.hint ? `argument-hint: "${cmd.hint}"` : null,
|
|
50
|
+
'---',
|
|
51
|
+
'',
|
|
52
|
+
'Run the following command:',
|
|
53
|
+
'',
|
|
54
|
+
'```',
|
|
55
|
+
`node bin/aw ${cmd.name} $ARGUMENTS`,
|
|
56
|
+
'```',
|
|
57
|
+
'',
|
|
58
|
+
].filter(v => v !== null).join('\n');
|
|
59
|
+
|
|
60
|
+
writeFileSync(join(commandsDir, fileName), content);
|
|
61
|
+
count++;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Codex skills — .agents/skills/<name>/SKILL.md
|
|
67
|
+
const agentsSkillsDir = join(cwd, '.agents/skills');
|
|
68
|
+
mkdirSync(agentsSkillsDir, { recursive: true });
|
|
69
|
+
for (const cmd of AW_COMMANDS) {
|
|
70
|
+
const skillDir = join(agentsSkillsDir, cmd.name);
|
|
71
|
+
mkdirSync(skillDir, { recursive: true });
|
|
72
|
+
const content = [
|
|
73
|
+
'---',
|
|
74
|
+
`name: ${cmd.name}`,
|
|
75
|
+
`description: ${cmd.description}`,
|
|
76
|
+
'---',
|
|
77
|
+
'',
|
|
78
|
+
`Run: \`node bin/aw ${cmd.name}\` followed by the user's arguments.`,
|
|
79
|
+
'',
|
|
80
|
+
].join('\n');
|
|
81
|
+
writeFileSync(join(skillDir, 'SKILL.md'), content);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (count > 0) {
|
|
85
|
+
fmt.logSuccess(`Generated ${count} aw commands`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return count;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Copy CLAUDE.md and AGENTS.md to project root.
|
|
93
|
+
*/
|
|
94
|
+
export function copyInstructions(cwd, tempDir, namespace) {
|
|
95
|
+
for (const file of ['CLAUDE.md', 'AGENTS.md']) {
|
|
96
|
+
const dest = join(cwd, file);
|
|
97
|
+
if (existsSync(dest)) continue;
|
|
98
|
+
|
|
99
|
+
if (tempDir) {
|
|
100
|
+
const src = join(tempDir, 'registry', file);
|
|
101
|
+
if (existsSync(src)) {
|
|
102
|
+
let content = readFileSync(src, 'utf8');
|
|
103
|
+
if (namespace) {
|
|
104
|
+
content = content.replace(/\{\{TEAM\}\}/g, namespace);
|
|
105
|
+
}
|
|
106
|
+
writeFileSync(dest, content);
|
|
107
|
+
fmt.logSuccess(`Created ${file}`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const content = file === 'CLAUDE.md'
|
|
113
|
+
? generateClaudeMd(cwd, namespace)
|
|
114
|
+
: generateAgentsMd(cwd, namespace);
|
|
115
|
+
if (content) {
|
|
116
|
+
writeFileSync(dest, content);
|
|
117
|
+
fmt.logSuccess(`Created ${file}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function generateClaudeMd(cwd, namespace) {
|
|
123
|
+
const team = namespace || 'my-team';
|
|
124
|
+
return `# CLAUDE.md — ${team}
|
|
125
|
+
|
|
126
|
+
Team: ${team} | Local-first orchestration via \`.aw_docs/\` | MCPs: \`memory/*\` (shared knowledge), \`git-jenkins\` (CI/CD), \`grafana\` (observability)
|
|
127
|
+
|
|
128
|
+
## Routing Rule (ABSOLUTE)
|
|
129
|
+
|
|
130
|
+
> **Every non-trivial task MUST call \`Skill(skill: "ghl-ai-task-router")\` BEFORE any response.**
|
|
131
|
+
>
|
|
132
|
+
> **Trivial** (do directly): typo fixes, single-line edits, git ops, file exploration, factual code questions.
|
|
133
|
+
>
|
|
134
|
+
> Everything else — including tasks phrased as questions, suggestions, or discussions — routes first.
|
|
135
|
+
> **No conversational responses first. No planning first. Route first.**
|
|
136
|
+
> **Execute, don't just recommend:** run the steps (tools, edits, workflows) instead of listing "next steps" for the user.
|
|
137
|
+
|
|
138
|
+
## Architecture
|
|
139
|
+
|
|
140
|
+
\`\`\`
|
|
141
|
+
.aw_registry/
|
|
142
|
+
├── ghl/ # Platform layer (shared, read-only)
|
|
143
|
+
│ ├── agents/ # 15 platform agents (reviewers, engineers, designer)
|
|
144
|
+
│ ├── skills/ # 77 platform skills (SKILL.md files)
|
|
145
|
+
│ ├── commands/ # Platform commands (health-check, help, discover)
|
|
146
|
+
│ └── evals/ # Platform agent & skill quality tests
|
|
147
|
+
│
|
|
148
|
+
├── ${team}/ # Team layer (from template, customizable)
|
|
149
|
+
│ ├── agents/ # Team agents (developers, testers, PM, coordinator)
|
|
150
|
+
│ ├── skills/ # Team-specific skills
|
|
151
|
+
│ ├── commands/ # Workflow commands (ship, plan, review, deploy, etc.)
|
|
152
|
+
│ ├── blueprints/ # Development workflow blueprints
|
|
153
|
+
│ └── evals/ # Team agent quality tests
|
|
154
|
+
│
|
|
155
|
+
└── .sync-config.json # Tracks synced paths and team namespace
|
|
156
|
+
|
|
157
|
+
.aw_docs/ # Local orchestration state
|
|
158
|
+
├── STATE.md # Active run, recent history
|
|
159
|
+
├── config.json # Sync settings
|
|
160
|
+
├── runs/<run-id>/ # Per-run state
|
|
161
|
+
│ ├── RUN.md # Run metadata + execution log
|
|
162
|
+
│ ├── steps/<NN>-<name>.md # Per-step state (YAML frontmatter)
|
|
163
|
+
│ └── transparency/ # _transparency JSONs
|
|
164
|
+
├── learnings/<agent>.md # Accumulated learnings per agent
|
|
165
|
+
├── learnings/_pending-sync.jsonl # Sync queue → MCP memory/store
|
|
166
|
+
└── tasks/BOARD.md # Task board
|
|
167
|
+
\`\`\`
|
|
168
|
+
|
|
169
|
+
Symlinked to \`.claude/{agents,skills,commands/aw,blueprints,evals}\`.
|
|
170
|
+
|
|
171
|
+
## Local-First Operations
|
|
172
|
+
|
|
173
|
+
All orchestration is file-based. No MCP calls for orchestration.
|
|
174
|
+
|
|
175
|
+
| Operation | How |
|
|
176
|
+
|-----------|-----|
|
|
177
|
+
| **Load agent** | Read \`.aw_registry/<ns>/agents/<slug>.md\` → skills list + identity |
|
|
178
|
+
| **Load skill** | Read \`.aw_registry/<ns>/skills/<slug>/SKILL.md\` → full body |
|
|
179
|
+
| **Create run** | Generate \`run-YYYYMMDDTHHMMSS\`, write \`.aw_docs/runs/<id>/RUN.md\` + step files |
|
|
180
|
+
| **Step result** | Update step .md frontmatter + write transparency JSON |
|
|
181
|
+
| **Close run** | Update RUN.md, append \`.aw_docs/learnings/\`, sync queue, eager \`memory/store\` |
|
|
182
|
+
| **Track task** | Update \`.aw_docs/tasks/BOARD.md\` YAML frontmatter |
|
|
183
|
+
| **Learn** | Append to \`.aw_docs/learnings/<agent>.md\` + \`_pending-sync.jsonl\` |
|
|
184
|
+
| **Recall** | Read \`.aw_docs/learnings/<agent>.md\` + MCP \`memory/search\` |
|
|
185
|
+
| **Discover agents** | Read \`.aw_registry/<ns>/\` or \`ls .claude/agents/\` |
|
|
186
|
+
|
|
187
|
+
## MCP (Remote Only)
|
|
188
|
+
|
|
189
|
+
Only these MCP calls are used:
|
|
190
|
+
|
|
191
|
+
\`\`\`
|
|
192
|
+
memory/search → Search shared team knowledge base
|
|
193
|
+
memory/store → Push learnings to shared knowledge (eager sync after runs)
|
|
194
|
+
memory/get → Fetch specific memory by ID
|
|
195
|
+
grafana/* → External observability
|
|
196
|
+
git-jenkins/* → External CI/CD pipelines
|
|
197
|
+
stitch/* → External design generation
|
|
198
|
+
\`\`\`
|
|
199
|
+
|
|
200
|
+
## Symlink Naming
|
|
201
|
+
|
|
202
|
+
All symlinks are prefixed with their namespace (\`ghl-\` or \`${team}-\`):
|
|
203
|
+
- \`.aw_registry/ghl/agents/security-reviewer.md\` → \`.claude/agents/ghl-security-reviewer.md\`
|
|
204
|
+
- \`.aw_registry/${team}/agents/frontend-developer.md\` → \`.claude/agents/${team}-frontend-developer.md\`
|
|
205
|
+
- \`.aw_registry/${team}/commands/ship.md\` → \`.claude/commands/aw/${team}-ship.md\`
|
|
206
|
+
|
|
207
|
+
## Dependency Rule
|
|
208
|
+
|
|
209
|
+
- **ghl/ is self-contained** — platform agents reference only platform skills
|
|
210
|
+
- **${team}/ can reference ghl/** — team agents use \`ghl-*\` prefixed skill names
|
|
211
|
+
- **ghl/ never references ${team}/** — platform layer knows nothing about team-specific resources
|
|
212
|
+
|
|
213
|
+
## How Agents Connect to Skills
|
|
214
|
+
|
|
215
|
+
Agents declare skills in frontmatter \`skills:\` list:
|
|
216
|
+
\`\`\`yaml
|
|
217
|
+
# ghl/agents/security-reviewer.md — platform skills directly
|
|
218
|
+
skills:
|
|
219
|
+
- security-review
|
|
220
|
+
- platform-services-authentication-authorization
|
|
221
|
+
|
|
222
|
+
# ${team}/agents/frontend-developer.md — ghl- prefix for platform skills
|
|
223
|
+
skills:
|
|
224
|
+
- ghl-vue-development # → ghl/skills/vue-development
|
|
225
|
+
- create-prd # → ${team}/skills/create-prd (local)
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
## Quick Reference
|
|
229
|
+
|
|
230
|
+
| Action | Command |
|
|
231
|
+
|--------|---------|
|
|
232
|
+
| Discover commands | Type \`/aw:\` and autocomplete |
|
|
233
|
+
| List all agents | \`ls .claude/agents/\` |
|
|
234
|
+
| List all skills | \`ls .claude/skills/\` |
|
|
235
|
+
| Pull updates | \`aw pull\` |
|
|
236
|
+
| Push changes | \`aw push <path>\` |
|
|
237
|
+
| Workspace status | \`aw status\` |
|
|
238
|
+
| Reset workspace | \`aw nuke\` |
|
|
239
|
+
|
|
240
|
+
## Workflow: 11 Quality Gates
|
|
241
|
+
|
|
242
|
+
\`\`\`
|
|
243
|
+
Search → Intake → Architecture → Design → Implementation (Ralph Loop) →
|
|
244
|
+
Testing → QA Validation → Code Review (5 parallel) → PR → Staging → Production
|
|
245
|
+
\`\`\`
|
|
246
|
+
|
|
247
|
+
Gates are shell scripts in \`scripts/gates/\` — CANNOT be bypassed by LLM judgment.
|
|
248
|
+
|
|
249
|
+
## Cost Optimization Rules
|
|
250
|
+
|
|
251
|
+
- **Commands**: Use parameterized single commands, not variant files
|
|
252
|
+
- **Skills**: Link to \`platform-docs/content/...\`, max 60 lines inline
|
|
253
|
+
- **Agents**: Definitions ≤5 KB, reference material in linked skills
|
|
254
|
+
- **Local-first**: No MCP calls for orchestration — file reads are faster and always available
|
|
255
|
+
`;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function generateAgentsMd(cwd, namespace) {
|
|
259
|
+
const team = namespace || 'my-team';
|
|
260
|
+
return `# AGENTS.md — ${team}
|
|
261
|
+
|
|
262
|
+
## Agent System
|
|
263
|
+
|
|
264
|
+
Two-layer agent system with local-first orchestration. Agents are loaded from \`.aw_registry/\` (symlinked to \`.claude/agents/\`).
|
|
265
|
+
|
|
266
|
+
### How Agents Are Loaded (Local-First)
|
|
267
|
+
|
|
268
|
+
Commands load agents entirely from local files — no MCP calls for agent/skill loading:
|
|
269
|
+
|
|
270
|
+
\`\`\`
|
|
271
|
+
1. Read .aw_registry/ghl/agents/<slug>.md → Agent identity, skills[] list, capabilities
|
|
272
|
+
OR .aw_registry/${team}/agents/<slug>.md
|
|
273
|
+
|
|
274
|
+
2. For each skill in the agent's skills[] frontmatter:
|
|
275
|
+
Read .aw_registry/ghl/skills/<slug>/SKILL.md → Full skill body (patterns, checklists)
|
|
276
|
+
OR .aw_registry/${team}/skills/<slug>/SKILL.md
|
|
277
|
+
|
|
278
|
+
3. Read .aw_docs/learnings/<slug>.md → Prior learnings from past runs
|
|
279
|
+
|
|
280
|
+
4. Call MCP memory/search({query: "..."}) → Recalled memories (only MCP call)
|
|
281
|
+
\`\`\`
|
|
282
|
+
|
|
283
|
+
### Platform Agents (ghl-)
|
|
284
|
+
|
|
285
|
+
Shared infrastructure agents — always available, self-contained. Loaded from \`.aw_registry/ghl/agents/\`.
|
|
286
|
+
|
|
287
|
+
| Agent | Role | Model Tier |
|
|
288
|
+
|-------|------|------------|
|
|
289
|
+
| \`ghl-architecture-reviewer\` | Architecture review, ADR compliance, coupling detection | Sonnet |
|
|
290
|
+
| \`ghl-security-reviewer\` | Security review, auth patterns, tenant isolation | Sonnet |
|
|
291
|
+
| \`ghl-performance-reviewer\` | Performance review, bundle analysis, N+1 detection | Sonnet |
|
|
292
|
+
| \`ghl-frontend-engineer\` | MFA architecture, event-bus, shared packages | Sonnet |
|
|
293
|
+
| \`ghl-services-engineer\` | Inter-service comms, workers, circuit breakers | Sonnet |
|
|
294
|
+
| \`ghl-infra-engineer\` | Kubernetes, Terraform, Jenkins, deployment | Sonnet |
|
|
295
|
+
| \`ghl-database-engineer\` | MongoDB, Redis, Elasticsearch, Firestore, migrations | Sonnet |
|
|
296
|
+
| \`ghl-product-designer\` | HighRise design system, accessibility, typography | Sonnet |
|
|
297
|
+
| \`ghl-frontend-core-reviewer\` | Vue 3 patterns, composables, Pinia stores | Sonnet |
|
|
298
|
+
| \`ghl-frontend-i18n-reviewer\` | i18n compliance, translation keys | Haiku |
|
|
299
|
+
| \`ghl-maintainability-reviewer\` | Code duplication, naming, complexity | Haiku |
|
|
300
|
+
| \`ghl-reliability-reviewer\` | Error handling, null guards, SSR safety | Haiku |
|
|
301
|
+
| \`ghl-sdet-engineer\` | Playwright POM, quality gates, performance testing | Haiku |
|
|
302
|
+
| \`ghl-devops-automator\` | Docker, Jenkins pipelines, deployment strategies | Haiku |
|
|
303
|
+
| \`ghl-data-engineer\` | CDC pipelines, ClickHouse, analytics data models | Haiku |
|
|
304
|
+
|
|
305
|
+
### Team Agents (${team}-)
|
|
306
|
+
|
|
307
|
+
Team-specific agents — customizable. Loaded from \`.aw_registry/${team}/agents/\`. Can reference platform skills via \`ghl-*\` prefix.
|
|
308
|
+
|
|
309
|
+
| Agent | Role | Model Tier |
|
|
310
|
+
|-------|------|------------|
|
|
311
|
+
| \`${team}-coordinator\` | Task decomposition, quality gate enforcement, orchestration | Opus |
|
|
312
|
+
| \`${team}-architect\` | ADR creation, data flow design, system architecture | Opus |
|
|
313
|
+
| \`${team}-product-manager\` | PRDs, feature prioritization, sprint planning | Opus |
|
|
314
|
+
| \`${team}-frontend-developer\` | Vue components, composables, TypeScript | Sonnet |
|
|
315
|
+
| \`${team}-backend-developer\` | NestJS services, API endpoints, workers | Sonnet |
|
|
316
|
+
| \`${team}-manager\` | Spec writing, user stories, team coordination | Sonnet |
|
|
317
|
+
| \`${team}-unit-tester\` | Component tests, composable tests, BDD | Haiku |
|
|
318
|
+
| \`${team}-e2e-tester\` | Cypress tests, Gherkin scenarios | Haiku |
|
|
319
|
+
| \`${team}-mutation-tester\` | Stryker mutation analysis, test improvement | Haiku |
|
|
320
|
+
| \`${team}-release-engineer\` | Release checklists, rollback plans, deploy | Sonnet |
|
|
321
|
+
| \`${team}-design-reviewer\` | Design token audit, accessibility review | Haiku |
|
|
322
|
+
| \`${team}-design-auditor\` | Dark mode audit, design token verification | Haiku |
|
|
323
|
+
| \`${team}-technical-writer\` | API docs, migration guides | Haiku |
|
|
324
|
+
| \`${team}-api-tester\` | API contract tests, auth flow tests | Haiku |
|
|
325
|
+
| \`${team}-visual-tester\` | Screenshot comparison, visual test plans | Haiku |
|
|
326
|
+
| \`${team}-responsive-tester\` | Breakpoint audit, touch target compliance | Haiku |
|
|
327
|
+
| \`${team}-performance-benchmarker\` | Core Web Vitals, k6 scripts | Haiku |
|
|
328
|
+
| \`${team}-stitch-designer\` | Multi-screen flows, screen prompts | Haiku |
|
|
329
|
+
| \`${team}-animation-specialist\` | Transitions, reduced motion compliance | Haiku |
|
|
330
|
+
| \`${team}-ux-researcher\` | Interview scripts, usability test plans | Haiku |
|
|
331
|
+
| \`${team}-onboarding-optimizer\` | Empty state design, onboarding flow critique | Haiku |
|
|
332
|
+
|
|
333
|
+
### Skill Resolution
|
|
334
|
+
|
|
335
|
+
Agents declare skills in their frontmatter \`skills:\` list. Commands read SKILL.md files directly from disk:
|
|
336
|
+
|
|
337
|
+
- **No prefix** → \`.aw_registry/${team}/skills/<name>/SKILL.md\`
|
|
338
|
+
- **\`ghl-\` prefix** → \`.aw_registry/ghl/skills/<name-without-ghl->/SKILL.md\`
|
|
339
|
+
|
|
340
|
+
\`\`\`yaml
|
|
341
|
+
# .aw_registry/ghl/agents/security-reviewer.md — platform skills directly
|
|
342
|
+
skills:
|
|
343
|
+
- security-review # → .aw_registry/ghl/skills/security-review/SKILL.md
|
|
344
|
+
- platform-services-authentication-authorization # → .aw_registry/ghl/skills/platform-services-.../SKILL.md
|
|
345
|
+
|
|
346
|
+
# .aw_registry/${team}/agents/frontend-developer.md — ghl- prefix for platform skills
|
|
347
|
+
skills:
|
|
348
|
+
- ghl-vue-development # → .aw_registry/ghl/skills/vue-development/SKILL.md
|
|
349
|
+
- create-prd # → .aw_registry/${team}/skills/create-prd/SKILL.md
|
|
350
|
+
\`\`\`
|
|
351
|
+
|
|
352
|
+
### How to Invoke
|
|
353
|
+
|
|
354
|
+
\`\`\`
|
|
355
|
+
# Via workflow commands (recommended — orchestrates multiple agents with .aw_docs/ tracking)
|
|
356
|
+
/aw:${team}-ship # Full ship: research → spec → design → plan → implement → test → review → deploy
|
|
357
|
+
/aw:${team}-work # Implementation: 6 agents, parallel DB/frontend, quality review + test
|
|
358
|
+
/aw:${team}-plan # Architecture + PM planning
|
|
359
|
+
/aw:${team}-review-all # Parallel code review with 10 reviewers in 3 waves
|
|
360
|
+
/aw:${team}-brainstorm # Requirement exploration with architect + PM
|
|
361
|
+
|
|
362
|
+
# Via Agent tool (direct invocation — no run tracking)
|
|
363
|
+
Agent(subagent_type: "ghl:security-reviewer", prompt: "Review this PR for security issues")
|
|
364
|
+
Agent(subagent_type: "${team}:frontend-developer", prompt: "Create a Vue component for...")
|
|
365
|
+
\`\`\`
|
|
366
|
+
|
|
367
|
+
### Learnings & Knowledge
|
|
368
|
+
|
|
369
|
+
Agent knowledge accumulates across runs via \`.aw_docs/learnings/\`:
|
|
370
|
+
|
|
371
|
+
\`\`\`
|
|
372
|
+
.aw_docs/learnings/
|
|
373
|
+
├── backend-developer.md # Learnings from all runs where this agent worked
|
|
374
|
+
├── frontend-developer.md # Appended after each workflow close
|
|
375
|
+
├── architecture-reviewer.md # Prior learnings loaded before each run
|
|
376
|
+
└── _pending-sync.jsonl # Queue for eager sync to MCP memory/store
|
|
377
|
+
\`\`\`
|
|
378
|
+
|
|
379
|
+
Before each run, prior learnings are read from these files and injected into agent prompts. After each run, new learnings are appended. Optionally synced to shared MCP memory for cross-project recall.
|
|
380
|
+
|
|
381
|
+
### Evals
|
|
382
|
+
|
|
383
|
+
Each agent has quality test cases in \`.claude/evals/\`:
|
|
384
|
+
\`\`\`
|
|
385
|
+
.claude/evals/ghl-agents-security-reviewer/ # Platform agent evals
|
|
386
|
+
.claude/evals/${team}-agents-frontend-developer/ # Team agent evals
|
|
387
|
+
.claude/evals/ghl-skills-vue-development/ # Skill evals
|
|
388
|
+
\`\`\`
|
|
389
|
+
|
|
390
|
+
Run with: \`/ghl:eval agent:<slug>\` or \`/ghl:eval skill:<slug>\`
|
|
391
|
+
`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Initialize .aw_docs/ directory for local-first orchestration state.
|
|
396
|
+
* Creates run tracking, learnings, task board, and cache directories.
|
|
397
|
+
*/
|
|
398
|
+
export function initAwDocs(cwd) {
|
|
399
|
+
const awDocsDir = join(cwd, '.aw_docs');
|
|
400
|
+
if (existsSync(awDocsDir)) return; // Already initialized
|
|
401
|
+
|
|
402
|
+
const dirs = [
|
|
403
|
+
'.aw_docs/runs',
|
|
404
|
+
'.aw_docs/learnings',
|
|
405
|
+
'.aw_docs/tasks',
|
|
406
|
+
'.aw_docs/cache',
|
|
407
|
+
];
|
|
408
|
+
|
|
409
|
+
for (const dir of dirs) {
|
|
410
|
+
mkdirSync(join(cwd, dir), { recursive: true });
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// STATE.md — workspace state
|
|
414
|
+
writeFileSync(join(awDocsDir, 'STATE.md'), `---
|
|
415
|
+
active_run: null
|
|
416
|
+
last_run: null
|
|
417
|
+
last_workflow: null
|
|
418
|
+
last_agent: null
|
|
419
|
+
updated_at: null
|
|
420
|
+
pending_sync_count: 0
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
# Workspace State
|
|
424
|
+
|
|
425
|
+
No active runs. Use \`/aw:<team>-<command>\` to start a workflow.
|
|
426
|
+
`);
|
|
427
|
+
|
|
428
|
+
// config.json — registry paths, sync settings
|
|
429
|
+
writeFileSync(join(awDocsDir, 'config.json'), JSON.stringify({
|
|
430
|
+
sync: {
|
|
431
|
+
eager: true,
|
|
432
|
+
batch_threshold: 10,
|
|
433
|
+
mcp_memory_enabled: true,
|
|
434
|
+
},
|
|
435
|
+
paths: {
|
|
436
|
+
runs: '.aw_docs/runs',
|
|
437
|
+
learnings: '.aw_docs/learnings',
|
|
438
|
+
tasks: '.aw_docs/tasks',
|
|
439
|
+
cache: '.aw_docs/cache',
|
|
440
|
+
},
|
|
441
|
+
}, null, 2) + '\n');
|
|
442
|
+
|
|
443
|
+
// BOARD.md — task board
|
|
444
|
+
writeFileSync(join(awDocsDir, 'tasks', 'BOARD.md'), `---
|
|
445
|
+
tasks: []
|
|
446
|
+
updated_at: null
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
# Task Board
|
|
450
|
+
|
|
451
|
+
No active tasks. Tasks are created during workflow execution.
|
|
452
|
+
`);
|
|
453
|
+
|
|
454
|
+
// _pending-sync.jsonl — sync queue (empty)
|
|
455
|
+
writeFileSync(join(awDocsDir, 'learnings', '_pending-sync.jsonl'), '');
|
|
456
|
+
|
|
457
|
+
fmt.logSuccess('Created .aw_docs/ (local orchestration state)');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function listNamespaceDirs(awDir) {
|
|
461
|
+
if (!existsSync(awDir)) return [];
|
|
462
|
+
return readdirSync(awDir, { withFileTypes: true })
|
|
463
|
+
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
464
|
+
.map(d => d.name);
|
|
465
|
+
}
|
|
466
|
+
|
package/link.mjs
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// link.mjs — Create symlinks from IDE dirs → .aw_registry/
|
|
2
|
+
|
|
3
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
|
|
4
|
+
import { join, relative } from 'node:path';
|
|
5
|
+
import { execSync } from 'node:child_process';
|
|
6
|
+
import * as fmt from './fmt.mjs';
|
|
7
|
+
|
|
8
|
+
const IDE_DIRS = ['.claude', '.cursor', '.codex'];
|
|
9
|
+
// Per-file symlink types
|
|
10
|
+
const FILE_TYPES = ['agents', 'blueprints'];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* List namespace directories inside .aw_registry/ (skip dotfiles).
|
|
14
|
+
*/
|
|
15
|
+
function listNamespaceDirs(awDir) {
|
|
16
|
+
if (!existsSync(awDir)) return [];
|
|
17
|
+
return readdirSync(awDir, { withFileTypes: true })
|
|
18
|
+
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
19
|
+
.map(d => d.name);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* List subdirectories (non-dot) in a directory.
|
|
24
|
+
*/
|
|
25
|
+
function listDirs(dir) {
|
|
26
|
+
if (!existsSync(dir)) return [];
|
|
27
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
28
|
+
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
29
|
+
.map(d => d.name);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Clean all symlinks from IDE type directories before re-linking.
|
|
34
|
+
*/
|
|
35
|
+
function cleanIdeSymlinks(cwd) {
|
|
36
|
+
for (const ide of IDE_DIRS) {
|
|
37
|
+
for (const type of [...FILE_TYPES, 'skills', 'evals']) {
|
|
38
|
+
const dir = join(cwd, ide, type);
|
|
39
|
+
if (!existsSync(dir)) continue;
|
|
40
|
+
for (const entry of readdirSync(dir)) {
|
|
41
|
+
const p = join(dir, entry);
|
|
42
|
+
try {
|
|
43
|
+
if (lstatSync(p).isSymbolicLink()) unlinkSync(p);
|
|
44
|
+
} catch { /* best effort */ }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Clean command symlinks (both flat legacy and namespaced aw/)
|
|
48
|
+
const cmdDir = join(cwd, ide, 'commands');
|
|
49
|
+
if (!existsSync(cmdDir)) continue;
|
|
50
|
+
for (const entry of readdirSync(cmdDir)) {
|
|
51
|
+
const p = join(cmdDir, entry);
|
|
52
|
+
try {
|
|
53
|
+
if (lstatSync(p).isSymbolicLink()) unlinkSync(p);
|
|
54
|
+
} catch { /* best effort */ }
|
|
55
|
+
}
|
|
56
|
+
// Clean inside commands/aw/ namespace dir
|
|
57
|
+
const awCmdDir = join(cmdDir, 'aw');
|
|
58
|
+
if (existsSync(awCmdDir)) {
|
|
59
|
+
for (const entry of readdirSync(awCmdDir)) {
|
|
60
|
+
const p = join(awCmdDir, entry);
|
|
61
|
+
try {
|
|
62
|
+
if (lstatSync(p).isSymbolicLink()) unlinkSync(p);
|
|
63
|
+
} catch { /* best effort */ }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Compute flat IDE name: namespace-slug (always includes namespace).
|
|
71
|
+
*/
|
|
72
|
+
function flatName(ns, name) {
|
|
73
|
+
return `${ns}-${name}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create/refresh all IDE symlinks.
|
|
78
|
+
*
|
|
79
|
+
* - agents, blueprints: per-file symlinks with flat names
|
|
80
|
+
* - skills: per-skill directory symlinks with flat names
|
|
81
|
+
* - commands: directory symlink per namespace → .claude/commands/<ns>/
|
|
82
|
+
* (Claude Code needs a directory at .claude/commands/<ns>/ for /<ns>:* autocomplete)
|
|
83
|
+
*/
|
|
84
|
+
export function linkWorkspace(cwd) {
|
|
85
|
+
const awDir = join(cwd, '.aw_registry');
|
|
86
|
+
if (!existsSync(awDir)) return 0;
|
|
87
|
+
|
|
88
|
+
let created = 0;
|
|
89
|
+
|
|
90
|
+
// Clean old symlinks first
|
|
91
|
+
cleanIdeSymlinks(cwd);
|
|
92
|
+
|
|
93
|
+
const namespaces = listNamespaceDirs(awDir);
|
|
94
|
+
|
|
95
|
+
for (const ns of namespaces) {
|
|
96
|
+
// Per-file types: agents, blueprints
|
|
97
|
+
for (const type of FILE_TYPES) {
|
|
98
|
+
const typeDir = join(awDir, ns, type);
|
|
99
|
+
if (!existsSync(typeDir)) continue;
|
|
100
|
+
|
|
101
|
+
for (const file of readdirSync(typeDir).filter(f => f.endsWith('.md') && !f.startsWith('.'))) {
|
|
102
|
+
const flat = flatName(ns, file);
|
|
103
|
+
|
|
104
|
+
for (const ide of IDE_DIRS) {
|
|
105
|
+
const linkDir = join(cwd, ide, type);
|
|
106
|
+
mkdirSync(linkDir, { recursive: true });
|
|
107
|
+
const linkPath = join(linkDir, flat);
|
|
108
|
+
const targetPath = join(awDir, ns, type, file);
|
|
109
|
+
const relTarget = relative(linkDir, targetPath);
|
|
110
|
+
try {
|
|
111
|
+
execSync(`ln -sfn "${relTarget}" "${linkPath}"`, { stdio: 'pipe' });
|
|
112
|
+
created++;
|
|
113
|
+
} catch { /* best effort */ }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Skills: per-skill directory symlinks
|
|
119
|
+
const skillsDir = join(awDir, ns, 'skills');
|
|
120
|
+
if (!existsSync(skillsDir)) continue;
|
|
121
|
+
for (const skill of listDirs(skillsDir)) {
|
|
122
|
+
const flat = flatName(ns, skill);
|
|
123
|
+
|
|
124
|
+
for (const ide of IDE_DIRS) {
|
|
125
|
+
const linkDir = join(cwd, ide, 'skills');
|
|
126
|
+
mkdirSync(linkDir, { recursive: true });
|
|
127
|
+
const linkPath = join(linkDir, flat);
|
|
128
|
+
const targetPath = join(skillsDir, skill);
|
|
129
|
+
const relTarget = relative(linkDir, targetPath);
|
|
130
|
+
try {
|
|
131
|
+
execSync(`ln -sfn "${relTarget}" "${linkPath}"`, { stdio: 'pipe' });
|
|
132
|
+
created++;
|
|
133
|
+
} catch { /* best effort */ }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Evals: per-eval-dir symlinks (evals/agents/<name>/, evals/skills/<name>/)
|
|
139
|
+
for (const ns of namespaces) {
|
|
140
|
+
const evalsDir = join(awDir, ns, 'evals');
|
|
141
|
+
if (!existsSync(evalsDir)) continue;
|
|
142
|
+
|
|
143
|
+
for (const subType of listDirs(evalsDir)) {
|
|
144
|
+
const subDir = join(evalsDir, subType);
|
|
145
|
+
for (const evalName of listDirs(subDir)) {
|
|
146
|
+
const flat = `${ns}-${subType}-${evalName}`;
|
|
147
|
+
|
|
148
|
+
for (const ide of IDE_DIRS) {
|
|
149
|
+
const linkDir = join(cwd, ide, 'evals');
|
|
150
|
+
mkdirSync(linkDir, { recursive: true });
|
|
151
|
+
const linkPath = join(linkDir, flat);
|
|
152
|
+
const targetPath = join(subDir, evalName);
|
|
153
|
+
const relTarget = relative(linkDir, targetPath);
|
|
154
|
+
try {
|
|
155
|
+
execSync(`ln -sfn "${relTarget}" "${linkPath}"`, { stdio: 'pipe' });
|
|
156
|
+
created++;
|
|
157
|
+
} catch { /* best effort */ }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Codex per-skill symlinks: .agents/skills/<name>
|
|
164
|
+
const agentsSkillsDir = join(cwd, '.agents/skills');
|
|
165
|
+
for (const ns of namespaces) {
|
|
166
|
+
const skillsDir = join(awDir, ns, 'skills');
|
|
167
|
+
if (!existsSync(skillsDir)) continue;
|
|
168
|
+
mkdirSync(agentsSkillsDir, { recursive: true });
|
|
169
|
+
for (const skill of listDirs(skillsDir)) {
|
|
170
|
+
const flat = flatName(ns, skill);
|
|
171
|
+
const linkPath = join(agentsSkillsDir, flat);
|
|
172
|
+
const targetPath = join(skillsDir, skill);
|
|
173
|
+
const relTarget = relative(agentsSkillsDir, targetPath);
|
|
174
|
+
try {
|
|
175
|
+
execSync(`ln -sfn "${relTarget}" "${linkPath}"`, { stdio: 'pipe' });
|
|
176
|
+
created++;
|
|
177
|
+
} catch { /* best effort */ }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Commands: per-file symlinks from <ns>/commands/ → .claude/commands/aw/
|
|
182
|
+
// All commands (hand-written + generated) live in namespace/commands/ as single source of truth
|
|
183
|
+
for (const ns of namespaces) {
|
|
184
|
+
const commandsDir = join(awDir, ns, 'commands');
|
|
185
|
+
if (!existsSync(commandsDir)) continue;
|
|
186
|
+
|
|
187
|
+
for (const file of readdirSync(commandsDir).filter(f => f.endsWith('.md') && !f.startsWith('.'))) {
|
|
188
|
+
const cmdFileName = `${ns}-${file}`;
|
|
189
|
+
|
|
190
|
+
for (const ide of IDE_DIRS) {
|
|
191
|
+
const linkDir = join(cwd, ide, 'commands', 'aw');
|
|
192
|
+
mkdirSync(linkDir, { recursive: true });
|
|
193
|
+
const linkPath = join(linkDir, cmdFileName);
|
|
194
|
+
const targetPath = join(commandsDir, file);
|
|
195
|
+
const relTarget = relative(linkDir, targetPath);
|
|
196
|
+
try {
|
|
197
|
+
execSync(`ln -sfn "${relTarget}" "${linkPath}"`, { stdio: 'pipe' });
|
|
198
|
+
created++;
|
|
199
|
+
} catch { /* best effort */ }
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (created > 0) {
|
|
205
|
+
fmt.logSuccess(`Linked ${created} IDE symlink${created > 1 ? 's' : ''}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return created;
|
|
209
|
+
}
|