dual-brain 0.1.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 (68) hide show
  1. package/AGENTS.md +97 -0
  2. package/CLAUDE.md +147 -0
  3. package/LICENSE +21 -0
  4. package/README.md +197 -0
  5. package/agents/implementer.md +22 -0
  6. package/agents/researcher.md +25 -0
  7. package/agents/verifier.md +30 -0
  8. package/bin/dual-brain.mjs +2868 -0
  9. package/hooks/auto-update-wrapper.mjs +102 -0
  10. package/hooks/auto-update.sh +67 -0
  11. package/hooks/budget-balancer.mjs +679 -0
  12. package/hooks/control-panel.mjs +1195 -0
  13. package/hooks/cost-logger.mjs +286 -0
  14. package/hooks/cost-report.mjs +351 -0
  15. package/hooks/decision-ledger.mjs +299 -0
  16. package/hooks/dual-brain-review.mjs +404 -0
  17. package/hooks/dual-brain-think.mjs +393 -0
  18. package/hooks/enforce-tier.mjs +469 -0
  19. package/hooks/failure-detector.mjs +138 -0
  20. package/hooks/gpt-work-dispatcher.mjs +512 -0
  21. package/hooks/head-guard.mjs +105 -0
  22. package/hooks/health-check.mjs +444 -0
  23. package/hooks/install-git-hooks.mjs +106 -0
  24. package/hooks/model-registry.mjs +859 -0
  25. package/hooks/plan-generator.mjs +544 -0
  26. package/hooks/profiles.mjs +254 -0
  27. package/hooks/quality-gate.mjs +355 -0
  28. package/hooks/risk-classifier.mjs +41 -0
  29. package/hooks/session-report.mjs +514 -0
  30. package/hooks/setup-wizard.mjs +130 -0
  31. package/hooks/summary-checkpoint.mjs +432 -0
  32. package/hooks/task-classifier.mjs +328 -0
  33. package/hooks/test-orchestrator.mjs +1077 -0
  34. package/hooks/vibe-memory.mjs +463 -0
  35. package/hooks/vibe-router.mjs +387 -0
  36. package/hooks/wave-orchestrator.mjs +1397 -0
  37. package/install.mjs +1541 -0
  38. package/mcp-server/README.md +81 -0
  39. package/mcp-server/index.mjs +388 -0
  40. package/orchestrator.json +215 -0
  41. package/package.json +108 -0
  42. package/playbooks/debug.json +49 -0
  43. package/playbooks/refactor.json +57 -0
  44. package/playbooks/security-audit.json +57 -0
  45. package/playbooks/security.json +38 -0
  46. package/playbooks/test-gen.json +48 -0
  47. package/plugin.json +22 -0
  48. package/review-rules.md +17 -0
  49. package/shell-hook.sh +26 -0
  50. package/skills/go.md +22 -0
  51. package/skills/review.md +19 -0
  52. package/skills/status.md +13 -0
  53. package/skills/think.md +22 -0
  54. package/src/brief.mjs +266 -0
  55. package/src/decide.mjs +635 -0
  56. package/src/decompose.mjs +331 -0
  57. package/src/detect.mjs +345 -0
  58. package/src/dispatch.mjs +942 -0
  59. package/src/health.mjs +253 -0
  60. package/src/index.mjs +44 -0
  61. package/src/install-hooks.mjs +100 -0
  62. package/src/playbook.mjs +257 -0
  63. package/src/profile.mjs +990 -0
  64. package/src/redact.mjs +192 -0
  65. package/src/repo.mjs +292 -0
  66. package/src/session.mjs +1036 -0
  67. package/src/tui.mjs +197 -0
  68. package/src/update-check.mjs +35 -0
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ // auto-update-wrapper.mjs — PostToolUse hook (Node.js entry point).
3
+ // Runs once per session, checks for dual-brain updates, installs silently.
4
+ // The parent exits immediately; all npm work happens in a detached child.
5
+
6
+ import { spawn } from 'child_process';
7
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
8
+ import { join, dirname, resolve } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const WORKSPACE = resolve(__dirname, '..');
13
+ const STATE_DIR = join(WORKSPACE, '.dualbrain');
14
+ const LOCK_FILE = join(STATE_DIR, '.update-checked');
15
+ const TWENTY_FOUR_HOURS = 24 * 60 * 60 * 1000;
16
+
17
+ // ── 1. Already checked recently? ─────────────────────────────────────────────
18
+ if (existsSync(LOCK_FILE)) {
19
+ try {
20
+ const lastCheck = parseInt(readFileSync(LOCK_FILE, 'utf8').trim(), 10);
21
+ if (Number.isFinite(lastCheck) && Date.now() - lastCheck < TWENTY_FOUR_HOURS) {
22
+ process.exit(0);
23
+ }
24
+ } catch {
25
+ // Corrupt lock file — proceed with check
26
+ }
27
+ }
28
+
29
+ // ── 2. Write lock BEFORE spawning (prevents concurrent session races) ─────────
30
+ try {
31
+ mkdirSync(STATE_DIR, { recursive: true });
32
+ writeFileSync(LOCK_FILE, String(Date.now()));
33
+ } catch {
34
+ process.exit(0); // Can't write state — bail silently
35
+ }
36
+
37
+ // ── 3. Resolve local version ─────────────────────────────────────────────────
38
+ let localVersion = '';
39
+ try {
40
+ const pkg = JSON.parse(readFileSync(join(WORKSPACE, 'package.json'), 'utf8'));
41
+ localVersion = pkg.version || '';
42
+ } catch {
43
+ process.exit(0);
44
+ }
45
+
46
+ if (!localVersion) process.exit(0);
47
+
48
+ // ── 4. Detach background worker — parent returns immediately ─────────────────
49
+ // The worker script is inlined as a node -e string to avoid needing a temp file.
50
+ const workerScript = `
51
+ import { spawnSync } from 'child_process';
52
+ import { readFileSync } from 'fs';
53
+
54
+ const localVersion = ${JSON.stringify(localVersion)};
55
+
56
+ // 3-second timeout npm check
57
+ const npmResult = spawnSync('npm', ['view', 'dual-brain', 'version'], {
58
+ encoding: 'utf8',
59
+ stdio: ['pipe', 'pipe', 'pipe'],
60
+ timeout: 3000,
61
+ });
62
+
63
+ const latestVersion = (npmResult.stdout || '').trim();
64
+ if (!latestVersion) process.exit(0);
65
+
66
+ // Compare: is latest strictly greater than local?
67
+ function versionGt(a, b) {
68
+ const ap = a.replace(/^v/i, '').split('.').map(n => parseInt(n, 10) || 0);
69
+ const bp = b.replace(/^v/i, '').split('.').map(n => parseInt(n, 10) || 0);
70
+ const len = Math.max(ap.length, bp.length);
71
+ for (let i = 0; i < len; i++) {
72
+ const diff = (bp[i] || 0) - (ap[i] || 0);
73
+ if (diff !== 0) return diff > 0;
74
+ }
75
+ return false;
76
+ }
77
+
78
+ if (!versionGt(localVersion, latestVersion)) process.exit(0);
79
+
80
+ // Newer version found — print notice then install
81
+ process.stderr.write('dual-brain: updating v' + localVersion + ' → ' + latestVersion + '...\\n');
82
+
83
+ spawnSync('npx', ['-y', 'dual-brain@latest', '--quiet'], {
84
+ stdio: 'ignore',
85
+ timeout: 120000,
86
+ });
87
+ `;
88
+
89
+ const child = spawn(
90
+ process.execPath,
91
+ ['--input-type=module'],
92
+ {
93
+ detached: true,
94
+ stdio: ['pipe', 'ignore', 'ignore'],
95
+ }
96
+ );
97
+
98
+ child.stdin.write(workerScript);
99
+ child.stdin.end();
100
+ child.unref(); // Let parent exit without waiting
101
+
102
+ process.exit(0);
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bash
2
+ # auto-update.sh — PostToolUse hook that silently keeps dual-brain current.
3
+ #
4
+ # Runs once per session (lock file prevents repeated checks).
5
+ # Never blocks: npm check has a 3-second timeout, install is backgrounded.
6
+ # Exit 0 always — update failures are silent, not fatal.
7
+
8
+ set -euo pipefail
9
+
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ WORKSPACE="$(cd "${SCRIPT_DIR}/.." && pwd)"
12
+ STATE_DIR="${WORKSPACE}/.dualbrain"
13
+ LOCK_FILE="${STATE_DIR}/.update-checked"
14
+ TWENTY_FOUR_HOURS=86400 # seconds
15
+
16
+ # ── 1. Already checked recently? ────────────────────────────────────────────
17
+ if [[ -f "${LOCK_FILE}" ]]; then
18
+ last_check=$(cat "${LOCK_FILE}" 2>/dev/null || echo 0)
19
+ now=$(date +%s)
20
+ age=$(( now - last_check ))
21
+ if (( age < TWENTY_FOUR_HOURS )); then
22
+ exit 0
23
+ fi
24
+ fi
25
+
26
+ # ── 2. Write lock BEFORE doing anything (prevents concurrent session races) ──
27
+ mkdir -p "${STATE_DIR}"
28
+ date +%s > "${LOCK_FILE}"
29
+
30
+ # ── 3. Get local version ─────────────────────────────────────────────────────
31
+ PKG_JSON="${WORKSPACE}/package.json"
32
+ if [[ ! -f "${PKG_JSON}" ]]; then
33
+ exit 0
34
+ fi
35
+
36
+ local_version=$(node -e "try{process.stdout.write(JSON.parse(require('fs').readFileSync('${PKG_JSON}','utf8')).version||'')}catch(e){}" 2>/dev/null || true)
37
+ if [[ -z "${local_version}" ]]; then
38
+ exit 0
39
+ fi
40
+
41
+ # ── 4. Check npm for latest (3-second timeout, silent on failure) ─────────────
42
+ latest_version=$(timeout 3 npm view dual-brain version 2>/dev/null || true)
43
+ if [[ -z "${latest_version}" ]]; then
44
+ exit 0
45
+ fi
46
+
47
+ # ── 5. Compare versions ───────────────────────────────────────────────────────
48
+ # Returns 1 if $1 < $2 (newer available), 0 otherwise
49
+ version_lt() {
50
+ [ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ] && [ "$1" != "$2" ]
51
+ }
52
+
53
+ if ! version_lt "${local_version}" "${latest_version}"; then
54
+ # Already up to date — lock file already updated above
55
+ exit 0
56
+ fi
57
+
58
+ # ── 6. Newer version found — update in background ────────────────────────────
59
+ echo "dual-brain: updating v${local_version} → v${latest_version}..." >&2
60
+
61
+ # Spawn a completely detached background process so this hook returns immediately
62
+ nohup bash -c "
63
+ npx -y dual-brain@latest --quiet 2>/dev/null || true
64
+ " > /dev/null 2>&1 &
65
+ disown $! 2>/dev/null || true
66
+
67
+ exit 0