@kleber.mottajr/juninho 1.1.0 → 1.3.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 (49) hide show
  1. package/README.md +112 -13
  2. package/dist/cli.js +40 -23
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config.d.ts +7 -0
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +8 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/installer.d.ts +2 -0
  9. package/dist/installer.d.ts.map +1 -1
  10. package/dist/installer.js +180 -54
  11. package/dist/installer.js.map +1 -1
  12. package/dist/lint-detection.d.ts +26 -0
  13. package/dist/lint-detection.d.ts.map +1 -0
  14. package/dist/lint-detection.js +226 -0
  15. package/dist/lint-detection.js.map +1 -0
  16. package/dist/models.js +4 -4
  17. package/dist/models.js.map +1 -1
  18. package/dist/project-types.d.ts +52 -0
  19. package/dist/project-types.d.ts.map +1 -0
  20. package/dist/project-types.js +284 -0
  21. package/dist/project-types.js.map +1 -0
  22. package/dist/templates/agents.d.ts +2 -1
  23. package/dist/templates/agents.d.ts.map +1 -1
  24. package/dist/templates/agents.js +7 -5
  25. package/dist/templates/agents.js.map +1 -1
  26. package/dist/templates/commands.d.ts.map +1 -1
  27. package/dist/templates/commands.js +225 -150
  28. package/dist/templates/commands.js.map +1 -1
  29. package/dist/templates/docs.d.ts +2 -1
  30. package/dist/templates/docs.d.ts.map +1 -1
  31. package/dist/templates/docs.js +61 -14
  32. package/dist/templates/docs.js.map +1 -1
  33. package/dist/templates/plugins.d.ts +2 -1
  34. package/dist/templates/plugins.d.ts.map +1 -1
  35. package/dist/templates/plugins.js +167 -102
  36. package/dist/templates/plugins.js.map +1 -1
  37. package/dist/templates/skills.d.ts +2 -1
  38. package/dist/templates/skills.d.ts.map +1 -1
  39. package/dist/templates/skills.js +708 -195
  40. package/dist/templates/skills.js.map +1 -1
  41. package/dist/templates/support-scripts.d.ts +2 -1
  42. package/dist/templates/support-scripts.d.ts.map +1 -1
  43. package/dist/templates/support-scripts.js +468 -21
  44. package/dist/templates/support-scripts.js.map +1 -1
  45. package/dist/templates/tools.d.ts +2 -1
  46. package/dist/templates/tools.d.ts.map +1 -1
  47. package/dist/templates/tools.js +315 -74
  48. package/dist/templates/tools.js.map +1 -1
  49. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # juninho
2
2
 
3
- Bootstrap the **Agentic Coding Framework** (framework.md) into any [OpenCode](https://opencode.ai) project in a single command.
3
+ Bootstrap the **Agentic Coding Framework** into any [OpenCode](https://opencode.ai) project in a single command. Multi-stack support for Node.js, Python, Go, Java/Kotlin, and generic projects.
4
4
 
5
5
  ## Install
6
6
 
@@ -14,29 +14,115 @@ npm install -g @kleber.mottajr/juninho
14
14
  # Navigate to your project
15
15
  cd my-opencode-project
16
16
 
17
- # Run setup — one command, everything configured
17
+ # Run setup — auto-detects project type
18
18
  juninho setup
19
19
 
20
+ # Or specify type explicitly
21
+ juninho setup --type python
22
+
20
23
  # Output:
21
24
  # [juninho] Installing Agentic Coding Framework...
25
+ # [juninho] ✓ Project type: Python
22
26
  # [juninho] ✓ Framework installed successfully!
23
27
  # [juninho] Open OpenCode — /j.plan, /j.spec and /j.implement are ready.
24
28
  ```
25
29
 
26
- ## What it does
30
+ ### Supported project types
27
31
 
28
- `juninho setup` automatically creates:
32
+ | Type | Detection | Skills |
33
+ |------|-----------|--------|
34
+ | `node-nextjs` | `package.json` with `next` dep | 9 (all skills) |
35
+ | `node-generic` | `package.json` without `next` | 5 |
36
+ | `python` | `pyproject.toml`, `requirements.txt`, `setup.py` | 5 |
37
+ | `go` | `go.mod` | 5 |
38
+ | `java` | `pom.xml`, `build.gradle`, `build.gradle.kts` | 5 |
39
+ | `generic` | Fallback | 4 |
29
40
 
30
- - **9 agents** in `.opencode/agents/` (j.planner, j.spec-writer, j.implementer, j.validator, j.reviewer, j.plan-reviewer, j.unify, j.explore, j.librarian)
31
- - **9 skills** in `.opencode/skills/` (j.test-writing, j.page-creation, j.api-route-creation, j.server-action-creation, j.schema-migration, j.agents-md-writing, j.domain-doc-writing, j.principle-doc-writing, j.shell-script-writing)
32
- - **11 plugins** in `.opencode/plugins/` (auto-discovered by OpenCode)
33
- - **4 tools** in `.opencode/tools/` (find-pattern, next-version, lsp, ast-grep)
34
- - **14 slash commands** in `.opencode/commands/` (/j.plan, /j.spec, /j.implement, /j.sync-docs, /j.init-deep, /j.start-work, /j.handoff, /j.ulw-loop, /j.check, /j.lint, /j.test, /j.pr-review, /j.status, /j.unify)
35
- - **State files** for persistent context and execution tracking
36
- - **Docs scaffold** with AGENTS.md, domain index, principles docs, and manifest
37
- - **Support scripts** in `.opencode/scripts/` for pre-commit, related tests, structure lint, and full checks
41
+ Java projects with Kotlin (`build.gradle.kts` with kotlin plugin or `.kt` files) automatically get Kotlin-specific lint (ktlint/detekt), test patterns, and skills.
42
+
43
+ ---
44
+
45
+ ## How It Works
46
+
47
+ The framework orchestrates a multi-agent AI workflow for spec-driven development. The developer never codes directly — instead, specialized agents handle planning, implementation, validation, and closeout through slash commands.
48
+
49
+ ### Specification & Planning
50
+
51
+ `/j.spec` conducts a 5-phase interview (Discovery → Requirements → Contract → Data → Review) with background codebase and external research. `/j.plan` runs 3 phases: Metis (intent analysis), Prometheus (interview + decomposition), and Momus (executability review). Both require explicit developer approval.
52
+
53
+ ![Spec & Plan Sequence](juninho-seq-spec-plan.png)
54
+
55
+ ### Implementation, Verification & Closeout
56
+
57
+ `/j.implement` executes wave-by-wave with git worktrees for parallelism. Each task follows the READ → ACT → COMMIT → VALIDATE loop. `@j.validator` gates every task against the spec. Pre-commit hooks run lint + related tests. `/j.check` runs repo-wide verification. `/j.unify` reconciles delivery, merges worktrees, updates docs, and creates a PR.
58
+
59
+ ![Implement Sequence](juninho-seq-implement.png)
60
+
61
+ ### Context Injection & Plugin Architecture
62
+
63
+ 12 plugins intercept every tool call across 5 tiers to inject context and enforce quality. External knowledge flows through Context7 (library docs) and Context-Mode (semantic search) MCP servers.
38
64
 
39
- Then patches `opencode.json` with agent definitions and Context7 MCP.
65
+ ![Context Injection Sequence](juninho-seq-context.png)
66
+
67
+ | Tier | Mechanism | What it provides |
68
+ |------|-----------|-----------------|
69
+ | **1** | `j.directory-agents-injector` | Hierarchical AGENTS.md (root → parent → current dir) |
70
+ | **2** | `j.carl-inject` (CARL) | Content-aware principles + domain docs via keyword matching (≤8KB) |
71
+ | **3** | `j.skill-inject` | File pattern → SKILL.md injection (e.g., `*.test.ts` → test-writing skill) |
72
+ | **4** | Plan `<skills>` declarations | Per-task required skills from plan.md |
73
+ | **5** | State files + `j.memory` | Cross-session persistent context |
74
+
75
+ ### Agent Roles
76
+
77
+ | Agent | Model | Role |
78
+ |-------|-------|------|
79
+ | `@j.spec-writer` | Strong | 5-phase interview → spec.md |
80
+ | `@j.planner` | Strong | 3-phase planning → plan.md |
81
+ | `@j.implementer` | Medium | Wave execution, READ→ACT→COMMIT→VALIDATE |
82
+ | `@j.validator` | Medium | Spec-first validation, BLOCK/FIX/NOTE/APPROVED |
83
+ | `@j.plan-reviewer` | Medium | Executability gate for plans |
84
+ | `@j.reviewer` | Medium | Post-PR advisory review (read-only) |
85
+ | `@j.unify` | Medium | Closeout: merge, docs, PR |
86
+ | `@j.explore` | Weak | Read-only codebase research |
87
+ | `@j.librarian` | Weak | External docs via Context7 + Context-Mode MCP |
88
+
89
+ ---
90
+
91
+ ## What it creates
92
+
93
+ `juninho setup` automatically generates:
94
+
95
+ - **9 agents** in `.opencode/agents/`
96
+ - **4–9 skills** in `.opencode/skills/` (filtered by project type)
97
+ - **12 plugins** in `.opencode/plugins/` (auto-discovered by OpenCode)
98
+ - **4 tools** in `.opencode/tools/` (find-pattern, next-version, lsp, ast-grep)
99
+ - **15 slash commands** in `.opencode/commands/`
100
+ - **State files** for persistent context and execution tracking
101
+ - **Docs scaffold** with AGENTS.md, domain index, principles docs, and manifest
102
+ - **Support scripts** in `.opencode/scripts/` for pre-commit, related tests, structure lint, and full checks
103
+ - **skill-map.json** for dynamic skill-to-pattern mapping
104
+
105
+ Then patches `opencode.json` with agent definitions, Context7 MCP, and Context-Mode MCP.
106
+
107
+ ### Slash Commands
108
+
109
+ | Command | Description |
110
+ |---------|-------------|
111
+ | `/j.spec` | Feature specification via 5-phase interview |
112
+ | `/j.plan` | Strategic planning with 3-phase process |
113
+ | `/j.implement` | Execute plan wave-by-wave with validation |
114
+ | `/j.check` | Repo-wide verification (typecheck + lint + tests) |
115
+ | `/j.unify` | Closeout: merge worktrees, update docs, create PR |
116
+ | `/j.pr-review` | Advisory post-PR code review |
117
+ | `/j.start-work` | Begin work session with context loading |
118
+ | `/j.handoff` | End-of-session handoff document |
119
+ | `/j.status` | Show execution state and task progress |
120
+ | `/j.ulw-loop` | Ultra work loop (parallel implementation) |
121
+ | `/j.init-deep` | Deep codebase initialization (AGENTS.md hierarchy) |
122
+ | `/j.sync-docs` | Refresh domain/principles documentation |
123
+ | `/j.finish-setup` | Scan codebase, generate dynamic skills + docs |
124
+ | `/j.lint` | Run structure lint on staged files |
125
+ | `/j.test` | Run tests related to staged files |
40
126
 
41
127
  ## Idempotency
42
128
 
@@ -48,6 +134,19 @@ Running `juninho setup` twice is safe — it detects `.opencode/.juninho-install
48
134
  juninho setup --force
49
135
  ```
50
136
 
137
+ ## CLI Options
138
+
139
+ ```bash
140
+ juninho setup [dir] [options]
141
+
142
+ Options:
143
+ --force Force reinstall even if already configured
144
+ --type <value> Project type: node-nextjs, node-generic, python, go, java, generic
145
+ --no-tty Non-interactive mode (skip prompts)
146
+ --version Show version number
147
+ --help Show help
148
+ ```
149
+
51
150
  ## License
52
151
 
53
152
  MIT
package/dist/cli.js CHANGED
@@ -6,39 +6,47 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const installer_js_1 = require("./installer.js");
8
8
  const path_1 = __importDefault(require("path"));
9
+ const project_types_js_1 = require("./project-types.js");
9
10
  const { version: VERSION } = require("../package.json");
10
11
  const args = process.argv.slice(2);
11
12
  const command = args[0] ?? "";
12
13
  const forceFlag = args.includes("--force");
13
- const targetDir = args.find(a => !a.startsWith("--") && a !== command) ?? process.cwd();
14
+ // Parse --type flag
15
+ const typeIdx = args.indexOf("--type");
16
+ const typeValue = typeIdx !== -1 ? args[typeIdx + 1] : undefined;
17
+ const targetDir = args.find((a, i) => !a.startsWith("--") && a !== command && i !== typeIdx + 1) ?? process.cwd();
14
18
  function showHelp() {
15
19
  console.log(`
16
20
  juninho v${VERSION} — Agentic Coding Framework bootstrapper for OpenCode
17
-
18
- Usage:
19
- juninho <command> [project-dir] [options]
20
-
21
- Commands:
22
- setup [dir] [--force] Install the framework into a project
23
-
21
+
22
+ Usage:
23
+ juninho <command> [project-dir] [options]
24
+
25
+ Commands:
26
+ setup [dir] [--force] Install the framework into a project
27
+
24
28
  Options:
25
29
  --force Reinstall even if already configured
30
+ --type <type> Set project type (skips auto-detection)
31
+ Values: ${project_types_js_1.VALID_PROJECT_TYPES.join(", ")}
26
32
  --version, -v Show juninho version
27
33
  --help, -h Show this help message
28
-
29
- Model Tiers:
30
- Strong → Planning & spec writing (default: claude-opus-4.6)
31
- Medium → Implementation & review (default: claude-sonnet-4.6)
32
- Weak → Research & exploration (default: claude-haiku-4.5)
33
-
34
- During setup, juninho detects available models via 'opencode models'
35
- and lets you choose the best model for each tier.
36
- To reconfigure models, run 'juninho setup --force'.
37
-
38
- Examples:
39
- juninho setup Install with auto-detected models
40
- juninho setup ./my-project Install into a specific directory
41
- juninho setup --force Reinstall & reconfigure models
34
+
35
+ Model Tiers:
36
+ Strong → Planning & spec writing (default: claude-opus-4.6)
37
+ Medium → Implementation & review (default: claude-sonnet-4.6)
38
+ Weak → Research & exploration (default: claude-haiku-4.5)
39
+
40
+ During setup, juninho detects available models via 'opencode models'
41
+ and lets you choose the best model for each tier.
42
+ To reconfigure models, run 'juninho setup --force'.
43
+
44
+ Examples:
45
+ juninho setup Auto-detect stack and models
46
+ juninho setup ./my-project Install into a specific directory
47
+ juninho setup --type python Force Python project type
48
+ juninho setup --type java ./spring-app Java/Kotlin project (Kotlin auto-detected)
49
+ juninho setup --force Reinstall & reconfigure models
42
50
  `);
43
51
  }
44
52
  function showVersion() {
@@ -51,7 +59,16 @@ else if (command === "--version" || command === "-v") {
51
59
  showVersion();
52
60
  }
53
61
  else if (command === "setup") {
54
- (0, installer_js_1.runSetup)(path_1.default.resolve(targetDir), { force: forceFlag })
62
+ // Validate --type if provided
63
+ if (typeValue !== undefined && !project_types_js_1.VALID_PROJECT_TYPES.includes(typeValue)) {
64
+ console.error(`[juninho] Invalid project type: ${typeValue}`);
65
+ console.error(`[juninho] Valid types: ${project_types_js_1.VALID_PROJECT_TYPES.join(", ")}`);
66
+ process.exit(1);
67
+ }
68
+ (0, installer_js_1.runSetup)(path_1.default.resolve(targetDir), {
69
+ force: forceFlag,
70
+ type: typeValue,
71
+ })
55
72
  .then(() => process.exit(0))
56
73
  .catch((e) => {
57
74
  console.error("[juninho] Error:", e.message);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;AACA,iDAAyC;AACzC,gDAAuB;AAEvB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAA;AAE9E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAClC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;AAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAA;AAEvF,SAAS,QAAQ;IACf,OAAO,CAAC,GAAG,CAAC;WACH,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BjB,CAAC,CAAA;AACF,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;AACtB,CAAC;AAED,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;IAC/D,QAAQ,EAAE,CAAA;AACZ,CAAC;KAAM,IAAI,OAAO,KAAK,WAAW,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;IACvD,WAAW,EAAE,CAAA;AACf,CAAC;KAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;IAC/B,IAAA,uBAAQ,EAAC,cAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;SACpD,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAC3B,KAAK,CAAC,CAAC,CAAQ,EAAE,EAAE;QAClB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAC,CAAA;AACN,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,KAAK,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAA;IACtD,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAA;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;AACA,iDAAyC;AACzC,gDAAuB;AACvB,yDAA0E;AAE1E,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAA;AAE9E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAClC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;AAE1C,oBAAoB;AACpB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;AACtC,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAEhE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CACzB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,OAAO,GAAG,CAAC,CACpE,IAAI,OAAO,CAAC,GAAG,EAAE,CAAA;AAElB,SAAS,QAAQ;IACf,OAAO,CAAC,GAAG,CAAC;WACH,OAAO;;;;;;;;;;;oCAWkB,sCAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;CAmBjE,CAAC,CAAA;AACF,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;AACtB,CAAC;AAED,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;IAC/D,QAAQ,EAAE,CAAA;AACZ,CAAC;KAAM,IAAI,OAAO,KAAK,WAAW,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;IACvD,WAAW,EAAE,CAAA;AACf,CAAC;KAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;IAC/B,8BAA8B;IAC9B,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,sCAAmB,CAAC,QAAQ,CAAC,SAAwB,CAAC,EAAE,CAAC;QACvF,OAAO,CAAC,KAAK,CAAC,mCAAmC,SAAS,EAAE,CAAC,CAAA;QAC7D,OAAO,CAAC,KAAK,CAAC,0BAA0B,sCAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAA,uBAAQ,EAAC,cAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;QAChC,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAoC;KAC3C,CAAC;SACC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAC3B,KAAK,CAAC,CAAC,CAAQ,EAAE,EAAE;QAClB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAC,CAAA;AACN,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,KAAK,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAA;IACtD,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAA;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC"}
package/dist/config.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  * Contains the user's chosen models for each tier (strong/medium/weak).
6
6
  */
7
7
  import { type ModelTier } from "./models.js";
8
+ import type { ProjectType, BuildTool } from "./project-types.js";
8
9
  export interface JuninhoConfig {
9
10
  /** Model ID for strong-tier agents (planner, spec-writer) */
10
11
  strong: string;
@@ -12,6 +13,12 @@ export interface JuninhoConfig {
12
13
  medium: string;
13
14
  /** Model ID for weak-tier agents (explore, librarian) */
14
15
  weak: string;
16
+ /** Detected or user-selected project type */
17
+ projectType?: ProjectType;
18
+ /** Whether the java project uses Kotlin */
19
+ isKotlin?: boolean;
20
+ /** Build tool for java projects */
21
+ buildTool?: BuildTool;
15
22
  }
16
23
  /**
17
24
  * Load saved configuration from .opencode/juninho-config.json.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,aAAa,CAAA;AAG9D,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;CACb;AAQD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAanE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAM1E;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CA6B/D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAM/E"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,aAAa,CAAA;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAEhE,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,mCAAmC;IACnC,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAQD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAoBnE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAM1E;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CA6B/D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAM/E"}
package/dist/config.js CHANGED
@@ -32,7 +32,14 @@ function loadConfig(projectDir) {
32
32
  try {
33
33
  const data = JSON.parse((0, fs_1.readFileSync)(p, "utf-8"));
34
34
  if (data.strong && data.medium && data.weak) {
35
- return { strong: data.strong, medium: data.medium, weak: data.weak };
35
+ return {
36
+ strong: data.strong,
37
+ medium: data.medium,
38
+ weak: data.weak,
39
+ projectType: data.projectType,
40
+ isKotlin: data.isKotlin,
41
+ buildTool: data.buildTool,
42
+ };
36
43
  }
37
44
  return null;
38
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;AA0BH,gCAaC;AAMD,gCAMC;AAUD,sCA6BC;AAKD,wCAMC;AAnGD,2BAAuE;AACvE,gDAAuB;AACvB,2CAA8D;AAC9D,iDAAwD;AAWxD,MAAM,eAAe,GAAG,qBAAqB,CAAA;AAE7C,SAAS,UAAU,CAAC,UAAkB;IACpC,OAAO,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,eAAe,CAAC,CAAA;AAC5D,CAAC;AAED;;;GAGG;AACH,SAAgB,UAAU,CAAC,UAAkB;IAC3C,MAAM,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,CAAA;IAChC,IAAI,CAAC,IAAA,eAAU,EAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAE/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAA;QACjD,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5C,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAA;QACtE,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,UAAU,CAAC,UAAkB,EAAE,MAAqB;IAClE,MAAM,GAAG,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;IAC9C,IAAI,CAAC,IAAA,eAAU,EAAC,GAAG,CAAC,EAAE,CAAC;QACrB,IAAA,cAAS,EAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACrC,CAAC;IACD,IAAA,kBAAa,EAAC,UAAU,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AAC/E,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,aAAa,CAAC,UAAkB;IAC9C,wBAAwB;IACxB,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,CAAA;IACpC,IAAI,KAAK;QAAE,OAAO,KAAK,CAAA;IAEvB,mBAAmB;IACnB,MAAM,SAAS,GAAG,IAAA,sCAAuB,GAAE,CAAA;IAC3C,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAA,4BAAgB,EAAC,SAAS,CAAC,CAAA;QACxC,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC;YACnC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC;YACnC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC;SAChC,CAAA;IACH,CAAC;IAED,wCAAwC;IACxC,MAAM,IAAI,KAAK,CACb,kDAAkD;QAClD,iEAAiE;QACjE,IAAI;QACJ,uBAAuB;QACvB,6DAA6D;QAC7D,sDAAsD;QACtD,IAAI;QACJ,eAAe;QACf,+DAA+D;QAC/D,oFAAoF,CACrF,CAAA;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,MAAqB;IAClD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI,EAAE,MAAM,CAAC,IAAI;KAClB,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
+ import { type ProjectType } from "./project-types.js";
1
2
  export interface SetupOptions {
2
3
  force?: boolean;
4
+ type?: ProjectType;
3
5
  }
4
6
  export declare function runSetup(projectDir: string, options?: SetupOptions): Promise<void>;
5
7
  //# sourceMappingURL=installer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAqLD,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+E5F"}
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAqBA,OAAO,EACL,KAAK,WAAW,EAQjB,MAAM,oBAAoB,CAAA;AAO3B,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,WAAW,CAAA;CACnB;AAoTD,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsH5F"}
package/dist/installer.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runSetup = runSetup;
7
7
  const fs_1 = require("fs");
8
8
  const readline_1 = require("readline");
9
+ const child_process_1 = require("child_process");
9
10
  const path_1 = __importDefault(require("path"));
10
11
  const agents_js_1 = require("./templates/agents.js");
11
12
  const skills_js_1 = require("./templates/skills.js");
@@ -18,6 +19,8 @@ const docs_js_1 = require("./templates/docs.js");
18
19
  const config_js_1 = require("./config.js");
19
20
  const discovery_js_1 = require("./discovery.js");
20
21
  const models_js_1 = require("./models.js");
22
+ const project_types_js_1 = require("./project-types.js");
23
+ const lint_detection_js_1 = require("./lint-detection.js");
21
24
  /* ─── Readline helper ─── */
22
25
  function ask(rl, question) {
23
26
  return new Promise((resolve) => {
@@ -141,33 +144,126 @@ async function manualEntry(rl, existing) {
141
144
  };
142
145
  }
143
146
  /* ─── Resolve models (auto or interactive) ─── */
144
- /**
145
- * Resolve models for setup:
146
- * 1. Saved config exists → use it
147
- * 2. Auto-discovery succeeds and finds exactly one per tier → use auto
148
- * 3. Otherwise → interactive selection
149
- */
150
147
  async function resolveModelsForSetup(projectDir, rl) {
151
- // 1. Check saved config
152
148
  const saved = (0, config_js_1.loadConfig)(projectDir);
153
149
  if (saved)
154
150
  return saved;
155
- // 2. Try auto-discovery (non-interactive happy path)
156
151
  const available = (0, discovery_js_1.discoverAvailableModels)();
157
152
  if (available.length > 0) {
158
153
  const best = (0, models_js_1.selectBestModels)(available);
159
154
  if (best.strong && best.medium && best.weak) {
160
- // All tiers have a clear best — use them automatically
161
155
  return { strong: best.strong, medium: best.medium, weak: best.weak };
162
156
  }
163
157
  }
164
- // 3. Non-interactive fallback (CI, piped stdin, no TTY)
165
158
  if (!process.stdin.isTTY) {
166
159
  return { strong: models_js_1.DEFAULT_MODELS.strong, medium: models_js_1.DEFAULT_MODELS.medium, weak: models_js_1.DEFAULT_MODELS.weak };
167
160
  }
168
- // 4. Interactive selection (discovery failed or ambiguous)
169
161
  return interactiveModelSelection(rl, null);
170
162
  }
163
+ /* ─── Resolve project type ─── */
164
+ const TYPE_LABELS = {
165
+ "node-nextjs": "Node.js + Next.js",
166
+ "node-generic": "Node.js (generic)",
167
+ "python": "Python",
168
+ "go": "Go",
169
+ "java": "Java / Kotlin (JVM)",
170
+ "generic": "Generic",
171
+ };
172
+ async function resolveProjectType(projectDir, rl, options, savedConfig) {
173
+ // 1. CLI flag takes precedence
174
+ if (options.type) {
175
+ const isKotlin = options.type === "java" && (0, project_types_js_1.detectKotlin)(projectDir);
176
+ if (isKotlin) {
177
+ console.log("[juninho] Kotlin detected in Java project");
178
+ }
179
+ return { projectType: options.type, isKotlin };
180
+ }
181
+ // 2. Saved config (unless --force)
182
+ if (savedConfig?.projectType && !options.force) {
183
+ return {
184
+ projectType: savedConfig.projectType,
185
+ isKotlin: savedConfig.isKotlin ?? false,
186
+ };
187
+ }
188
+ // 3. Auto-detect
189
+ const detected = (0, project_types_js_1.detectProjectType)(projectDir);
190
+ if (detected) {
191
+ const isKotlin = detected === "java" && (0, project_types_js_1.detectKotlin)(projectDir);
192
+ const label = isKotlin ? "Java/Kotlin (JVM)" : TYPE_LABELS[detected];
193
+ if (process.stdin.isTTY) {
194
+ const response = await ask(rl, `[juninho] Tipo de projeto detectado: ${label}. Confirma? (S/n): `);
195
+ if (response.toLowerCase() === "n") {
196
+ return interactiveTypeSelection(rl, projectDir);
197
+ }
198
+ }
199
+ return { projectType: detected, isKotlin };
200
+ }
201
+ // 4. No detection — interactive or fallback
202
+ if (process.stdin.isTTY) {
203
+ console.log("[juninho] Tipo de projeto não detectado automaticamente.");
204
+ return interactiveTypeSelection(rl, projectDir);
205
+ }
206
+ return { projectType: "generic", isKotlin: false };
207
+ }
208
+ async function interactiveTypeSelection(rl, projectDir) {
209
+ console.log("");
210
+ console.log("[juninho] Selecione o tipo de projeto:");
211
+ project_types_js_1.VALID_PROJECT_TYPES.forEach((t, i) => {
212
+ console.log(` ${i + 1}) ${TYPE_LABELS[t]}`);
213
+ });
214
+ const response = await ask(rl, ` Escolha (1-${project_types_js_1.VALID_PROJECT_TYPES.length}): `);
215
+ const idx = parseInt(response, 10);
216
+ const projectType = (idx >= 1 && idx <= project_types_js_1.VALID_PROJECT_TYPES.length)
217
+ ? project_types_js_1.VALID_PROJECT_TYPES[idx - 1]
218
+ : "generic";
219
+ const isKotlin = projectType === "java" && (0, project_types_js_1.detectKotlin)(projectDir);
220
+ if (isKotlin) {
221
+ console.log("[juninho] Kotlin detected in Java project");
222
+ }
223
+ return { projectType, isKotlin };
224
+ }
225
+ /* ─── Lint detection and suggestion ─── */
226
+ async function handleLintDetection(projectDir, projectType, isKotlin, buildTool, rl) {
227
+ const result = (0, lint_detection_js_1.detectLintTool)(projectDir, projectType, isKotlin);
228
+ if (result.detected) {
229
+ console.log(`[juninho] ✓ Linter detectado: ${result.detected}${result.configFile ? ` (${result.configFile})` : ""}`);
230
+ return result.detected;
231
+ }
232
+ // No linter detected — suggest if interactive
233
+ if (!process.stdin.isTTY)
234
+ return undefined;
235
+ const suggestions = (0, lint_detection_js_1.suggestLintTools)(projectType, isKotlin, buildTool);
236
+ if (suggestions.length === 0)
237
+ return undefined;
238
+ const typeLabel = isKotlin ? "Kotlin" : TYPE_LABELS[projectType];
239
+ console.log("");
240
+ console.log(`[juninho] Nenhum linter detectado. Sugestões para projetos ${typeLabel}:`);
241
+ suggestions.forEach((s, i) => {
242
+ console.log(` ${i + 1}) ${s.name} — ${s.command}`);
243
+ });
244
+ console.log(` ${suggestions.length + 1}) Skip`);
245
+ const response = await ask(rl, ` Escolha (1-${suggestions.length + 1}) ou Enter para skip: `);
246
+ const idx = parseInt(response, 10);
247
+ if (idx >= 1 && idx <= suggestions.length) {
248
+ const chosen = suggestions[idx - 1];
249
+ if (chosen.install) {
250
+ console.log(`[juninho] Instalando ${chosen.name}...`);
251
+ try {
252
+ (0, child_process_1.execSync)(chosen.install, {
253
+ cwd: projectDir,
254
+ stdio: "inherit",
255
+ timeout: 120_000,
256
+ });
257
+ console.log(`[juninho] ✓ ${chosen.name} instalado`);
258
+ }
259
+ catch {
260
+ console.log(`[juninho] ⚠ Falha ao instalar ${chosen.name}. Continue manualmente.`);
261
+ }
262
+ }
263
+ return chosen.name;
264
+ }
265
+ return undefined;
266
+ }
171
267
  /* ─── Main setup ─── */
172
268
  async function runSetup(projectDir, options = {}) {
173
269
  const marker = path_1.default.join(projectDir, ".opencode", ".juninho-installed");
@@ -186,40 +282,55 @@ async function runSetup(projectDir, options = {}) {
186
282
  console.log(`[juninho] Strong: ${models.strong}`);
187
283
  console.log(`[juninho] Medium: ${models.medium}`);
188
284
  console.log(`[juninho] Weak: ${models.weak}`);
285
+ // Step 0.5: Resolve project type
286
+ const savedConfig = (0, config_js_1.loadConfig)(projectDir);
287
+ const { projectType, isKotlin } = await resolveProjectType(projectDir, rl, options, savedConfig);
288
+ const buildTool = projectType === "java" ? (0, project_types_js_1.detectBuildTool)(projectDir) : undefined;
289
+ const typeLabel = isKotlin ? "Java/Kotlin" : TYPE_LABELS[projectType];
290
+ console.log(`[juninho] ✓ Project type: ${typeLabel}${buildTool ? ` (${buildTool})` : ""}`);
291
+ // Step 0.7: Detect/suggest lint tool
292
+ const lintTool = await handleLintDetection(projectDir, projectType, isKotlin, buildTool, rl);
189
293
  // Step 1: Create directory structure
190
- createDirectories(projectDir);
294
+ const config = (0, project_types_js_1.getEffectiveConfig)(projectType, isKotlin, buildTool);
295
+ createDirectories(projectDir, config.skills);
191
296
  console.log("[juninho] ✓ Directories created");
192
- // Step 2: Save model config
193
- (0, config_js_1.saveConfig)(projectDir, models);
194
- console.log("[juninho] ✓ Model config saved");
195
- // Step 3: Write agents (with resolved models)
196
- (0, agents_js_1.writeAgents)(projectDir, models);
297
+ // Step 2: Save config (models + project type)
298
+ const fullConfig = {
299
+ ...models,
300
+ projectType,
301
+ isKotlin: isKotlin || undefined,
302
+ buildTool,
303
+ };
304
+ (0, config_js_1.saveConfig)(projectDir, fullConfig);
305
+ console.log("[juninho] ✓ Config saved");
306
+ // Step 3: Write agents
307
+ (0, agents_js_1.writeAgents)(projectDir, models, projectType, isKotlin, buildTool);
197
308
  console.log("[juninho] ✓ Agents created (9)");
198
- // Step 4: Write skills
199
- (0, skills_js_1.writeSkills)(projectDir);
200
- console.log("[juninho] ✓ Skills created (9)");
309
+ // Step 4: Write skills (filtered by project type)
310
+ (0, skills_js_1.writeSkills)(projectDir, projectType, isKotlin);
311
+ console.log(`[juninho] ✓ Skills created (${config.skills.length})`);
201
312
  // Step 5: Write plugins
202
- (0, plugins_js_1.writePlugins)(projectDir);
313
+ (0, plugins_js_1.writePlugins)(projectDir, projectType, isKotlin);
203
314
  console.log("[juninho] ✓ Plugins created (12)");
204
315
  // Step 6: Write tools
205
- (0, tools_js_1.writeTools)(projectDir);
316
+ (0, tools_js_1.writeTools)(projectDir, projectType, isKotlin, buildTool);
206
317
  console.log("[juninho] ✓ Tools created (4)");
207
318
  // Step 7: Write support scripts
208
- (0, support_scripts_js_1.writeSupportScripts)(projectDir);
319
+ (0, support_scripts_js_1.writeSupportScripts)(projectDir, projectType, isKotlin, lintTool);
209
320
  console.log("[juninho] ✓ Support scripts created (4)");
210
321
  // Step 8: Write commands
211
322
  (0, commands_js_1.writeCommands)(projectDir);
212
- console.log("[juninho] ✓ Commands created (14)");
323
+ console.log("[juninho] ✓ Commands created (15)");
213
324
  // Step 9: Write state files
214
325
  (0, state_js_1.writeState)(projectDir);
215
326
  console.log("[juninho] ✓ State files created");
216
- // Step 10: Write docs
217
- (0, docs_js_1.writeDocs)(projectDir);
327
+ // Step 10: Write docs (parameterized by project type)
328
+ (0, docs_js_1.writeDocs)(projectDir, projectType, isKotlin);
218
329
  console.log("[juninho] ✓ Docs scaffold created");
219
- // Step 11: Patch opencode.json (with resolved models)
330
+ // Step 11: Patch opencode.json
220
331
  (0, docs_js_1.patchOpencodeJson)(projectDir, models);
221
332
  console.log("[juninho] ✓ opencode.json patched");
222
- // Step 12: Install pre-commit hook (outer validation loop)
333
+ // Step 12: Install pre-commit hook
223
334
  writePreCommitHook(projectDir);
224
335
  // Step 13: Write marker
225
336
  (0, fs_1.writeFileSync)(marker, new Date().toISOString());
@@ -227,6 +338,30 @@ async function runSetup(projectDir, options = {}) {
227
338
  console.log("[juninho] ✓ Framework installed successfully!");
228
339
  console.log("[juninho] Open OpenCode — /j.plan, /j.spec and /j.implement are ready.");
229
340
  console.log("[juninho] Agents: @j.planner, @j.spec-writer, @j.implementer, @j.validator, @j.reviewer, @j.unify, @j.explore, @j.librarian");
341
+ // Step 14: Offer /j.finish-setup
342
+ if (process.stdin.isTTY) {
343
+ console.log("");
344
+ const finishResponse = await ask(rl, "[juninho] Deseja executar /j.finish-setup agora para gerar skills e docs do projeto? (S/n): ");
345
+ if (finishResponse.toLowerCase() !== "n") {
346
+ console.log("[juninho] Executando /j.finish-setup via opencode...");
347
+ try {
348
+ (0, child_process_1.execSync)('opencode run "/j.finish-setup"', {
349
+ cwd: projectDir,
350
+ stdio: "inherit",
351
+ timeout: 600_000, // 10 minutes
352
+ });
353
+ }
354
+ catch {
355
+ console.log("[juninho] ⚠ Falha ao executar /j.finish-setup. Execute manualmente no OpenCode.");
356
+ }
357
+ }
358
+ else {
359
+ console.log("[juninho] Rode /j.finish-setup no OpenCode quando quiser gerar skills e documentação.");
360
+ }
361
+ }
362
+ else {
363
+ console.log("[juninho] Rode /j.finish-setup no OpenCode quando quiser gerar skills e documentação do projeto.");
364
+ }
230
365
  }
231
366
  finally {
232
367
  rl.close();
@@ -235,32 +370,30 @@ async function runSetup(projectDir, options = {}) {
235
370
  function writePreCommitHook(projectDir) {
236
371
  const gitHooksDir = path_1.default.join(projectDir, ".git", "hooks");
237
372
  if (!(0, fs_1.existsSync)(gitHooksDir)) {
238
- // Not a git repo or hooks dir doesn't exist — skip silently
239
373
  return;
240
374
  }
241
375
  const hookPath = path_1.default.join(gitHooksDir, "pre-commit");
242
376
  if ((0, fs_1.existsSync)(hookPath)) {
243
377
  const existing = (0, fs_1.readFileSync)(hookPath, "utf-8");
244
378
  if (!existing.includes("installed by juninho")) {
245
- // Preserve existing hook — do not overwrite
246
379
  console.log("[juninho] ⚠ pre-commit hook already exists — skipping (not installed by juninho)");
247
380
  return;
248
381
  }
249
382
  }
250
- const hookContent = `#!/bin/sh
251
- # Deterministic outer validation loop — installed by juninho
252
- # Runs structure lint + related tests before every commit.
253
- # Do not bypass with --no-verify.
254
- set -e
255
-
256
- ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
257
-
258
- if [ ! -x "$ROOT_DIR/.opencode/scripts/pre-commit.sh" ]; then
259
- echo "[pre-commit] FAIL: .opencode/scripts/pre-commit.sh not found or not executable"
260
- exit 1
261
- fi
262
-
263
- exec "$ROOT_DIR/.opencode/scripts/pre-commit.sh"
383
+ const hookContent = `#!/bin/sh
384
+ # Deterministic outer validation loop — installed by juninho
385
+ # Runs structure lint + related tests before every commit.
386
+ # Do not bypass with --no-verify.
387
+ set -e
388
+
389
+ ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
390
+
391
+ if [ ! -x "$ROOT_DIR/.opencode/scripts/pre-commit.sh" ]; then
392
+ echo "[pre-commit] FAIL: .opencode/scripts/pre-commit.sh not found or not executable"
393
+ exit 1
394
+ fi
395
+
396
+ exec "$ROOT_DIR/.opencode/scripts/pre-commit.sh"
264
397
  `;
265
398
  (0, fs_1.writeFileSync)(hookPath, hookContent);
266
399
  try {
@@ -271,20 +404,13 @@ exec "$ROOT_DIR/.opencode/scripts/pre-commit.sh"
271
404
  console.log("[juninho] ✓ pre-commit hook written (chmod not supported on this platform — make it executable manually)");
272
405
  }
273
406
  }
274
- function createDirectories(projectDir) {
407
+ function createDirectories(projectDir, skills) {
275
408
  const dirs = [
276
409
  ".opencode",
277
410
  ".opencode/agents",
278
411
  ".opencode/skills",
279
- ".opencode/skills/j.test-writing",
280
- ".opencode/skills/j.page-creation",
281
- ".opencode/skills/j.api-route-creation",
282
- ".opencode/skills/j.server-action-creation",
283
- ".opencode/skills/j.schema-migration",
284
- ".opencode/skills/j.agents-md-writing",
285
- ".opencode/skills/j.domain-doc-writing",
286
- ".opencode/skills/j.principle-doc-writing",
287
- ".opencode/skills/j.shell-script-writing",
412
+ // Only create skill directories for the relevant project type
413
+ ...skills.map((s) => `.opencode/skills/${s}`),
288
414
  ".opencode/plugins",
289
415
  ".opencode/tools",
290
416
  ".opencode/scripts",