@amsterdamdatalabs/enact-extensions 0.1.5 → 0.1.10

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 (105) hide show
  1. package/README.md +2 -2
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/install.d.ts.map +1 -1
  7. package/dist/install.js +15 -1
  8. package/dist/install.js.map +1 -1
  9. package/dist/internal/agents.d.ts +29 -0
  10. package/dist/internal/agents.d.ts.map +1 -0
  11. package/dist/internal/agents.js +83 -0
  12. package/dist/internal/agents.js.map +1 -0
  13. package/extensions/enact-context/hooks/hooks.json +20 -0
  14. package/extensions/enact-core/.agents/plugin.json +39 -0
  15. package/extensions/enact-core/hooks/hooks.json +14 -0
  16. package/extensions/enact-factory/.agents/plugin.json +9 -3
  17. package/extensions/enact-factory/agents/architect.toml +30 -0
  18. package/extensions/enact-factory/agents/code-reviewer.toml +29 -0
  19. package/extensions/enact-factory/agents/critic.toml +35 -0
  20. package/extensions/enact-factory/agents/executor.toml +23 -0
  21. package/extensions/enact-factory/agents/explore.toml +22 -0
  22. package/extensions/enact-factory/agents/planner.toml +23 -0
  23. package/extensions/enact-factory/agents/verifier.toml +29 -0
  24. package/extensions/enact-factory/skills/ai-slop-cleaner/SKILL.md +52 -0
  25. package/extensions/enact-factory/skills/azdo-ci-strategy/SKILL.md +262 -0
  26. package/extensions/enact-factory/skills/azdo-ci-strategy/references/build-failures.md +60 -0
  27. package/extensions/enact-factory/skills/azdo-ci-strategy/references/cli-reference.md +87 -0
  28. package/extensions/enact-factory/skills/azdo-ci-strategy/references/policies-and-pipelines.md +132 -0
  29. package/extensions/enact-factory/skills/azdo-ci-strategy/references/troubleshooting.md +53 -0
  30. package/extensions/enact-factory/skills/deep-interview/SKILL.md +72 -0
  31. package/extensions/enact-factory/skills/drive-loop/SKILL.md +259 -0
  32. package/extensions/enact-factory/skills/drive-loop/references/contract-schema.md +107 -0
  33. package/extensions/enact-factory/skills/hyperplan/SKILL.md +51 -0
  34. package/extensions/enact-factory/skills/looplan/SKILL.md +103 -0
  35. package/extensions/enact-factory/skills/plan/SKILL.md +71 -0
  36. package/extensions/enact-factory/skills/remove-deadcode/SKILL.md +41 -0
  37. package/extensions/enact-factory/skills/research/SKILL.md +73 -0
  38. package/extensions/enact-factory/skills/review/SKILL.md +48 -0
  39. package/extensions/enact-factory/skills/security-research/SKILL.md +54 -0
  40. package/extensions/enact-factory/skills/tdd/SKILL.md +56 -0
  41. package/extensions/enact-factory/skills/trace/SKILL.md +37 -0
  42. package/extensions/enact-factory/skills/ultraqa/SKILL.md +79 -0
  43. package/extensions/enact-factory/skills/work-with-workitem/SKILL.md +51 -0
  44. package/extensions/enact-factory/skills/workitem-triage/SKILL.md +15 -0
  45. package/extensions/enact-loop/.agents/plugin.json +46 -0
  46. package/extensions/enact-loop/.mcp.json +1 -0
  47. package/extensions/enact-loop/hooks/hooks.json +27 -0
  48. package/extensions/enact-loop/skills/enact-loop/SKILL.md +327 -0
  49. package/extensions/enact-operator/.agents/plugin.json +0 -1
  50. package/extensions/enact-operator/hooks/hooks.json +0 -35
  51. package/extensions/enact-wiki/skills/wiki/SKILL.md +42 -0
  52. package/extensions/plugin-dev/.agents/plugin.json +4 -6
  53. package/extensions/plugin-dev/agents/plugin-validator.md +1 -1
  54. package/extensions/plugin-dev/skills/agent-development/SKILL.md +7 -7
  55. package/extensions/plugin-dev/{commands/create-plugin.md → skills/create-plugin/SKILL.md} +44 -37
  56. package/extensions/plugin-dev/skills/plugin-dev-guide/SKILL.md +13 -14
  57. package/extensions/plugin-dev/skills/skill-development/SKILL.md +0 -2
  58. package/extensions/plugin-dev/{commands/start.md → skills/start/SKILL.md} +7 -6
  59. package/package.json +11 -6
  60. package/scripts/check-hooks.mjs +174 -0
  61. package/scripts/check-principles.mjs +101 -0
  62. package/scripts/enact-extensions.mjs +87 -3
  63. package/scripts/lib/run-validate.mjs +36 -2
  64. package/scripts/lib/ups-router.mjs +432 -0
  65. package/spec/enact.json +4 -0
  66. package/spec/enact.md +5 -2
  67. package/extensions/cmux/.agents/plugin.json +0 -37
  68. package/extensions/cmux/skills/cmux/SKILL.md +0 -82
  69. package/extensions/cmux/skills/cmux/agents/openai.yaml +0 -4
  70. package/extensions/cmux/skills/cmux/references/handles-and-identify.md +0 -35
  71. package/extensions/cmux/skills/cmux/references/panes-surfaces.md +0 -37
  72. package/extensions/cmux/skills/cmux/references/trigger-flash-and-health.md +0 -23
  73. package/extensions/cmux/skills/cmux/references/windows-workspaces.md +0 -31
  74. package/extensions/cmux/skills/cmux-vm-monitor/SKILL.md +0 -122
  75. package/extensions/cmux/skills/cmux-vm-monitor/agents/openai.yaml +0 -4
  76. package/extensions/cmux/skills/cmux-vm-monitor/references/cmux-commands.md +0 -66
  77. package/extensions/cmux/skills/cmux-vm-monitor/scripts/codex_vm_monitor.sh +0 -45
  78. package/extensions/cmux/skills/cmux-workspace/SKILL.md +0 -93
  79. package/extensions/devops/.agents/plugin.json +0 -36
  80. package/extensions/devops/skills/azure-devops-cli/SKILL.md +0 -431
  81. package/extensions/devops/skills/azure-devops-cli/agents/openai.yaml +0 -4
  82. package/extensions/devops/skills/ci-pipeline-strategy/SKILL.md +0 -217
  83. package/extensions/devops/skills/ci-pipeline-strategy/agents/openai.yaml +0 -4
  84. package/extensions/enact-factory/hooks/user-prompt-submit.mjs +0 -67
  85. package/extensions/enact-operator/commands/doctor.md +0 -39
  86. package/extensions/enact-operator/commands/setup.md +0 -51
  87. package/extensions/plugin-dev/.mcp.json +0 -3
  88. package/extensions/plugin-dev/commands/_archive/create-marketplace.md +0 -427
  89. package/extensions/plugin-dev/commands/_archive/plugin-dev-guide.md +0 -12
  90. package/extensions/plugin-dev/hooks/hooks.json +0 -3
  91. package/extensions/plugin-dev/skills/command-development/SKILL.md +0 -763
  92. package/extensions/plugin-dev/skills/command-development/examples/plugin-commands.md +0 -612
  93. package/extensions/plugin-dev/skills/command-development/examples/simple-commands.md +0 -527
  94. package/extensions/plugin-dev/skills/command-development/references/advanced-workflows.md +0 -762
  95. package/extensions/plugin-dev/skills/command-development/references/documentation-patterns.md +0 -769
  96. package/extensions/plugin-dev/skills/command-development/references/frontmatter-reference.md +0 -508
  97. package/extensions/plugin-dev/skills/command-development/references/interactive-commands.md +0 -966
  98. package/extensions/plugin-dev/skills/command-development/references/marketplace-considerations.md +0 -943
  99. package/extensions/plugin-dev/skills/command-development/references/plugin-features-reference.md +0 -637
  100. package/extensions/plugin-dev/skills/command-development/references/plugin-integration.md +0 -191
  101. package/extensions/plugin-dev/skills/command-development/references/skill-tool.md +0 -447
  102. package/extensions/plugin-dev/skills/command-development/references/testing-strategies.md +0 -723
  103. package/extensions/plugin-dev/skills/command-development/scripts/check-frontmatter.sh +0 -234
  104. package/extensions/plugin-dev/skills/command-development/scripts/validate-command.sh +0 -160
  105. /package/extensions/enact-operator/{skills/_variants.md → docs/skill-variants.md} +0 -0
@@ -1,12 +1,18 @@
1
1
  ---
2
- description: Create plugins with guided 8-phase workflow
3
- argument-hint: [plugin-description]
4
- allowed-tools: Read, Write, Edit, Grep, Glob, Bash(mkdir:*), Bash(git init:*), TaskCreate, TaskGet, TaskUpdate, TaskList, AskUserQuestion, Skill, Task
5
- model: sonnet
2
+ name: create-plugin
3
+ description: >-
4
+ Guided workflow to create a complete Enact plugin from concept to tested
5
+ implementation. Use when someone says "create a plugin", "build a plugin",
6
+ "/create-plugin", or describes plugin functionality they want to scaffold.
6
7
  ---
7
8
 
8
9
  # Plugin Creation Workflow
9
10
 
11
+ > **Commands are skills.** Enact plugins have no separate "commands" component.
12
+ > A user-initiated action (e.g. `/create-plugin`) is an *invocable skill* — a
13
+ > skill whose `SKILL.md` carries an invocable frontmatter and `$ARGUMENTS`.
14
+ > Wherever you'd reach for a "command", create a skill instead.
15
+
10
16
  Guide the user through creating a complete, high-quality Claude Code plugin from initial concept to tested implementation. Follow a systematic approach: understand requirements, design components, clarify details, implement following best practices, validate, and test.
11
17
 
12
18
  ## Core Principles
@@ -55,8 +61,10 @@ Guide the user through creating a complete, high-quality Claude Code plugin from
55
61
 
56
62
  1. Load plugin-structure skill to understand component types
57
63
  2. Analyze plugin requirements and determine needed components:
58
- - **Skills**: Does it need specialized knowledge? (hooks API, MCP patterns, etc.)
59
- - **Commands**: User-initiated actions? (deploy, configure, analyze)
64
+ - **Skills**: Specialized knowledge AND user-initiated actions. A skill can
65
+ be passive (auto-triggered by its description) or *invocable* (a slash
66
+ entry point like `/deploy` carrying `$ARGUMENTS`). There is no separate
67
+ "command" component — invocable skills replace commands.
60
68
  - **Agents**: Autonomous tasks? (validation, generation, analysis)
61
69
  - **Hooks**: Event-driven automation? (validation, notifications)
62
70
  - **MCP**: External service integration? (databases, APIs)
@@ -68,13 +76,13 @@ Guide the user through creating a complete, high-quality Claude Code plugin from
68
76
  - Rough triggering/usage patterns
69
77
  4. Present component plan to user as table:
70
78
  ```
71
- | Component Type | Count | Purpose |
72
- |----------------|-------|---------|
73
- | Skills | 2 | Hook patterns, MCP usage |
74
- | Commands | 3 | Deploy, configure, validate |
75
- | Agents | 1 | Autonomous validation |
76
- | Hooks | 0 | Not needed |
77
- | MCP | 1 | Database integration |
79
+ | Component Type | Count | Purpose |
80
+ |--------------------|-------|---------|
81
+ | Skills (passive) | 2 | Hook patterns, MCP usage |
82
+ | Skills (invocable) | 3 | /deploy, /configure, /validate |
83
+ | Agents | 1 | Autonomous validation |
84
+ | Hooks | 0 | Not needed |
85
+ | MCP | 1 | Database integration |
78
86
  ```
79
87
  5. Get user confirmation or adjustments
80
88
 
@@ -91,8 +99,8 @@ Guide the user through creating a complete, high-quality Claude Code plugin from
91
99
  **Actions**:
92
100
 
93
101
  1. For each component in the plan, identify underspecified aspects:
94
- - **Skills**: What triggers them? What knowledge do they provide? How detailed?
95
- - **Commands**: What arguments? What tools? Interactive or automated?
102
+ - **Skills (passive)**: What triggers them? What knowledge do they provide? How detailed?
103
+ - **Skills (invocable)**: What slash name? What `$ARGUMENTS`? Interactive or automated?
96
104
  - **Agents**: When to trigger (proactive/reactive)? What tools? Output format?
97
105
  - **Hooks**: Which events? Prompt or command based? Validation criteria?
98
106
  - **MCP**: What server type? Authentication? Which tools?
@@ -135,12 +143,11 @@ Guide the user through creating a complete, high-quality Claude Code plugin from
135
143
  3. Create directory structure using bash:
136
144
  ```bash
137
145
  mkdir -p plugin-name/.agents
138
- mkdir -p plugin-name/skills # if needed
139
- mkdir -p plugin-name/commands # if needed
146
+ mkdir -p plugin-name/skills # passive + invocable skills (no commands/ dir)
140
147
  mkdir -p plugin-name/agents # if needed
141
148
  mkdir -p plugin-name/hooks # if needed
142
149
  ```
143
- 4. Create **canonical** `.agents/plugin.json` (see `plugin-structure` skill and `spec/enact.json`):
150
+ 4. Create **canonical** `.agents/plugin.json` (see `plugin-structure` skill and `spec/enact.json`). Declare only the components that exist on disk — an undeclared dir silently fails to install, and a declared-but-empty component (e.g. `"hooks"` pointing at `{ "hooks": {} }`) is a guard error:
144
151
  ```json
145
152
  {
146
153
  "name": "plugin-name",
@@ -148,12 +155,12 @@ Guide the user through creating a complete, high-quality Claude Code plugin from
148
155
  "description": "[brief description]",
149
156
  "targets": ["claude", "codex", "cursor"],
150
157
  "skills": "./skills/",
151
- "commands": "./commands/",
152
158
  "hooks": "./hooks/hooks.json",
153
159
  "mcpServers": "./.mcp.json",
154
160
  "author": { "name": "[author]" }
155
161
  }
156
162
  ```
163
+ There is no `commands` field — that field is banned by the repo guard.
157
164
  5. Sync and validate host manifests:
158
165
  ```bash
159
166
  cd /path/to/plugin-name
@@ -181,8 +188,7 @@ git commit -m "feat: initial plugin structure"
181
188
 
182
189
  **LOAD RELEVANT SKILLS** before implementing each component type:
183
190
 
184
- - Skills: Load skill-development skill
185
- - Commands: Load command-development skill
191
+ - Skills (passive + invocable): Load skill-development skill
186
192
  - Agents: Load agent-development skill
187
193
  - Hooks: Load hook-development skill
188
194
  - MCP: Load mcp-integration skill
@@ -207,16 +213,17 @@ git commit -m "feat: initial plugin structure"
207
213
  - Create utility scripts if needed
208
214
  3. Use skill-reviewer agent to validate each skill
209
215
 
210
- ### For Commands
216
+ ### For Invocable Skills (slash entry points — formerly "commands")
211
217
 
212
- 1. Load command-development skill using Skill tool
213
- 2. For each command:
214
- - Write command markdown with frontmatter
215
- - Include clear description and argument-hint
216
- - Specify allowed-tools (minimal necessary)
217
- - Write instructions FOR Claude (not TO user)
218
- - Provide usage examples and tips
219
- - Reference relevant skills if applicable
218
+ 1. Load skill-development skill using Skill tool
219
+ 2. For each invocable skill, create a normal `skills/<name>/SKILL.md` and:
220
+ - Give it invocable frontmatter (slash name, `argument-hint`, optional
221
+ `allowed-tools` kept minimal)
222
+ - Consume user input via `$ARGUMENTS`
223
+ - Write the body as instructions FOR Claude (not TO the user)
224
+ - Provide usage examples; reference passive skills where useful
225
+ - Use `references/` and `scripts/` for progressive disclosure
226
+ 3. Use skill-reviewer agent to validate it like any other skill
220
227
 
221
228
  ### For Agents
222
229
 
@@ -265,7 +272,7 @@ git commit -m "feat: initial plugin structure"
265
272
  1. Load plugin-settings skill using Skill tool
266
273
  2. Create settings template in README
267
274
  3. Create example .claude/plugin-name.local.md file (as documentation)
268
- 4. Implement settings reading in hooks/commands as needed
275
+ 4. Implement settings reading in hooks/skills as needed
269
276
  5. Add to .gitignore: `.claude/*.local.md`
270
277
 
271
278
  **Progress tracking**: Update tasks as each component is completed
@@ -329,7 +336,7 @@ git commit -m "feat: initial plugin structure"
329
336
 
330
337
  2. **Verification checklist** for user to perform:
331
338
  - [ ] Skills load when triggered (ask questions with trigger phrases)
332
- - [ ] Commands appear in `/help` and execute correctly
339
+ - [ ] Invocable skills appear in `/help` and execute correctly
333
340
  - [ ] Agents trigger on appropriate scenarios
334
341
  - [ ] Hooks activate on events (if applicable)
335
342
  - [ ] MCP servers connect (if applicable)
@@ -337,7 +344,7 @@ git commit -m "feat: initial plugin structure"
337
344
 
338
345
  3. **Testing recommendations**:
339
346
  - For skills: Ask questions using trigger phrases from descriptions
340
- - For commands: Run `/plugin-name:command-name` with various arguments
347
+ - For invocable skills: Run `/plugin-name:skill-name` with various arguments
341
348
  - For agents: Create scenarios matching agent examples
342
349
  - For hooks: Use `claude --debug` to see hook execution
343
350
  - For MCP: Use `/mcp` to verify servers and tools
@@ -391,7 +398,7 @@ git commit -m "feat: initial plugin structure"
391
398
  - Update marketplace metadata.version (bump patch version)
392
399
 
393
400
  - If create new marketplace:
394
- - Suggest using `/plugin-dev:create-marketplace` command
401
+ - Suggest using the `/plugin-dev:create-marketplace` skill
395
402
  - Or create minimal marketplace.json with this plugin as first entry
396
403
  - Validate marketplace after update using plugin-validator agent
397
404
 
@@ -399,7 +406,7 @@ git commit -m "feat: initial plugin structure"
399
406
  - Mark all tasks complete
400
407
  - List what was created:
401
408
  - Plugin name and purpose
402
- - Components created (X skills, Y commands, Z agents, etc.)
409
+ - Components created (X skills incl. invocable, Y agents, etc.)
403
410
  - Key files and their purposes
404
411
  - Total file count and structure
405
412
  - If added to marketplace:
@@ -448,7 +455,7 @@ git commit -m "feat: initial plugin structure"
448
455
  ### Skills to Load by Phase
449
456
 
450
457
  - **Phase 2**: plugin-structure
451
- - **Phase 5**: skill-development, command-development, agent-development, hook-development, mcp-integration, lsp-integration, plugin-settings (as needed)
458
+ - **Phase 5**: skill-development, agent-development, hook-development, mcp-integration, lsp-integration, plugin-settings (as needed)
452
459
  - **Phase 6**: (agents will use skills automatically)
453
460
  - **Phase 8**: marketplace-structure (if publishing to marketplace)
454
461
 
@@ -480,7 +487,7 @@ Every component must meet these standards:
480
487
  ### Phase 2: Component Planning
481
488
 
482
489
  - Skills: 1 (migration best practices)
483
- - Commands: 3 (create-migration, run-migrations, rollback)
490
+ - Invocable skills: 3 (create-migration, run-migrations, rollback)
484
491
  - Agents: 1 (migration-validator)
485
492
  - MCP: 1 (database connection)
486
493
 
@@ -22,8 +22,7 @@ See `ORGANIZATION.md` in the enact-extensions repo root.
22
22
  | Skill | Purpose |
23
23
  |-------|---------|
24
24
  | **plugin-structure** | Multi-platform layout and manifests |
25
- | **skill-development** | `skills/*/SKILL.md` |
26
- | **command-development** | Slash commands in `commands/` |
25
+ | **skill-development** | `skills/*/SKILL.md` — passive and invocable (slash) skills |
27
26
  | **hook-development** | `hooks/hooks.json` |
28
27
  | **mcp-integration** | `.mcp.json` |
29
28
  | **agent-development** | `agents/*.md` (Claude/Cursor) |
@@ -46,14 +45,15 @@ Use when the user needs to:
46
45
  - Learn about component auto-discovery
47
46
  - Use ${CLAUDE_PLUGIN_ROOT} for portable paths
48
47
 
49
- ### Adding User-Facing Commands
48
+ ### Adding User-Facing Slash Entry Points (invocable skills)
50
49
 
51
- **Skill: `command-development`**
50
+ **Skill: `skill-development`**
52
51
 
53
- Use when the user needs to:
52
+ There is no separate "command" component — a user-initiated `/name` action is an
53
+ *invocable skill*. Use `skill-development` when the user needs to:
54
54
 
55
- - Create slash commands (/command-name)
56
- - Configure command frontmatter (description, allowed-tools, model)
55
+ - Create an invocable skill (`/skill-name`)
56
+ - Configure invocable frontmatter (description, allowed-tools, model)
57
57
  - Use dynamic arguments ($ARGUMENTS, $1, $2)
58
58
  - Reference files with @ syntax
59
59
  - Execute bash inline with `[BANG]` backticks
@@ -147,9 +147,8 @@ Use when the user needs to:
147
147
  ```
148
148
  User wants to...
149
149
  ├── Create/organize a plugin structure? → plugin-structure
150
- ├── Add a simple slash command (no bundled resources)? → command-development
150
+ ├── Add a slash entry point / any skill (passive or invocable)? → skill-development
151
151
  ├── Create an autonomous agent? → agent-development
152
- ├── Add a complex skill with scripts/references? → skill-development
153
152
  ├── React to Claude Code events? → hook-development
154
153
  ├── Integrate external service/API? → mcp-integration
155
154
  ├── Add code intelligence/LSP? → lsp-integration
@@ -162,7 +161,7 @@ User wants to...
162
161
  ### Building a Complete Plugin
163
162
 
164
163
  1. **Start**: Load `plugin-structure` skill to create directory layout
165
- 2. **Add features**: Load `command-development` for user-facing commands
164
+ 2. **Add features**: Load `skill-development` for passive and invocable skills
166
165
  3. **Automation**: Load `hook-development` for event-driven behavior
167
166
  4. **Configuration**: Load `plugin-settings` if user configuration needed
168
167
  5. **Validation**: Use plugin-validator agent to validate structure
@@ -171,14 +170,14 @@ User wants to...
171
170
 
172
171
  1. **Start**: Load `plugin-structure` for basic structure
173
172
  2. **Integration**: Load `mcp-integration` to configure MCP servers
174
- 3. **Commands**: Load `command-development` to create commands that use MCP tools
173
+ 3. **Skills**: Load `skill-development` to create invocable skills that use MCP tools
175
174
  4. **Agents**: Load `agent-development` for autonomous MCP workflows
176
175
 
177
176
  ### Building a Code Intelligence Plugin
178
177
 
179
178
  1. **Start**: Load `plugin-structure` for basic structure
180
179
  2. **LSP**: Load `lsp-integration` to configure language servers
181
- 3. **Commands**: Load `command-development` for commands using LSP features
180
+ 3. **Skills**: Load `skill-development` for invocable skills using LSP features
182
181
 
183
182
  ### Building a Skill-Focused Plugin
184
183
 
@@ -198,9 +197,9 @@ The plugin-dev plugin also provides 3 agents:
198
197
 
199
198
  Use agents proactively after creating components to ensure quality.
200
199
 
201
- ## Available Commands
200
+ ## Available Invocable Skills
202
201
 
203
- | Command | Purpose |
202
+ | Invocable skill | Purpose |
204
203
  | -------------------------------- | --------------------------------------------------- |
205
204
  | `/plugin-dev:plugin-dev-guide` | Overview and skill routing |
206
205
  | `/plugin-dev:start` | Entry point - choose plugin or marketplace creation |
@@ -559,6 +559,4 @@ Plugin-dev's skills demonstrate best practices:
559
559
  - `../hook-development/` - Progressive disclosure, utilities
560
560
  - `../agent-development/` - AI-assisted creation, references
561
561
  - `../mcp-integration/` - Comprehensive references
562
- - `../plugin-settings/` - Real-world examples
563
- - `../command-development/` - Clear critical concepts
564
562
  - `../plugin-structure/` - Good organization
@@ -1,8 +1,9 @@
1
1
  ---
2
- description: Start plugin development - choose your path
3
- argument-hint: [description]
4
- allowed-tools: AskUserQuestion, Skill, TaskCreate, TaskGet, TaskUpdate, TaskList
5
- model: sonnet
2
+ name: start
3
+ description: >-
4
+ Entry point for plugin development routes the user to create or validate an
5
+ Enact plugin. Use when someone says "start a plugin", "new plugin", "/start",
6
+ or asks how to begin authoring a plugin.
6
7
  disable-model-invocation: true
7
8
  ---
8
9
 
@@ -34,7 +35,7 @@ Welcome to the Enact Plugin Manager!
34
35
 
35
36
  **Plugin** → One bundle for Claude, Codex, and Cursor
36
37
  - Canonical manifest: .agents/plugin.json
37
- - Shared components: skills/, commands/, hooks/, .mcp.json
38
+ - Shared components: skills/ (passive + invocable), agents/, hooks/, .mcp.json
38
39
  - Host copies: .claude-plugin/, .codex-plugin/, .cursor-plugin/
39
40
  - Example: ../enact-operator/extensions/ (sibling top-level submodule of enact-os)
40
41
 
@@ -74,7 +75,7 @@ Option 2:
74
75
 
75
76
  - Load `plugin-structure` when the user is new to multi-platform layout.
76
77
  - After editing `.agents/plugin.json`, remind them to run `enact-extensions sync`.
77
- - Marketplace authoring is archived under `commands/_archive/` — only mention if explicitly requested.
78
+ - Marketplace authoring is archived under `skills/_archive/` — only mention if explicitly requested.
78
79
 
79
80
  ---
80
81
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amsterdamdatalabs/enact-extensions",
3
- "version": "0.1.5",
3
+ "version": "0.1.10",
4
4
  "description": "Create and validate Enact multi-platform plugin manifests",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -31,22 +31,27 @@
31
31
  },
32
32
  "scripts": {
33
33
  "build": "rm -rf dist && tsc -p tsconfig.json",
34
- "prepublishOnly": "npm run build",
34
+ "prepublishOnly": "npm run check:principles && npm run check:hooks && npm run build",
35
+ "check:principles": "node scripts/check-principles.mjs",
36
+ "check:hooks": "node scripts/check-hooks.mjs",
37
+ "check:hooks:smoke": "node scripts/check-hooks.mjs --smoke",
35
38
  "typecheck": "tsc -p tsconfig.json --noEmit",
36
39
  "validate": "npm run build && node scripts/enact-extensions.mjs validate extensions/plugin-dev",
37
40
  "sync": "npm run build && node scripts/enact-extensions.mjs sync extensions/plugin-dev",
38
41
  "setup:context": "bash scripts/setup-enact-context.sh",
39
- "test": "npm run build && node --test tests/*.test.mjs",
40
- "postinstall": "node scripts/postinstall.mjs"
42
+ "test": "npm run check:principles && npm run check:hooks:smoke && npm run build && node --test tests/*.test.mjs",
43
+ "postinstall": "node scripts/postinstall.mjs",
44
+ "prepare": "husky"
41
45
  },
42
46
  "dependencies": {
47
+ "@iarna/toml": "^2.2.5",
43
48
  "ajv": "^8.17.1",
44
- "ajv-formats": "^3.0.1",
45
- "@iarna/toml": "^2.2.5"
49
+ "ajv-formats": "^3.0.1"
46
50
  },
47
51
  "devDependencies": {
48
52
  "@types/node": "^22.0.0",
49
53
  "esbuild": "^0.28.0",
54
+ "husky": "^9.1.7",
50
55
  "typescript": "^5.9.3"
51
56
  },
52
57
  "engines": {
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Repo guard: validate every plugin's hooks before they ship.
4
+ *
5
+ * Born from real breakage: enact-operator shipped hook entries for a
6
+ * doom-loop CLI that had been deleted (module-not-found at runtime), and
7
+ * enact-factory's npm package omitted `extensions/` from its files whitelist
8
+ * so `enact-factory hook ...` could never load. Both passed every static check
9
+ * yet failed the moment Claude Code fired the hook. plugin-dev meanwhile
10
+ * declared a `hooks` component whose hooks.json was `{ "hooks": {} }` — a
11
+ * component that installs nothing.
12
+ *
13
+ * Two passes:
14
+ * STATIC (always, no binaries): every plugin that declares a `hooks` manifest
15
+ * field must have a hooks file that is valid JSON, has a NON-EMPTY `hooks`
16
+ * object, and whose every entry is a well-formed `command` hook.
17
+ * SMOKE (--smoke, best-effort): for each distinct hook command whose binary
18
+ * resolves on PATH, actually run it with a synthetic payload in an isolated
19
+ * HOME/TMPDIR and fail if it crashes on module resolution (the exact class
20
+ * of bug above). Binaries that aren't installed are skipped, so this stays
21
+ * green in CI where the plugin CLIs are absent.
22
+ *
23
+ * Run via `npm run check:hooks` (static) or `npm run check:hooks:smoke`.
24
+ */
25
+ import { readFileSync, existsSync, readdirSync, statSync, mkdtempSync, rmSync } from "node:fs";
26
+ import { join, dirname, resolve } from "node:path";
27
+ import { fileURLToPath } from "node:url";
28
+ import { tmpdir } from "node:os";
29
+ import { spawnSync, execSync } from "node:child_process";
30
+
31
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
32
+ const EXT = join(ROOT, "extensions");
33
+ const SMOKE = process.argv.includes("--smoke");
34
+
35
+ const errors = [];
36
+ const warnings = [];
37
+ const notes = [];
38
+ const err = (p, m) => errors.push(`[ERROR] ${p}: ${m}`);
39
+ const warn = (p, m) => warnings.push(`[warn] ${p}: ${m}`);
40
+ const note = (m) => notes.push(`[note] ${m}`);
41
+
42
+ function isDir(p) {
43
+ try { return statSync(p).isDirectory(); } catch { return false; }
44
+ }
45
+
46
+ // Module-resolution / load crash signatures — the failure class this guard exists for.
47
+ const CRASH = [
48
+ "Cannot find module",
49
+ "Cannot find package",
50
+ "ERR_MODULE_NOT_FOUND",
51
+ "MODULE_NOT_FOUND",
52
+ "ERR_REQUIRE_ESM",
53
+ "ERR_PACKAGE_PATH_NOT_EXPORTED",
54
+ ];
55
+
56
+ function onPath(bin) {
57
+ try { execSync(`command -v ${bin}`, { stdio: "ignore", shell: "/bin/sh" }); return true; }
58
+ catch { return false; }
59
+ }
60
+
61
+ // Walk a hooks.json object, yielding { event, command } for every command entry.
62
+ function* commandEntries(hooks, id) {
63
+ for (const [event, groups] of Object.entries(hooks)) {
64
+ if (!Array.isArray(groups)) { err(id, `hooks.${event} is not an array`); continue; }
65
+ for (const g of groups) {
66
+ const list = g && g.hooks;
67
+ if (!Array.isArray(list)) { err(id, `hooks.${event} group has no hooks[] array`); continue; }
68
+ for (const h of list) {
69
+ if (!h || h.type !== "command") { err(id, `hooks.${event} entry is not a command hook (type="${h && h.type}")`); continue; }
70
+ if (typeof h.command !== "string" || !h.command.trim()) { err(id, `hooks.${event} command hook has empty command`); continue; }
71
+ yield { event, command: h.command.trim() };
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ const smokePayload = JSON.stringify({
78
+ session_id: "hooks-smoke",
79
+ transcript_path: "/dev/null",
80
+ cwd: ROOT,
81
+ hook_event_name: "Smoke",
82
+ tool_name: "Bash",
83
+ tool_input: { command: "true" },
84
+ tool_response: {},
85
+ });
86
+
87
+ function smokeRun(command) {
88
+ const parts = command.split(/\s+/);
89
+ const bin = parts[0];
90
+ const args = parts.slice(1);
91
+ const home = mkdtempSync(join(tmpdir(), "hooks-smoke-"));
92
+ try {
93
+ const r = spawnSync(bin, args, {
94
+ input: smokePayload,
95
+ timeout: 8000,
96
+ encoding: "utf8",
97
+ env: { ...process.env, HOME: home, TMPDIR: home, XDG_CONFIG_HOME: join(home, ".config"), XDG_STATE_HOME: join(home, ".state") },
98
+ });
99
+ const out = `${r.stdout || ""}\n${r.stderr || ""}`;
100
+ if (r.error && r.error.code === "ETIMEDOUT") return { ok: true, soft: "timed out (hook may wait on stdin/state) — not a crash" };
101
+ if (r.error) return { ok: false, why: r.error.message };
102
+ const hit = CRASH.find((s) => out.includes(s));
103
+ if (hit) return { ok: false, why: `module-load crash: "${hit}"` };
104
+ if (r.signal) return { ok: false, why: `killed by signal ${r.signal}` };
105
+ return { ok: true };
106
+ } finally {
107
+ rmSync(home, { recursive: true, force: true });
108
+ }
109
+ }
110
+
111
+ if (!existsSync(EXT)) {
112
+ console.error(`No extensions dir at ${EXT}`);
113
+ process.exit(1);
114
+ }
115
+
116
+ const smokeCommands = new Set();
117
+
118
+ for (const name of readdirSync(EXT)) {
119
+ const root = join(EXT, name);
120
+ if (!isDir(root)) continue;
121
+ const manifestPath = join(root, ".agents", "plugin.json");
122
+ if (!existsSync(manifestPath)) continue;
123
+ const id = `extensions/${name}`;
124
+ let m;
125
+ try { m = JSON.parse(readFileSync(manifestPath, "utf8")); }
126
+ catch (e) { err(id, `unreadable .agents/plugin.json: ${e.message}`); continue; }
127
+
128
+ const field = m.hooks;
129
+ if (!field) continue; // no hooks declared — nothing to validate here (E2 in check-principles covers undeclared dirs)
130
+
131
+ const hooksPath = resolve(root, field);
132
+ if (!existsSync(hooksPath)) { err(id, `manifest hooks -> "${field}" but file is missing at ${hooksPath}`); continue; }
133
+
134
+ let doc;
135
+ try { doc = JSON.parse(readFileSync(hooksPath, "utf8")); }
136
+ catch (e) { err(id, `${field} is not valid JSON: ${e.message}`); continue; }
137
+
138
+ const hooks = doc && doc.hooks;
139
+ if (!hooks || typeof hooks !== "object") { err(id, `${field} has no "hooks" object`); continue; }
140
+ if (Object.keys(hooks).length === 0) {
141
+ err(id, `${field} declares an empty hooks object — a hooks component that registers nothing; drop the "hooks" manifest field and the file`);
142
+ continue;
143
+ }
144
+
145
+ for (const { command } of commandEntries(hooks, id)) {
146
+ smokeCommands.add(command);
147
+ }
148
+ }
149
+
150
+ if (SMOKE) {
151
+ const cmds = [...smokeCommands].sort();
152
+ const bins = new Set(cmds.map((c) => c.split(/\s+/)[0]));
153
+ const present = new Set([...bins].filter(onPath));
154
+ for (const b of bins) if (!present.has(b)) note(`smoke skipped for "${b}" — not on PATH (can't test what isn't installed)`);
155
+ for (const c of cmds) {
156
+ const bin = c.split(/\s+/)[0];
157
+ if (!present.has(bin)) continue;
158
+ const r = smokeRun(c);
159
+ if (r.ok && r.soft) warn(`smoke`, `\`${c}\` ${r.soft}`);
160
+ else if (!r.ok) err(`smoke`, `\`${c}\` ${r.why}`);
161
+ }
162
+ } else {
163
+ note(`static only — run with --smoke to exercise installed hook binaries (${smokeCommands.size} distinct commands)`);
164
+ }
165
+
166
+ for (const n of notes) console.log(n);
167
+ for (const w of warnings) console.log(w);
168
+ for (const e of errors) console.log(e);
169
+ console.log(
170
+ errors.length
171
+ ? `\n✗ hooks check FAILED: ${errors.length} error(s), ${warnings.length} warning(s)`
172
+ : `\n✓ hooks check passed (${warnings.length} warning(s))`,
173
+ );
174
+ process.exit(errors.length ? 1 : 0);
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Repo guard: enforce the authoring principles in docs/AUTHORING-GUIDELINES.md.
4
+ * Fails (exit 1) on any ERROR. Pure Node, no deps. Run via `npm run
5
+ * check:principles`; also wired into `test` and `prepublishOnly`, and the
6
+ * optional `.githooks/pre-commit`.
7
+ *
8
+ * Rules:
9
+ * E1 No commands. A `commands/` dir or a `commands` manifest field is banned —
10
+ * commands are skills.
11
+ * E2 Declare every component. A `skills/`, `agents/`, or `hooks/` dir on disk
12
+ * must be named by the matching manifest field (else it silently doesn't
13
+ * install).
14
+ * E3 Skill hygiene. Each `skills/<dir>/` needs a `SKILL.md` with `name` +
15
+ * `description` frontmatter, and no `README.md`.
16
+ * E4 `targets` must not include "enact" (enact is the canonical source).
17
+ */
18
+ import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
19
+ import { join, dirname } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+
22
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
23
+ const EXT = join(ROOT, "extensions");
24
+
25
+ const errors = [];
26
+ const warnings = [];
27
+ const err = (p, m) => errors.push(`[ERROR] ${p}: ${m}`);
28
+ const warn = (p, m) => warnings.push(`[warn] ${p}: ${m}`);
29
+
30
+ function frontmatter(file) {
31
+ const t = readFileSync(file, "utf8");
32
+ if (!t.startsWith("---")) return null;
33
+ const m = /^---\n([\s\S]*?)\n---/.exec(t);
34
+ if (!m) return null;
35
+ const body = m[1];
36
+ return {
37
+ name: /(^|\n)name:/.test(body),
38
+ description: /(^|\n)description:/.test(body),
39
+ };
40
+ }
41
+
42
+ function isDir(p) {
43
+ try { return statSync(p).isDirectory(); } catch { return false; }
44
+ }
45
+
46
+ if (!existsSync(EXT)) {
47
+ console.error(`No extensions dir at ${EXT}`);
48
+ process.exit(1);
49
+ }
50
+
51
+ for (const name of readdirSync(EXT)) {
52
+ const root = join(EXT, name);
53
+ if (!isDir(root)) continue;
54
+ const manifestPath = join(root, ".agents", "plugin.json");
55
+ if (!existsSync(manifestPath)) continue;
56
+ const id = `extensions/${name}`;
57
+ let m;
58
+ try { m = JSON.parse(readFileSync(manifestPath, "utf8")); }
59
+ catch (e) { err(id, `unreadable .agents/plugin.json: ${e.message}`); continue; }
60
+
61
+ // E1 — no commands
62
+ if ("commands" in m) err(id, `manifest declares "commands" — commands are skills; remove it`);
63
+ if (isDir(join(root, "commands"))) err(id, `has a commands/ dir — commands are skills; migrate to skills/`);
64
+
65
+ // E4 — targets must not include enact
66
+ if (Array.isArray(m.targets) && m.targets.includes("enact"))
67
+ err(id, `targets includes "enact" — enact is the canonical source, never a target`);
68
+
69
+ // E2 — declare components that exist on disk
70
+ for (const comp of ["skills", "agents", "hooks"]) {
71
+ if (isDir(join(root, comp)) && !m[comp])
72
+ err(id, `${comp}/ exists on disk but manifest has no "${comp}" field → it will NOT install`);
73
+ }
74
+
75
+ // E3 — skill hygiene
76
+ const skillsDir = join(root, "skills");
77
+ if (isDir(skillsDir)) {
78
+ for (const s of readdirSync(skillsDir)) {
79
+ const sp = join(skillsDir, s);
80
+ if (!isDir(sp)) { warn(id, `skills/${s} is a loose file, not a skill dir`); continue; }
81
+ const smd = join(sp, "SKILL.md");
82
+ if (!existsSync(smd)) { err(id, `skills/${s} has no SKILL.md`); continue; }
83
+ const fm = frontmatter(smd);
84
+ if (!fm) err(id, `skills/${s}/SKILL.md has no YAML frontmatter`);
85
+ else {
86
+ if (!fm.name) err(id, `skills/${s}/SKILL.md frontmatter missing "name"`);
87
+ if (!fm.description) err(id, `skills/${s}/SKILL.md frontmatter missing "description"`);
88
+ }
89
+ if (existsSync(join(sp, "README.md"))) err(id, `skills/${s} contains README.md (not allowed in a skill)`);
90
+ }
91
+ }
92
+ }
93
+
94
+ for (const w of warnings) console.log(w);
95
+ for (const e of errors) console.log(e);
96
+ console.log(
97
+ errors.length
98
+ ? `\n✗ principles check FAILED: ${errors.length} error(s), ${warnings.length} warning(s)`
99
+ : `\n✓ principles check passed (${warnings.length} warning(s))`,
100
+ );
101
+ process.exit(errors.length ? 1 : 0);