@onion-ai/cli 1.0.0-beta.1

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 (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +529 -0
  3. package/bin/onion.js +6 -0
  4. package/framework/CLAUDE.md +45 -0
  5. package/framework/VERSION +1 -0
  6. package/framework/agents/compliance/iso-22301-specialist.md +985 -0
  7. package/framework/agents/compliance/iso-27001-specialist.md +713 -0
  8. package/framework/agents/compliance/pmbok-specialist.md +739 -0
  9. package/framework/agents/compliance/security-information-master.md +907 -0
  10. package/framework/agents/compliance/soc2-specialist.md +889 -0
  11. package/framework/agents/deployment/docker-specialist.md +1192 -0
  12. package/framework/agents/development/c4-architecture-specialist.md +745 -0
  13. package/framework/agents/development/c4-documentation-specialist.md +695 -0
  14. package/framework/agents/development/clickup-specialist.md +396 -0
  15. package/framework/agents/development/cursor-specialist.md +277 -0
  16. package/framework/agents/development/docs-reverse-engineer.md +417 -0
  17. package/framework/agents/development/gamma-api-specialist.md +1168 -0
  18. package/framework/agents/development/gitflow-specialist.md +1206 -0
  19. package/framework/agents/development/linux-security-specialist.md +675 -0
  20. package/framework/agents/development/mermaid-specialist.md +515 -0
  21. package/framework/agents/development/nodejs-specialist.md +672 -0
  22. package/framework/agents/development/nx-migration-specialist.md +866 -0
  23. package/framework/agents/development/nx-monorepo-specialist.md +618 -0
  24. package/framework/agents/development/postgres-specialist.md +1123 -0
  25. package/framework/agents/development/react-developer.md +131 -0
  26. package/framework/agents/development/runflow-specialist.md +277 -0
  27. package/framework/agents/development/system-documentation-orchestrator.md +1387 -0
  28. package/framework/agents/development/task-specialist.md +677 -0
  29. package/framework/agents/git/branch-code-reviewer.md +225 -0
  30. package/framework/agents/git/branch-documentation-writer.md +161 -0
  31. package/framework/agents/git/branch-metaspec-checker.md +67 -0
  32. package/framework/agents/git/branch-test-planner.md +176 -0
  33. package/framework/agents/meta/agent-creator-specialist.md +1266 -0
  34. package/framework/agents/meta/command-creator-specialist.md +1676 -0
  35. package/framework/agents/meta/metaspec-gate-keeper.md +240 -0
  36. package/framework/agents/meta/onion.md +824 -0
  37. package/framework/agents/product/branding-positioning-specialist.md +1029 -0
  38. package/framework/agents/product/extract-meeting-specialist.md +394 -0
  39. package/framework/agents/product/meeting-consolidator.md +482 -0
  40. package/framework/agents/product/pain-price-specialist.md +508 -0
  41. package/framework/agents/product/presentation-orchestrator.md +1190 -0
  42. package/framework/agents/product/product-agent.md +201 -0
  43. package/framework/agents/product/story-points-framework-specialist.md +538 -0
  44. package/framework/agents/product/storytelling-business-specialist.md +890 -0
  45. package/framework/agents/research/research-agent.md +292 -0
  46. package/framework/agents/review/code-reviewer.md +154 -0
  47. package/framework/agents/review/corporate-compliance-specialist.md +370 -0
  48. package/framework/agents/testing/test-agent.md +424 -0
  49. package/framework/agents/testing/test-engineer.md +294 -0
  50. package/framework/agents/testing/test-planner.md +117 -0
  51. package/framework/commands/common/prompts/README.md +208 -0
  52. package/framework/commands/common/prompts/clickup-patterns.md +144 -0
  53. package/framework/commands/common/prompts/code-review-checklist.md +168 -0
  54. package/framework/commands/common/prompts/git-workflow-patterns.md +235 -0
  55. package/framework/commands/common/prompts/output-formats.md +240 -0
  56. package/framework/commands/common/prompts/technical.md +194 -0
  57. package/framework/commands/common/templates/abstraction-template.md +399 -0
  58. package/framework/commands/common/templates/agent-template.md +353 -0
  59. package/framework/commands/common/templates/business_context_template.md +748 -0
  60. package/framework/commands/common/templates/command-template.md +273 -0
  61. package/framework/commands/common/templates/technical_context_template.md +526 -0
  62. package/framework/commands/design/screen-spec.md +505 -0
  63. package/framework/commands/development/runflow-dev.md +465 -0
  64. package/framework/commands/docs/build-business-docs.md +299 -0
  65. package/framework/commands/docs/build-compliance-docs.md +143 -0
  66. package/framework/commands/docs/build-index.md +119 -0
  67. package/framework/commands/docs/build-tech-docs.md +221 -0
  68. package/framework/commands/docs/docs-health.md +141 -0
  69. package/framework/commands/docs/help.md +278 -0
  70. package/framework/commands/docs/refine-vision.md +25 -0
  71. package/framework/commands/docs/reverse-consolidate.md +158 -0
  72. package/framework/commands/docs/sync-sessions.md +354 -0
  73. package/framework/commands/docs/validate-docs.md +157 -0
  74. package/framework/commands/engineer/bump.md +29 -0
  75. package/framework/commands/engineer/docs.md +11 -0
  76. package/framework/commands/engineer/hotfix.md +183 -0
  77. package/framework/commands/engineer/plan.md +85 -0
  78. package/framework/commands/engineer/pr-update.md +219 -0
  79. package/framework/commands/engineer/pr.md +117 -0
  80. package/framework/commands/engineer/pre-pr.md +81 -0
  81. package/framework/commands/engineer/start.md +254 -0
  82. package/framework/commands/engineer/validate-phase-sync.md +134 -0
  83. package/framework/commands/engineer/warm-up.md +20 -0
  84. package/framework/commands/engineer/work.md +155 -0
  85. package/framework/commands/f/company-context-extractor.md +93 -0
  86. package/framework/commands/f/process-meetings.md +103 -0
  87. package/framework/commands/git/README.md +682 -0
  88. package/framework/commands/git/code-review.md +213 -0
  89. package/framework/commands/git/fast-commit.md +43 -0
  90. package/framework/commands/git/feature/finish.md +88 -0
  91. package/framework/commands/git/feature/publish.md +89 -0
  92. package/framework/commands/git/feature/start.md +172 -0
  93. package/framework/commands/git/help.md +100 -0
  94. package/framework/commands/git/hotfix/finish.md +96 -0
  95. package/framework/commands/git/hotfix/start.md +92 -0
  96. package/framework/commands/git/init.md +111 -0
  97. package/framework/commands/git/release/finish.md +96 -0
  98. package/framework/commands/git/release/start.md +93 -0
  99. package/framework/commands/git/sync.md +199 -0
  100. package/framework/commands/meta/all-tools.md +58 -0
  101. package/framework/commands/meta/analyze-complex-problem.md +186 -0
  102. package/framework/commands/meta/create-abstraction.md +882 -0
  103. package/framework/commands/meta/create-agent-express.md +98 -0
  104. package/framework/commands/meta/create-agent.md +210 -0
  105. package/framework/commands/meta/create-command.md +203 -0
  106. package/framework/commands/meta/create-knowledge-base.md +143 -0
  107. package/framework/commands/meta/create-task-structure.md +150 -0
  108. package/framework/commands/meta/setup-integration.md +274 -0
  109. package/framework/commands/onion.md +169 -0
  110. package/framework/commands/product/README.md +249 -0
  111. package/framework/commands/product/analyze-pain-price.md +694 -0
  112. package/framework/commands/product/branding.md +458 -0
  113. package/framework/commands/product/check.md +46 -0
  114. package/framework/commands/product/checklist-sync.md +239 -0
  115. package/framework/commands/product/collect.md +95 -0
  116. package/framework/commands/product/consolidate-meetings.md +291 -0
  117. package/framework/commands/product/estimate.md +511 -0
  118. package/framework/commands/product/extract-meeting.md +226 -0
  119. package/framework/commands/product/feature.md +416 -0
  120. package/framework/commands/product/light-arch.md +82 -0
  121. package/framework/commands/product/presentation.md +174 -0
  122. package/framework/commands/product/refine.md +161 -0
  123. package/framework/commands/product/spec.md +79 -0
  124. package/framework/commands/product/task-check.md +378 -0
  125. package/framework/commands/product/task.md +603 -0
  126. package/framework/commands/product/validate-task.md +325 -0
  127. package/framework/commands/product/warm-up.md +24 -0
  128. package/framework/commands/quick/analisys.md +17 -0
  129. package/framework/commands/test/e2e.md +377 -0
  130. package/framework/commands/test/integration.md +508 -0
  131. package/framework/commands/test/unit.md +381 -0
  132. package/framework/commands/validate/collab/pair-testing.md +657 -0
  133. package/framework/commands/validate/collab/three-amigos.md +534 -0
  134. package/framework/commands/validate/qa-points/estimate.md +660 -0
  135. package/framework/commands/validate/test-strategy/analyze.md +1201 -0
  136. package/framework/commands/validate/test-strategy/create.md +411 -0
  137. package/framework/commands/validate/workflow.md +370 -0
  138. package/framework/commands/warm-up.md +20 -0
  139. package/framework/docs/architecture/acoplamento-clickup-problema-analise.md +468 -0
  140. package/framework/docs/architecture/desacoplamento-roadmap.md +364 -0
  141. package/framework/docs/architecture/validacao-fase-1.md +235 -0
  142. package/framework/docs/c4/c4-detection-rules.md +395 -0
  143. package/framework/docs/c4/c4-documentation-templates.md +579 -0
  144. package/framework/docs/c4/c4-mermaid-patterns.md +331 -0
  145. package/framework/docs/c4/c4-templates.md +256 -0
  146. package/framework/docs/clickup/clickup-acceptance-criteria-strategy.md +329 -0
  147. package/framework/docs/clickup/clickup-auto-update-strategy.md +340 -0
  148. package/framework/docs/clickup/clickup-comment-formatter.md +239 -0
  149. package/framework/docs/clickup/clickup-description-fix.md +384 -0
  150. package/framework/docs/clickup/clickup-dual-comment-strategy.md +528 -0
  151. package/framework/docs/clickup/clickup-formatting.md +302 -0
  152. package/framework/docs/clickup/separador-tamanho-otimizado.md +258 -0
  153. package/framework/docs/engineer/pre-pr-acceptance-validation.md +256 -0
  154. package/framework/docs/onion/ESPERANTO.md +293 -0
  155. package/framework/docs/onion/agents-reference.md +832 -0
  156. package/framework/docs/onion/clickup-integration.md +780 -0
  157. package/framework/docs/onion/commands-guide.md +924 -0
  158. package/framework/docs/onion/engineering-flows.md +900 -0
  159. package/framework/docs/onion/getting-started.md +803 -0
  160. package/framework/docs/onion/maintenance-checklist.md +421 -0
  161. package/framework/docs/onion/naming-conventions.md +286 -0
  162. package/framework/docs/onion/practical-examples.md +854 -0
  163. package/framework/docs/product/story-points-integration.md +269 -0
  164. package/framework/docs/product/story-points-validation.md +237 -0
  165. package/framework/docs/reviews/task-manager-docs-review-2025-11-24.md +184 -0
  166. package/framework/docs/strategies/clickup-comment-patterns.md +766 -0
  167. package/framework/docs/strategies/clickup-integration-tests.md +602 -0
  168. package/framework/docs/strategies/clickup-mcp-wrappers-tests.md +888 -0
  169. package/framework/docs/strategies/clickup-regression-tests.md +587 -0
  170. package/framework/docs/strategies/visual-patterns.md +315 -0
  171. package/framework/docs/templates/README.md +649 -0
  172. package/framework/docs/templates/adr-template.md +226 -0
  173. package/framework/docs/templates/analysis-template.md +280 -0
  174. package/framework/docs/templates/execution-plan-template.md +430 -0
  175. package/framework/docs/templates/guide-template.md +367 -0
  176. package/framework/docs/templates/phase-execution-prompt-template.md +504 -0
  177. package/framework/docs/templates/reference-template.md +522 -0
  178. package/framework/docs/templates/solution-template.md +390 -0
  179. package/framework/docs/tools/README.md +356 -0
  180. package/framework/docs/tools/agents.md +365 -0
  181. package/framework/docs/tools/commands.md +669 -0
  182. package/framework/docs/tools/cursor.md +539 -0
  183. package/framework/docs/tools/mcps.md +937 -0
  184. package/framework/docs/tools/rules.md +461 -0
  185. package/framework/rules/language-and-documentation.mdc +371 -0
  186. package/framework/rules/nestjs-controllers.md +83 -0
  187. package/framework/rules/nestjs-dtos.md +255 -0
  188. package/framework/rules/nestjs-modules.md +141 -0
  189. package/framework/rules/nestjs-services.md +230 -0
  190. package/framework/rules/nx-rules.mdc +41 -0
  191. package/framework/rules/onion-patterns.mdc +197 -0
  192. package/framework/skills/codebase-visualizer/SKILL.md +26 -0
  193. package/framework/skills/codebase-visualizer/scripts/visualize.py +131 -0
  194. package/framework/skills/collect/SKILL.md +84 -0
  195. package/framework/skills/create-rule/SKILL.md +152 -0
  196. package/framework/skills/db-schema-visualizer/SKILL.md +49 -0
  197. package/framework/skills/db-schema-visualizer/scripts/visualize.py +1191 -0
  198. package/framework/skills/sync-meetings/SKILL.md +239 -0
  199. package/framework/utils/clickup-mcp-wrappers.md +744 -0
  200. package/framework/utils/date-time-standards.md +200 -0
  201. package/framework/utils/task-manager/README.md +94 -0
  202. package/framework/utils/task-manager/adapters/asana.md +377 -0
  203. package/framework/utils/task-manager/adapters/clickup.md +467 -0
  204. package/framework/utils/task-manager/adapters/linear.md +421 -0
  205. package/framework/utils/task-manager/detector.md +299 -0
  206. package/framework/utils/task-manager/factory.md +363 -0
  207. package/framework/utils/task-manager/interface.md +248 -0
  208. package/framework/utils/task-manager/types.md +409 -0
  209. package/package.json +41 -0
  210. package/src/cli.js +73 -0
  211. package/src/commands/doctor.js +191 -0
  212. package/src/commands/init.js +287 -0
  213. package/src/commands/install.js +261 -0
  214. package/src/commands/list.js +152 -0
  215. package/src/commands/uninstall.js +90 -0
  216. package/src/commands/update.js +26 -0
  217. package/src/utils/fs.js +89 -0
  218. package/src/utils/log.js +35 -0
  219. package/src/utils/paths.js +32 -0
  220. package/src/utils/prompt.js +76 -0
@@ -0,0 +1,261 @@
1
+ import { readdirSync, readFileSync, existsSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import {
5
+ CLAUDE_HOME, ONION_HOME, ONION_MANIFEST,
6
+ ONION_COMMANDS, ONION_AGENTS, CLAUDE_SKILLS,
7
+ CLAUDE_MD, FRAMEWORK_SOURCE
8
+ } from '../utils/paths.js';
9
+ import { ensureDir, copyDir, readFile, writeFile, exists, countFiles, removeDir } from '../utils/fs.js';
10
+ import * as log from '../utils/log.js';
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+
14
+ /** Markers we add to CLAUDE.md to load Onion */
15
+ const CLAUDE_MD_MARKER_START = '# === Onion Framework (auto-managed — do not edit) ===';
16
+ const CLAUDE_MD_MARKER_END = '# === End Onion Framework ===';
17
+
18
+ const CLAUDE_MD_BLOCK = `${CLAUDE_MD_MARKER_START}
19
+ # Onion Framework - AI-powered development workflow system
20
+ # Installed by @onion-ai/cli — https://github.com/onion-ai/cli
21
+ #
22
+ # Commands: Use /onion:* to access all framework commands
23
+ # Agents: Use @onion-* to access all framework agents
24
+ # Docs: ~/.claude/onion/docs/ for reference documentation
25
+ #
26
+ # To update: npx @onion-ai/cli update
27
+ # To remove: npx @onion-ai/cli uninstall
28
+
29
+ @onion/CLAUDE.md
30
+ ${CLAUDE_MD_MARKER_END}`;
31
+
32
+ export async function install(opts = {}) {
33
+ const { force = false, dryRun = false } = opts;
34
+
35
+ log.header('Onion Framework Installer');
36
+
37
+ // 1. Check prerequisites
38
+ log.step('Checking prerequisites...');
39
+
40
+ if (!exists(CLAUDE_HOME)) {
41
+ log.error(`Claude Code config not found at ${CLAUDE_HOME}`);
42
+ log.info('Install Claude Code first: https://claude.ai/claude-code');
43
+ process.exit(1);
44
+ }
45
+ log.success('Claude Code detected');
46
+
47
+ // Check for existing installation
48
+ if (exists(ONION_HOME) && !force) {
49
+ const manifest = readManifest();
50
+ if (manifest) {
51
+ log.warn(`Onion Framework v${manifest.version} is already installed`);
52
+ log.info('Use --force to overwrite or run: onion update');
53
+ process.exit(1);
54
+ }
55
+ }
56
+
57
+ // Check framework source exists
58
+ if (!exists(FRAMEWORK_SOURCE)) {
59
+ log.error('Framework files not found in package. This is a bug.');
60
+ log.info('Please report: https://github.com/onion-ai/cli/issues');
61
+ process.exit(1);
62
+ }
63
+
64
+ if (dryRun) {
65
+ log.header('Dry run — no changes will be made');
66
+ }
67
+
68
+ // 2. Count what we're installing
69
+ const commandCount = countFiles(join(FRAMEWORK_SOURCE, 'commands'));
70
+ const agentCount = countFiles(join(FRAMEWORK_SOURCE, 'agents'));
71
+ const skillCount = countSkillDirs(join(FRAMEWORK_SOURCE, 'skills'));
72
+ const docCount = countFiles(join(FRAMEWORK_SOURCE, 'docs'));
73
+
74
+ log.info(`Found: ${commandCount} commands, ${agentCount} agents, ${skillCount} skills, ${docCount} docs`);
75
+
76
+ if (dryRun) {
77
+ log.blank();
78
+ log.step(`Would create: ${ONION_HOME}`);
79
+ log.step(`Would create: ${ONION_COMMANDS} (commands with onion: prefix)`);
80
+ log.step(`Would create: ${ONION_AGENTS} (agents)`);
81
+ log.step(`Would install skills to: ${CLAUDE_SKILLS}`);
82
+ log.step(`Would patch: ${CLAUDE_MD}`);
83
+ log.blank();
84
+ log.success('Dry run complete. Run without --dry-run to install.');
85
+ return;
86
+ }
87
+
88
+ // 3. Clean existing installation if --force
89
+ if (force) {
90
+ log.step('Removing existing installation...');
91
+ removeDir(ONION_HOME);
92
+ removeDir(ONION_COMMANDS);
93
+ removeDir(ONION_AGENTS);
94
+ removeOnionSkills(CLAUDE_SKILLS);
95
+ }
96
+
97
+ // 4. Install framework home (docs, utils, rules, CLAUDE.md)
98
+ log.step('Installing framework core...');
99
+ ensureDir(ONION_HOME);
100
+
101
+ const coreDirs = ['docs', 'utils', 'rules'];
102
+ for (const dir of coreDirs) {
103
+ const src = join(FRAMEWORK_SOURCE, dir);
104
+ if (exists(src)) {
105
+ copyDir(src, join(ONION_HOME, dir));
106
+ }
107
+ }
108
+
109
+ // Copy Onion CLAUDE.md entry point
110
+ const onionClaudeMd = join(FRAMEWORK_SOURCE, 'CLAUDE.md');
111
+ if (existsSync(onionClaudeMd)) {
112
+ const content = readFileSync(onionClaudeMd, 'utf-8');
113
+ writeFile(join(ONION_HOME, 'CLAUDE.md'), content);
114
+ }
115
+ log.success('Installed framework core (docs, utils, rules)');
116
+
117
+ // 5. Install commands → ~/.claude/commands/onion/
118
+ log.step('Installing commands (onion: prefix)...');
119
+ const cmdSrc = join(FRAMEWORK_SOURCE, 'commands');
120
+ if (exists(cmdSrc)) {
121
+ copyDir(cmdSrc, ONION_COMMANDS);
122
+ log.success(`Installed ${commandCount} commands → /onion:*`);
123
+ }
124
+
125
+ // 6. Install agents → ~/.claude/agents/onion/
126
+ log.step('Installing agents...');
127
+ const agentSrc = join(FRAMEWORK_SOURCE, 'agents');
128
+ if (exists(agentSrc)) {
129
+ copyDir(agentSrc, ONION_AGENTS);
130
+ log.success(`Installed ${agentCount} agents`);
131
+ }
132
+
133
+ // 7. Install skills → ~/.claude/skills/onion-<name>/
134
+ log.step('Installing skills...');
135
+ const skillSrc = join(FRAMEWORK_SOURCE, 'skills');
136
+ if (exists(skillSrc)) {
137
+ installSkills(skillSrc, CLAUDE_SKILLS);
138
+ log.success(`Installed ${skillCount} skills`);
139
+ }
140
+
141
+ // 8. Patch CLAUDE.md
142
+ log.step('Configuring Claude Code integration...');
143
+ patchClaudeMd();
144
+ log.success('Updated ~/.claude/CLAUDE.md');
145
+
146
+ // 9. Write manifest
147
+ const manifest = {
148
+ version: getPackageVersion(),
149
+ installedAt: new Date().toISOString(),
150
+ components: {
151
+ commands: commandCount,
152
+ agents: agentCount,
153
+ skills: skillCount,
154
+ docs: docCount
155
+ },
156
+ paths: {
157
+ home: ONION_HOME,
158
+ commands: ONION_COMMANDS,
159
+ agents: ONION_AGENTS,
160
+ skills: CLAUDE_SKILLS
161
+ }
162
+ };
163
+ writeFile(ONION_MANIFEST, JSON.stringify(manifest, null, 2));
164
+
165
+ // 10. Summary
166
+ log.blank();
167
+ log.header('Installation complete!');
168
+ log.info('Available commands in Claude Code:');
169
+ log.step('/onion — Intelligent entry point');
170
+ log.step('/onion:product:task — Create structured tasks');
171
+ log.step('/onion:engineer:start — Start feature development');
172
+ log.step('/onion:engineer:work — Continue active feature');
173
+ log.step('/onion:git:feature:start — Start GitFlow feature branch');
174
+ log.blank();
175
+ log.info('Run "onion doctor" to verify installation');
176
+ log.info('Run "onion list" to see all components');
177
+ log.blank();
178
+ }
179
+
180
+ /**
181
+ * Install skills with onion- prefix into Claude's skills directory.
182
+ */
183
+ function installSkills(srcDir, destDir) {
184
+ ensureDir(destDir);
185
+ const entries = readdirSync(srcDir, { withFileTypes: true });
186
+ for (const entry of entries) {
187
+ if (entry.isDirectory()) {
188
+ copyDir(join(srcDir, entry.name), join(destDir, `onion-${entry.name}`));
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Remove only onion-prefixed skills from Claude's skills directory.
195
+ */
196
+ function removeOnionSkills(skillsDir) {
197
+ if (!existsSync(skillsDir)) return;
198
+ const entries = readdirSync(skillsDir, { withFileTypes: true });
199
+ for (const entry of entries) {
200
+ if (entry.isDirectory() && entry.name.startsWith('onion-')) {
201
+ removeDir(join(skillsDir, entry.name));
202
+ }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Count skill directories.
208
+ */
209
+ function countSkillDirs(dir) {
210
+ if (!existsSync(dir)) return 0;
211
+ return readdirSync(dir, { withFileTypes: true })
212
+ .filter(e => e.isDirectory())
213
+ .length;
214
+ }
215
+
216
+ /**
217
+ * Patch CLAUDE.md to include Onion framework reference.
218
+ * Non-destructive — preserves existing content.
219
+ */
220
+ function patchClaudeMd() {
221
+ let content = readFile(CLAUDE_MD) || '';
222
+
223
+ // Remove any existing Onion block
224
+ const startIdx = content.indexOf(CLAUDE_MD_MARKER_START);
225
+ const endIdx = content.indexOf(CLAUDE_MD_MARKER_END);
226
+ if (startIdx !== -1 && endIdx !== -1) {
227
+ content = content.substring(0, startIdx).trimEnd() +
228
+ '\n' +
229
+ content.substring(endIdx + CLAUDE_MD_MARKER_END.length);
230
+ }
231
+
232
+ // Append Onion block at the end
233
+ content = content.trimEnd() + '\n\n' + CLAUDE_MD_BLOCK + '\n';
234
+
235
+ writeFile(CLAUDE_MD, content);
236
+ }
237
+
238
+ /**
239
+ * Read the installation manifest.
240
+ */
241
+ function readManifest() {
242
+ const content = readFile(ONION_MANIFEST);
243
+ if (!content) return null;
244
+ try {
245
+ return JSON.parse(content);
246
+ } catch {
247
+ return null;
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Get the package version.
253
+ */
254
+ function getPackageVersion() {
255
+ try {
256
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8'));
257
+ return pkg.version;
258
+ } catch {
259
+ return 'unknown';
260
+ }
261
+ }
@@ -0,0 +1,152 @@
1
+ import { readdirSync, existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import {
4
+ ONION_COMMANDS, ONION_AGENTS, CLAUDE_SKILLS, ONION_HOME
5
+ } from '../utils/paths.js';
6
+ import { exists, readFile } from '../utils/fs.js';
7
+ import * as log from '../utils/log.js';
8
+
9
+ export async function list(opts = {}) {
10
+ const showAll = !opts.commands && !opts.agents && !opts.skills;
11
+
12
+ const manifest = readManifest();
13
+ if (!manifest) {
14
+ log.error('Onion Framework is not installed');
15
+ log.info('Run: npx @onion-ai/cli install');
16
+ process.exit(1);
17
+ }
18
+
19
+ log.header(`Onion Framework v${manifest.version}`);
20
+
21
+ if (showAll || opts.commands) {
22
+ listCommands();
23
+ }
24
+
25
+ if (showAll || opts.agents) {
26
+ listAgents();
27
+ }
28
+
29
+ if (showAll || opts.skills) {
30
+ listSkills();
31
+ }
32
+
33
+ log.blank();
34
+ }
35
+
36
+ function listCommands() {
37
+ log.step('Commands (/onion:*):');
38
+ if (!exists(ONION_COMMANDS)) {
39
+ log.warn(' No commands directory found');
40
+ return;
41
+ }
42
+
43
+ const commands = collectCommands(ONION_COMMANDS, '');
44
+ for (const cmd of commands.sort()) {
45
+ console.log(` /onion:${cmd}`);
46
+ }
47
+ console.log(` (${commands.length} total)`);
48
+ log.blank();
49
+ }
50
+
51
+ function listAgents() {
52
+ log.step('Agents:');
53
+ if (!exists(ONION_AGENTS)) {
54
+ log.warn(' No agents directory found');
55
+ return;
56
+ }
57
+
58
+ const agents = collectAgents(ONION_AGENTS);
59
+ for (const agent of agents.sort()) {
60
+ console.log(` @${agent}`);
61
+ }
62
+ console.log(` (${agents.length} total)`);
63
+ log.blank();
64
+ }
65
+
66
+ function listSkills() {
67
+ log.step('Skills:');
68
+ if (!existsSync(CLAUDE_SKILLS)) {
69
+ log.warn(' No skills directory found');
70
+ return;
71
+ }
72
+
73
+ const entries = readdirSync(CLAUDE_SKILLS, { withFileTypes: true });
74
+ const skills = entries
75
+ .filter(e => e.isDirectory() && e.name.startsWith('onion-'))
76
+ .map(e => e.name);
77
+
78
+ for (const skill of skills.sort()) {
79
+ console.log(` ${skill}`);
80
+ }
81
+ console.log(` (${skills.length} total)`);
82
+ log.blank();
83
+ }
84
+
85
+ /**
86
+ * Recursively collect command paths from directory structure.
87
+ * Maps directory/file.md → "directory:file"
88
+ */
89
+ function collectCommands(dir, prefix) {
90
+ const results = [];
91
+ const entries = readdirSync(dir, { withFileTypes: true });
92
+
93
+ for (const entry of entries) {
94
+ if (entry.isDirectory()) {
95
+ const newPrefix = prefix ? `${prefix}:${entry.name}` : entry.name;
96
+ results.push(...collectCommands(join(dir, entry.name), newPrefix));
97
+ } else if (entry.name.endsWith('.md')) {
98
+ const name = entry.name.replace('.md', '');
99
+ const cmdPath = prefix ? `${prefix}:${name}` : name;
100
+ results.push(cmdPath);
101
+ }
102
+ }
103
+
104
+ return results;
105
+ }
106
+
107
+ /**
108
+ * Recursively collect agent names from directory structure.
109
+ */
110
+ function collectAgents(dir) {
111
+ const results = [];
112
+ const entries = readdirSync(dir, { withFileTypes: true });
113
+
114
+ for (const entry of entries) {
115
+ if (entry.isDirectory()) {
116
+ results.push(...collectAgents(join(dir, entry.name)));
117
+ } else if (entry.name.endsWith('.md')) {
118
+ // Try to read the agent name from frontmatter
119
+ const name = extractFrontmatterName(join(dir, entry.name)) ||
120
+ entry.name.replace('.md', '');
121
+ results.push(name);
122
+ }
123
+ }
124
+
125
+ return results;
126
+ }
127
+
128
+ /**
129
+ * Extract the 'name' field from YAML frontmatter.
130
+ */
131
+ function extractFrontmatterName(filePath) {
132
+ try {
133
+ const content = readFileSync(filePath, 'utf-8');
134
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
135
+ if (!fmMatch) return null;
136
+ const nameMatch = fmMatch[1].match(/^name:\s*(.+)$/m);
137
+ if (!nameMatch) return null;
138
+ return nameMatch[1].trim().replace(/['"]/g, '');
139
+ } catch {
140
+ return null;
141
+ }
142
+ }
143
+
144
+ function readManifest() {
145
+ const content = readFile(join(ONION_HOME, 'manifest.json'));
146
+ if (!content) return null;
147
+ try {
148
+ return JSON.parse(content);
149
+ } catch {
150
+ return null;
151
+ }
152
+ }
@@ -0,0 +1,90 @@
1
+ import { readdirSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import {
4
+ ONION_HOME, ONION_MANIFEST, ONION_COMMANDS, ONION_AGENTS,
5
+ CLAUDE_SKILLS, CLAUDE_MD
6
+ } from '../utils/paths.js';
7
+ import { readFile, writeFile, removeDir, exists } from '../utils/fs.js';
8
+ import * as log from '../utils/log.js';
9
+
10
+ const CLAUDE_MD_MARKER_START = '# === Onion Framework (auto-managed — do not edit) ===';
11
+ const CLAUDE_MD_MARKER_END = '# === End Onion Framework ===';
12
+
13
+ export async function uninstall(opts = {}) {
14
+ log.header('Onion Framework Uninstaller');
15
+
16
+ if (!exists(ONION_HOME) && !exists(ONION_COMMANDS)) {
17
+ log.warn('Onion Framework is not installed');
18
+ return;
19
+ }
20
+
21
+ // 1. Remove commands
22
+ log.step('Removing commands...');
23
+ if (exists(ONION_COMMANDS)) {
24
+ removeDir(ONION_COMMANDS);
25
+ log.success('Removed commands from ~/.claude/commands/onion/');
26
+ }
27
+
28
+ // 2. Remove agents
29
+ log.step('Removing agents...');
30
+ if (exists(ONION_AGENTS)) {
31
+ removeDir(ONION_AGENTS);
32
+ log.success('Removed agents from ~/.claude/agents/onion/');
33
+ }
34
+
35
+ // 3. Remove onion-prefixed skills
36
+ log.step('Removing skills...');
37
+ if (existsSync(CLAUDE_SKILLS)) {
38
+ const entries = readdirSync(CLAUDE_SKILLS, { withFileTypes: true });
39
+ let removed = 0;
40
+ for (const entry of entries) {
41
+ if (entry.isDirectory() && entry.name.startsWith('onion-')) {
42
+ removeDir(join(CLAUDE_SKILLS, entry.name));
43
+ removed++;
44
+ }
45
+ }
46
+ if (removed > 0) {
47
+ log.success(`Removed ${removed} skills from ~/.claude/skills/`);
48
+ }
49
+ }
50
+
51
+ // 4. Remove framework home
52
+ log.step('Removing framework core...');
53
+ if (exists(ONION_HOME)) {
54
+ removeDir(ONION_HOME);
55
+ log.success('Removed ~/.claude/onion/');
56
+ }
57
+
58
+ // 5. Unpatch CLAUDE.md
59
+ log.step('Restoring Claude Code configuration...');
60
+ unpatchClaudeMd();
61
+ log.success('Restored ~/.claude/CLAUDE.md');
62
+
63
+ log.blank();
64
+ log.header('Uninstall complete');
65
+ log.info('The Onion Framework has been removed.');
66
+ log.info('Your Claude Code configuration is intact.');
67
+ log.blank();
68
+ }
69
+
70
+ /**
71
+ * Remove the Onion block from CLAUDE.md.
72
+ */
73
+ function unpatchClaudeMd() {
74
+ let content = readFile(CLAUDE_MD);
75
+ if (!content) return;
76
+
77
+ const startIdx = content.indexOf(CLAUDE_MD_MARKER_START);
78
+ const endIdx = content.indexOf(CLAUDE_MD_MARKER_END);
79
+
80
+ if (startIdx === -1 || endIdx === -1) return;
81
+
82
+ content = content.substring(0, startIdx).trimEnd() +
83
+ '\n' +
84
+ content.substring(endIdx + CLAUDE_MD_MARKER_END.length).trimStart();
85
+
86
+ // Clean up excess blank lines
87
+ content = content.replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
88
+
89
+ writeFile(CLAUDE_MD, content);
90
+ }
@@ -0,0 +1,26 @@
1
+ import { readFile } from '../utils/fs.js';
2
+ import { ONION_MANIFEST } from '../utils/paths.js';
3
+ import * as log from '../utils/log.js';
4
+
5
+ export async function update() {
6
+ log.header('Onion Framework Update');
7
+
8
+ // Read current manifest
9
+ const manifestContent = readFile(ONION_MANIFEST);
10
+ if (!manifestContent) {
11
+ log.error('Onion Framework is not installed');
12
+ log.info('Run: npx @onion-ai/cli install');
13
+ process.exit(1);
14
+ }
15
+
16
+ const manifest = JSON.parse(manifestContent);
17
+ log.info(`Current version: ${manifest.version}`);
18
+
19
+ // Re-run install with --force
20
+ log.step('Reinstalling framework...');
21
+ const { install } = await import('./install.js');
22
+ await install({ force: true });
23
+
24
+ log.blank();
25
+ log.success('Update complete');
26
+ }
@@ -0,0 +1,89 @@
1
+ import { existsSync, mkdirSync, cpSync, rmSync, readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
2
+ import { join, relative } from 'path';
3
+
4
+ /**
5
+ * Ensure a directory exists, creating it recursively if needed.
6
+ */
7
+ export function ensureDir(dir) {
8
+ if (!existsSync(dir)) {
9
+ mkdirSync(dir, { recursive: true });
10
+ }
11
+ }
12
+
13
+ /**
14
+ * Copy a directory recursively.
15
+ */
16
+ export function copyDir(src, dest) {
17
+ ensureDir(dest);
18
+ cpSync(src, dest, { recursive: true });
19
+ }
20
+
21
+ /**
22
+ * Remove a directory recursively.
23
+ */
24
+ export function removeDir(dir) {
25
+ if (existsSync(dir)) {
26
+ rmSync(dir, { recursive: true, force: true });
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Read a file as UTF-8 string. Returns null if not found.
32
+ */
33
+ export function readFile(filePath) {
34
+ if (!existsSync(filePath)) return null;
35
+ return readFileSync(filePath, 'utf-8');
36
+ }
37
+
38
+ /**
39
+ * Write a string to a file, creating parent directories if needed.
40
+ */
41
+ export function writeFile(filePath, content) {
42
+ const dir = filePath.substring(0, filePath.lastIndexOf('/'));
43
+ ensureDir(dir);
44
+ writeFileSync(filePath, content, 'utf-8');
45
+ }
46
+
47
+ /**
48
+ * Check if a path exists.
49
+ */
50
+ export function exists(p) {
51
+ return existsSync(p);
52
+ }
53
+
54
+ /**
55
+ * Count files in a directory recursively.
56
+ */
57
+ export function countFiles(dir, ext = '.md') {
58
+ if (!existsSync(dir)) return 0;
59
+ let count = 0;
60
+ const entries = readdirSync(dir, { withFileTypes: true });
61
+ for (const entry of entries) {
62
+ const fullPath = join(dir, entry.name);
63
+ if (entry.isDirectory()) {
64
+ count += countFiles(fullPath, ext);
65
+ } else if (entry.name.endsWith(ext)) {
66
+ count++;
67
+ }
68
+ }
69
+ return count;
70
+ }
71
+
72
+ /**
73
+ * List all files in a directory recursively.
74
+ */
75
+ export function listFiles(dir, ext = null) {
76
+ const results = [];
77
+ if (!existsSync(dir)) return results;
78
+
79
+ const entries = readdirSync(dir, { withFileTypes: true });
80
+ for (const entry of entries) {
81
+ const fullPath = join(dir, entry.name);
82
+ if (entry.isDirectory()) {
83
+ results.push(...listFiles(fullPath, ext));
84
+ } else if (!ext || entry.name.endsWith(ext)) {
85
+ results.push(fullPath);
86
+ }
87
+ }
88
+ return results;
89
+ }
@@ -0,0 +1,35 @@
1
+ const RESET = '\x1b[0m';
2
+ const GREEN = '\x1b[32m';
3
+ const YELLOW = '\x1b[33m';
4
+ const RED = '\x1b[31m';
5
+ const CYAN = '\x1b[36m';
6
+ const DIM = '\x1b[2m';
7
+ const BOLD = '\x1b[1m';
8
+
9
+ export function success(msg) {
10
+ console.log(`${GREEN} ✓${RESET} ${msg}`);
11
+ }
12
+
13
+ export function warn(msg) {
14
+ console.log(`${YELLOW} ⚠${RESET} ${msg}`);
15
+ }
16
+
17
+ export function error(msg) {
18
+ console.log(`${RED} ✗${RESET} ${msg}`);
19
+ }
20
+
21
+ export function info(msg) {
22
+ console.log(`${CYAN} ℹ${RESET} ${msg}`);
23
+ }
24
+
25
+ export function step(msg) {
26
+ console.log(`${DIM} →${RESET} ${msg}`);
27
+ }
28
+
29
+ export function header(msg) {
30
+ console.log(`\n${BOLD}${msg}${RESET}\n`);
31
+ }
32
+
33
+ export function blank() {
34
+ console.log('');
35
+ }
@@ -0,0 +1,32 @@
1
+ import { homedir } from 'os';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+
7
+ /** Root of the npm package (where package.json lives) */
8
+ export const PACKAGE_ROOT = join(__dirname, '..', '..');
9
+
10
+ /** The framework files shipped with the package */
11
+ export const FRAMEWORK_SOURCE = join(PACKAGE_ROOT, 'framework');
12
+
13
+ /** Claude Code global config directory */
14
+ export const CLAUDE_HOME = join(homedir(), '.claude');
15
+
16
+ /** Where Onion framework gets installed */
17
+ export const ONION_HOME = join(CLAUDE_HOME, 'onion');
18
+
19
+ /** Standard Claude Code discovery paths */
20
+ export const CLAUDE_COMMANDS = join(CLAUDE_HOME, 'commands');
21
+ export const CLAUDE_AGENTS = join(CLAUDE_HOME, 'agents');
22
+ export const CLAUDE_SKILLS = join(CLAUDE_HOME, 'skills');
23
+
24
+ /** Onion-namespaced paths within Claude's discovery directories */
25
+ export const ONION_COMMANDS = join(CLAUDE_COMMANDS, 'onion');
26
+ export const ONION_AGENTS = join(CLAUDE_AGENTS, 'onion');
27
+
28
+ /** Claude Code main config file */
29
+ export const CLAUDE_MD = join(CLAUDE_HOME, 'CLAUDE.md');
30
+
31
+ /** Onion manifest (tracks what was installed) */
32
+ export const ONION_MANIFEST = join(ONION_HOME, 'manifest.json');