golem-cc 2.1.2 → 3.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 (84) hide show
  1. package/.claude/commands/golem/build.md +18 -0
  2. package/.claude/commands/golem/config.md +39 -0
  3. package/.claude/commands/golem/continue.md +73 -0
  4. package/.claude/commands/golem/doctor.md +46 -0
  5. package/.claude/commands/golem/document.md +138 -0
  6. package/.claude/commands/golem/help.md +58 -0
  7. package/.claude/commands/golem/pause.md +130 -0
  8. package/.claude/commands/golem/plan.md +111 -0
  9. package/.claude/commands/golem/review.md +166 -0
  10. package/.claude/commands/golem/security.md +186 -0
  11. package/.claude/commands/golem/simplify.md +76 -0
  12. package/.claude/commands/golem/spec.md +105 -0
  13. package/.claude/commands/golem/status.md +33 -0
  14. package/.golem/agents/code-simplifier.md +54 -0
  15. package/.golem/agents/review-architecture.md +59 -0
  16. package/.golem/agents/review-logic.md +50 -0
  17. package/.golem/agents/review-security.md +50 -0
  18. package/.golem/agents/review-style.md +48 -0
  19. package/.golem/agents/review-tests.md +48 -0
  20. package/.golem/agents/spec-builder.md +60 -0
  21. package/.golem/bin/golem.mjs +270 -0
  22. package/.golem/lib/build.mjs +557 -0
  23. package/.golem/lib/claude.mjs +95 -0
  24. package/.golem/lib/config.mjs +421 -0
  25. package/.golem/lib/display.mjs +191 -0
  26. package/.golem/lib/doctor.mjs +197 -0
  27. package/.golem/lib/document.mjs +792 -0
  28. package/.golem/lib/gates.mjs +78 -0
  29. package/.golem/lib/init.mjs +166 -0
  30. package/.golem/lib/output.mjs +40 -0
  31. package/.golem/lib/ratelimit.mjs +86 -0
  32. package/.golem/lib/security.mjs +603 -0
  33. package/.golem/lib/simplify.mjs +101 -0
  34. package/.golem/lib/tui.mjs +368 -0
  35. package/.golem/lib/usage.mjs +119 -0
  36. package/.golem/lib/worktree.mjs +509 -0
  37. package/.golem/prompts/build.md +23 -0
  38. package/.golem/prompts/document-inline.md +66 -0
  39. package/.golem/prompts/document-markdown.md +80 -0
  40. package/.golem/prompts/simplify.md +35 -0
  41. package/README.md +141 -142
  42. package/bin/golem-shim.mjs +36 -0
  43. package/bin/install.mjs +193 -0
  44. package/package.json +27 -32
  45. package/.env.example +0 -17
  46. package/bin/golem +0 -1040
  47. package/commands/golem/build.md +0 -235
  48. package/commands/golem/config.md +0 -55
  49. package/commands/golem/doctor.md +0 -137
  50. package/commands/golem/help.md +0 -212
  51. package/commands/golem/plan.md +0 -214
  52. package/commands/golem/review.md +0 -376
  53. package/commands/golem/security.md +0 -204
  54. package/commands/golem/simplify.md +0 -94
  55. package/commands/golem/spec.md +0 -226
  56. package/commands/golem/status.md +0 -60
  57. package/dist/api/freshworks.d.ts +0 -61
  58. package/dist/api/freshworks.d.ts.map +0 -1
  59. package/dist/api/freshworks.js +0 -119
  60. package/dist/api/freshworks.js.map +0 -1
  61. package/dist/api/gitea.d.ts +0 -96
  62. package/dist/api/gitea.d.ts.map +0 -1
  63. package/dist/api/gitea.js +0 -154
  64. package/dist/api/gitea.js.map +0 -1
  65. package/dist/cli/index.d.ts +0 -9
  66. package/dist/cli/index.d.ts.map +0 -1
  67. package/dist/cli/index.js +0 -352
  68. package/dist/cli/index.js.map +0 -1
  69. package/dist/sync/ticket-sync.d.ts +0 -53
  70. package/dist/sync/ticket-sync.d.ts.map +0 -1
  71. package/dist/sync/ticket-sync.js +0 -226
  72. package/dist/sync/ticket-sync.js.map +0 -1
  73. package/dist/types.d.ts +0 -125
  74. package/dist/types.d.ts.map +0 -1
  75. package/dist/types.js +0 -5
  76. package/dist/types.js.map +0 -1
  77. package/dist/worktree/manager.d.ts +0 -54
  78. package/dist/worktree/manager.d.ts.map +0 -1
  79. package/dist/worktree/manager.js +0 -190
  80. package/dist/worktree/manager.js.map +0 -1
  81. package/golem/agents/code-simplifier.md +0 -81
  82. package/golem/agents/spec-builder.md +0 -90
  83. package/golem/prompts/PROMPT_build.md +0 -71
  84. package/golem/prompts/PROMPT_plan.md +0 -102
@@ -0,0 +1,78 @@
1
+ import { spawn } from 'node:child_process';
2
+
3
+ const TIMEOUT_MS = 120_000;
4
+ const MAX_OUTPUT_LINES = 200;
5
+
6
+ function truncateOutput(text) {
7
+ const lines = text.split('\n');
8
+ if (lines.length <= MAX_OUTPUT_LINES) return text;
9
+ return lines.slice(0, MAX_OUTPUT_LINES).join('\n') + `\n... (truncated ${lines.length - MAX_OUTPUT_LINES} lines)`;
10
+ }
11
+
12
+ function runCommand(command, timeoutMs) {
13
+ return new Promise((resolve) => {
14
+ const start = Date.now();
15
+ let stdout = '';
16
+ let stderr = '';
17
+ let killed = false;
18
+
19
+ const child = spawn('sh', ['-c', command], { stdio: 'pipe' });
20
+
21
+ const timer = setTimeout(() => {
22
+ killed = true;
23
+ child.kill('SIGKILL');
24
+ }, timeoutMs);
25
+
26
+ child.stdout.on('data', (chunk) => { stdout += chunk; });
27
+ child.stderr.on('data', (chunk) => { stderr += chunk; });
28
+
29
+ child.on('close', (code) => {
30
+ clearTimeout(timer);
31
+ const duration = Date.now() - start;
32
+ const output = truncateOutput((stdout + stderr).trimEnd());
33
+
34
+ if (killed) {
35
+ resolve({ exitCode: 1, output: output + `\n[TIMEOUT] Gate killed after ${timeoutMs / 1000}s`, duration });
36
+ } else {
37
+ resolve({ exitCode: code ?? 1, output, duration });
38
+ }
39
+ });
40
+
41
+ child.on('error', (err) => {
42
+ clearTimeout(timer);
43
+ resolve({ exitCode: 1, output: err.message, duration: Date.now() - start });
44
+ });
45
+ });
46
+ }
47
+
48
+ export async function runGates(config, { timeoutMs = TIMEOUT_MS } = {}) {
49
+ const gates = config.gates;
50
+ if (!gates || gates.length === 0) {
51
+ return { passed: true, results: [], failedGate: null };
52
+ }
53
+
54
+ const results = [];
55
+
56
+ for (let i = 0; i < gates.length; i++) {
57
+ const gate = gates[i];
58
+ if (!gate.enabled) {
59
+ results.push({ name: gate.name, passed: true, exitCode: 0, output: '', duration: 0, skipped: true });
60
+ continue;
61
+ }
62
+
63
+ const { exitCode, output, duration } = await runCommand(gate.command, timeoutMs);
64
+ const passed = exitCode === 0;
65
+ results.push({ name: gate.name, passed, exitCode, output, duration });
66
+
67
+ if (!passed) {
68
+ for (let j = i + 1; j < gates.length; j++) {
69
+ if (gates[j].enabled) {
70
+ results.push({ name: gates[j].name, passed: false, exitCode: -1, output: '', duration: 0, skipped: true });
71
+ }
72
+ }
73
+ return { passed: false, results, failedGate: gate.name };
74
+ }
75
+ }
76
+
77
+ return { passed: true, results, failedGate: null };
78
+ }
@@ -0,0 +1,166 @@
1
+ import { readFile, writeFile, access, mkdir } from 'node:fs/promises';
2
+ import { readFileSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { header, success, warn, info } from './output.mjs';
5
+ import { saveConfig, loadConfig, detectFramework } from './config.mjs';
6
+
7
+ async function fileExists(path) {
8
+ try { await access(path); return true; } catch { return false; }
9
+ }
10
+
11
+ function readPkg(dir) {
12
+ try { return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8')); }
13
+ catch { return null; }
14
+ }
15
+
16
+ export function detectTestCommand(dir) {
17
+ const pkg = readPkg(dir);
18
+ const testScript = pkg?.scripts?.test;
19
+ if (testScript && testScript !== 'echo "Error: no test specified" && exit 1') return testScript;
20
+ if (existsSync(join(dir, 'vitest.config.ts')) || existsSync(join(dir, 'vitest.config.js'))) return 'npx vitest run';
21
+ if (existsSync(join(dir, 'jest.config.ts')) || existsSync(join(dir, 'jest.config.js')) || existsSync(join(dir, 'jest.config.mjs'))) return 'npx jest';
22
+ return 'node --test';
23
+ }
24
+
25
+ export function detectBuildCommand(dir, framework) {
26
+ if (framework === 'nuxt') return 'npx nuxt build';
27
+ if (framework === 'next') return 'npx next build';
28
+ if (readPkg(dir)?.scripts?.build) return 'npm run build';
29
+ return '# No build step — ESM modules run directly';
30
+ }
31
+
32
+ export function detectLintCommand(dir) {
33
+ if (readPkg(dir)?.scripts?.lint) return 'npm run lint';
34
+ for (const f of ['eslint.config.js', 'eslint.config.mjs', '.eslintrc.json', '.eslintrc.js', '.eslintrc.yml']) {
35
+ if (existsSync(join(dir, f))) return 'npx eslint .';
36
+ }
37
+ if (existsSync(join(dir, 'biome.json'))) return 'npx biome check .';
38
+ return '# TBD — will be set up during implementation';
39
+ }
40
+
41
+ export function detectTypeCheck(dir) {
42
+ if (existsSync(join(dir, 'tsconfig.json'))) return 'npx tsc --noEmit';
43
+ return '# N/A — plain JavaScript, no TypeScript';
44
+ }
45
+
46
+ export function generateAgentsMd(dir, framework) {
47
+ const testCmd = detectTestCommand(dir);
48
+ const buildCmd = detectBuildCommand(dir, framework);
49
+ const lintCmd = detectLintCommand(dir);
50
+ const typeCheck = detectTypeCheck(dir);
51
+
52
+ const pkg = readPkg(dir);
53
+ const learnings = [];
54
+ if (framework) learnings.push(`Project uses ${framework} framework`);
55
+ if (pkg?.type === 'module') learnings.push('Project uses ESM (type: module in package.json)');
56
+ if (!framework && pkg?.type !== 'module') learnings.push('Standard Node.js project');
57
+
58
+ return `# Operational Commands
59
+
60
+ ## Testing
61
+
62
+ \`\`\`bash
63
+ ${testCmd}
64
+ \`\`\`
65
+
66
+ ## Type Checking
67
+
68
+ \`\`\`bash
69
+ ${typeCheck}
70
+ \`\`\`
71
+
72
+ ## Linting
73
+
74
+ \`\`\`bash
75
+ ${lintCmd}
76
+ \`\`\`
77
+
78
+ ## Build
79
+
80
+ \`\`\`bash
81
+ ${buildCmd}
82
+ \`\`\`
83
+
84
+ ## Learnings
85
+
86
+ ${learnings.map(l => `- ${l}`).join('\n')}
87
+ `;
88
+ }
89
+
90
+ async function ensureGitignore(dir) {
91
+ const giPath = join(dir, '.gitignore');
92
+ const entries = ['.env', '.claude/*', '!.claude/commands/', '!.claude/commands/**'];
93
+ let content = '';
94
+
95
+ if (await fileExists(giPath)) {
96
+ content = await readFile(giPath, 'utf-8');
97
+ }
98
+
99
+ const lines = content.split('\n');
100
+ const missing = entries.filter(e => !lines.some(l => l.trim() === e));
101
+
102
+ if (missing.length === 0) return false;
103
+
104
+ const addition = '\n# Golem\n' + missing.join('\n') + '\n';
105
+ await writeFile(giPath, content.trimEnd() + addition);
106
+ return true;
107
+ }
108
+
109
+ export async function runInit() {
110
+ const cwd = process.cwd();
111
+
112
+ header('Golem Init');
113
+ info('Analyzing project...');
114
+
115
+ // Detect framework
116
+ const framework = await detectFramework();
117
+ if (framework) {
118
+ success(`Detected framework: ${framework}`);
119
+ } else {
120
+ info('No framework detected (plain Node.js project)');
121
+ }
122
+
123
+ // Detect tools
124
+ const testCmd = detectTestCommand(cwd);
125
+ const buildCmd = detectBuildCommand(cwd, framework);
126
+ const lintCmd = detectLintCommand(cwd);
127
+ const typeCheck = detectTypeCheck(cwd);
128
+
129
+ info(`Test: ${testCmd}`);
130
+ info(`Build: ${buildCmd}`);
131
+ info(`Lint: ${lintCmd}`);
132
+ info(`Type check: ${typeCheck}`);
133
+
134
+ // Ensure .golem/ directories exist
135
+ for (const dir of ['specs', 'prompts', 'agents', 'lib', 'bin']) {
136
+ await mkdir(join(cwd, '.golem', dir), { recursive: true });
137
+ }
138
+ success('Ensured .golem/ directory structure');
139
+
140
+ // Generate AGENTS.md
141
+ const agentsPath = join(cwd, '.golem', 'AGENTS.md');
142
+ if (await fileExists(agentsPath)) {
143
+ warn('AGENTS.md already exists — overwriting with detected settings');
144
+ }
145
+ await writeFile(agentsPath, generateAgentsMd(cwd, framework));
146
+ success('Generated AGENTS.md');
147
+
148
+ // Create/update config.json
149
+ const config = await loadConfig();
150
+ if (framework) config.framework = framework;
151
+ await saveConfig(config);
152
+ success('Updated .golem/config.json');
153
+
154
+ // Ensure .gitignore entries
155
+ if (await ensureGitignore(cwd)) {
156
+ success('Updated .gitignore');
157
+ }
158
+
159
+ console.log();
160
+ success('Init complete! Next steps:');
161
+ info(' 1. Run /golem:spec in Claude Code to build your specs');
162
+ info(' 2. Run /golem:plan to generate an implementation plan');
163
+ info(' 3. Run npx golem build to start building');
164
+
165
+ return { framework, testCmd, buildCmd, lintCmd, typeCheck };
166
+ }
@@ -0,0 +1,40 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import Table from 'cli-table3';
4
+
5
+ export function header(text) {
6
+ console.log();
7
+ console.log(chalk.bold.cyan(`◆ ${text}`));
8
+ console.log(chalk.dim('─'.repeat(60)));
9
+ }
10
+
11
+ export function success(text) {
12
+ console.log(chalk.green(`✓ ${text}`));
13
+ }
14
+
15
+ export function fail(text) {
16
+ console.log(chalk.red(`✗ ${text}`));
17
+ }
18
+
19
+ export function warn(text) {
20
+ console.log(chalk.yellow(`⚠ ${text}`));
21
+ }
22
+
23
+ export function info(text) {
24
+ console.log(chalk.blue(`ℹ ${text}`));
25
+ }
26
+
27
+ export function spinner(text) {
28
+ return ora({ text, color: 'cyan' }).start();
29
+ }
30
+
31
+ export function table(headers, rows) {
32
+ const t = new Table({
33
+ head: headers.map(h => chalk.cyan(h)),
34
+ style: { head: [], border: ['dim'] },
35
+ });
36
+ for (const row of rows) {
37
+ t.push(row);
38
+ }
39
+ console.log(t.toString());
40
+ }
@@ -0,0 +1,86 @@
1
+ import { execSync } from 'node:child_process';
2
+
3
+ const USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
4
+ const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
5
+
6
+ let cache = { data: null, fetchedAt: 0 };
7
+
8
+ function getOAuthToken() {
9
+ try {
10
+ const raw = execSync(
11
+ 'security find-generic-password -s "Claude Code-credentials" -w',
12
+ { stdio: 'pipe', encoding: 'utf-8' },
13
+ ).trim();
14
+ const parsed = JSON.parse(raw);
15
+ // Nested format: { claudeAiOauth: { accessToken } } or flat: { accessToken }
16
+ return parsed.claudeAiOauth?.accessToken || parsed.accessToken || null;
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
22
+ export async function fetchUsage() {
23
+ const now = Date.now();
24
+ if (cache.data && (now - cache.fetchedAt) < CACHE_TTL_MS) {
25
+ return cache.data;
26
+ }
27
+
28
+ const token = getOAuthToken();
29
+ if (!token) return null;
30
+
31
+ try {
32
+ const res = await fetch(USAGE_URL, {
33
+ headers: {
34
+ 'Authorization': `Bearer ${token}`,
35
+ 'anthropic-beta': 'oauth-2025-04-20',
36
+ },
37
+ });
38
+ if (!res.ok) return null;
39
+
40
+ const body = await res.json();
41
+
42
+ function parseBucket(bucket) {
43
+ if (!bucket) return null;
44
+ return {
45
+ utilization: bucket.utilization ?? 0,
46
+ resetsAt: bucket.resets_at ?? null,
47
+ };
48
+ }
49
+
50
+ const extra = body.extra_usage;
51
+
52
+ const data = {
53
+ fiveHour: parseBucket(body.five_hour) || { utilization: 0, resetsAt: null },
54
+ sevenDay: parseBucket(body.seven_day) || { utilization: 0, resetsAt: null },
55
+ opus: parseBucket(body.seven_day_opus),
56
+ sonnet: parseBucket(body.seven_day_sonnet),
57
+ extraUsage: extra ? {
58
+ enabled: extra.is_enabled,
59
+ limit: extra.monthly_limit / 100,
60
+ used: extra.used_credits / 100,
61
+ utilization: extra.utilization,
62
+ } : null,
63
+ };
64
+
65
+ cache = { data, fetchedAt: now };
66
+ return data;
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+
72
+ export function clearUsageCache() {
73
+ cache = { data: null, fetchedAt: 0 };
74
+ }
75
+
76
+ export function formatResetTime(isoString) {
77
+ if (!isoString) return 'unknown';
78
+ const reset = new Date(isoString);
79
+ const diff = reset - Date.now();
80
+ if (diff <= 0) return 'now';
81
+ const mins = Math.ceil(diff / 60_000);
82
+ if (mins < 60) return `${mins}m`;
83
+ const h = Math.floor(mins / 60);
84
+ const m = mins % 60;
85
+ return `${h}h ${m}m`;
86
+ }