ima-claude 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +463 -0
  3. package/dist/cli.js +1064 -0
  4. package/package.json +49 -0
  5. package/platforms/claude/adapter.ts +115 -0
  6. package/platforms/junie/adapter.ts +254 -0
  7. package/platforms/junie/agents-template.md +113 -0
  8. package/platforms/junie/hook-translations.md +84 -0
  9. package/platforms/shared/detector.ts +27 -0
  10. package/platforms/shared/installer.ts +202 -0
  11. package/platforms/shared/types.ts +78 -0
  12. package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
  13. package/plugins/ima-claude/agents/explorer.md +30 -0
  14. package/plugins/ima-claude/agents/implementer.md +30 -0
  15. package/plugins/ima-claude/agents/memory.md +42 -0
  16. package/plugins/ima-claude/agents/reviewer.md +53 -0
  17. package/plugins/ima-claude/agents/tester.md +33 -0
  18. package/plugins/ima-claude/agents/wp-developer.md +46 -0
  19. package/plugins/ima-claude/hooks/README.md +145 -0
  20. package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
  21. package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
  22. package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
  23. package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
  24. package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
  25. package/plugins/ima-claude/hooks/docs_organization.py +104 -0
  26. package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
  27. package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
  28. package/plugins/ima-claude/hooks/hook_logger.py +69 -0
  29. package/plugins/ima-claude/hooks/hooks.json +239 -0
  30. package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
  31. package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
  32. package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
  33. package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
  34. package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
  35. package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
  36. package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
  37. package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
  38. package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
  39. package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
  40. package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
  41. package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
  42. package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
  43. package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
  44. package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
  45. package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
  46. package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
  47. package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
  48. package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
  49. package/plugins/ima-claude/personalities/README.md +45 -0
  50. package/plugins/ima-claude/personalities/enable-40k.md +69 -0
  51. package/plugins/ima-claude/personalities/enable-templars.md +69 -0
  52. package/plugins/ima-claude/skills/.research-summary.md +340 -0
  53. package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
  54. package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
  55. package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
  56. package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
  57. package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
  58. package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
  59. package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
  60. package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
  61. package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
  62. package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
  63. package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
  64. package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
  65. package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
  66. package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
  67. package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
  68. package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
  69. package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
  70. package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
  71. package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
  72. package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
  73. package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
  74. package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
  75. package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
  76. package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
  77. package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
  78. package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
  79. package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
  80. package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
  81. package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
  82. package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
  83. package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
  84. package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
  85. package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
  86. package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
  87. package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
  88. package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
  89. package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
  90. package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
  91. package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
  92. package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
  93. package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
  94. package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
  95. package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
  96. package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
  97. package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
  98. package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
  99. package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
  100. package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
  101. package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
  102. package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
  103. package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
  104. package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
  105. package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
  106. package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
  107. package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
  108. package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
  109. package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
  110. package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
  111. package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
  112. package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
  113. package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
  114. package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
  115. package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
  116. package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
  117. package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
  118. package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
  119. package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
  120. package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
  121. package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
  122. package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
  123. package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
  124. package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
  125. package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
  126. package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
  127. package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
  128. package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
  129. package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
  130. package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
  131. package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
  132. package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
  133. package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
  134. package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
  135. package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
  136. package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
  137. package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
  138. package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
  139. package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
  140. package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
  141. package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
  142. package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
  143. package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
  144. package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
  145. package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
  146. package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
  147. package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
  148. package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
  149. package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
  150. package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
  151. package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
  152. package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
  153. package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
  154. package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
  155. package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
  156. package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
  157. package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
  158. package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
  159. package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
  160. package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
  161. package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
  162. package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
  163. package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
  164. package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
  165. package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
  166. package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
  167. package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
  168. package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
  169. package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
  170. package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
  171. package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
  172. package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
  173. package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
  174. package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
  175. package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
  176. package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
  177. package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
  178. package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
  179. package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
  180. package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
  181. package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
  182. package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
@@ -0,0 +1,200 @@
1
+ ---
2
+ name: "compound-bridge"
3
+ description: "Compound Engineering integration — memory bridge (Compound → Vestige/Qdrant), Vestige → Compound research, role separation (plan vs task-master), per-project config template. Triggers when Compound workflows complete or when orchestrating plan/review workflows. Triggers on: /workflows:compound, /workflows:plan, /workflows:review, compound engineering, compound-engineering.local."
4
+ ---
5
+
6
+ # Compound Bridge — Compound Engineering + ima-claude Integration
7
+
8
+ Minimal connective tissue between Compound Engineering (structured workflows) and ima-claude (coding standards + memory). Removing this skill returns both systems to standalone behavior.
9
+
10
+ ## Memory Bridge: Compound → Vestige/Qdrant
11
+
12
+ After Compound workflow events, **automatically store insights** without being asked:
13
+
14
+ | After This Event | Store This | Where |
15
+ |---|---|---|
16
+ | `/workflows:compound` writes a solution | Root cause + key insight (1-2 sentences) | Vestige `smart_ingest`, node_type: `pattern` |
17
+ | `/workflows:compound` writes a solution (>500 words) | Full solution content | Qdrant `qdrant-store`, collection: `compound-solutions` |
18
+ | `/workflows:plan` completes with research | Key decisions + approach chosen | Vestige `smart_ingest`, node_type: `decision` |
19
+ | `/workflows:review` finds P1/P2 issues | Pattern summary of findings | Vestige `smart_ingest`, node_type: `pattern` |
20
+
21
+ ### Compound → Vestige Example
22
+
23
+ After `/workflows:compound` finishes writing to `docs/solutions/`:
24
+
25
+ ```
26
+ mcp__vestige__smart_ingest
27
+ content: "Root cause: {concise root cause}. Fix: {approach taken}. Key insight: {what we learned}"
28
+ node_type: "pattern"
29
+ ```
30
+
31
+ ### Compound → Qdrant Example (Large Solutions)
32
+
33
+ For solutions over ~500 words, also store the full document in Qdrant for RAG retrieval:
34
+
35
+ ```
36
+ mcp__qdrant-memory__qdrant-store
37
+ information: "{full solution content}"
38
+ metadata: {"type": "compound-solution", "source": "docs/solutions/{filename}"}
39
+ ```
40
+
41
+ ## Memory Bridge: Vestige → Compound Research
42
+
43
+ When orchestrating `/workflows:plan`, **search Vestige before/alongside** the learnings-researcher agent:
44
+
45
+ ```
46
+ mcp__vestige__search query: "{feature keywords}" limit: 5
47
+ ```
48
+
49
+ Include Vestige results alongside file-based learnings, marked as cross-project provenance:
50
+
51
+ ```markdown
52
+ ### Prior Knowledge (Cross-Project — Vestige)
53
+ - {vestige result 1}
54
+ - {vestige result 2}
55
+
56
+ ### Prior Solutions (Project — docs/solutions/)
57
+ - {learnings-researcher results}
58
+ ```
59
+
60
+ This supplements but does not replace the learnings-researcher's `docs/solutions/` grep.
61
+
62
+ ## Role Separation: Planning
63
+
64
+ Both `task-master` and `/workflows:plan` handle planning. They have different lanes:
65
+
66
+ | Need | Use | Why |
67
+ |---|---|---|
68
+ | Formal feature planning with research | `/workflows:plan` | Research agents, structured documentation, living plan file |
69
+ | Ad-hoc work breakdown during implementation | `task-master` | Decomposition patterns, storage strategy, agent delegation |
70
+ | Breaking a plan into executable tasks | Both | Plan creates the doc; task-master principles guide breakdown |
71
+
72
+ ### task-master Principles That Enhance Compound Workflows
73
+
74
+ These apply when `/workflows:work` creates its task list:
75
+
76
+ - **Two-level max** agent delegation (Compound's swarm already respects this)
77
+ - **Model selection**: Sonnet for execution, Opus for orchestration
78
+ - **Minimal context principle** for subagents — only include what they need
79
+ - **Vertical decomposition** for sequential work, **horizontal** for parallel
80
+
81
+ ## Per-Project Config: `compound-engineering.local.md`
82
+
83
+ Create this file in project roots where both systems are used. It tells Compound's review agents about our coding standards.
84
+
85
+ ### Template
86
+
87
+ ```markdown
88
+ ---
89
+ review_agents:
90
+ - code-simplicity-reviewer
91
+ - security-sentinel
92
+ - performance-oracle
93
+ - kieran-typescript-reviewer
94
+ - kieran-python-reviewer
95
+ - architecture-strategist
96
+ - pattern-recognition-specialist
97
+ - julik-frontend-races-reviewer
98
+ - agent-native-reviewer
99
+ ---
100
+
101
+ ## Coding Standards (ima-claude)
102
+
103
+ FP-first: pure functions, composition, immutability. Anti-over-engineering: YAGNI strictly, boring code wins. Native patterns only — no custom pipe/compose/curry utilities.
104
+
105
+ Language skills auto-activate by context:
106
+ - JavaScript: js-fp | PHP: php-fp | Vue: js-fp-vue | React: js-fp-react
107
+ - WordPress JS: js-fp-wordpress, jquery | WordPress PHP: php-fp-wordpress
108
+ - Quasar: quasar-fp | Bootstrap: ima-bootstrap
109
+
110
+ ## Work Decomposition (task-master)
111
+
112
+ - Two-level max agent delegation
113
+ - Sonnet for execution, Opus for orchestration
114
+ - Minimal context principle for subagents
115
+ ```
116
+
117
+ ### When to Create
118
+
119
+ Create `compound-engineering.local.md` when:
120
+ - A project uses both ima-claude skills AND Compound Engineering workflows
121
+ - You're about to run `/workflows:review` for the first time in a project
122
+
123
+ Don't create it for projects that only use one system.
124
+
125
+ ## What Works Without This Skill
126
+
127
+ These integrate naturally — no bridge needed:
128
+
129
+ - ima-claude language/FP skills auto-activate during `/workflows:work` by file type
130
+ - Compound's research agents (learnings-researcher, best-practices-researcher) fill gaps ima-claude doesn't cover
131
+ - Compound's 15 specialized review agents complement our FP-focused standards
132
+ - Compound's brainstorm workflow is genuinely new capability
133
+
134
+ ## Artifact Resilience: Surviving Branch Switches & Context Compaction
135
+
136
+ Compound workflows write artifacts to the working tree (`docs/brainstorms/`, `docs/plans/`, `docs/solutions/`, `todos/`). These files are **not committed** during workflows. Git branch switching during `/workflows:work` **destroys them**. Context compaction loses agent results that reference them. This section prevents that.
137
+
138
+ ### Rule 1: Shadow Copy to `.claude/compound/`
139
+
140
+ **After EVERY workflow artifact write**, immediately copy the file to `.claude/compound/`:
141
+
142
+ ```
143
+ # After /workflows:brainstorm writes to docs/brainstorms/
144
+ cp docs/brainstorms/{file}.md .claude/compound/brainstorms/{file}.md
145
+
146
+ # After /workflows:plan writes to docs/plans/
147
+ cp docs/plans/{file}.md .claude/compound/plans/{file}.md
148
+
149
+ # After /workflows:compound writes to docs/solutions/
150
+ cp docs/solutions/{category}/{file}.md .claude/compound/solutions/{category}/{file}.md
151
+
152
+ # After /workflows:review writes to todos/
153
+ cp todos/{file}.md .claude/compound/todos/{file}.md
154
+ ```
155
+
156
+ Create directories with `mkdir -p` as needed. The `.claude/` directory is gitignored and **survives branch switches** — just like Claude Code's own plan files.
157
+
158
+ **Also shadow-copy on edits**: When `/workflows:work` updates plan checkboxes (`- [ ]` → `- [x]`), copy the updated plan to the shadow location too.
159
+
160
+ ### Rule 2: Eager Memory Bridge (Store Immediately, Not Just at Completion)
161
+
162
+ Don't wait until a workflow finishes to bridge to memory. Store **immediately after each artifact write**:
163
+
164
+ | After Writing | Store Immediately |
165
+ |---|---|
166
+ | Brainstorm document | Vestige `smart_ingest`: key decisions + open questions, node_type: `decision` |
167
+ | Plan document | Vestige `smart_ingest`: approach + task list summary, node_type: `decision` |
168
+ | Plan checkbox update | Vestige `smart_ingest`: progress snapshot (X of Y tasks done), node_type: `observation` |
169
+ | Review todo file | Vestige `smart_ingest`: finding summary + priority, node_type: `pattern` |
170
+ | Solution document | Vestige + Qdrant (per existing rules above) |
171
+
172
+ This ensures that even if context compacts or the session dies, the knowledge survives in memory.
173
+
174
+ ### Rule 3: Pre-Branch-Switch Checkpoint
175
+
176
+ **Before ANY `git checkout`, `git switch`, or worktree operation** during a Compound workflow:
177
+
178
+ 1. Verify all workflow artifacts have shadow copies in `.claude/compound/`
179
+ 2. If any are missing, create them immediately
180
+ 3. Store a Vestige snapshot: `smart_ingest` with content summarizing current workflow state (which phase, what's done, what's next), node_type: `observation`
181
+
182
+ ### Rule 4: Recovery from Shadow Copies
183
+
184
+ If workflow artifacts are lost (branch switch, reset, or interrupted session):
185
+
186
+ 1. Check `.claude/compound/` for shadow copies
187
+ 2. Restore them to their working-tree locations (`docs/plans/`, etc.)
188
+ 3. Check Vestige for the most recent workflow state snapshot
189
+ 4. Resume from where we left off
190
+
191
+ ### Rule 5: Commit `compound-engineering.local.md` Early
192
+
193
+ This file is **persistent project config**, not a transient artifact. When creating or modifying `compound-engineering.local.md`, commit it to the current branch promptly so it survives branch switches via git rather than shadow copies.
194
+
195
+ ## What This Skill Does NOT Do
196
+
197
+ - Modify the Compound Engineering plugin — it stays as-is from the marketplace
198
+ - Create custom scripts or utilities — all integration is skill instructions
199
+ - Add new MCP servers — uses existing Vestige, Qdrant, Serena
200
+ - Force workflows — both systems remain independently functional
@@ -0,0 +1,440 @@
1
+ ---
2
+ name: "discourse"
3
+ description: "Discourse plugin development - plugin.rb, after_initialize, admin routes, Guardian auth, security patterns"
4
+ ---
5
+
6
+ # Discourse Plugin Development
7
+
8
+ Discourse plugins are Rails engines with conventions. Work with the framework — its plugin API, Guardian authorization system, and event hooks exist for good reason.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Building or modifying Discourse plugins
13
+ - Adding admin UI to a Discourse plugin
14
+ - Implementing authentication hooks or user lifecycle extensions
15
+ - Migrating data into Discourse (import scripts)
16
+ - Reviewing Discourse plugin security
17
+
18
+ ## Core Philosophy
19
+
20
+ > Discourse plugins are Rails. Discourse adds its own authorization layer (Guardian). Respect both.
21
+
22
+ - **`after_initialize`** is where plugin logic wires into Discourse
23
+ - **Guardian** is the authorization layer — every action checks it
24
+ - **Plugin API** hooks (`register_*`, `add_*`) are the extension points — don't monkey-patch core
25
+ - **StaffConstraint** protects admin routes — use it, never roll your own
26
+ - **RuboCop** is enforced — Discourse ships lint rules
27
+
28
+ **Foundation**: Reference `../rails/SKILL.md` for Rails security practices and `../ruby-fp/SKILL.md` for Ruby patterns.
29
+
30
+ ## Plugin Structure
31
+
32
+ ```
33
+ my-plugin/
34
+ ├── plugin.rb # Manifest + bootstrap (required)
35
+ ├── about.json # Metadata for admin panel
36
+ ├── app/
37
+ │ ├── controllers/
38
+ │ │ └── admin/
39
+ │ │ └── my_plugin_controller.rb
40
+ │ ├── models/
41
+ │ │ └── my_plugin_record.rb
42
+ │ └── serializers/
43
+ │ └── my_plugin_serializer.rb
44
+ ├── config/
45
+ │ ├── locales/
46
+ │ │ └── server.en.yml
47
+ │ └── settings.yml # Site settings
48
+ ├── db/
49
+ │ └── migrate/
50
+ │ └── 20250101000000_create_my_plugin.rb
51
+ ├── assets/
52
+ │ └── javascripts/
53
+ │ └── admin/ # Ember components for admin UI
54
+ ├── lib/
55
+ │ └── my_plugin/ # Pure logic (no Discourse deps)
56
+ └── spec/
57
+ └── plugin_helper.rb
58
+ ```
59
+
60
+ ## plugin.rb: Manifest + Bootstrap
61
+
62
+ ```ruby
63
+ # frozen_string_literal: true
64
+
65
+ # name: my-plugin
66
+ # about: Brief plugin description
67
+ # version: 1.0.0
68
+ # authors: Your Name
69
+ # url: https://github.com/yourorg/my-plugin
70
+
71
+ # Autoloading — define module first, then require engine
72
+ module ::MyPlugin
73
+ PLUGIN_NAME = "my-plugin"
74
+ end
75
+
76
+ require_relative "lib/my_plugin/engine"
77
+
78
+ # All wiring happens inside after_initialize
79
+ after_initialize do
80
+ # Register settings, hooks, extensions here
81
+ # This runs after Discourse core is fully loaded
82
+ end
83
+ ```
84
+
85
+ ## after_initialize: The Plugin Entry Point
86
+
87
+ ```ruby
88
+ after_initialize do
89
+ # Extend models — use class_eval in after_initialize, not top-level monkey-patches
90
+ User.class_eval do
91
+ has_one :my_plugin_profile, dependent: :destroy
92
+ end
93
+
94
+ # Register custom fields
95
+ register_post_custom_field_type('wp_original_id', :integer)
96
+ register_topic_custom_field_type('imported_from', :string)
97
+
98
+ # Wire into Discourse events (preferred over overriding methods)
99
+ on(:user_created) do |user|
100
+ MyPlugin::UserSetup.call(user)
101
+ end
102
+
103
+ on(:post_created) do |post, opts, user|
104
+ MyPlugin::PostSync.call(post, user)
105
+ end
106
+
107
+ # Add serializer extensions
108
+ add_to_serializer(:user, :wp_user_id) do
109
+ object.custom_fields['wp_user_id']
110
+ end
111
+ end
112
+ ```
113
+
114
+ ## Admin Routes + Controller
115
+
116
+ Every admin route needs `StaffConstraint` — never expose admin actions without it.
117
+
118
+ ```ruby
119
+ # In plugin.rb — register the admin nav link
120
+ add_admin_route 'my_plugin.title', 'my-plugin'
121
+
122
+ # Wire the route
123
+ Discourse::Application.routes.append do
124
+ get '/admin/plugins/my-plugin' => 'admin/my_plugin#index',
125
+ constraints: StaffConstraint.new
126
+ post '/admin/plugins/my-plugin/action' => 'admin/my_plugin#action',
127
+ constraints: StaffConstraint.new
128
+ end
129
+ ```
130
+
131
+ ```ruby
132
+ # app/controllers/admin/my_plugin_controller.rb
133
+ # frozen_string_literal: true
134
+
135
+ class ::Admin::MyPluginController < ::Admin::AdminController
136
+ # Admin::AdminController already requires:
137
+ # - User is logged in
138
+ # - User is staff (moderator or admin)
139
+ # For admin-only actions, add:
140
+ before_action :ensure_admin, only: [:dangerous_action]
141
+
142
+ def index
143
+ render json: {
144
+ stats: MyPlugin::Stats.summary,
145
+ settings: SiteSetting.my_plugin_enabled
146
+ }
147
+ end
148
+
149
+ def action
150
+ # Strong parameters for any mutating action
151
+ attrs = params.require(:my_plugin).permit(:field_one, :field_two)
152
+ result = MyPlugin::SomeService.call(attrs.to_h.symbolize_keys)
153
+
154
+ if result.success?
155
+ render json: { success: true, data: result.data }
156
+ else
157
+ render json: { success: false, errors: result.errors }, status: :unprocessable_entity
158
+ end
159
+ end
160
+ end
161
+ ```
162
+
163
+ ## Guardian: Authorization Layer
164
+
165
+ Guardian is Discourse's authorization system. Always check it for user-facing actions.
166
+
167
+ ```ruby
168
+ # Check permissions before acting
169
+ def update_post
170
+ post = Post.find(params[:id])
171
+
172
+ # guardian.can_edit_post? checks ownership, trust level, staff status
173
+ unless guardian.can_edit_post?(post)
174
+ return render json: failed_json, status: :forbidden
175
+ end
176
+
177
+ post.update!(body: params[:body])
178
+ render json: PostSerializer.new(post, scope: guardian).as_json
179
+ end
180
+
181
+ # Extending Guardian for plugin-specific permissions
182
+ # In after_initialize:
183
+ module ::Guardian::MyPluginExtensions
184
+ def can_use_my_feature?
185
+ authenticated? && (is_staff? || user.trust_level >= 2)
186
+ end
187
+ end
188
+
189
+ Guardian.prepend(::Guardian::MyPluginExtensions)
190
+ ```
191
+
192
+ ## Security Non-Negotiables
193
+
194
+ ### 1. Never Raw SQL with Interpolation — Use ActiveRecord or Named Params
195
+
196
+ ```ruby
197
+ # BAD — SQL injection
198
+ User.where("username = '#{params[:username]}'")
199
+ DB.query("SELECT * FROM users WHERE id = #{user_id}")
200
+
201
+ # GOOD — ActiveRecord parameterization
202
+ User.where(username: params[:username])
203
+ User.where("created_at > ?", params[:since])
204
+
205
+ # GOOD — Discourse DB helper with named bind params (always use this for raw SQL)
206
+ DB.query("SELECT * FROM users WHERE id = :id", id: user_id.to_i)
207
+ DB.query("UPDATE users SET flag = TRUE WHERE id = :id", id: user_id.to_i)
208
+ ```
209
+
210
+ ### 2. Staff-Only Admin Routes
211
+
212
+ ```ruby
213
+ # ALWAYS use StaffConstraint on admin routes
214
+ get '/admin/plugins/my-plugin' => 'admin/my_plugin#index',
215
+ constraints: StaffConstraint.new
216
+
217
+ # Admin::AdminController gives you these checks automatically:
218
+ # - ensure_logged_in
219
+ # - ensure_staff
220
+ # For admin-only (not just moderator), add ensure_admin
221
+ ```
222
+
223
+ ### 3. No Sensitive Data in Logs
224
+
225
+ ```ruby
226
+ # BAD — logs hash values, tokens, passwords
227
+ Rails.logger.warn("[MyPlugin] Processing hash: #{user_hash}")
228
+ Rails.logger.info("[MyPlugin] Token: #{token}")
229
+
230
+ # GOOD — log what happened, not the sensitive value
231
+ Rails.logger.info("[MyPlugin] Processing user #{user.id}")
232
+ Rails.logger.warn("[MyPlugin] Auth failed for user #{user.id}")
233
+ ```
234
+
235
+ ### 4. Custom Fields — Validate Types
236
+
237
+ ```ruby
238
+ # Register field types to prevent type confusion
239
+ register_user_custom_field_type('my_plugin_id', :integer)
240
+ register_post_custom_field_type('source_url', :string)
241
+
242
+ # Whitelist custom fields for API/serializer exposure
243
+ DiscoursePluginRegistry.serialized_current_user_fields << 'my_plugin_id'
244
+
245
+ # Access safely — always coerce type
246
+ user_id = (user.custom_fields['my_plugin_id'].to_i rescue nil)
247
+ ```
248
+
249
+ ### 5. Rate Limiting on Sensitive Endpoints
250
+
251
+ ```ruby
252
+ # For endpoints that test credentials or perform expensive operations
253
+ RateLimiter.new(current_user, "my_plugin_sensitive_action", 5, 1.minute).performed!
254
+ # Raises RateLimiter::LimitExceeded if over limit
255
+ ```
256
+
257
+ ## Site Settings
258
+
259
+ ```yaml
260
+ # config/settings.yml
261
+ plugins:
262
+ my_plugin_enabled:
263
+ default: false
264
+ client: false # server-side only unless UI needs it
265
+ my_plugin_max_items:
266
+ default: 100
267
+ min: 1
268
+ max: 1000
269
+ type: integer
270
+ ```
271
+
272
+ ```ruby
273
+ # Access in code
274
+ SiteSetting.my_plugin_enabled
275
+ SiteSetting.my_plugin_max_items
276
+
277
+ # Guard features
278
+ return unless SiteSetting.my_plugin_enabled
279
+ ```
280
+
281
+ ## Migrations
282
+
283
+ ```ruby
284
+ # db/migrate/20250101000000_create_my_plugin_records.rb
285
+ # frozen_string_literal: true
286
+
287
+ class CreateMyPluginRecords < ActiveRecord::Migration[7.0]
288
+ def change
289
+ create_table :my_plugin_records do |t|
290
+ t.integer :user_id, null: false
291
+ t.string :source_id, null: false
292
+ t.text :data
293
+ t.timestamps
294
+ end
295
+
296
+ add_index :my_plugin_records, :user_id
297
+ add_index :my_plugin_records, :source_id, unique: true
298
+ add_foreign_key :my_plugin_records, :users
299
+ end
300
+ end
301
+ ```
302
+
303
+ ## Import Script Pattern (Standalone Ruby)
304
+
305
+ Import scripts inherit from `ImportScripts::Base` and run outside the Rails request cycle.
306
+
307
+ ```ruby
308
+ # frozen_string_literal: true
309
+
310
+ require_relative "base"
311
+
312
+ class ImportScripts::MyImport < ImportScripts::Base
313
+ def initialize
314
+ super
315
+ @client = Mysql2::Client.new(
316
+ host: ENV.fetch('SOURCE_DB_HOST'),
317
+ username: ENV.fetch('SOURCE_DB_USER'),
318
+ password: ENV.fetch('SOURCE_DB_PASSWORD'),
319
+ database: ENV.fetch('SOURCE_DB_NAME')
320
+ )
321
+ end
322
+
323
+ def perform
324
+ import_users
325
+ import_categories
326
+ import_posts
327
+ end
328
+
329
+ private
330
+
331
+ def import_users
332
+ puts "Importing users..."
333
+
334
+ # Parameterized query — never interpolate
335
+ users = @client.query(
336
+ "SELECT id, email, username, display_name FROM wp_users WHERE user_status = 0",
337
+ as: :hash
338
+ )
339
+
340
+ create_users(users) do |user|
341
+ {
342
+ id: user['id'],
343
+ email: user['email'],
344
+ username: normalize_username(user['username']),
345
+ name: user['display_name']
346
+ }
347
+ end
348
+ end
349
+
350
+ def normalize_username(raw)
351
+ # Pure function — transform only, no side effects
352
+ raw.to_s.strip.downcase.gsub(/[^a-z0-9_]/, '_').truncate(20)
353
+ end
354
+ end
355
+
356
+ ImportScripts::MyImport.new.perform
357
+ ```
358
+
359
+ ## Testing
360
+
361
+ ```ruby
362
+ # spec/plugin_helper.rb — loads Discourse test env
363
+ require 'rails_helper'
364
+
365
+ # spec/requests/admin/my_plugin_spec.rb
366
+ RSpec.describe Admin::MyPluginController do
367
+ fab!(:admin) { Fabricate(:admin) }
368
+ fab!(:user) { Fabricate(:user) }
369
+
370
+ before { sign_in(admin) }
371
+
372
+ describe "GET #index" do
373
+ it "returns success for admin" do
374
+ get "/admin/plugins/my-plugin.json"
375
+ expect(response.status).to eq(200)
376
+ end
377
+ end
378
+
379
+ describe "authorization" do
380
+ it "rejects non-staff" do
381
+ sign_in(user)
382
+ get "/admin/plugins/my-plugin.json"
383
+ expect(response.status).to eq(404) # Discourse returns 404 for staff routes
384
+ end
385
+ end
386
+ end
387
+
388
+ # Pure logic specs — no Discourse dependencies
389
+ RSpec.describe MyPlugin::UserSetup do
390
+ describe ".call" do
391
+ it "normalizes username" do
392
+ expect(described_class.normalize("John Doe!")).to eq("john_doe_")
393
+ end
394
+ end
395
+ end
396
+ ```
397
+
398
+ ## Security Checklist
399
+
400
+ - [ ] `StaffConstraint.new` on all admin routes
401
+ - [ ] Inherited from `Admin::AdminController` for admin endpoints
402
+ - [ ] Strong parameters on all mutating endpoints
403
+ - [ ] No string interpolation in SQL — use ActiveRecord or `DB.query` with `:named` params
404
+ - [ ] `guardian.can_*?` checked before user-facing mutations
405
+ - [ ] No sensitive values (tokens, hashes, passwords) in log output
406
+ - [ ] Rate limiting on credential-testing or expensive endpoints
407
+ - [ ] Custom field types registered (`register_*_custom_field_type`)
408
+ - [ ] Site settings used for feature flags, not hardcoded booleans
409
+
410
+ ## Common Pitfalls
411
+
412
+ | Pitfall | Correct Approach |
413
+ |---------|-----------------|
414
+ | Monkey-patching Discourse classes | Use `class_eval` / `prepend` in `after_initialize` |
415
+ | Direct DB string interpolation | `DB.query("... WHERE id = :id", id: val)` |
416
+ | Checking `current_user.staff?` in controller | Inherit `Admin::AdminController` instead |
417
+ | Logging `user.password_hash` for debugging | Log `user.id` only |
418
+ | `params[:field]` without strong params | Always `params.require().permit()` |
419
+ | Hardcoded credentials in plugin.rb | `ENV.fetch('KEY')` or Rails credentials |
420
+
421
+ ## When to Load Reference Files
422
+
423
+ ### Security Examples
424
+ **File**: [`references/security.md`](references/security.md)
425
+ **Load when**: Implementing auth hooks, custom Guardian checks, SQL safety in import scripts
426
+ **Contains**: Guardian extension patterns, DB.query vs ActiveRecord, rate limiting, log hygiene
427
+
428
+ ### Admin UI (Ember)
429
+ **File**: [`references/admin-ui.md`](references/admin-ui.md)
430
+ **Load when**: Building admin panel Ember components
431
+ **Contains**: Route setup, Ember component patterns, REST adapter, admin nav
432
+
433
+ ### Import Scripts
434
+ **File**: [`references/import-scripts.md`](references/import-scripts.md)
435
+ **Load when**: Writing data migration scripts
436
+ **Contains**: ImportScripts::Base lifecycle, batching, lookup maps, resume/idempotency
437
+
438
+ ---
439
+
440
+ **Evidence Base**: Discourse Developer Docs, Discourse GitHub (discourse-solved, discourse-data-explorer), Discourse CVE history (2024–2026), Rails Security Guide.