devtronic 1.0.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 (52) hide show
  1. package/README.md +61 -0
  2. package/dist/chunk-B6Q6YVID.js +728 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +4214 -0
  5. package/dist/plugin-JTDPHWUF.js +18 -0
  6. package/package.json +70 -0
  7. package/templates/antigravity/.agents/rules/architecture.md +31 -0
  8. package/templates/antigravity/.agents/rules/quality.md +44 -0
  9. package/templates/claude-code/.claude/agents/architecture-checker.md +142 -0
  10. package/templates/claude-code/.claude/agents/code-reviewer.md +73 -0
  11. package/templates/claude-code/.claude/agents/commit-changes.md +74 -0
  12. package/templates/claude-code/.claude/agents/dependency-checker.md +112 -0
  13. package/templates/claude-code/.claude/agents/doc-sync.md +99 -0
  14. package/templates/claude-code/.claude/agents/error-investigator.md +142 -0
  15. package/templates/claude-code/.claude/agents/quality-runner.md +123 -0
  16. package/templates/claude-code/.claude/agents/test-generator.md +114 -0
  17. package/templates/claude-code/.claude/rules/architecture.md +257 -0
  18. package/templates/claude-code/.claude/rules/quality.md +103 -0
  19. package/templates/claude-code/.claude/skills/audit/SKILL.md +426 -0
  20. package/templates/claude-code/.claude/skills/audit/report-template.md +137 -0
  21. package/templates/claude-code/.claude/skills/backlog/SKILL.md +231 -0
  22. package/templates/claude-code/.claude/skills/brief/SKILL.md +425 -0
  23. package/templates/claude-code/.claude/skills/briefing/SKILL.md +172 -0
  24. package/templates/claude-code/.claude/skills/checkpoint/SKILL.md +284 -0
  25. package/templates/claude-code/.claude/skills/create-plan/SKILL.md +446 -0
  26. package/templates/claude-code/.claude/skills/create-skill/SKILL.md +245 -0
  27. package/templates/claude-code/.claude/skills/execute-plan/SKILL.md +398 -0
  28. package/templates/claude-code/.claude/skills/generate-tests/SKILL.md +358 -0
  29. package/templates/claude-code/.claude/skills/generate-tests/test-patterns.md +349 -0
  30. package/templates/claude-code/.claude/skills/handoff/SKILL.md +178 -0
  31. package/templates/claude-code/.claude/skills/investigate/SKILL.md +376 -0
  32. package/templates/claude-code/.claude/skills/learn/SKILL.md +231 -0
  33. package/templates/claude-code/.claude/skills/opensrc/SKILL.md +104 -0
  34. package/templates/claude-code/.claude/skills/post-review/SKILL.md +437 -0
  35. package/templates/claude-code/.claude/skills/quick/SKILL.md +188 -0
  36. package/templates/claude-code/.claude/skills/recap/SKILL.md +190 -0
  37. package/templates/claude-code/.claude/skills/research/SKILL.md +450 -0
  38. package/templates/claude-code/.claude/skills/scaffold/SKILL.md +281 -0
  39. package/templates/claude-code/.claude/skills/scaffold/bootstrap-commands.md +104 -0
  40. package/templates/claude-code/.claude/skills/scaffold/examples-backend.md +105 -0
  41. package/templates/claude-code/.claude/skills/scaffold/examples-frontend.md +197 -0
  42. package/templates/claude-code/.claude/skills/scaffold/examples-spa-ddd.md +667 -0
  43. package/templates/claude-code/.claude/skills/scaffold/structures.md +236 -0
  44. package/templates/claude-code/.claude/skills/setup/SKILL.md +227 -0
  45. package/templates/claude-code/.claude/skills/spec/SKILL.md +235 -0
  46. package/templates/claude-code/.claude/skills/summary/SKILL.md +279 -0
  47. package/templates/claude-code/.claude/skills/worktree/SKILL.md +266 -0
  48. package/templates/cursor/.cursor/rules/architecture.mdc +36 -0
  49. package/templates/cursor/.cursor/rules/quality.mdc +49 -0
  50. package/templates/github-copilot/.github/copilot-instructions.md +40 -0
  51. package/templates/opencode/.opencode/rules/architecture.md +31 -0
  52. package/templates/opencode/.opencode/rules/quality.md +44 -0
@@ -0,0 +1,728 @@
1
+ // src/generators/plugin.ts
2
+ import { join as join2, dirname as dirname2 } from "path";
3
+
4
+ // src/types.ts
5
+ var ADDONS = {
6
+ orchestration: {
7
+ name: "orchestration",
8
+ label: "Orchestration Workflow",
9
+ description: "Structured pre-planning alignment, session recaps, and context rotation for long multi-session work.",
10
+ skills: ["briefing", "recap", "handoff"],
11
+ agents: []
12
+ }
13
+ };
14
+ var PRESETS = {
15
+ "nextjs-clean": {
16
+ name: "nextjs-clean",
17
+ description: "Next.js with Clean Architecture",
18
+ config: {
19
+ framework: "nextjs",
20
+ architecture: "clean",
21
+ layers: ["domain", "application", "infrastructure", "presentation"],
22
+ stateManagement: ["Zustand"],
23
+ dataFetching: ["React Query"],
24
+ ui: ["Tailwind CSS"],
25
+ validation: ["Zod"]
26
+ }
27
+ },
28
+ "react-clean": {
29
+ name: "react-clean",
30
+ description: "React (Vite) with Clean Architecture",
31
+ config: {
32
+ framework: "react",
33
+ architecture: "clean",
34
+ layers: ["domain", "application", "infrastructure", "presentation"],
35
+ stateManagement: ["Zustand"],
36
+ dataFetching: ["React Query"],
37
+ ui: ["Tailwind CSS"]
38
+ }
39
+ },
40
+ monorepo: {
41
+ name: "monorepo",
42
+ description: "Monorepo with Clean Architecture per app",
43
+ config: {
44
+ architecture: "clean",
45
+ layers: ["apps", "packages", "libs"]
46
+ }
47
+ },
48
+ "feature-based": {
49
+ name: "feature-based",
50
+ description: "Feature-based architecture (co-located modules)",
51
+ config: {
52
+ architecture: "feature-based",
53
+ layers: ["features", "shared"]
54
+ }
55
+ },
56
+ minimal: {
57
+ name: "minimal",
58
+ description: "Quality checks only, no architecture rules",
59
+ config: {
60
+ architecture: "none",
61
+ layers: []
62
+ }
63
+ }
64
+ };
65
+
66
+ // src/utils/files.ts
67
+ import { createHash } from "crypto";
68
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, readdirSync } from "fs";
69
+ import { dirname, join, relative } from "path";
70
+ var MANIFEST_DIR = ".ai-template";
71
+ var MANIFEST_FILE = "manifest.json";
72
+ function fileExists(path) {
73
+ return existsSync(path);
74
+ }
75
+ function readFile(path) {
76
+ return readFileSync(path, "utf-8");
77
+ }
78
+ function writeFile(path, content) {
79
+ const dir = dirname(path);
80
+ if (!existsSync(dir)) {
81
+ mkdirSync(dir, { recursive: true });
82
+ }
83
+ writeFileSync(path, content, "utf-8");
84
+ }
85
+ function ensureDir(path) {
86
+ if (!existsSync(path)) {
87
+ mkdirSync(path, { recursive: true });
88
+ }
89
+ }
90
+ function calculateChecksum(content) {
91
+ return createHash("md5").update(content).digest("hex");
92
+ }
93
+ function getManifestPath(targetDir) {
94
+ return join(targetDir, MANIFEST_DIR, MANIFEST_FILE);
95
+ }
96
+ function readManifest(targetDir) {
97
+ const manifestPath = getManifestPath(targetDir);
98
+ if (!fileExists(manifestPath)) {
99
+ return null;
100
+ }
101
+ try {
102
+ const raw = JSON.parse(readFile(manifestPath));
103
+ return {
104
+ version: raw.version ?? "unknown",
105
+ implantedAt: raw.implantedAt ?? "unknown",
106
+ selectedIDEs: raw.selectedIDEs ?? [],
107
+ projectConfig: raw.projectConfig,
108
+ files: raw.files ?? {},
109
+ installMode: raw.installMode,
110
+ pluginPath: raw.pluginPath
111
+ };
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+ function writeManifest(targetDir, manifest) {
117
+ const manifestPath = getManifestPath(targetDir);
118
+ ensureDir(dirname(manifestPath));
119
+ writeFile(manifestPath, JSON.stringify(manifest, null, 2));
120
+ }
121
+ function getAllFilesRecursive(dir, baseDir = dir) {
122
+ const files = [];
123
+ if (!existsSync(dir)) {
124
+ return files;
125
+ }
126
+ const entries = readdirSync(dir, { withFileTypes: true });
127
+ for (const entry of entries) {
128
+ const fullPath = join(dir, entry.name);
129
+ if (entry.isDirectory()) {
130
+ files.push(...getAllFilesRecursive(fullPath, baseDir));
131
+ } else {
132
+ files.push(relative(baseDir, fullPath));
133
+ }
134
+ }
135
+ return files;
136
+ }
137
+ function createManifestEntry(content) {
138
+ const checksum = calculateChecksum(content);
139
+ return {
140
+ checksum,
141
+ modified: false,
142
+ originalChecksum: checksum
143
+ };
144
+ }
145
+ function getSubdirectories(dir) {
146
+ if (!existsSync(dir)) {
147
+ return [];
148
+ }
149
+ return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
150
+ }
151
+
152
+ // src/generators/hooks.ts
153
+ function generateHooks(config, packageManager) {
154
+ const lintFixCmd = buildLintFixCommand(packageManager, config);
155
+ const hooksConfig = {
156
+ description: "r-bart devtronic \u2014 workflow hooks",
157
+ hooks: {
158
+ SessionStart: [
159
+ {
160
+ matcher: "startup",
161
+ hooks: [
162
+ {
163
+ type: "prompt",
164
+ prompt: "Quick project orientation: First check if thoughts/STATE.md exists \u2014 if so, read it and summarize the current project state. Then check git status, recent commits, and any in-progress work. Give a 3-line summary prioritizing STATE.md context if available.\n\nContext: $ARGUMENTS",
165
+ model: "haiku",
166
+ timeout: 30
167
+ }
168
+ ]
169
+ }
170
+ ],
171
+ // Synchronous lint-fix: must complete before Claude reads the file again
172
+ PostToolUse: [
173
+ {
174
+ matcher: "Write|Edit",
175
+ hooks: [
176
+ {
177
+ type: "command",
178
+ command: `${lintFixCmd} 2>/dev/null || true`,
179
+ timeout: 30,
180
+ statusMessage: "Auto-linting..."
181
+ }
182
+ ]
183
+ }
184
+ ],
185
+ Stop: [
186
+ {
187
+ hooks: [
188
+ {
189
+ type: "command",
190
+ command: "${CLAUDE_PLUGIN_ROOT}/scripts/stop-guard.sh",
191
+ timeout: 60,
192
+ statusMessage: "Running quality checks..."
193
+ },
194
+ {
195
+ type: "prompt",
196
+ prompt: 'If thoughts/plans/ contains a recent plan with a "Done Criteria" section, quickly check: are there unmet criteria? If yes, list them as a brief reminder. Do NOT run a full review. If no plan or all criteria met, say nothing.',
197
+ model: "haiku",
198
+ timeout: 15,
199
+ statusMessage: "Checking done criteria..."
200
+ }
201
+ ]
202
+ }
203
+ ],
204
+ SubagentStop: [
205
+ {
206
+ hooks: [
207
+ {
208
+ type: "prompt",
209
+ prompt: [
210
+ "A subagent has finished. Based on the metadata below, assess if it completed successfully.",
211
+ "Consider the agent type and whether the session ended normally.",
212
+ 'Respond with {"ok": true} if it appears complete, or {"ok": false, "reason": "..."} if something seems off.',
213
+ "",
214
+ 'IMPORTANT: If stop_hook_active is true, always respond {"ok": true} to prevent infinite loops.',
215
+ "",
216
+ "Context: $ARGUMENTS"
217
+ ].join("\n"),
218
+ model: "haiku",
219
+ timeout: 30
220
+ }
221
+ ]
222
+ }
223
+ ],
224
+ PreCompact: [
225
+ {
226
+ matcher: "auto",
227
+ hooks: [
228
+ {
229
+ type: "command",
230
+ command: "${CLAUDE_PLUGIN_ROOT}/scripts/checkpoint.sh",
231
+ timeout: 30,
232
+ statusMessage: "Auto-checkpoint before compaction..."
233
+ }
234
+ ]
235
+ }
236
+ ]
237
+ }
238
+ };
239
+ return JSON.stringify(hooksConfig, null, 2);
240
+ }
241
+ function buildLintFixCommand(packageManager, config) {
242
+ const pm = packageManager || "npm";
243
+ const run = pm === "npm" ? "npm run" : pm;
244
+ if (config.qualityCommand.includes("lint")) {
245
+ return `${run} lint:fix -- --quiet`;
246
+ }
247
+ return `${run} lint --fix --quiet`;
248
+ }
249
+ function generateStopGuardScript(config) {
250
+ const safeCmd = config.qualityCommand.replace(/'/g, "'\\''");
251
+ return `#!/bin/bash
252
+ # Quality gate: runs checks before allowing Claude to stop
253
+ # Generated by devtronic
254
+
255
+ INPUT=$(cat)
256
+
257
+ # Prevent infinite loops: if already triggered by a stop hook, allow stop
258
+ if echo "$INPUT" | grep -q '"stop_hook_active"'; then
259
+ if echo "$INPUT" | grep -q '"stop_hook_active"[[:space:]]*:[[:space:]]*true'; then
260
+ exit 0
261
+ fi
262
+ fi
263
+
264
+ # Run quality checks
265
+ QUALITY_CMD='${safeCmd}'
266
+ if ! eval "$QUALITY_CMD" > /dev/null 2>&1; then
267
+ echo "Quality checks failed. Run '$QUALITY_CMD' to see details." >&2
268
+ exit 2
269
+ fi
270
+
271
+ exit 0
272
+ `;
273
+ }
274
+ function generateCheckpointScript() {
275
+ return `#!/bin/bash
276
+ # Auto-checkpoint before context compaction
277
+ # Generated by devtronic
278
+
279
+ CHECKPOINT_DIR="thoughts/checkpoints"
280
+ TIMESTAMP=$(date +%Y%m%d_%H%M%S)
281
+
282
+ mkdir -p "$CHECKPOINT_DIR"
283
+
284
+ {
285
+ echo "# Auto-checkpoint: $TIMESTAMP"
286
+ echo ""
287
+ echo "## Git Status"
288
+ git diff --stat 2>/dev/null || echo "Not a git repo"
289
+ echo ""
290
+ echo "## Recent Commits"
291
+ git log --oneline -5 2>/dev/null || echo "No commits"
292
+ } > "$CHECKPOINT_DIR/\${TIMESTAMP}_pre-compact.md"
293
+
294
+ echo "Checkpoint saved: $CHECKPOINT_DIR/\${TIMESTAMP}_pre-compact.md"
295
+
296
+ # Update persistent state (minimal \u2014 skill-level checkpoint writes richer state)
297
+ STATE_FILE="thoughts/STATE.md"
298
+ mkdir -p "$(dirname "$STATE_FILE")"
299
+ BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
300
+ {
301
+ echo "# Project State"
302
+ echo ""
303
+ echo "**Updated**: $(date '+%Y-%m-%d %H:%M') (auto-compact)"
304
+ echo "**Branch**: $BRANCH"
305
+ echo ""
306
+ echo "## Last Auto-Checkpoint"
307
+ echo ""
308
+ echo "Context was compacted. See: \\\`$CHECKPOINT_DIR/\${TIMESTAMP}_pre-compact.md\\\`"
309
+ echo ""
310
+ echo "## Recent Commits"
311
+ echo ""
312
+ git log --oneline -5 2>/dev/null || echo "No commits"
313
+ } > "$STATE_FILE"
314
+ `;
315
+ }
316
+
317
+ // src/generators/rules.ts
318
+ function getFrameworkDisplayName(framework) {
319
+ const names = {
320
+ nextjs: "Next.js",
321
+ react: "React",
322
+ vue: "Vue",
323
+ nuxt: "Nuxt",
324
+ express: "Express",
325
+ nestjs: "NestJS",
326
+ fastify: "Fastify",
327
+ hono: "Hono",
328
+ astro: "Astro",
329
+ svelte: "Svelte",
330
+ sveltekit: "SvelteKit",
331
+ remix: "Remix",
332
+ unknown: "Project"
333
+ };
334
+ return names[framework] || "Project";
335
+ }
336
+ function getArchitectureLabel(architecture) {
337
+ const labels = {
338
+ clean: "Clean + DDD",
339
+ layered: "Layered",
340
+ mvc: "MVC",
341
+ "feature-based": "Feature-Based",
342
+ flat: "Flat",
343
+ none: "None"
344
+ };
345
+ return labels[architecture] || architecture;
346
+ }
347
+ function getArchitectureOneLiner(config) {
348
+ if (config.architecture === "clean") {
349
+ return "Dependencies point INWARD only: Presentation \u2192 Application \u2192 Domain \u2190 Infrastructure.";
350
+ }
351
+ if (config.architecture === "layered") {
352
+ return "Layered: Routes/Controllers \u2192 Services \u2192 Repositories. Each layer calls only the one below.";
353
+ }
354
+ if (config.architecture === "mvc") {
355
+ return "Model-View-Controller: Models hold data/logic, Views handle UI, Controllers orchestrate.";
356
+ }
357
+ if (config.architecture === "feature-based") {
358
+ return "Each feature is self-contained with its own components, hooks, API calls, and types.";
359
+ }
360
+ if (config.architecture === "none") {
361
+ return "No architecture rules configured. Run `devtronic config set architecture clean` to add rules later.";
362
+ }
363
+ return "Document your architecture patterns.";
364
+ }
365
+ function getCodePatternsBullets(config) {
366
+ const lines = [];
367
+ if (config.stateManagement.length > 0) {
368
+ lines.push(`- **UI state**: ${config.stateManagement.join(", ")}`);
369
+ }
370
+ if (config.dataFetching.length > 0) {
371
+ lines.push(`- **Server state**: ${config.dataFetching.join(", ")}`);
372
+ }
373
+ if (config.architecture === "clean") {
374
+ lines.push("- **Domain state**: Use cases");
375
+ }
376
+ if (config.architecture === "layered") {
377
+ lines.push("- **Business logic**: Services layer");
378
+ }
379
+ if (config.orm.length > 0) {
380
+ lines.push(`- **ORM**: ${config.orm.join(", ")}`);
381
+ if (config.architecture === "clean") {
382
+ lines.push("- Repository interfaces in `domain/`, implementations in `infrastructure/`");
383
+ }
384
+ if (config.architecture === "layered") {
385
+ lines.push("- Database access only in `repositories/`");
386
+ }
387
+ }
388
+ lines.push("- Never access DB from UI");
389
+ return lines.join("\n");
390
+ }
391
+ function getCommandsBlock(scripts, packageManager, qualityCommand) {
392
+ const pm = packageManager || "npm";
393
+ const run = pm === "npm" ? "npm run" : pm;
394
+ const devCmd = scripts.dev ? `\`${run} ${scripts.dev}\`` : "`# Add your dev command`";
395
+ const buildCmd = scripts.build ? `\`${run} ${scripts.build}\`` : "`# Add your build command`";
396
+ return `- **Dev**: ${devCmd}
397
+ - **Build**: ${buildCmd}
398
+ - **Quality**: \`${qualityCommand}\``;
399
+ }
400
+ function generateQualityCommands(scripts, packageManager) {
401
+ const pm = packageManager || "npm";
402
+ const run = pm === "npm" ? "npm run" : pm;
403
+ const commands = [];
404
+ if (scripts.typecheck) {
405
+ commands.push(`${run} ${scripts.typecheck}`);
406
+ }
407
+ if (scripts.lint) {
408
+ commands.push(`${run} ${scripts.lint}`);
409
+ }
410
+ if (scripts.test) {
411
+ commands.push(`${run} ${scripts.test}`);
412
+ }
413
+ if (commands.length === 0) {
414
+ return "# No quality scripts detected - add your commands here";
415
+ }
416
+ return commands.join(" && ");
417
+ }
418
+ function generateClaudeMd(config, scripts, packageManager) {
419
+ const frameworkName = getFrameworkDisplayName(config.framework);
420
+ const archLabel = getArchitectureLabel(config.architecture);
421
+ const archOneLiner = getArchitectureOneLiner(config);
422
+ const codePatterns = getCodePatternsBullets(config);
423
+ const commands = getCommandsBlock(scripts, packageManager, config.qualityCommand);
424
+ const skillsSection = generateSkillsSection(config.enabledAddons);
425
+ const archSection = config.architecture === "none" ? `## Architecture
426
+
427
+ ${archOneLiner}` : `## Architecture
428
+
429
+ ${archOneLiner}
430
+
431
+ IMPORTANT: See \`docs/ARCHITECTURE.md\` for structure. See \`.claude/rules/\` for enforcement rules.`;
432
+ const workflowSection = config.enabledAddons?.includes("orchestration") ? `## Workflow
433
+
434
+ - **New feature**: \`/briefing\` \u2192 \`/spec\` \u2192 \`/create-plan\` \u2192 \`/generate-tests\` \u2192 \`/execute-plan\` \u2192 \`/recap\`
435
+ - **Bug fix**: \`/brief\` \u2192 fix \u2192 test \u2192 \`/summary\` \u2192 \`/post-review\`
436
+ - **Refactor**: \`/brief\` \u2192 \`/create-plan\` \u2192 \`/execute-plan\` \u2192 \`/summary\` \u2192 \`/post-review\`
437
+ - **Session start**: \`/brief\` for orientation
438
+ - **Session end**: \`/handoff\` for clean context rotation
439
+
440
+ > \`/briefing\` for pre-planning alignment. \`/recap\` for quick summaries. \`/checkpoint\` to save progress.` : `## Workflow
441
+
442
+ - **New feature**: \`/brief\` \u2192 \`/spec\` \u2192 \`/create-plan\` \u2192 \`/generate-tests\` \u2192 \`/execute-plan\` \u2192 \`/summary\` \u2192 \`/post-review\`
443
+ - **Bug fix**: \`/brief\` \u2192 fix \u2192 test \u2192 \`/summary\` \u2192 \`/post-review\`
444
+ - **Refactor**: \`/brief\` \u2192 \`/create-plan\` \u2192 implement \u2192 \`/summary\` \u2192 \`/post-review\`
445
+
446
+ > \`/brief\` for session orientation (with pre-flight checks). \`/summary\` to document changes. \`/checkpoint\` to save progress.`;
447
+ return `# ${frameworkName}
448
+
449
+ ${frameworkName} project${config.architecture !== "none" ? ` with ${archLabel} architecture` : ""}.
450
+
451
+ ## Commands
452
+
453
+ ${commands}
454
+
455
+ Run quality checks after every change.
456
+
457
+ ## Code Style
458
+
459
+ - **Files**: PascalCase components, camelCase utils
460
+ - **Code**: camelCase vars/functions, PascalCase types
461
+ - **Unused**: Prefix with \`_\`
462
+
463
+ ## Code Patterns
464
+
465
+ ${codePatterns}
466
+
467
+ ${archSection}
468
+
469
+ ${workflowSection}
470
+
471
+ ## Available Skills
472
+
473
+ ${skillsSection}
474
+
475
+ ## Project Notes
476
+
477
+ Maintain notes in \`thoughts/notes/\` updated after every PR.
478
+
479
+ ## Gotchas
480
+
481
+ <!-- Claude fills this section via self-improvement. Do not delete. -->
482
+
483
+ ## Self-Improvement
484
+
485
+ After every significant correction, update this file:
486
+
487
+ "Update CLAUDE.md so you don't make that mistake again."
488
+
489
+ Add learned rules to the **Gotchas** section above. Keep rules:
490
+ - Concise (one line each)
491
+ - Absolute directives (ALWAYS/NEVER)
492
+ - Concrete with actual commands/code
493
+
494
+ ## References
495
+
496
+ - **docs/ARCHITECTURE.md** \u2014 Folder structure
497
+ - **docs/worktrees.md** \u2014 Parallel sessions with git worktrees
498
+ - **.claude/skills/** \u2014 Available workflows
499
+ - **.claude/agents/** \u2014 Specialized helpers
500
+ `;
501
+ }
502
+ function generateAgentsMdFromConfig(config, scripts, packageManager) {
503
+ const frameworkName = getFrameworkDisplayName(config.framework);
504
+ const archLabel = getArchitectureLabel(config.architecture);
505
+ const archOneLiner = getArchitectureOneLiner(config);
506
+ const codePatterns = getCodePatternsBullets(config);
507
+ const commands = getCommandsBlock(scripts, packageManager, config.qualityCommand);
508
+ const agentsArchSection = config.architecture === "none" ? `## Architecture
509
+
510
+ ${archOneLiner}` : `## Architecture
511
+
512
+ ${archOneLiner} See \`docs/ARCHITECTURE.md\` for detailed structure.`;
513
+ const skillsSection = generateSkillsSection(config.enabledAddons);
514
+ const workflowSection = config.enabledAddons?.includes("orchestration") ? `## Workflow
515
+
516
+ - **New feature**: \`/briefing\` \u2192 \`/spec\` \u2192 \`/create-plan\` \u2192 \`/generate-tests\` \u2192 \`/execute-plan\` \u2192 \`/recap\`
517
+ - **Bug fix**: \`/brief\` \u2192 fix \u2192 test \u2192 \`/summary\`
518
+ - **Session start**: \`/brief\` for orientation
519
+ - **Session end**: \`/handoff\` for clean context rotation` : `## Workflow
520
+
521
+ - **New feature**: \`/brief\` \u2192 \`/spec\` \u2192 \`/create-plan\` \u2192 \`/generate-tests\` \u2192 \`/execute-plan\` \u2192 \`/summary\` \u2192 \`/post-review\`
522
+ - **Bug fix**: \`/brief\` \u2192 fix \u2192 test \u2192 \`/summary\``;
523
+ return `# ${frameworkName}
524
+
525
+ ${frameworkName} project${config.architecture !== "none" ? ` with ${archLabel} architecture` : ""}.
526
+
527
+ ## Commands
528
+
529
+ ${commands}
530
+
531
+ Run quality checks after every change.
532
+
533
+ ## Code Style
534
+
535
+ - **Files**: PascalCase components, camelCase utils
536
+ - **Code**: camelCase vars/functions, PascalCase types
537
+ - **Unused**: Prefix with \`_\`
538
+
539
+ ## Code Patterns
540
+
541
+ ${codePatterns}
542
+
543
+ ${agentsArchSection}
544
+
545
+ ${workflowSection}
546
+
547
+ ## Available Skills
548
+
549
+ ${skillsSection}
550
+ `;
551
+ }
552
+ var CORE_SKILLS = [
553
+ { name: "brief", desc: "Session orientation with pre-flight checks" },
554
+ { name: "spec", desc: "Product specification interview (PRD)" },
555
+ { name: "research", desc: "Codebase investigation (--deep, --external)" },
556
+ { name: "create-plan", desc: "Phased implementation plan with task dependencies" },
557
+ { name: "execute-plan", desc: "Parallel phase execution of plans" },
558
+ { name: "quick", desc: "Fast ad-hoc tasks: implement, verify, commit" },
559
+ { name: "generate-tests", desc: "Failing tests from spec (Tests-as-DoD)" },
560
+ { name: "post-review", desc: "Pre-PR review (architecture, quality, requirements)" },
561
+ { name: "audit", desc: "Codebase audit (security, complexity, architecture)" },
562
+ { name: "summary", desc: "Post-change documentation" },
563
+ { name: "checkpoint", desc: "Save session progress for resumption" },
564
+ { name: "backlog", desc: "Issue management with BACK-### IDs" },
565
+ { name: "investigate", desc: "Deep error and bug analysis" },
566
+ { name: "learn", desc: "Post-task teaching breakdown" },
567
+ { name: "scaffold", desc: "Create new projects from scratch" },
568
+ { name: "setup", desc: "Interactive project configuration" },
569
+ { name: "worktree", desc: "Git worktree management" },
570
+ { name: "opensrc", desc: "Fetch npm/GitHub source for full context" },
571
+ { name: "create-skill", desc: "Generate new custom skills" }
572
+ ];
573
+ var ADDON_SKILLS = {
574
+ orchestration: [
575
+ { name: "briefing", desc: "Pre-planning alignment Q&A" },
576
+ { name: "recap", desc: "Quick session summary from git activity" },
577
+ { name: "handoff", desc: "Context rotation for fresh sessions" }
578
+ ]
579
+ };
580
+ function generateSkillsSection(enabledAddons) {
581
+ const lines = CORE_SKILLS.map((s) => `- \`/${s.name}\` \u2014 ${s.desc}`);
582
+ const addons = enabledAddons || [];
583
+ for (const addon of addons) {
584
+ const addonSkills = ADDON_SKILLS[addon];
585
+ if (addonSkills) {
586
+ for (const s of addonSkills) {
587
+ lines.push(`- \`/${s.name}\` \u2014 ${s.desc}`);
588
+ }
589
+ }
590
+ }
591
+ return lines.join("\n");
592
+ }
593
+
594
+ // src/generators/plugin.ts
595
+ var PLUGIN_NAME = "devtronic";
596
+ var MARKETPLACE_NAME = "devtronic-local";
597
+ var PLUGIN_DIR = ".claude-plugins";
598
+ var BASE_SKILL_COUNT = CORE_SKILLS.length;
599
+ function generatePluginJson(cliVersion, addonSkillCount = 0) {
600
+ const skillLabel = addonSkillCount > 0 ? `${BASE_SKILL_COUNT} + ${addonSkillCount} addon skills` : `${BASE_SKILL_COUNT} skills`;
601
+ return JSON.stringify(
602
+ {
603
+ name: PLUGIN_NAME,
604
+ version: cliVersion,
605
+ description: `devtronic \u2014 ${skillLabel}, 8 agents, full workflow hooks`,
606
+ author: {
607
+ name: "r-bart",
608
+ url: "https://github.com/r-bart/devtronic"
609
+ },
610
+ repository: "https://github.com/r-bart/devtronic",
611
+ license: "MIT",
612
+ keywords: ["agentic", "architecture", "clean-architecture", "ddd", "workflow", "skills"],
613
+ skills: "./skills/",
614
+ agents: "./agents/",
615
+ hooks: "./hooks/hooks.json"
616
+ },
617
+ null,
618
+ 2
619
+ );
620
+ }
621
+ function generateMarketplaceJson(addonSkillCount = 0) {
622
+ const skillLabel = addonSkillCount > 0 ? `${BASE_SKILL_COUNT} + ${addonSkillCount} addon skills` : `${BASE_SKILL_COUNT} skills`;
623
+ return JSON.stringify(
624
+ {
625
+ name: MARKETPLACE_NAME,
626
+ owner: {
627
+ name: "r-bart",
628
+ url: "https://github.com/r-bart/devtronic"
629
+ },
630
+ plugins: [
631
+ {
632
+ name: PLUGIN_NAME,
633
+ source: `./${PLUGIN_NAME}`,
634
+ description: `devtronic \u2014 ${skillLabel}, 8 agents, full workflow hooks`
635
+ }
636
+ ]
637
+ },
638
+ null,
639
+ 2
640
+ );
641
+ }
642
+ function generatePlugin(targetDir, templatesDir, cliVersion, config, packageManager) {
643
+ const files = {};
644
+ const pluginRoot = join2(PLUGIN_DIR, PLUGIN_NAME);
645
+ const addonOnlySkills = new Set(
646
+ Object.values(ADDONS).flatMap((a) => a.skills)
647
+ );
648
+ const enabledAddonSkills = new Set(
649
+ (config.enabledAddons || []).flatMap((a) => ADDONS[a]?.skills ?? [])
650
+ );
651
+ const addonSkillCount = enabledAddonSkills.size;
652
+ const marketplaceContent = generateMarketplaceJson(addonSkillCount);
653
+ const marketplaceRelPath = join2(PLUGIN_DIR, ".claude-plugin", "marketplace.json");
654
+ writeGeneratedFile(targetDir, marketplaceRelPath, marketplaceContent, files);
655
+ const pluginJsonContent = generatePluginJson(cliVersion, addonSkillCount);
656
+ const pluginJsonRelPath = join2(pluginRoot, ".claude-plugin", "plugin.json");
657
+ writeGeneratedFile(targetDir, pluginJsonRelPath, pluginJsonContent, files);
658
+ const templateClaudeDir = join2(templatesDir, "claude-code", ".claude");
659
+ const skillsSourceDir = join2(templateClaudeDir, "skills");
660
+ const skillDirs = getSubdirectories(skillsSourceDir);
661
+ for (const dir of skillDirs) {
662
+ const isAddonSkill = addonOnlySkills.has(dir);
663
+ if (!isAddonSkill || enabledAddonSkills.has(dir)) {
664
+ copyTemplateDir(
665
+ targetDir,
666
+ join2(skillsSourceDir, dir),
667
+ join2(pluginRoot, "skills", dir),
668
+ files
669
+ );
670
+ }
671
+ }
672
+ copyTemplateDir(targetDir, join2(templateClaudeDir, "agents"), join2(pluginRoot, "agents"), files);
673
+ const hooksContent = generateHooks(config, packageManager);
674
+ const hooksRelPath = join2(pluginRoot, "hooks", "hooks.json");
675
+ writeGeneratedFile(targetDir, hooksRelPath, hooksContent, files);
676
+ const scriptContent = generateCheckpointScript();
677
+ const scriptRelPath = join2(pluginRoot, "scripts", "checkpoint.sh");
678
+ writeGeneratedFile(targetDir, scriptRelPath, scriptContent, files);
679
+ const stopGuardContent = generateStopGuardScript(config);
680
+ const stopGuardRelPath = join2(pluginRoot, "scripts", "stop-guard.sh");
681
+ writeGeneratedFile(targetDir, stopGuardRelPath, stopGuardContent, files);
682
+ return { files, pluginPath: pluginRoot };
683
+ }
684
+ function writeGeneratedFile(targetDir, relPath, content, files) {
685
+ const absPath = join2(targetDir, relPath);
686
+ ensureDir(dirname2(absPath));
687
+ writeFile(absPath, content);
688
+ files[relPath] = createManifestEntry(content);
689
+ }
690
+ function copyTemplateDir(targetDir, sourceDir, destRelDir, files) {
691
+ const templateFiles = getAllFilesRecursive(sourceDir);
692
+ if (templateFiles.length === 0) {
693
+ throw new Error(`No template files found in ${sourceDir}. The CLI package may be corrupted.`);
694
+ }
695
+ for (const file of templateFiles) {
696
+ const sourceContent = readFile(join2(sourceDir, file));
697
+ const destRelPath = join2(destRelDir, file);
698
+ const destAbsPath = join2(targetDir, destRelPath);
699
+ ensureDir(dirname2(destAbsPath));
700
+ writeFile(destAbsPath, sourceContent);
701
+ files[destRelPath] = createManifestEntry(sourceContent);
702
+ }
703
+ }
704
+
705
+ export {
706
+ ADDONS,
707
+ PRESETS,
708
+ MANIFEST_DIR,
709
+ fileExists,
710
+ readFile,
711
+ writeFile,
712
+ ensureDir,
713
+ calculateChecksum,
714
+ readManifest,
715
+ writeManifest,
716
+ getAllFilesRecursive,
717
+ createManifestEntry,
718
+ generateQualityCommands,
719
+ generateClaudeMd,
720
+ generateAgentsMdFromConfig,
721
+ PLUGIN_NAME,
722
+ MARKETPLACE_NAME,
723
+ PLUGIN_DIR,
724
+ BASE_SKILL_COUNT,
725
+ generatePluginJson,
726
+ generateMarketplaceJson,
727
+ generatePlugin
728
+ };