@automagik/genie 0.260202.1833 → 0.260203.43
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.
- package/.beads/README.md +81 -0
- package/.beads/config.yaml +67 -0
- package/.beads/interactions.jsonl +0 -0
- package/.beads/issues.jsonl +9 -0
- package/.beads/metadata.json +4 -0
- package/.claude/skills/brainstorm/SKILL.md +53 -0
- package/.claude/skills/genie-base/SKILL.md +66 -0
- package/.claude/skills/genie-base/assets/workspace/AGENTS.md +191 -0
- package/.claude/skills/genie-base/assets/workspace/ENVIRONMENT.md +18 -0
- package/.claude/skills/genie-base/assets/workspace/HEARTBEAT.md +4 -0
- package/.claude/skills/genie-base/assets/workspace/IDENTITY.md +17 -0
- package/.claude/skills/genie-base/assets/workspace/MEMORY.md +16 -0
- package/.claude/skills/genie-base/assets/workspace/ROLE.md +14 -0
- package/.claude/skills/genie-base/assets/workspace/SOUL.md +36 -0
- package/.claude/skills/genie-base/assets/workspace/TOOLS.md +25 -0
- package/.claude/skills/genie-base/assets/workspace/USER.md +13 -0
- package/.claude/skills/genie-base/assets/workspace/memory/2026-01-30.md +6 -0
- package/.claude/skills/genie-base/assets/workspace/memory/2026-01-31.md +16 -0
- package/.claude/skills/genie-base/assets/workspace/memory/882c22be-9710-41c1-91f8-ed82947ef6ce.txt +1 -0
- package/.claude/skills/genie-base/scripts/install-workspace.sh +107 -0
- package/.claude/skills/genie-base/scripts/sanity-sweep.sh +60 -0
- package/.claude/skills/genie-blank-init/SKILL.md +37 -0
- package/.claude/skills/genie-blank-init/assets/BOOTSTRAP.md +44 -0
- package/.claude/skills/genie-blank-init/assets/IDENTITY.md +9 -0
- package/.claude/skills/genie-blank-init/assets/SOUL.md +10 -0
- package/.claude/skills/genie-blank-init/assets/USER.md +9 -0
- package/.claude/skills/genie-blank-init/scripts/apply-blank-init.sh +117 -0
- package/.claude/skills/genie-forge/SKILL.md +171 -0
- package/.claude/skills/genie-plan-review/CLAUDE.md +11 -0
- package/.claude/skills/genie-plan-review/SKILL.md +53 -0
- package/.claude/skills/genie-review/SKILL.md +171 -0
- package/.claude/skills/genie-wish/SKILL.md +141 -0
- package/.claude-plugin/marketplace.json +18 -0
- package/.genie/.gitkeep +3 -0
- package/.genie/backlog/hooks-v2.md +82 -0
- package/.genie/wishes/upgrade-brainstorm-handoff/wish.md +124 -0
- package/.gitattributes +3 -0
- package/AGENTS.md +75 -0
- package/bun.lock +55 -0
- package/dist/claudio.js +1 -1
- package/dist/genie.js +1 -1
- package/dist/term.js +123 -99
- package/docs/CO-ORCHESTRATION-GUIDE.md +368 -0
- package/package.json +5 -1
- package/plugin/.claude-plugin/plugin.json +18 -0
- package/plugin/README.md +120 -0
- package/plugin/agents/implementor.md +92 -0
- package/plugin/agents/quality-reviewer.md +113 -0
- package/plugin/agents/spec-reviewer.md +90 -0
- package/plugin/hooks/hooks.json +3 -0
- package/plugin/references/review-criteria.md +72 -0
- package/plugin/references/wish-template.md +92 -0
- package/plugin/scripts/genie.cjs +141 -0
- package/plugin/scripts/smart-install.js +308 -0
- package/plugin/scripts/src/install-genie-cli.sh +120 -0
- package/plugin/scripts/src/validate-completion.ts +142 -0
- package/plugin/scripts/src/validate-wish.ts +137 -0
- package/plugin/scripts/term.cjs +229 -0
- package/plugin/scripts/validate-completion.cjs +16 -0
- package/plugin/scripts/validate-wish.cjs +17 -0
- package/plugin/scripts/worker-service.cjs +28 -0
- package/plugin/skills/brainstorm/SKILL.md +106 -0
- package/plugin/skills/forge/SKILL.md +171 -0
- package/plugin/skills/genie-base/SKILL.md +99 -0
- package/plugin/skills/genie-base/assets/workspace/AGENTS.md +191 -0
- package/plugin/skills/genie-base/assets/workspace/ENVIRONMENT.md +18 -0
- package/plugin/skills/genie-base/assets/workspace/HEARTBEAT.md +4 -0
- package/plugin/skills/genie-base/assets/workspace/IDENTITY.md +17 -0
- package/plugin/skills/genie-base/assets/workspace/MEMORY.md +16 -0
- package/plugin/skills/genie-base/assets/workspace/ROLE.md +14 -0
- package/plugin/skills/genie-base/assets/workspace/SOUL.md +36 -0
- package/plugin/skills/genie-base/assets/workspace/TOOLS.md +25 -0
- package/plugin/skills/genie-base/assets/workspace/USER.md +13 -0
- package/plugin/skills/genie-base/scripts/install-workspace.sh +107 -0
- package/plugin/skills/genie-base/scripts/sanity-sweep.sh +60 -0
- package/plugin/skills/genie-blank-init/SKILL.md +73 -0
- package/plugin/skills/genie-blank-init/assets/BOOTSTRAP.md +44 -0
- package/plugin/skills/genie-blank-init/assets/IDENTITY.md +9 -0
- package/plugin/skills/genie-blank-init/assets/SOUL.md +10 -0
- package/plugin/skills/genie-blank-init/assets/USER.md +9 -0
- package/plugin/skills/genie-blank-init/scripts/apply-blank-init.sh +117 -0
- package/plugin/skills/genie-cli-dev/CLAUDE.md +19 -0
- package/plugin/skills/genie-cli-dev/SKILL.md +292 -0
- package/plugin/skills/plan-review/SKILL.md +101 -0
- package/plugin/skills/review/SKILL.md +221 -0
- package/plugin/skills/wish/SKILL.md +110 -0
- package/plugin/skills/work-orchestration/SKILL.md +110 -0
- package/scripts/build.js +132 -0
- package/scripts/smart-install.js +308 -0
- package/scripts/sync.js +134 -0
- package/src/lib/beads-registry.ts +595 -0
- package/src/lib/orchestrator/event-monitor.ts +2 -0
- package/src/lib/skill-loader.ts +215 -0
- package/src/lib/tmux.ts +30 -11
- package/src/lib/version.ts +1 -1
- package/src/lib/worker-registry.ts +10 -0
- package/src/services/worker-service.ts +351 -0
- package/src/term-commands/close.ts +48 -3
- package/src/term-commands/create.ts +95 -0
- package/src/term-commands/daemon.ts +176 -0
- package/src/term-commands/kill.ts +56 -2
- package/src/term-commands/orchestrate.ts +3 -2
- package/src/term-commands/send.ts +43 -15
- package/src/term-commands/spawn.ts +446 -0
- package/src/term-commands/split.ts +20 -8
- package/src/term-commands/work.ts +279 -37
- package/src/term-commands/workers.ts +36 -2
- package/src/term.ts +120 -7
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wish
|
|
3
|
+
description: "Use when starting non-trivial work that needs structured planning - converts ideas into wish documents with scope, acceptance criteria, and execution tasks."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Wish - Turn Ideas Into Plans
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Convert a validated design (from `/brainstorm`) or direct request into a structured wish document. Create native Claude Code tasks for execution.
|
|
11
|
+
|
|
12
|
+
**Output:** `.genie/wishes/<slug>/wish.md` + native tasks ready for `/forge`
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## The Flow
|
|
17
|
+
|
|
18
|
+
### Phase 1: Brainstorm (if needed)
|
|
19
|
+
|
|
20
|
+
If coming from `/brainstorm`, the design is already validated. If starting fresh:
|
|
21
|
+
|
|
22
|
+
Enter plan mode to explore the codebase read-only. Understand the landscape before proposing anything.
|
|
23
|
+
|
|
24
|
+
**One question at a time. Multiple choice when possible.**
|
|
25
|
+
|
|
26
|
+
1. **Resonate** - Understand the intent
|
|
27
|
+
- What does the user actually want? (not just what they said)
|
|
28
|
+
- Ask clarifying questions, one at a time
|
|
29
|
+
- Prefer multiple choice: "Which of these best describes what you want?"
|
|
30
|
+
|
|
31
|
+
2. **Explore approaches** - Propose 2-3 alternatives
|
|
32
|
+
- Present options with trade-offs
|
|
33
|
+
- Lead with your recommendation and explain why
|
|
34
|
+
- Let the user choose (or propose something different)
|
|
35
|
+
|
|
36
|
+
3. **Align on scope** - Define boundaries
|
|
37
|
+
- What's IN scope?
|
|
38
|
+
- What's explicitly OUT of scope?
|
|
39
|
+
- What assumptions are we making?
|
|
40
|
+
|
|
41
|
+
### Phase 2: Design (200-300 Word Sections)
|
|
42
|
+
|
|
43
|
+
Present the design incrementally. Check after each section.
|
|
44
|
+
|
|
45
|
+
**Sections to cover:**
|
|
46
|
+
- Summary (what and why, 2-3 sentences)
|
|
47
|
+
- Architecture approach
|
|
48
|
+
- Key decisions with rationale
|
|
49
|
+
- Risks and mitigations
|
|
50
|
+
|
|
51
|
+
### Phase 3: Plan (Bite-Sized Tasks)
|
|
52
|
+
|
|
53
|
+
Break the design into execution groups. Each task should be:
|
|
54
|
+
- **Small enough** to implement in one focused session
|
|
55
|
+
- **Testable** with clear acceptance criteria
|
|
56
|
+
- **Independent** where possible (minimize blocking dependencies)
|
|
57
|
+
|
|
58
|
+
**Per task, define:**
|
|
59
|
+
- What to do (deliverables)
|
|
60
|
+
- Acceptance criteria (checkboxes)
|
|
61
|
+
- Validation command (how to verify it worked)
|
|
62
|
+
- Files likely touched
|
|
63
|
+
|
|
64
|
+
### Phase 4: Write the Wish Document
|
|
65
|
+
|
|
66
|
+
Write to `.genie/wishes/<slug>/wish.md` using the template from `references/wish-template.md`.
|
|
67
|
+
|
|
68
|
+
**Required sections:**
|
|
69
|
+
- Status, Slug, Created date
|
|
70
|
+
- Summary (what and why)
|
|
71
|
+
- Scope (IN and OUT - OUT must not be empty)
|
|
72
|
+
- Decisions with rationale
|
|
73
|
+
- Success Criteria (checkboxes)
|
|
74
|
+
- Execution Groups with acceptance criteria and validation commands
|
|
75
|
+
|
|
76
|
+
### Phase 5: Create Native Tasks
|
|
77
|
+
|
|
78
|
+
After writing the wish document:
|
|
79
|
+
- Use `TaskCreate` for each execution group
|
|
80
|
+
- Set up `addBlockedBy` dependencies between tasks
|
|
81
|
+
- Include wish slug and group name in task description
|
|
82
|
+
|
|
83
|
+
### Phase 6: Handoff
|
|
84
|
+
|
|
85
|
+
Output: **"Wish documented. Run `/plan-review` to validate, then `/forge` to begin execution."**
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Key Principles
|
|
90
|
+
|
|
91
|
+
- **One question at a time** - Never ask multiple questions in one message
|
|
92
|
+
- **Multiple choice preferred** - Easier for users to answer
|
|
93
|
+
- **YAGNI ruthlessly** - Remove unnecessary features from all designs
|
|
94
|
+
- **Acceptance criteria are king** - Every task must have testable criteria
|
|
95
|
+
- **Validation commands required** - Every task must have a way to verify completion
|
|
96
|
+
- **Explore before proposing** - Read code before designing changes
|
|
97
|
+
- **Small tasks** - If a task feels big, split it
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Never Do
|
|
102
|
+
|
|
103
|
+
- Skip brainstorming and jump to plan writing
|
|
104
|
+
- Ask more than one question per message
|
|
105
|
+
- Create tasks without acceptance criteria
|
|
106
|
+
- Create tasks without validation commands
|
|
107
|
+
- Write a wish without user agreement on scope
|
|
108
|
+
- Start implementation during wish creation (plan mode only)
|
|
109
|
+
- Create overly broad tasks ("implement everything")
|
|
110
|
+
- Leave OUT scope empty (always exclude something explicitly)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: work-orchestration
|
|
3
|
+
description: "How to orchestrate real work with the human using term, claudio, and beads. Stop planning, start doing."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Work Orchestration
|
|
7
|
+
|
|
8
|
+
## The Stack
|
|
9
|
+
|
|
10
|
+
- **term** - Terminal/tmux orchestration
|
|
11
|
+
- **claudio** - Claude Code API automation
|
|
12
|
+
- **beads** - Task/issue tracking (bd CLI)
|
|
13
|
+
- **worktrees** - Isolated git branches for parallel work
|
|
14
|
+
|
|
15
|
+
## Start Working NOW
|
|
16
|
+
|
|
17
|
+
### 1. Check Current State
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
term ls # What sessions exist?
|
|
21
|
+
term workers # Any active workers?
|
|
22
|
+
bd ls # What issues are ready?
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Pick or Create Work
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Work on existing issue
|
|
29
|
+
term work <bd-id>
|
|
30
|
+
|
|
31
|
+
# Or create new issue and work it
|
|
32
|
+
term create "Fix the thing" && term work next
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 3. Execute
|
|
36
|
+
|
|
37
|
+
The worker spawns Claude in a pane. You can:
|
|
38
|
+
- Watch: `tmux attach -t genie`
|
|
39
|
+
- Approve: `term approve <worker>`
|
|
40
|
+
- Answer: `term answer <worker> <choice>`
|
|
41
|
+
- Check: `term orc status genie`
|
|
42
|
+
|
|
43
|
+
### 4. Close When Done
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
term close <bd-id> # Closes issue, cleans worktree
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Parallel Work Pattern
|
|
52
|
+
|
|
53
|
+
Split work across multiple panes/workers:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# In session "genie"
|
|
57
|
+
term split genie h # New pane (uses ACTIVE pane)
|
|
58
|
+
term spawn forge # Spawn Claude with skill
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or use workers for tracked work:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
term work bd-001 # Worker 1
|
|
65
|
+
term work bd-002 # Worker 2 (parallel)
|
|
66
|
+
term workers # See both
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Human-AI Handoff
|
|
72
|
+
|
|
73
|
+
**Human does:**
|
|
74
|
+
- Approves permissions
|
|
75
|
+
- Answers questions
|
|
76
|
+
- Provides context when stuck
|
|
77
|
+
- Reviews before merge
|
|
78
|
+
|
|
79
|
+
**AI does:**
|
|
80
|
+
- Implements tasks
|
|
81
|
+
- Runs tests
|
|
82
|
+
- Creates commits (when asked)
|
|
83
|
+
- Reports blockers
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Quick Commands
|
|
88
|
+
|
|
89
|
+
| Action | Command |
|
|
90
|
+
|--------|---------|
|
|
91
|
+
| See sessions | `term ls` |
|
|
92
|
+
| See workers | `term workers` |
|
|
93
|
+
| See issues | `bd ls` |
|
|
94
|
+
| Start work | `term work <id>` |
|
|
95
|
+
| Split pane | `term split genie h` |
|
|
96
|
+
| Approve | `term approve <worker>` |
|
|
97
|
+
| Answer | `term answer <worker> 1` |
|
|
98
|
+
| Check status | `term orc status genie` |
|
|
99
|
+
| Close work | `term close <id>` |
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Stop Planning, Start Doing
|
|
104
|
+
|
|
105
|
+
1. What needs to be done? (one thing)
|
|
106
|
+
2. Is there an issue? Create one if not
|
|
107
|
+
3. `term work <id>` or just start coding
|
|
108
|
+
4. Ship it
|
|
109
|
+
|
|
110
|
+
No more circles.
|
package/scripts/build.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build script for automagik-genie plugin
|
|
5
|
+
* Bundles TypeScript CLIs into standalone CJS executables using esbuild
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { build } from 'esbuild';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const rootDir = path.join(__dirname, '..');
|
|
15
|
+
|
|
16
|
+
const TARGETS = [
|
|
17
|
+
{ name: 'genie', source: 'src/genie.ts' },
|
|
18
|
+
{ name: 'term', source: 'src/term.ts' },
|
|
19
|
+
{ name: 'worker-service', source: 'src/services/worker-service.ts' },
|
|
20
|
+
{ name: 'validate-wish', source: '.claude-plugin/scripts/validate-wish.ts' },
|
|
21
|
+
{ name: 'validate-completion', source: '.claude-plugin/scripts/validate-completion.ts' }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
async function buildPlugin() {
|
|
25
|
+
console.log('Building automagik-genie plugin...\n');
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Read version from package.json
|
|
29
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf-8'));
|
|
30
|
+
const version = packageJson.version;
|
|
31
|
+
console.log(`Version: ${version}`);
|
|
32
|
+
|
|
33
|
+
// Create output directory
|
|
34
|
+
const scriptsDir = path.join(rootDir, 'plugin/scripts');
|
|
35
|
+
if (!fs.existsSync(scriptsDir)) {
|
|
36
|
+
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Generate plugin/package.json for dependency installation
|
|
40
|
+
console.log('\nGenerating plugin package.json...');
|
|
41
|
+
const pluginPackageJson = {
|
|
42
|
+
name: 'automagik-genie-plugin',
|
|
43
|
+
version: version,
|
|
44
|
+
private: true,
|
|
45
|
+
description: 'Runtime dependencies for automagik-genie bundled CLIs',
|
|
46
|
+
type: 'module',
|
|
47
|
+
dependencies: {},
|
|
48
|
+
engines: {
|
|
49
|
+
node: '>=18.0.0',
|
|
50
|
+
bun: '>=1.0.0'
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
fs.writeFileSync(
|
|
54
|
+
path.join(rootDir, 'plugin/package.json'),
|
|
55
|
+
JSON.stringify(pluginPackageJson, null, 2) + '\n'
|
|
56
|
+
);
|
|
57
|
+
console.log('plugin/package.json generated');
|
|
58
|
+
|
|
59
|
+
// Build each target
|
|
60
|
+
for (const target of TARGETS) {
|
|
61
|
+
const sourcePath = path.join(rootDir, target.source);
|
|
62
|
+
|
|
63
|
+
// Check if source exists
|
|
64
|
+
if (!fs.existsSync(sourcePath)) {
|
|
65
|
+
console.log(`\nSkipping ${target.name} (source not found: ${target.source})`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(`\nBuilding ${target.name}...`);
|
|
70
|
+
|
|
71
|
+
const outfile = `${scriptsDir}/${target.name}.cjs`;
|
|
72
|
+
|
|
73
|
+
await build({
|
|
74
|
+
entryPoints: [sourcePath],
|
|
75
|
+
bundle: true,
|
|
76
|
+
platform: 'node',
|
|
77
|
+
target: 'node18',
|
|
78
|
+
format: 'cjs',
|
|
79
|
+
outfile,
|
|
80
|
+
minify: true,
|
|
81
|
+
logLevel: 'error',
|
|
82
|
+
external: ['bun', 'bun:*'],
|
|
83
|
+
define: {
|
|
84
|
+
'__GENIE_VERSION__': `"${version}"`
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Add shebang (esbuild banner can cause duplicates if source has shebang)
|
|
89
|
+
const content = fs.readFileSync(outfile, 'utf-8');
|
|
90
|
+
const shebang = '#!/usr/bin/env bun\n';
|
|
91
|
+
// Remove any existing shebangs and add fresh one
|
|
92
|
+
const cleanContent = content.replace(/^#!.*\n/gm, '');
|
|
93
|
+
fs.writeFileSync(outfile, shebang + cleanContent);
|
|
94
|
+
|
|
95
|
+
// Make executable
|
|
96
|
+
fs.chmodSync(outfile, 0o755);
|
|
97
|
+
|
|
98
|
+
const stats = fs.statSync(`${scriptsDir}/${target.name}.cjs`);
|
|
99
|
+
console.log(` ${target.name}.cjs (${(stats.size / 1024).toFixed(2)} KB)`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Copy smart-install.js (stays as Node.js, not bundled)
|
|
103
|
+
const smartInstallSrc = path.join(rootDir, 'scripts/smart-install.js');
|
|
104
|
+
const smartInstallDest = path.join(scriptsDir, 'smart-install.js');
|
|
105
|
+
if (fs.existsSync(smartInstallSrc)) {
|
|
106
|
+
fs.copyFileSync(smartInstallSrc, smartInstallDest);
|
|
107
|
+
console.log('\nCopied smart-install.js');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Update plugin.json version
|
|
111
|
+
const pluginJsonPath = path.join(rootDir, 'plugin/.claude-plugin/plugin.json');
|
|
112
|
+
if (fs.existsSync(pluginJsonPath)) {
|
|
113
|
+
const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf-8'));
|
|
114
|
+
pluginJson.version = version;
|
|
115
|
+
fs.writeFileSync(pluginJsonPath, JSON.stringify(pluginJson, null, 2) + '\n');
|
|
116
|
+
console.log('Updated plugin.json version');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log('\nBuild complete!');
|
|
120
|
+
console.log(`Output: plugin/scripts/`);
|
|
121
|
+
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error('\nBuild failed:', error.message);
|
|
124
|
+
if (error.errors) {
|
|
125
|
+
console.error('\nBuild errors:');
|
|
126
|
+
error.errors.forEach(err => console.error(` - ${err.text}`));
|
|
127
|
+
}
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
buildPlugin();
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Smart Install Script for automagik-genie
|
|
4
|
+
*
|
|
5
|
+
* Ensures required dependencies are installed:
|
|
6
|
+
* - Bun runtime (auto-installs if missing)
|
|
7
|
+
* - tmux (guides user if missing - can't auto-install)
|
|
8
|
+
* - beads (auto-installs if missing via npm)
|
|
9
|
+
*
|
|
10
|
+
* Also handles:
|
|
11
|
+
* - Dependency installation when version changes
|
|
12
|
+
* - CLI symlink creation for agent access
|
|
13
|
+
* - Version marker management
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, symlinkSync, unlinkSync } from 'fs';
|
|
16
|
+
import { execSync, spawnSync } from 'child_process';
|
|
17
|
+
import { join } from 'path';
|
|
18
|
+
import { homedir } from 'os';
|
|
19
|
+
|
|
20
|
+
const ROOT = process.env.CLAUDE_PLUGIN_ROOT || join(homedir(), '.claude', 'plugins', 'automagik-genie');
|
|
21
|
+
const GENIE_DIR = join(homedir(), '.genie');
|
|
22
|
+
const MARKER = join(GENIE_DIR, '.install-version');
|
|
23
|
+
const BIN_DIR = join(homedir(), '.local', 'bin');
|
|
24
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
25
|
+
|
|
26
|
+
// Common installation paths (handles fresh installs before PATH reload)
|
|
27
|
+
const BUN_COMMON_PATHS = IS_WINDOWS
|
|
28
|
+
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
|
29
|
+
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun', '/opt/homebrew/bin/bun'];
|
|
30
|
+
|
|
31
|
+
const BEADS_COMMON_PATHS = IS_WINDOWS
|
|
32
|
+
? [join(homedir(), 'AppData', 'Roaming', 'npm', 'bd.cmd')]
|
|
33
|
+
: [join(homedir(), '.local', 'bin', 'bd'), '/usr/local/bin/bd', '/opt/homebrew/bin/bd'];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get the Bun executable path
|
|
37
|
+
*/
|
|
38
|
+
function getBunPath() {
|
|
39
|
+
// Try PATH first
|
|
40
|
+
try {
|
|
41
|
+
const result = spawnSync('bun', ['--version'], {
|
|
42
|
+
encoding: 'utf-8',
|
|
43
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
44
|
+
shell: IS_WINDOWS
|
|
45
|
+
});
|
|
46
|
+
if (result.status === 0) return 'bun';
|
|
47
|
+
} catch {
|
|
48
|
+
// Not in PATH
|
|
49
|
+
}
|
|
50
|
+
return BUN_COMMON_PATHS.find(existsSync) || null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isBunInstalled() {
|
|
54
|
+
return getBunPath() !== null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getBunVersion() {
|
|
58
|
+
const bunPath = getBunPath();
|
|
59
|
+
if (!bunPath) return null;
|
|
60
|
+
try {
|
|
61
|
+
const result = spawnSync(bunPath, ['--version'], {
|
|
62
|
+
encoding: 'utf-8',
|
|
63
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
64
|
+
shell: IS_WINDOWS
|
|
65
|
+
});
|
|
66
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Install Bun automatically
|
|
74
|
+
*/
|
|
75
|
+
function installBun() {
|
|
76
|
+
console.error('Installing Bun runtime...');
|
|
77
|
+
try {
|
|
78
|
+
if (IS_WINDOWS) {
|
|
79
|
+
execSync('powershell -c "irm bun.sh/install.ps1 | iex"', { stdio: 'inherit', shell: true });
|
|
80
|
+
} else {
|
|
81
|
+
execSync('curl -fsSL https://bun.sh/install | bash', { stdio: 'inherit', shell: true });
|
|
82
|
+
}
|
|
83
|
+
if (!isBunInstalled()) {
|
|
84
|
+
throw new Error('Bun installation completed but binary not found. Please restart your terminal.');
|
|
85
|
+
}
|
|
86
|
+
console.error(`Bun ${getBunVersion()} installed`);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error('Failed to install Bun. Please install manually:');
|
|
89
|
+
if (IS_WINDOWS) {
|
|
90
|
+
console.error(' winget install Oven-sh.Bun');
|
|
91
|
+
} else {
|
|
92
|
+
console.error(' curl -fsSL https://bun.sh/install | bash');
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if tmux is installed
|
|
100
|
+
*/
|
|
101
|
+
function isTmuxInstalled() {
|
|
102
|
+
try {
|
|
103
|
+
const result = spawnSync('tmux', ['-V'], {
|
|
104
|
+
encoding: 'utf-8',
|
|
105
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
106
|
+
shell: IS_WINDOWS
|
|
107
|
+
});
|
|
108
|
+
return result.status === 0;
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get tmux version
|
|
116
|
+
*/
|
|
117
|
+
function getTmuxVersion() {
|
|
118
|
+
try {
|
|
119
|
+
const result = spawnSync('tmux', ['-V'], {
|
|
120
|
+
encoding: 'utf-8',
|
|
121
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
122
|
+
shell: IS_WINDOWS
|
|
123
|
+
});
|
|
124
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if beads (bd) is installed
|
|
132
|
+
*/
|
|
133
|
+
function getBeadsPath() {
|
|
134
|
+
try {
|
|
135
|
+
const result = spawnSync('bd', ['--version'], {
|
|
136
|
+
encoding: 'utf-8',
|
|
137
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
138
|
+
shell: IS_WINDOWS
|
|
139
|
+
});
|
|
140
|
+
if (result.status === 0) return 'bd';
|
|
141
|
+
} catch {
|
|
142
|
+
// Not in PATH
|
|
143
|
+
}
|
|
144
|
+
return BEADS_COMMON_PATHS.find(existsSync) || null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function isBeadsInstalled() {
|
|
148
|
+
return getBeadsPath() !== null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Install beads via npm
|
|
153
|
+
*/
|
|
154
|
+
function installBeads() {
|
|
155
|
+
console.error('Installing beads (bd)...');
|
|
156
|
+
try {
|
|
157
|
+
execSync('npm install -g @anthropic-ai/bd', { stdio: 'inherit', shell: true });
|
|
158
|
+
if (!isBeadsInstalled()) {
|
|
159
|
+
throw new Error('beads installation completed but bd not found.');
|
|
160
|
+
}
|
|
161
|
+
console.error('beads installed');
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error('Failed to install beads. Please install manually:');
|
|
164
|
+
console.error(' npm install -g @anthropic-ai/bd');
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if dependencies need to be installed
|
|
171
|
+
*/
|
|
172
|
+
function needsInstall() {
|
|
173
|
+
if (!existsSync(join(ROOT, 'node_modules'))) return true;
|
|
174
|
+
if (!existsSync(join(ROOT, 'package.json'))) return false; // No package.json = no deps needed
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8'));
|
|
178
|
+
if (!existsSync(MARKER)) return true;
|
|
179
|
+
const marker = JSON.parse(readFileSync(MARKER, 'utf-8'));
|
|
180
|
+
return pkg.version !== marker.version || getBunVersion() !== marker.bun;
|
|
181
|
+
} catch {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Install dependencies using Bun
|
|
188
|
+
*/
|
|
189
|
+
function installDeps() {
|
|
190
|
+
const bunPath = getBunPath();
|
|
191
|
+
if (!bunPath) {
|
|
192
|
+
throw new Error('Bun executable not found');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.error('Installing dependencies...');
|
|
196
|
+
|
|
197
|
+
// Ensure .genie directory exists
|
|
198
|
+
if (!existsSync(GENIE_DIR)) {
|
|
199
|
+
mkdirSync(GENIE_DIR, { recursive: true });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const bunCmd = IS_WINDOWS && bunPath.includes(' ') ? `"${bunPath}"` : bunPath;
|
|
203
|
+
execSync(`${bunCmd} install`, { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS });
|
|
204
|
+
|
|
205
|
+
// Write version marker
|
|
206
|
+
let version = 'unknown';
|
|
207
|
+
try {
|
|
208
|
+
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8'));
|
|
209
|
+
version = pkg.version;
|
|
210
|
+
} catch {
|
|
211
|
+
// Ignore
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
writeFileSync(MARKER, JSON.stringify({
|
|
215
|
+
version,
|
|
216
|
+
bun: getBunVersion(),
|
|
217
|
+
tmux: getTmuxVersion(),
|
|
218
|
+
installedAt: new Date().toISOString()
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Create CLI symlinks for agent access
|
|
224
|
+
*/
|
|
225
|
+
function createCliSymlinks() {
|
|
226
|
+
// Ensure bin directory exists
|
|
227
|
+
if (!existsSync(BIN_DIR)) {
|
|
228
|
+
mkdirSync(BIN_DIR, { recursive: true });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const links = [
|
|
232
|
+
{ name: 'genie', target: join(ROOT, 'scripts', 'genie.cjs') },
|
|
233
|
+
{ name: 'term', target: join(ROOT, 'scripts', 'term.cjs') }
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
for (const { name, target } of links) {
|
|
237
|
+
const linkPath = join(BIN_DIR, name);
|
|
238
|
+
|
|
239
|
+
// Skip if target doesn't exist
|
|
240
|
+
if (!existsSync(target)) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Remove existing symlink if it exists
|
|
245
|
+
try {
|
|
246
|
+
if (existsSync(linkPath)) {
|
|
247
|
+
unlinkSync(linkPath);
|
|
248
|
+
}
|
|
249
|
+
} catch {
|
|
250
|
+
// Ignore errors
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Create symlink
|
|
254
|
+
try {
|
|
255
|
+
symlinkSync(target, linkPath);
|
|
256
|
+
console.error(`Created symlink: ${linkPath} -> ${target}`);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error(`Warning: Could not create symlink for ${name}: ${error.message}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Main execution
|
|
264
|
+
try {
|
|
265
|
+
// 1. Check/install Bun
|
|
266
|
+
if (!isBunInstalled()) {
|
|
267
|
+
installBun();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 2. Check tmux - REQUIRED, but can't auto-install
|
|
271
|
+
if (!isTmuxInstalled()) {
|
|
272
|
+
console.error('');
|
|
273
|
+
console.error('ERROR: tmux is required but not installed.');
|
|
274
|
+
console.error('');
|
|
275
|
+
console.error('Please install tmux manually:');
|
|
276
|
+
if (process.platform === 'darwin') {
|
|
277
|
+
console.error(' brew install tmux');
|
|
278
|
+
} else if (process.platform === 'linux') {
|
|
279
|
+
console.error(' sudo apt install tmux # Debian/Ubuntu');
|
|
280
|
+
console.error(' sudo dnf install tmux # Fedora/RHEL');
|
|
281
|
+
console.error(' sudo pacman -S tmux # Arch');
|
|
282
|
+
} else if (IS_WINDOWS) {
|
|
283
|
+
console.error(' WSL is required for tmux on Windows');
|
|
284
|
+
console.error(' Inside WSL: sudo apt install tmux');
|
|
285
|
+
}
|
|
286
|
+
console.error('');
|
|
287
|
+
console.error('Then restart Claude Code.');
|
|
288
|
+
process.exit(2); // Exit code 2 = blocking error for Claude to process
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 3. Check/install beads
|
|
292
|
+
if (!isBeadsInstalled()) {
|
|
293
|
+
installBeads();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 4. Install dependencies if needed
|
|
297
|
+
if (needsInstall()) {
|
|
298
|
+
installDeps();
|
|
299
|
+
console.error('Dependencies installed');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// 5. Create CLI symlinks
|
|
303
|
+
createCliSymlinks();
|
|
304
|
+
|
|
305
|
+
} catch (e) {
|
|
306
|
+
console.error('Installation failed:', e.message);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|