@jairodri/huckleberry-cli 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/agents/cos.json +20 -0
  2. package/agents/hermes/AGENTS.md +62 -0
  3. package/agents/hermes/SOUL.md +29 -0
  4. package/agents/hermes/fix-kimaki/SKILL.md +100 -0
  5. package/agents/hermes/hermes-fixer.md +115 -0
  6. package/agents/hermes.json +26 -0
  7. package/bin/huckleberry.js +4 -0
  8. package/kanban/SKILL.md +167 -0
  9. package/kanban/kanban +3 -0
  10. package/kanban/package.json +9 -0
  11. package/kanban/src/cli.mjs +553 -0
  12. package/kanban/src/dispatcher.mjs +479 -0
  13. package/kanban/src/server.mjs +108 -0
  14. package/kanban/templates/b2b-outreach.yaml +84 -0
  15. package/kanban/templates/blog-social-campaign.yaml +1161 -0
  16. package/kanban/templates/client-onboard.yaml +107 -0
  17. package/kanban/templates/cro-pipeline.yaml +296 -0
  18. package/kanban/templates/social-campaign.yaml +356 -0
  19. package/mcp/gemini-codegen-server.mjs +134 -0
  20. package/package.json +46 -0
  21. package/plugins/env-inject.ts +61 -0
  22. package/plugins/inkstone-gate.ts +55 -0
  23. package/scripts/heartbeat.sh +41 -0
  24. package/scripts/install-services.ps1 +82 -0
  25. package/scripts/install-services.sh +74 -0
  26. package/scripts/keep-awake.ps1 +48 -0
  27. package/scripts/keep-awake.sh +31 -0
  28. package/scripts/keepalive.sh +43 -0
  29. package/skills/curated.txt +68 -0
  30. package/src/cli.js +227 -0
  31. package/src/interview.js +78 -0
  32. package/src/postinstall.js +22 -0
  33. package/taskmaster/SKILL.md +78 -0
  34. package/taskmaster/dispatcher.mjs +146 -0
  35. package/taskmaster/next-task.js +123 -0
  36. package/taskmaster/update-status.js +118 -0
  37. package/templates/AGENTS.md +90 -0
  38. package/templates/COS_PROTOCOLS.md +71 -0
  39. package/templates/EXECUTIVE-MODE.md +29 -0
  40. package/templates/HEARTBEAT.md +42 -0
  41. package/templates/IDENTITY.md +22 -0
  42. package/templates/MEMORY.md +17 -0
  43. package/templates/ORG.md +33 -0
  44. package/templates/SOUL.md +43 -0
  45. package/templates/TOOLS.md +66 -0
  46. package/templates/business-context-gate.md +9 -0
  47. package/templates/evergreen.md +29 -0
  48. package/templates/opencode.json +80 -0
  49. package/wiki/discord.md +52 -0
  50. package/wiki/inkstone.md +64 -0
  51. package/wiki/kimaki.md +48 -0
  52. package/wiki/opencode.md +43 -0
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ # Huckleberry Heartbeat — reads taskmaster and reports status
3
+ # Called by cron every 2-4 hours
4
+ set -euo pipefail
5
+
6
+ HB_DIR="${HOME}/.huckleberry"
7
+ TASK_FILE="${HB_DIR}/taskmaster/tasks/tasks.json"
8
+ STATE_FILE="${HB_DIR}/heartbeat-state.json"
9
+ LOG="${HB_DIR}/heartbeat.log"
10
+
11
+ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG"; }
12
+
13
+ if [ ! -f "$TASK_FILE" ]; then
14
+ log "No task file found — first run or empty"
15
+ echo '{"last_report": null, "tasks_since_last": []}' > "$STATE_FILE"
16
+ exit 0
17
+ fi
18
+
19
+ log "=== Heartbeat ==="
20
+
21
+ # Read task statuses
22
+ COMPLETED=$(python3 -c "
23
+ import json
24
+ with open('$TASK_FILE') as f: d = json.load(f)
25
+ tasks = d.get('tasks', [])
26
+ done = [t for t in tasks if t.get('status') == 'done']
27
+ active = [t for t in tasks if t.get('status') == 'in-progress']
28
+ blocked = [t for t in tasks if t.get('status') == 'blocked']
29
+ print(f'{len(done)}|{len(active)}|{len(blocked)}')
30
+ " 2>/dev/null || echo "0|0|0")
31
+
32
+ echo "$COMPLETED"
33
+ log "Status: $COMPLETED tasks (done|active|blocked)"
34
+
35
+ # Save state for COS to read
36
+ echo "{
37
+ \"checked_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
38
+ \"task_summary\": \"$COMPLETED\"
39
+ }" > "$STATE_FILE"
40
+
41
+ log "=== Heartbeat complete ==="
@@ -0,0 +1,82 @@
1
+ # Huckleberry Windows Services Setup
2
+ # Creates Scheduled Tasks matching the 12 macOS launchd agents
3
+ # Run as Administrator: Right-click > Run as Administrator
4
+
5
+ $hbDir = "$env:USERPROFILE\.huckleberry"
6
+ $kimakiDir = "$env:USERPROFILE\.kimaki"
7
+ $logDir = "$env:USERPROFILE\.huckleberry\logs"
8
+ New-Item -ItemType Directory -Path $logDir -Force | Out-Null
9
+
10
+ function New-Task {
11
+ param(
12
+ [string]$Name,
13
+ [string]$Command,
14
+ [string]$Args = "",
15
+ [string]$Trigger = "AtStartup",
16
+ [int]$IntervalSeconds = 0,
17
+ [string]$Description = ""
18
+ )
19
+
20
+ $taskName = "Huckleberry-$Name"
21
+ $action = New-ScheduledTaskAction -Execute $Command -Argument $Args
22
+ $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
23
+
24
+ if ($Trigger -eq "AtStartup") {
25
+ $trigger = New-ScheduledTaskTrigger -AtStartup -RandomDelay (New-TimeSpan -Seconds 30)
26
+ } elseif ($Trigger -eq "Repetition") {
27
+ $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Seconds $IntervalSeconds) -RepetitionDuration ([TimeSpan]::MaxValue)
28
+ }
29
+
30
+ try {
31
+ Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
32
+ Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Description $Description -Force | Out-Null
33
+ Write-Host " [OK] $taskName" -ForegroundColor Green
34
+ } catch {
35
+ Write-Host " [WARN] $taskName — $_" -ForegroundColor Yellow
36
+ }
37
+ }
38
+
39
+ Write-Host ""
40
+ Write-Host " Huckleberry — Setting up Windows services..." -ForegroundColor Cyan
41
+ Write-Host ""
42
+
43
+ # 1. kimaki bot (KeepAlive = always running)
44
+ New-Task -Name "kimaki-bot" -Command "powershell.exe" -Args "-WindowStyle Hidden -Command `"cd '$kimakiDir'; .\start-kimaki.sh`"" -Description "Huckleberry kimaki Discord bridge"
45
+
46
+ # 2. kimaki watchdog
47
+ New-Task -Name "kimaki-watchdog" -Command "node" -Args "$kimakiDir\watchdog\index.js" -Description "Huckleberry kimaki watchdog"
48
+
49
+ # 3. Health monitor (every 5 min)
50
+ New-Task -Name "kimaki-monitor" -Command "powershell.exe" -Args "-WindowStyle Hidden -File `"$hbDir\scripts\keepalive.ps1`"" -Trigger "Repetition" -IntervalSeconds 300 -Description "Huckleberry health monitor (every 5min)"
51
+
52
+ # 4. Kanban server
53
+ New-Task -Name "kanban-server" -Command "node" -Args "$hbDir\kanban\src\server.mjs" -Description "Huckleberry kanban API server"
54
+
55
+ # 5. Kanban dispatcher
56
+ New-Task -Name "kanban-dispatcher" -Command "node" -Args "$hbDir\kanban\src\dispatcher.mjs" -Description "Huckleberry kanban task dispatcher"
57
+
58
+ # 6. Kanban completion watchdog
59
+ New-Task -Name "kanban-completion-watchdog" -Command "node" -Args "$hbDir\kanban\src\completion-watchdog.mjs" -Description "Huckleberry kanban completion tracker"
60
+
61
+ # 7. Kanban stall detector
62
+ New-Task -Name "kanban-stall-detector" -Command "node" -Args "$hbDir\kanban\src\stall-detector.mjs" -Description "Huckleberry kanban stall detection"
63
+
64
+ # 8. Kanban tunnel (keepalive)
65
+ New-Task -Name "kanban-tunnel" -Command "powershell.exe" -Args "-WindowStyle Hidden -File `"$hbDir\scripts\keepalive.ps1`"" -Description "Huckleberry kanban tunnel keepalive"
66
+
67
+ # 9. Dream dispatcher (every 30 min)
68
+ New-Task -Name "dream-dispatcher" -Command "node" -Args "$hbDir\taskmaster\dispatcher.mjs" -Trigger "Repetition" -IntervalSeconds 1800 -Description "Huckleberry dream cycle dispatcher (every 30min)"
69
+
70
+ # 10. Dream distiller (hourly)
71
+ New-Task -Name "dream-distiller" -Command "powershell.exe" -Args "-WindowStyle Hidden -File `"$hbDir\scripts\heartbeat.ps1`"" -Trigger "Repetition" -IntervalSeconds 3600 -Description "Huckleberry memory distiller"
72
+
73
+ # 11. Keystone load balancer
74
+ New-Task -Name "keystone" -Command "node" -Args "$env:USERPROFILE\keystone\keystone.js" -Description "Huckleberry keystone load balancer"
75
+
76
+ # 12. Monitor and notify (every 5 min)
77
+ New-Task -Name "monitor-and-notify" -Command "powershell.exe" -Args "-WindowStyle Hidden -File `"$hbDir\scripts\heartbeat.ps1`"" -Trigger "Repetition" -IntervalSeconds 300 -Description "Huckleberry notifications"
78
+
79
+ Write-Host ""
80
+ Write-Host " Services configured. Start manually or reboot." -ForegroundColor Green
81
+ Write-Host " To remove: .\huckleberry\scripts\uninstall-services.ps1" -ForegroundColor DarkGray
82
+ Write-Host ""
@@ -0,0 +1,74 @@
1
+ #!/bin/bash
2
+ # Huckleberry macOS Services Setup
3
+ # Installs launchd plist files matching the 12 agents
4
+ set -euo pipefail
5
+
6
+ HB_DIR="$HOME/.huckleberry"
7
+ LAUNCH_DIR="$HOME/Library/LaunchAgents"
8
+ LOG_DIR="$HB_DIR/logs"
9
+ mkdir -p "$LAUNCH_DIR" "$LOG_DIR"
10
+
11
+ SERVICES=(
12
+ # Name, Label, Program, Args, Interval (0 = KeepAlive)
13
+ "kimaki-bot:com.huckleberry.kimaki.bot:/bin/bash:$HOME/.kimaki/start-kimaki.sh:0"
14
+ "kimaki-watchdog:com.huckleberry.kimaki.watchdog:/opt/homebrew/bin/node:$HOME/.kimaki/watchdog/index.js:0"
15
+ "kimaki-monitor:com.huckleberry.kimaki.monitor:/bin/bash:$HB_DIR/scripts/keepalive.sh:300"
16
+ "kanban-server:com.huckleberry.kanban.server:/opt/homebrew/bin/node:$HB_DIR/kanban/src/server.mjs:0"
17
+ "kanban-dispatcher:com.huckleberry.kanban.dispatcher:/opt/homebrew/bin/node:$HB_DIR/kanban/src/dispatcher.mjs:0"
18
+ "kanban-completion-watchdog:com.huckleberry.kanban.completion-watchdog:/opt/homebrew/bin/node:$HB_DIR/kanban/src/completion-watchdog.mjs:0"
19
+ "kanban-stall-detector:com.huckleberry.kanban.stall-detector:/opt/homebrew/bin/node:$HB_DIR/kanban/src/stall-detector.mjs:0"
20
+ "kanban-tunnel:com.huckleberry.kanban.tunnel:/bin/bash:$HB_DIR/scripts/keepalive.sh:0"
21
+ "dream-dispatcher:com.huckleberry.dream.dispatcher:/opt/homebrew/bin/node:$HB_DIR/taskmaster/dispatcher.mjs:1800"
22
+ "dream-distiller:com.huckleberry.dream.distiller:/bin/bash:$HB_DIR/scripts/heartbeat.sh:3600"
23
+ "keystone:com.huckleberry.keystone:/opt/homebrew/bin/node:$HOME/keystone/keystone.js:0"
24
+ "monitor-and-notify:com.huckleberry.monitor.notify:/bin/bash:$HB_DIR/scripts/heartbeat.sh:300"
25
+ )
26
+
27
+ for service in "${SERVICES[@]}"; do
28
+ IFS=":" read -r name label prog args interval <<< "$service"
29
+ PLIST="$LAUNCH_DIR/$label.plist"
30
+
31
+ cat > "$PLIST" << PLISTEOF
32
+ <?xml version="1.0" encoding="UTF-8"?>
33
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
34
+ <plist version="1.0">
35
+ <dict>
36
+ <key>Label</key>
37
+ <string>$label</string>
38
+ <key>ProgramArguments</key>
39
+ <array>
40
+ <string>$prog</string>
41
+ <string>$args</string>
42
+ </array>
43
+ <key>RunAtLoad</key>
44
+ <true/>
45
+ PLISTEOF
46
+
47
+ if [ "$interval" = "0" ]; then
48
+ # KeepAlive service (always running)
49
+ cat >> "$PLIST" << 'KEEPA'
50
+ <key>KeepAlive</key>
51
+ <true/>
52
+ KEEPA
53
+ else
54
+ # Periodic service
55
+ echo " <key>StartInterval</key>" >> "$PLIST"
56
+ echo " <integer>$interval</integer>" >> "$PLIST"
57
+ fi
58
+
59
+ cat >> "$PLIST" << 'ENDPLIST'
60
+ <key>StandardOutPath</key>
61
+ <string>'$LOG_DIR'/'$name'.log</string>
62
+ <key>StandardErrorPath</key>
63
+ <string>'$LOG_DIR'/'$name'.err</string>
64
+ </dict>
65
+ </plist>
66
+ ENDPLIST
67
+
68
+ # Load the plist
69
+ launchctl load "$PLIST" 2>/dev/null && echo " [OK] $label" || echo " [WARN] $label"
70
+ done
71
+
72
+ echo ""
73
+ echo " All services installed. Check with: launchctl list | grep huckleberry"
74
+ echo " Remove: launchctl unload ~/Library/LaunchAgents/com.huckleberry.*.plist"
@@ -0,0 +1,48 @@
1
+ # Huckleberry Sleep Prevention
2
+ # Prevents Windows from sleeping while Huckleberry services are running
3
+ # Equivalent to macOS `caffeinate`
4
+
5
+ $scriptName = "huckleberry-keep-awake"
6
+ $logFile = "$env:USERPROFILE\.huckleberry\keep-awake.log"
7
+
8
+ function Log($msg) {
9
+ $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
10
+ Add-Content -Path $logFile -Value "[$ts] $msg"
11
+ }
12
+
13
+ Log "Keep-awake started"
14
+
15
+ # Method 1: Disable sleep via powercfg (temporary, needs admin)
16
+ try {
17
+ powercfg -change -standby-timeout-ac 0 2>&1 | Out-Null
18
+ powercfg -change -hibernate-timeout-ac 0 2>&1 | Out-Null
19
+ Log "Sleep disabled via powercfg"
20
+ } catch {
21
+ Log "powercfg failed (may need admin): $_"
22
+ }
23
+
24
+ # Method 2: Keep system awake by setting thread execution state
25
+ # Run this in a loop to prevent sleep
26
+ while ($true) {
27
+ try {
28
+ # Use Add-Type to call kernel32 SetThreadExecutionState
29
+ Add-Type -TypeDefinition @"
30
+ using System;
31
+ using System.Runtime.InteropServices;
32
+ public class SleepPreventer {
33
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
34
+ public static extern uint SetThreadExecutionState(uint esFlags);
35
+ public static void PreventSleep() {
36
+ SetThreadExecutionState(0x80000003); // ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED
37
+ }
38
+ public static void AllowSleep() {
39
+ SetThreadExecutionState(0x80000000); // ES_CONTINUOUS
40
+ }
41
+ }
42
+ "@ -ErrorAction SilentlyContinue
43
+ [SleepPreventer]::PreventSleep()
44
+ } catch {
45
+ # Fallback: keep process alive
46
+ }
47
+ Start-Sleep -Seconds 60
48
+ }
@@ -0,0 +1,31 @@
1
+ #!/bin/bash
2
+ # Huckleberry macOS Sleep Prevention
3
+ # Uses caffeinate to prevent sleep while services run
4
+
5
+ LOGFILE="$HOME/.huckleberry/keep-awake.log"
6
+ PIDFILE="$HOME/.huckleberry/keep-awake.pid"
7
+
8
+ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOGFILE"; }
9
+
10
+ case "${1:-start}" in
11
+ start)
12
+ log "Starting caffeinate..."
13
+ caffeinate -dimsu -w $$ &
14
+ CPID=$!
15
+ echo $CPID > "$PIDFILE"
16
+ log "caffeinate PID: $CPID"
17
+ ;;
18
+ stop)
19
+ if [ -f "$PIDFILE" ]; then
20
+ kill $(cat "$PIDFILE") 2>/dev/null && log "caffeinate stopped"
21
+ rm -f "$PIDFILE"
22
+ fi
23
+ ;;
24
+ status)
25
+ if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
26
+ echo "Keep-awake is running"
27
+ else
28
+ echo "Keep-awake is stopped"
29
+ fi
30
+ ;;
31
+ esac
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # Huckleberry Keepalive — checks and restarts critical services
3
+ set -euo pipefail
4
+
5
+ HB_DIR="${HOME}/.huckleberry"
6
+ LOG="${HB_DIR}/keepalive.log"
7
+ MAX_RESTARTS=3
8
+ RESTART_COUNT=0
9
+
10
+ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG"; }
11
+
12
+ check_service() {
13
+ local name=$1
14
+ local pid_pattern=$2
15
+ local restart_cmd=$3
16
+
17
+ if pgrep -f "$pid_pattern" > /dev/null 2>&1; then
18
+ log "$name: OK"
19
+ return 0
20
+ else
21
+ log "$name: DOWN — restarting..."
22
+ eval "$restart_cmd" &
23
+ log "$name: restart issued"
24
+ return 1
25
+ fi
26
+ }
27
+
28
+ log "=== Keepalive check ==="
29
+
30
+ # Check Inkstone MCP
31
+ check_service "inkstone" "inkstone.*mcp/server" "node ~/.huckleberry/mcp/inkstone/dist/mcp/server.js &"
32
+
33
+ # Check opencode server if running
34
+ if pgrep -f "opencode" > /dev/null 2>&1; then
35
+ log "opencode: OK"
36
+ fi
37
+
38
+ # Check kimaki
39
+ if pgrep -f "kimaki" > /dev/null 2>&1; then
40
+ log "kimaki: OK"
41
+ fi
42
+
43
+ log "=== Keepalive complete ==="
@@ -0,0 +1,68 @@
1
+ # Curated Skills for Huckleberry Starter Pack (~45)
2
+
3
+ # Core Infrastructure
4
+ claw-compactor
5
+ context-recovery
6
+ humanizer
7
+ guardrail-framework
8
+ self-improve
9
+
10
+ # Memory & Knowledge
11
+ inkstone-admin
12
+ project-rag
13
+ notebooklm
14
+
15
+ # Research & Intelligence
16
+ agent-reach
17
+ smart-search
18
+ firecrawl
19
+ perplexity-research
20
+ competitive-intel
21
+
22
+ # Content & Writing
23
+ copywriting
24
+ copy-editing
25
+ concreteness-fixer
26
+ article-generator
27
+ seo-content
28
+ narrative-research-article
29
+ hook-generator
30
+ content-strategy
31
+
32
+ # Brand & Voice
33
+ brand-voice
34
+ voice-extraction
35
+ voice-brand-alignment
36
+ positioning-angles
37
+
38
+ # Outreach & Sales
39
+ cold-email
40
+ lead-email-craft
41
+ warm-outbound
42
+ b2b-cold-outreach
43
+
44
+ # Social Distribution
45
+ social-construction
46
+ social-campaign
47
+ typefully
48
+ x-post-generator-openclaw
49
+ muse
50
+ platform-formatting
51
+
52
+ # Marketing & Growth
53
+ cmo
54
+ brainstorm
55
+ audience-research
56
+ landing-page-architect
57
+ content-repost-manager
58
+
59
+ # SEO & Analytics
60
+ keyword-research
61
+ ai-seo
62
+ seo-audit
63
+ schema-markup
64
+ auto-indexing
65
+
66
+ # Creative
67
+ image-gen
68
+ visual-style
package/src/cli.js ADDED
@@ -0,0 +1,227 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, copyFileSync } from "node:fs";
2
+ import { join, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { execSync } from "node:child_process";
5
+ import { runInterview } from "./interview.js";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const ROOT = join(__dirname, "..");
9
+ const PACKAGE_ROOT = ROOT;
10
+
11
+ const VERSION = JSON.parse(
12
+ readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8")
13
+ ).version;
14
+
15
+ const commands = { install, update, "install-pack": installPack, interview: runInterview, help, version: () => console.log(`@huckleberry/cli v${VERSION}`) };
16
+
17
+ export async function run(args) {
18
+ const cmd = args[0];
19
+ if (!cmd || cmd === "--help" || cmd === "-h") return help();
20
+ if (cmd === "--version" || cmd === "-v") return console.log(`@huckleberry/cli v${VERSION}`);
21
+ const handler = commands[cmd];
22
+ if (!handler) {
23
+ console.error(`Unknown command: ${cmd}\nRun 'huckleberry help' for usage.`);
24
+ process.exit(1);
25
+ }
26
+ try { await handler(args.slice(1)); }
27
+ catch (err) { console.error(`[ERROR] ${err.message}`); process.exit(1); }
28
+ }
29
+
30
+ async function install() {
31
+ console.log(`\n Huckleberry AI System v${VERSION} — Installing...`);
32
+ const homeDir = process.env.HOME || process.env.USERPROFILE;
33
+ const hbDir = join(homeDir, ".huckleberry");
34
+ const templateDir = join(PACKAGE_ROOT, "templates");
35
+
36
+ console.log(` [1/8] Platform: ${detectPlatform()}`);
37
+ console.log(` [2/8] Priming OmniRoute with free models...`);
38
+
39
+ console.log(` [3/8] Creating bootstrap files...`);
40
+ mkdirSync(hbDir, { recursive: true });
41
+ mkdirSync(join(hbDir, "memory"), { recursive: true });
42
+ mkdirSync(join(hbDir, "taskmaster", "tasks"), { recursive: true });
43
+ mkdirSync(join(hbDir, "scripts"), { recursive: true });
44
+ mkdirSync(join(hbDir, "wiki"), { recursive: true });
45
+ mkdirSync(join(hbDir, "agents", "hermes"), { recursive: true });
46
+ mkdirSync(join(hbDir, "agents", "hermes", "fix-kimaki"), { recursive: true });
47
+
48
+ for (const t of ["SOUL.md","IDENTITY.md","COS_PROTOCOLS.md","TOOLS.md","HEARTBEAT.md","AGENTS.md","MEMORY.md","EXECUTIVE-MODE.md","ORG.md"]) {
49
+ const src = join(templateDir, t);
50
+ if (existsSync(src)) copyTemplate(src, join(hbDir, t));
51
+ }
52
+
53
+ // Copy Hermes agent bootstrap files
54
+ const hermesSrc = join(PACKAGE_ROOT, "agents", "hermes");
55
+ if (existsSync(hermesSrc)) copyDirSync(hermesSrc, join(hbDir, "agents", "hermes"));
56
+ // Copy agent configs
57
+ if (existsSync(join(PACKAGE_ROOT, "agents", "cos.json"))) copyFileSync(join(PACKAGE_ROOT, "agents", "cos.json"), join(hbDir, "agents", "cos.json"));
58
+ if (existsSync(join(PACKAGE_ROOT, "agents", "hermes.json"))) copyFileSync(join(PACKAGE_ROOT, "agents", "hermes.json"), join(hbDir, "agents", "hermes.json"));
59
+
60
+ console.log(` [4/8] Setting up ~/.opencode/...`);
61
+ const opencodeDir = join(homeDir, ".opencode");
62
+ mkdirSync(opencodeDir, { recursive: true });
63
+ mkdirSync(join(opencodeDir, "plugin"), { recursive: true });
64
+ mkdirSync(join(opencodeDir, "mcp"), { recursive: true });
65
+
66
+ const ocConfig = join(templateDir, "opencode.json");
67
+ if (existsSync(ocConfig)) {
68
+ const config = readFileSync(ocConfig, "utf-8")
69
+ .replace(/\{\{USERPROFILE\}\}/g, homeDir.replace(/\\/g, "/"))
70
+ .replace(/\{\{HUCKLEBERRY_HOME\}\}/g, hbDir.replace(/\\/g, "/"));
71
+ writeFileSync(join(opencodeDir, "opencode.json"), config, "utf-8");
72
+ }
73
+
74
+ if (existsSync(join(PACKAGE_ROOT, "plugins"))) copyDirSync(join(PACKAGE_ROOT, "plugins"), join(opencodeDir, "plugin"));
75
+ if (existsSync(join(PACKAGE_ROOT, "mcp"))) copyDirSync(join(PACKAGE_ROOT, "mcp"), join(opencodeDir, "mcp"));
76
+
77
+ console.log(` [5/8] Deploying skills...`);
78
+ const skillsDir = join(homeDir, ".claude", "skills");
79
+ mkdirSync(skillsDir, { recursive: true });
80
+ const curated = join(PACKAGE_ROOT, "skills", "curated.txt");
81
+ if (existsSync(curated)) {
82
+ const list = readFileSync(curated, "utf-8").split("\n").filter(Boolean);
83
+ console.log(` Skills manifest: ${list.length} skills listed`);
84
+ }
85
+
86
+ console.log(` [6/8] Setting up taskmaster...`);
87
+ const taskmasterSrc = join(PACKAGE_ROOT, "taskmaster");
88
+ if (existsSync(taskmasterSrc)) copyDirSync(taskmasterSrc, join(hbDir, "taskmaster"));
89
+ writeFileSync(join(hbDir, "taskmaster", "tasks", "tasks.json"), JSON.stringify({ projects: [], tasks: [] }, null, 2), "utf-8");
90
+
91
+ console.log(` [7/10] Writing scripts...`);
92
+ if (existsSync(join(PACKAGE_ROOT, "scripts"))) copyDirSync(join(PACKAGE_ROOT, "scripts"), join(hbDir, "scripts"));
93
+
94
+ console.log(` [8/10] Writing wiki...`);
95
+ if (existsSync(join(PACKAGE_ROOT, "wiki"))) copyDirSync(join(PACKAGE_ROOT, "wiki"), join(hbDir, "wiki"));
96
+
97
+ console.log(` [9/10] Installing Inkstone memory system...`);
98
+ const inkstoneDir = join(hbDir, "mcp", "inkstone");
99
+ mkdirSync(inkstoneDir, { recursive: true });
100
+ // Create a standalone package.json that depends on @jairodri/huckleberry-inkstone
101
+ writeFileSync(
102
+ join(inkstoneDir, "package.json"),
103
+ JSON.stringify({
104
+ name: "inkstone",
105
+ private: true,
106
+ version: "1.0.0",
107
+ dependencies: { "@jairodri/huckleberry-inkstone": "^1.0.0" },
108
+ }, null, 2),
109
+ "utf-8"
110
+ );
111
+ try {
112
+ execSync("npm install --production 2>/dev/null", { cwd: inkstoneDir, stdio: "pipe", timeout: 120000 });
113
+ console.log(` Inkstone installed to ${inkstoneDir}`);
114
+ } catch {
115
+ console.error(` Inkstone npm install failed. Run manually: cd ${inkstoneDir} && npm install`);
116
+ }
117
+
118
+ console.log(` [10/10] Setting up kanban workflows...`);
119
+ const kanbanDir = join(hbDir, "kanban");
120
+ mkdirSync(kanbanDir, { recursive: true });
121
+ mkdirSync(join(kanbanDir, "src"), { recursive: true });
122
+ mkdirSync(join(kanbanDir, "templates"), { recursive: true });
123
+ const kanbanSrc = join(PACKAGE_ROOT, "kanban");
124
+ if (existsSync(kanbanSrc)) copyDirSync(kanbanSrc, kanbanDir);
125
+ // Init kanban DB
126
+ try {
127
+ execSync("node src/cli.mjs init 2>/dev/null", { cwd: kanbanDir, stdio: "pipe", timeout: 30000 });
128
+ console.log(` Kanban workflows installed`);
129
+ } catch {
130
+ console.error(` Kanban init skipped (requires bcrypt-sqlite). Install: cd ${kanbanDir} && npm install`);
131
+ }
132
+
133
+ console.log(` [11/12] Setting up background services...`);
134
+ const platform = detectPlatform();
135
+ const scriptsDir = join(hbDir, "scripts");
136
+ if (platform === "Windows" && existsSync(join(scriptsDir, "install-services.ps1"))) {
137
+ try {
138
+ execSync(`powershell -ExecutionPolicy Bypass -File "${join(scriptsDir, "install-services.ps1")}" 2>&1`, { stdio: "pipe", timeout: 60000 });
139
+ console.log(` 12 Windows Scheduled Tasks created`);
140
+ } catch {
141
+ console.error(` Windows services setup skipped (run manually: install-services.ps1)`);
142
+ }
143
+ } else if (platform === "macOS" && existsSync(join(scriptsDir, "install-services.sh"))) {
144
+ try {
145
+ execSync(`bash "${join(scriptsDir, "install-services.sh")}" 2>&1`, { stdio: "pipe", timeout: 30000 });
146
+ console.log(` 12 macOS launchd agents installed`);
147
+ } catch {
148
+ console.error(` macOS services setup skipped (run manually: install-services.sh)`);
149
+ }
150
+ } else {
151
+ console.log(` Service setup skipped for ${platform}`);
152
+ }
153
+
154
+ console.log(` [12/12] Sleep prevention...`);
155
+ if (platform === "Windows") {
156
+ console.log(` Windows: Huckleberry prevents sleep while running (keep-awake.ps1)`);
157
+ console.log(` Disable sleep manually: powercfg -change -standby-timeout-ac 0`);
158
+ } else if (platform === "macOS") {
159
+ console.log(` macOS: caffeinate prevents sleep while running (keep-awake.sh)`);
160
+ }
161
+
162
+ console.log(`\n ✓ Huckleberry v${VERSION} installed to ${hbDir}`);
163
+ console.log(` ✓ Config: ${join(opencodeDir, "opencode.json")}`);
164
+ console.log(`\n Next steps:`);
165
+ console.log(` 1. npm install -g opencode`);
166
+ console.log(` 2. npm install -g kimaki`);
167
+ console.log(` 3. huckleberry interview`);
168
+ console.log(` 4. cd ~/.huckleberry && opencode\n`);
169
+ }
170
+
171
+ async function update() {
172
+ console.log(`\n Updating Huckleberry...\n`);
173
+ try {
174
+ execSync("npm update -g @huckleberry/cli", { stdio: "inherit" });
175
+ console.log(` ✓ @huckleberry/cli updated`);
176
+ } catch {
177
+ console.error(` ⚠ @huckleberry/cli update failed`);
178
+ }
179
+
180
+ // Update Inkstone independently
181
+ const homeDir = process.env.HOME || process.env.USERPROFILE;
182
+ const inkstoneDir = join(homeDir, ".huckleberry", "mcp", "inkstone");
183
+ if (existsSync(join(inkstoneDir, "package.json"))) {
184
+ console.log(` Updating Inkstone memory system...`);
185
+ try {
186
+ execSync("npm update @jairodri/huckleberry-inkstone 2>/dev/null", { cwd: inkstoneDir, stdio: "pipe", timeout: 60000 });
187
+ console.log(` ✓ Inkstone updated`);
188
+ } catch {
189
+ console.error(` ⚠ Inkstone update failed. Run: cd ${inkstoneDir} && npm install`);
190
+ }
191
+ }
192
+
193
+ console.log(`\n ✓ Huckleberry updated\n`);
194
+ }
195
+
196
+ async function installPack(args) {
197
+ const packName = args[0];
198
+ if (!packName) { console.error("Usage: huckleberry install-pack <pack-name>\nAvailable packs: content-creator, outreach, seo, creative, video"); process.exit(1); }
199
+ console.log(`\n Installing pack: ${packName}...\n`);
200
+ console.log(` Pack system coming soon — each pack includes: agent config + skills + kanban templates + channel setup\n`);
201
+ }
202
+
203
+ function help() {
204
+ console.log(`\n Huckleberry AI System v${VERSION}\n\n Usage: huckleberry <command>\n\n Commands:\n install One-click install\n update Update to latest version\n install-pack Install agent/skill pack\n help Show this help\n version Show version\n\n Packs:\n content-creator Writing + social + content agent\n outreach Sales + cold email + lead management\n seo Keyword research + rankings\n creative Images + branding + design\n video Video production\n`);
205
+ }
206
+
207
+ function detectPlatform() {
208
+ const p = process.platform;
209
+ return p === "darwin" ? "macOS" : p === "win32" ? "Windows" : p;
210
+ }
211
+
212
+ function copyTemplate(src, dst) {
213
+ try {
214
+ let content = readFileSync(src, "utf-8");
215
+ content = content.replace(/\{\{VERSION\}\}/g, VERSION).replace(/\{\{INSTALL_DATE\}\}/g, new Date().toISOString().split("T")[0]);
216
+ writeFileSync(dst, content, "utf-8");
217
+ } catch (err) { console.error(` Failed: ${src} — ${err.message}`); }
218
+ }
219
+
220
+ function copyDirSync(src, dst) {
221
+ mkdirSync(dst, { recursive: true });
222
+ for (const entry of readdirSync(src)) {
223
+ if (entry === ".git" || entry === "node_modules") continue;
224
+ const s = join(src, entry), d = join(dst, entry);
225
+ if (statSync(s).isDirectory()) copyDirSync(s, d); else copyFileSync(s, d);
226
+ }
227
+ }