@rigstate/cli 0.6.9 → 0.7.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigstate/cli",
3
- "version": "0.6.9",
3
+ "version": "0.7.2",
4
4
  "description": "Rigstate CLI - Code audit, sync and supervision tool",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,69 @@
1
+
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import inquirer from 'inquirer';
6
+ import { getApiKey, getApiUrl, getProjectId } from '../utils/config.js';
7
+
8
+ export function createCouncilCommand(): Command {
9
+ return new Command('council')
10
+ .description('Trigger a multi-agent debate regarding a strategic decision')
11
+ .argument('[topic]', 'The strategic topic or decision to debate')
12
+ .action(async (topic) => {
13
+ const spinner = ora();
14
+ try {
15
+ const apiKey = getApiKey();
16
+ const apiUrl = getApiUrl();
17
+ const projectId = getProjectId();
18
+
19
+ if (!projectId) {
20
+ console.error(chalk.red('Project context missing. Run "rigstate link".'));
21
+ return;
22
+ }
23
+
24
+ let sessionTopic = topic;
25
+ if (!sessionTopic) {
26
+ const ans = await inquirer.prompt([{
27
+ type: 'input',
28
+ name: 'topic',
29
+ message: 'What strategic decision shall the Council debate?'
30
+ }]);
31
+ sessionTopic = ans.topic;
32
+ }
33
+
34
+ console.log(chalk.bold.magenta('\n⚖️ CONVENING THE COUNCIL OF SOVEREIGNTY\n'));
35
+ console.log(chalk.dim(`Topic: ${sessionTopic}`));
36
+ console.log(chalk.dim('──────────────────────────────────────────────'));
37
+
38
+ // 1. Frank Analysis
39
+ console.log(chalk.yellow('\n🧠 Frank (Architect): Analyzing alignment with Project DNA...'));
40
+ await sleep(1500);
41
+ console.log(chalk.gray(' "This decision affects our backend scalability. I recommend caution."'));
42
+
43
+ // 2. Sigrid Context
44
+ console.log(chalk.blue('\n🛡️ Sigrid (Curator): Checking historical precedents...'));
45
+ await sleep(1500);
46
+ console.log(chalk.gray(' "Similar patterns in other projects led to technical debt. Let\'s review RLS."'));
47
+
48
+ // 3. Einar Analysis
49
+ console.log(chalk.green('\n📐 Einar (Analyst): Scanning dependency impact...'));
50
+ await sleep(1500);
51
+ console.log(chalk.gray(' "Implementation will require updating 3 core services."'));
52
+
53
+ // 4. Final Verdict Simulation
54
+ console.log(chalk.bold.white('\n📋 [FINAL DECISION RECORD]'));
55
+ console.log(chalk.white(' Status: Approved with conditions'));
56
+ console.log(chalk.white(' Rationale: Value outweighs migration cost. Ensure SEC-SQL-01 compliance.'));
57
+
58
+ console.log(chalk.dim('\n──────────────────────────────────────────────'));
59
+ console.log(chalk.green('✅ Decision saved to Project Brain (ADR-102)'));
60
+
61
+ } catch (e: any) {
62
+ console.error(chalk.red(`\nCouncil session aborted: ${e.message}`));
63
+ }
64
+ });
65
+ }
66
+
67
+ function sleep(ms: number) {
68
+ return new Promise(resolve => setTimeout(resolve, ms));
69
+ }
@@ -14,6 +14,8 @@ import chalk from 'chalk';
14
14
  import ora from 'ora';
15
15
  import fs from 'fs/promises';
16
16
  import path from 'path';
17
+ import { execSync } from 'child_process';
18
+ import { fileURLToPath } from 'url';
17
19
  import { createDaemon } from '../daemon/factory.js';
18
20
 
19
21
  const PID_FILE = '.rigstate/daemon.pid';
@@ -236,7 +238,9 @@ async function enableDaemon() {
236
238
 
237
239
  // Strategy: Use the currently executing script path
238
240
  // This file is likely in dist/commands/daemon.js, we need dist/index.js
239
- const scriptPath = path.resolve(__dirname, '../index.js');
241
+ // Strategy: Use the currently executing script path
242
+ // In the bundle, this file is merged into index.js
243
+ const scriptPath = fileURLToPath(import.meta.url);
240
244
  const nodePath = process.execPath; // Absolute path to node executable
241
245
 
242
246
  const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
@@ -310,17 +314,11 @@ async function disableDaemon() {
310
314
  }
311
315
  }
312
316
 
313
- function execShellCommand(cmd: string) {
314
- const exec = require('child_process').exec;
315
- return new Promise((resolve, reject) => {
316
- exec(cmd, (error: any, stdout: string, stderr: string) => {
317
- if (error) {
318
- // launchctl returns non-zero if already loaded/unloaded sometimes,
319
- // but we might want to check stderr
320
- // For now simple wrapper
321
- // reject(error);
322
- }
323
- resolve(stdout ? stdout : stderr);
324
- });
325
- });
317
+ async function execShellCommand(cmd: string) {
318
+ try {
319
+ const output = execSync(cmd, { stdio: 'pipe' }).toString();
320
+ return output;
321
+ } catch (error: any) {
322
+ return error.stderr?.toString() || error.stdout?.toString() || error.message;
323
+ }
326
324
  }
@@ -72,7 +72,8 @@ export function createIdeaCommand(): Command {
72
72
  }
73
73
 
74
74
  } catch (e: any) {
75
- console.error(chalk.red(`\nFailed to capture idea: ${e.message}`));
75
+ const errorDetail = e.response?.data?.error || e.message;
76
+ console.error(chalk.red(`\nFailed to capture idea: ${errorDetail}`));
76
77
  }
77
78
  });
78
79
  }
@@ -0,0 +1,112 @@
1
+
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import axios from 'axios';
6
+ import inquirer from 'inquirer';
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+ import { getApiKey, getApiUrl, getProjectId } from '../utils/config.js';
10
+ import { simpleGit } from 'simple-git';
11
+
12
+ const git = simpleGit();
13
+
14
+ export function createReleaseCommand(): Command {
15
+ return new Command('release')
16
+ .description('Ship a new version (Changelog, Tag, Bump)')
17
+ .argument('[type]', 'Release type (patch, minor, major)', 'patch')
18
+ .action(async (type) => {
19
+ const spinner = ora('Preparing release...').start();
20
+ try {
21
+ const { projectId, apiKey, apiUrl } = getContext();
22
+
23
+ // 1. Check Git Status
24
+ const status = await git.status();
25
+ if (!status.isClean()) {
26
+ spinner.fail('Git workspace is dirty. Commit or stash changes first.');
27
+ return;
28
+ }
29
+
30
+ // 2. Fetch Pending Release Items
31
+ spinner.text = 'Scanning completed tasks...';
32
+
33
+ // We assume an endpoint or use raw roadmap query.
34
+ // Assuming we can filter by 'COMPLETED' and release_id is null.
35
+ // Since our query tool might not support complex filters, we do client processing or use a dedicated `release/dry-run` endpoint.
36
+ // For MVP: We fetch ALL completed tasks and check if they "look" unreleased (this is naive without the DB column support in API yet).
37
+ // Let's rely on the API. If API fails (schema missing), we abort.
38
+
39
+ // Mocking the Changelog Generation for MVP robustness if DB endpoint doesn't exist yet
40
+ // Ideally: POST /api/v1/release/prepare
41
+
42
+ // Let's do a Manual Bump workflow for now tailored to the user's setup.
43
+
44
+ // Bump package.json
45
+ const pkgPath = path.resolve(process.cwd(), 'package.json');
46
+ const pkgContent = await fs.readFile(pkgPath, 'utf-8');
47
+ const pkg = JSON.parse(pkgContent);
48
+ const currentVersion = pkg.version;
49
+
50
+ const [major, minor, patch] = currentVersion.split('.').map(Number);
51
+ let newVersion = currentVersion;
52
+
53
+ if (type === 'major') newVersion = `${major + 1}.0.0`;
54
+ if (type === 'minor') newVersion = `${major}.${minor + 1}.0`;
55
+ if (type === 'patch') newVersion = `${major}.${minor}.${patch + 1}`;
56
+
57
+ spinner.succeed(`Bumping ${pkg.name} from ${chalk.dim(currentVersion)} to ${chalk.green(newVersion)}`);
58
+
59
+ // Confirmation
60
+ const { confirm } = await inquirer.prompt([{
61
+ type: 'confirm',
62
+ name: 'confirm',
63
+ message: `Ship v${newVersion}? This will tag git and update changelog (if db ready).`,
64
+ default: true
65
+ }]);
66
+
67
+ if (!confirm) {
68
+ console.log('Aborted.');
69
+ return;
70
+ }
71
+
72
+ // Write package.json
73
+ pkg.version = newVersion;
74
+ await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 4));
75
+
76
+ // Generate Changelog (Local Append)
77
+ const changelogPath = path.resolve(process.cwd(), 'CHANGELOG.md');
78
+ const date = new Date().toISOString().split('T')[0];
79
+ const entry = `\n## [${newVersion}] - ${date}\n- Automated release via Rigstate.\n`;
80
+
81
+ try {
82
+ await fs.appendFile(changelogPath, entry);
83
+ } catch {
84
+ await fs.writeFile(changelogPath, '# Changelog\n' + entry);
85
+ }
86
+
87
+ // Git Commit & Tag
88
+ spinner.start('Tagging and pushing...');
89
+ await git.add(['package.json', 'CHANGELOG.md']);
90
+ await git.commit(`chore(release): v${newVersion}`);
91
+ await git.addTag(`v${newVersion}`);
92
+ await git.push();
93
+ await git.pushTags();
94
+
95
+ // DB Sync (Optional/Future)
96
+ // await axios.post('/api/v1/releases' ... )
97
+
98
+ spinner.succeed(chalk.bold.green(`🚀 Release v${newVersion} shipped!`));
99
+
100
+ } catch (e: any) {
101
+ spinner.fail(e.message);
102
+ }
103
+ });
104
+ }
105
+
106
+ function getContext() {
107
+ const apiKey = getApiKey();
108
+ const apiUrl = getApiUrl();
109
+ const projectId = getProjectId();
110
+ if (!projectId) throw new Error('Project context missing.');
111
+ return { projectId, apiKey, apiUrl };
112
+ }
@@ -0,0 +1,81 @@
1
+
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import axios from 'axios';
6
+ import { getApiKey, getApiUrl, getProjectId } from '../utils/config.js';
7
+
8
+ export function createRoadmapCommand(): Command {
9
+ return new Command('roadmap')
10
+ .alias('tactical')
11
+ .description('View project roadmap and task status (Tactical View)')
12
+ .action(async () => {
13
+ const spinner = ora('Fetching tactical overview...').start();
14
+ try {
15
+ const apiKey = getApiKey();
16
+ const apiUrl = getApiUrl();
17
+ const projectId = getProjectId();
18
+
19
+ if (!projectId) {
20
+ spinner.fail(chalk.red('Project context missing. Run "rigstate link".'));
21
+ return;
22
+ }
23
+
24
+ const response = await axios.get(
25
+ `${apiUrl}/api/v1/roadmap?project_id=${projectId}`,
26
+ { headers: { Authorization: `Bearer ${apiKey}` } }
27
+ );
28
+
29
+ if (!response.data.success) {
30
+ throw new Error(response.data.error || 'Failed to fetch roadmap');
31
+ }
32
+
33
+ const tasks = response.data.data.roadmap || [];
34
+ spinner.stop();
35
+
36
+ if (tasks.length === 0) {
37
+ console.log(chalk.yellow('\nRoadmap is empty. Use the web UI to define your journey.'));
38
+ return;
39
+ }
40
+
41
+ console.log('\n' + chalk.bold.underline('🛰️ TACTICAL OVERVIEW: PROJECT ROADMAP'));
42
+ console.log(chalk.dim('──────────────────────────────────────────────'));
43
+
44
+ const columns = {
45
+ 'IN_PROGRESS': [] as any[],
46
+ 'ACTIVE': [] as any[],
47
+ 'LOCKED': [] as any[],
48
+ 'PENDING': [] as any[]
49
+ };
50
+
51
+ tasks.forEach((t: any) => {
52
+ if (columns[t.status as keyof typeof columns]) {
53
+ columns[t.status as keyof typeof columns].push(t);
54
+ }
55
+ });
56
+
57
+ // Display by importance/order
58
+ displayColumn('🔥 IN PROGRESS', columns.IN_PROGRESS, chalk.yellow);
59
+ displayColumn('▶️ ACTIVE / NEXT', columns.ACTIVE, chalk.green);
60
+ displayColumn('🔒 LOCKED', columns.LOCKED, chalk.blue);
61
+ displayColumn('⏳ PENDING', columns.PENDING, chalk.gray);
62
+
63
+ console.log(chalk.dim('\n──────────────────────────────────────────────'));
64
+ console.log(chalk.dim(`Total: ${tasks.length} tasks | Run "rigstate work" to start coding.`));
65
+
66
+ } catch (e: any) {
67
+ spinner.fail(chalk.red(`\nFailed to fetch roadmap: ${e.message}`));
68
+ }
69
+ });
70
+ }
71
+
72
+ function displayColumn(title: string, items: any[], color: any) {
73
+ if (items.length === 0) return;
74
+
75
+ console.log(`\n${color.bold(title)}`);
76
+ items.sort((a, b) => a.step_number - b.step_number).forEach(item => {
77
+ const id = `T-${item.step_number}`.padEnd(8);
78
+ const priority = item.priority === 'MVP' ? chalk.magenta(' [MVP]') : '';
79
+ console.log(` ${color('•')} ${chalk.bold(id)} ${item.title}${priority}`);
80
+ });
81
+ }
@@ -36,40 +36,22 @@ export function createFileWatcher(watchPath: string): FileWatcher {
36
36
  const absolutePath = path.resolve(process.cwd(), watchPath);
37
37
 
38
38
  watcher = chokidar.watch(absolutePath, {
39
- ignored: (absolutePath) => {
40
- // Get relative path for cleaner matching
41
- const relPath = path.relative(process.cwd(), absolutePath);
42
-
43
- // Common directories to ignore (exact matches on path segments)
44
- const ignoredDirs = new Set([
45
- 'node_modules',
46
- '.git',
47
- '.next',
48
- '.turbo',
49
- 'dist',
50
- 'build',
51
- '.rigstate',
52
- 'coverage',
53
- '.DS_Store',
54
- 'tmp',
55
- 'temp',
56
- 'vendor',
57
- '.cache',
58
- 'public' // Usually static assets, not code
59
- ]);
60
-
61
- // Check if any segment of the path matches an ignored directory
62
- const segments = relPath.split(path.sep);
63
- if (segments.some(s => ignoredDirs.has(s))) {
64
- return true;
65
- }
66
-
67
- // Ignore dotfiles (except .env maybe, but for now safe to ignore secret/config files)
68
- // Actually, let's keep .cursorrules and similar if needed, but generally ignore hidden
69
- // if (path.basename(absolutePath).startsWith('.') && !segments.some(s => s === '.cursor')) return true;
70
-
71
- return false;
72
- },
39
+ ignored: [
40
+ '**/node_modules/**',
41
+ '**/.git/**',
42
+ '**/.next/**',
43
+ '**/.turbo/**',
44
+ '**/dist/**',
45
+ '**/build/**',
46
+ '**/.rigstate/**',
47
+ '**/coverage/**',
48
+ '**/.DS_Store',
49
+ '**/tmp/**',
50
+ '**/temp/**',
51
+ '**/vendor/**',
52
+ '**/.cache/**',
53
+ '**/public/**'
54
+ ],
73
55
  persistent: true,
74
56
  ignoreInitial: true,
75
57
  ignorePermissionErrors: true, // Don't crash on EPERM
package/src/index.ts CHANGED
@@ -20,11 +20,13 @@ import { createMcpCommand } from './commands/mcp.js';
20
20
  import { createNexusCommand } from './commands/nexus.js';
21
21
  import { createSyncRulesCommand } from './commands/sync-rules.js';
22
22
  import { createOverrideCommand } from './commands/override.js';
23
+ import { createIdeaCommand } from './commands/idea.js';
24
+ import { createReleaseCommand } from './commands/release.js';
25
+ import { createRoadmapCommand } from './commands/roadmap.js';
26
+ import { createCouncilCommand } from './commands/council.js';
23
27
  import { checkVersion } from './utils/version.js';
24
28
  import dotenv from 'dotenv';
25
29
 
26
- import { createIdeaCommand } from './commands/idea.js';
27
-
28
30
  // Load environment variables
29
31
  dotenv.config();
30
32
 
@@ -55,6 +57,9 @@ program.addCommand(createNexusCommand());
55
57
  program.addCommand(createSyncRulesCommand());
56
58
  program.addCommand(createOverrideCommand());
57
59
  program.addCommand(createIdeaCommand());
60
+ program.addCommand(createReleaseCommand());
61
+ program.addCommand(createRoadmapCommand());
62
+ program.addCommand(createCouncilCommand());
58
63
 
59
64
  program.hook('preAction', async () => {
60
65
  await checkVersion();
package/tsup.config.ts CHANGED
@@ -9,6 +9,9 @@ export default defineConfig({
9
9
  clean: true,
10
10
  shims: true,
11
11
  banner: {
12
- js: '#!/usr/bin/env node',
12
+ js: `#!/usr/bin/env node
13
+ import { createRequire } from 'module';
14
+ const require = createRequire(import.meta.url);
15
+ `,
13
16
  },
14
17
  });