@rigstate/cli 0.7.25 → 0.7.29
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/dist/index.cjs +967 -751
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +964 -748
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/link.ts +99 -14
- package/src/commands/plan.ts +165 -0
- package/src/commands/work.ts +6 -0
- package/src/daemon/core.ts +28 -11
- package/src/index.ts +3 -0
- package/.rigstate/daemon.state.json +0 -8
package/package.json
CHANGED
package/src/commands/link.ts
CHANGED
|
@@ -9,9 +9,9 @@ import { getApiUrl, getApiKey } from '../utils/config.js';
|
|
|
9
9
|
export function createLinkCommand() {
|
|
10
10
|
return new Command('link')
|
|
11
11
|
.description('Link current directory to a Rigstate project')
|
|
12
|
-
.argument('
|
|
12
|
+
.argument('[projectId]', 'Project ID to link')
|
|
13
13
|
.action(async (projectId) => {
|
|
14
|
-
// Check Global Override
|
|
14
|
+
// Check Global Override first
|
|
15
15
|
try {
|
|
16
16
|
const globalPath = path.join(os.homedir(), '.rigstate', 'config.json');
|
|
17
17
|
const globalData = await fs.readFile(globalPath, 'utf-8').catch(() => null);
|
|
@@ -20,14 +20,60 @@ export function createLinkCommand() {
|
|
|
20
20
|
const cwd = process.cwd();
|
|
21
21
|
if (config.overrides && config.overrides[cwd]) {
|
|
22
22
|
const overrideId = config.overrides[cwd];
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
console.warn(chalk.yellow(`Global override detected. Enforcing project ID: ${overrideId}`));
|
|
24
|
+
if (!projectId) projectId = overrideId;
|
|
25
|
+
else if (projectId !== overrideId) {
|
|
26
|
+
console.warn(chalk.red(`Ignoring provided ID ${projectId}. Using override.`));
|
|
25
27
|
projectId = overrideId;
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
} catch (e) { }
|
|
30
32
|
|
|
33
|
+
// Interactive Selection if no ID
|
|
34
|
+
if (!projectId) {
|
|
35
|
+
try {
|
|
36
|
+
const inquirer = (await import('inquirer')).default;
|
|
37
|
+
const { getApiKey: _getApiKey, getApiUrl: _getApiUrl } = await import('../utils/config.js');
|
|
38
|
+
const apiKey = getApiKey();
|
|
39
|
+
const apiUrl = getApiUrl();
|
|
40
|
+
|
|
41
|
+
if (!apiKey) {
|
|
42
|
+
console.error(chalk.red('Not authenticated. Please run "rigstate login" or provide a Project ID.'));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(chalk.dim('Fetching your projects...'));
|
|
47
|
+
const axios = (await import('axios')).default;
|
|
48
|
+
const response = await axios.get(`${apiUrl}/api/v1/projects`, {
|
|
49
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!response.data.success || !response.data.data.projects?.length) {
|
|
53
|
+
console.error(chalk.yellow('No projects found. Create one at https://app.rigstate.com'));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const choices = response.data.data.projects.map((p: any) => ({
|
|
58
|
+
name: `${p.name} (${p.id})`,
|
|
59
|
+
value: p.id
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
const answer = await inquirer.prompt([{
|
|
63
|
+
type: 'list',
|
|
64
|
+
name: 'id',
|
|
65
|
+
message: 'Select project to link:',
|
|
66
|
+
choices
|
|
67
|
+
}]);
|
|
68
|
+
projectId = answer.id;
|
|
69
|
+
|
|
70
|
+
} catch (e: any) {
|
|
71
|
+
console.error(chalk.red(`Failed to fetch projects: ${e.message}`));
|
|
72
|
+
console.error('Please provide project ID manually: rigstate link <id>');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
31
77
|
const manifestPath = path.join(process.cwd(), '.rigstate');
|
|
32
78
|
|
|
33
79
|
const content: any = {
|
|
@@ -42,6 +88,9 @@ export function createLinkCommand() {
|
|
|
42
88
|
}
|
|
43
89
|
|
|
44
90
|
try {
|
|
91
|
+
// Ensure .rigstate dir exists
|
|
92
|
+
await fs.mkdir(path.dirname(manifestPath), { recursive: true });
|
|
93
|
+
|
|
45
94
|
await fs.writeFile(manifestPath, JSON.stringify(content, null, 2), 'utf-8');
|
|
46
95
|
console.log(chalk.green(`✔ Linked to project ID: ${projectId}`));
|
|
47
96
|
console.log(chalk.dim(`Created local context manifest at .rigstate`));
|
|
@@ -52,7 +101,7 @@ export function createLinkCommand() {
|
|
|
52
101
|
console.log('');
|
|
53
102
|
|
|
54
103
|
const { getApiKey: _getApiKey, getApiUrl: _getApiUrl } = await import('../utils/config.js');
|
|
55
|
-
const apiKey = getApiKey();
|
|
104
|
+
const apiKey = getApiKey();
|
|
56
105
|
const apiUrl = getApiUrl();
|
|
57
106
|
|
|
58
107
|
if (apiKey) {
|
|
@@ -67,19 +116,14 @@ export function createLinkCommand() {
|
|
|
67
116
|
await syncProjectRules(projectId, apiKey, apiUrl);
|
|
68
117
|
|
|
69
118
|
// 3. Git Hooks (Auto-Vaccine)
|
|
70
|
-
console.log(chalk.blue('🛡️ Injecting Guardian hooks...'));
|
|
71
|
-
const { createHooksCommand } = await import('./hooks.js');
|
|
72
|
-
// We can't easily invoke the command action directly without refactoring,
|
|
73
|
-
// but we can reuse the logic. For now, let's replicate the simple install logic
|
|
74
|
-
// or better yet, make a shared utility.
|
|
75
|
-
// Actually, the simplest way for v0.7.25 is to call the install logic directly if possible.
|
|
76
|
-
// Let's use the helper function at the bottom of this file which I see exists.
|
|
119
|
+
console.log(chalk.blue('🛡️ Injecting Guardian hooks & Safety nets...'));
|
|
77
120
|
await installHooks(process.cwd());
|
|
121
|
+
await hardenGitIgnore(process.cwd());
|
|
78
122
|
|
|
79
123
|
console.log('');
|
|
80
124
|
console.log(chalk.bold.green('🚀 Link Complete! Your environment is ready.'));
|
|
81
125
|
|
|
82
|
-
// 4. Tactical Suggestion
|
|
126
|
+
// 4. Tactical Suggestion
|
|
83
127
|
const { suggestNextMove } = await import('./suggest.js');
|
|
84
128
|
await suggestNextMove(projectId, apiKey, apiUrl);
|
|
85
129
|
} else {
|
|
@@ -88,7 +132,7 @@ export function createLinkCommand() {
|
|
|
88
132
|
}
|
|
89
133
|
|
|
90
134
|
} catch (error: any) {
|
|
91
|
-
if (error.message
|
|
135
|
+
if (error.message?.includes('Not authenticated')) {
|
|
92
136
|
console.warn(chalk.yellow('⚠️ Not authenticated. Run "rigstate login" to enable automation features.'));
|
|
93
137
|
} else {
|
|
94
138
|
console.error(chalk.red(`Failed to link project: ${error.message}`));
|
|
@@ -97,6 +141,47 @@ export function createLinkCommand() {
|
|
|
97
141
|
});
|
|
98
142
|
}
|
|
99
143
|
|
|
144
|
+
async function hardenGitIgnore(cwd: string) {
|
|
145
|
+
const fs = await import('fs/promises');
|
|
146
|
+
const path = await import('path');
|
|
147
|
+
const ignorePath = path.join(cwd, '.gitignore');
|
|
148
|
+
|
|
149
|
+
const REQUIRED_IGNORES = [
|
|
150
|
+
'# Rigstate - Runtime Artifacts (Do not commit)',
|
|
151
|
+
'.rigstate/ACTIVE_VIOLATIONS.md',
|
|
152
|
+
'.rigstate/CURRENT_CONTEXT.md',
|
|
153
|
+
'.rigstate/daemon.pid',
|
|
154
|
+
'.rigstate/daemon.state.json',
|
|
155
|
+
'.rigstate/*.log',
|
|
156
|
+
'.rigstate/*.bak',
|
|
157
|
+
'# Keep identity tracked',
|
|
158
|
+
'!.rigstate/identity.json'
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
let content = '';
|
|
163
|
+
try {
|
|
164
|
+
content = await fs.readFile(ignorePath, 'utf-8');
|
|
165
|
+
} catch {
|
|
166
|
+
// No .gitignore, start fresh
|
|
167
|
+
content = '';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const missing = REQUIRED_IGNORES.filter(line => !content.includes(line) && !line.startsWith('#'));
|
|
171
|
+
|
|
172
|
+
if (missing.length > 0) {
|
|
173
|
+
console.log(chalk.dim(' Configuring .gitignore for Rigstate safety...'));
|
|
174
|
+
const toAppend = '\n\n' + REQUIRED_IGNORES.join('\n') + '\n';
|
|
175
|
+
await fs.writeFile(ignorePath, content + toAppend, 'utf-8');
|
|
176
|
+
console.log(chalk.green(' ✔ .gitignore updated (Artifacts protected)'));
|
|
177
|
+
} else {
|
|
178
|
+
console.log(chalk.green(' ✔ .gitignore already hardened'));
|
|
179
|
+
}
|
|
180
|
+
} catch (e: any) {
|
|
181
|
+
console.warn(chalk.yellow(` Could not update .gitignore: ${e.message}`));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
100
185
|
async function installHooks(cwd: string) {
|
|
101
186
|
const fs = await import('fs/promises');
|
|
102
187
|
const path = await import('path');
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import axios from 'axios';
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import { getApiKey, getApiUrl, getProjectId } from '../utils/config.js';
|
|
9
|
+
|
|
10
|
+
export function createPlanCommand(): Command {
|
|
11
|
+
const plan = new Command('plan');
|
|
12
|
+
|
|
13
|
+
plan
|
|
14
|
+
.description('Generate an implementation plan for a roadmap task')
|
|
15
|
+
.argument('[taskId]', 'Task ID (e.g. T-10) or UUID')
|
|
16
|
+
.action(async (taskId) => {
|
|
17
|
+
await executePlan(taskId);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return plan;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function executePlan(taskId?: string) {
|
|
24
|
+
const spinner = ora('Initializing Planning Mode...').start();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const { projectId, apiKey, apiUrl } = getContext();
|
|
28
|
+
|
|
29
|
+
// 1. Resolve Task ID if missing or short
|
|
30
|
+
let realId = taskId;
|
|
31
|
+
let taskTitle = '';
|
|
32
|
+
let taskDescription = '';
|
|
33
|
+
|
|
34
|
+
if (!taskId) {
|
|
35
|
+
spinner.text = 'Fetching actionable tasks...';
|
|
36
|
+
// Interactive selection
|
|
37
|
+
const response = await axios.get(
|
|
38
|
+
`${apiUrl}/api/v1/roadmap?project_id=${projectId}`,
|
|
39
|
+
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (!response.data.success) throw new Error('Failed to fetch roadmap');
|
|
43
|
+
const tasks: any[] = response.data.data.roadmap || [];
|
|
44
|
+
|
|
45
|
+
const choices = tasks
|
|
46
|
+
.filter(t => ['ACTIVE', 'IN_PROGRESS', 'PENDING'].includes(t.status))
|
|
47
|
+
.map(t => ({
|
|
48
|
+
name: `T-${t.step_number}: ${t.title}`,
|
|
49
|
+
value: t
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
if (choices.length === 0) {
|
|
53
|
+
spinner.fail('No actionable tasks found in roadmap.');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
spinner.stop();
|
|
58
|
+
const answer = await inquirer.prompt([{
|
|
59
|
+
type: 'list',
|
|
60
|
+
name: 'task',
|
|
61
|
+
message: 'Select a task to plan:',
|
|
62
|
+
choices
|
|
63
|
+
}]);
|
|
64
|
+
|
|
65
|
+
realId = answer.task.id;
|
|
66
|
+
taskTitle = answer.task.title;
|
|
67
|
+
taskDescription = answer.task.description;
|
|
68
|
+
taskId = `T-${answer.task.step_number}`;
|
|
69
|
+
} else {
|
|
70
|
+
// Fetch specific task details
|
|
71
|
+
spinner.text = `Fetching details for ${taskId}...`;
|
|
72
|
+
const response = await axios.get(
|
|
73
|
+
`${apiUrl}/api/v1/roadmap?project_id=${projectId}`,
|
|
74
|
+
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
|
|
75
|
+
);
|
|
76
|
+
const task = response.data.data.roadmap.find((t: any) =>
|
|
77
|
+
t.id === taskId ||
|
|
78
|
+
`T-${t.step_number}` === taskId ||
|
|
79
|
+
t.step_number.toString() === taskId
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (!task) throw new Error(`Task ${taskId} not found.`);
|
|
83
|
+
realId = task.id;
|
|
84
|
+
taskTitle = task.title;
|
|
85
|
+
taskDescription = task.description;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 2. Generate Context File
|
|
89
|
+
spinner.start('Generating Context for Frank...');
|
|
90
|
+
const contextPath = path.join(process.cwd(), '.rigstate', 'CURRENT_CONTEXT.md');
|
|
91
|
+
const contextContent = `
|
|
92
|
+
# 🎯 Active Mission: ${taskTitle}
|
|
93
|
+
**ID:** ${taskId}
|
|
94
|
+
|
|
95
|
+
## 📝 Description
|
|
96
|
+
${taskDescription}
|
|
97
|
+
|
|
98
|
+
## 🛡️ Architectural Constraints
|
|
99
|
+
- Follow strictly the rules in .cursor/rules/
|
|
100
|
+
- Ensure zero violations in ACTIVE_VIOLATIONS.md
|
|
101
|
+
- Update IMPLEMENTATION_PLAN.md before writing code.
|
|
102
|
+
|
|
103
|
+
*Generated by Rigstate CLI at ${new Date().toLocaleString()}*
|
|
104
|
+
`;
|
|
105
|
+
await fs.mkdir(path.dirname(contextPath), { recursive: true });
|
|
106
|
+
await fs.writeFile(contextPath, contextContent.trim());
|
|
107
|
+
|
|
108
|
+
// 3. Generate Plan Template
|
|
109
|
+
const planPath = path.join(process.cwd(), 'IMPLEMENTATION_PLAN.md');
|
|
110
|
+
const planExists = await fs.stat(planPath).then(() => true).catch(() => false);
|
|
111
|
+
|
|
112
|
+
if (!planExists) {
|
|
113
|
+
const planTemplate = `
|
|
114
|
+
# 📋 Implementation Plan: ${taskTitle}
|
|
115
|
+
|
|
116
|
+
## 1. 🔍 Analysis
|
|
117
|
+
- [ ] Understand the requirements in .rigstate/CURRENT_CONTEXT.md
|
|
118
|
+
- [ ] Check for existing architectural patterns
|
|
119
|
+
|
|
120
|
+
## 2. 🏗️ Proposed Changes
|
|
121
|
+
[Frank: List the files you intend to modify and the nature of the changes]
|
|
122
|
+
|
|
123
|
+
## 3. ✅ Verification
|
|
124
|
+
- [ ] Run tests
|
|
125
|
+
- [ ] Verification Step 1...
|
|
126
|
+
|
|
127
|
+
## 4. 🚀 Execution
|
|
128
|
+
[Frank: Log your progress here]
|
|
129
|
+
`;
|
|
130
|
+
await fs.writeFile(planPath, planTemplate.trim());
|
|
131
|
+
spinner.succeed(chalk.green('Created new IMPLEMENTATION_PLAN.md'));
|
|
132
|
+
} else {
|
|
133
|
+
spinner.info(chalk.yellow('IMPLEMENTATION_PLAN.md already exists. Preserving it.'));
|
|
134
|
+
// Optionally append or just leave it
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 4. Update Status (Optional - maybe set to IN_PROGRESS?)
|
|
138
|
+
// For now, let's keep it pure planning.
|
|
139
|
+
|
|
140
|
+
console.log('');
|
|
141
|
+
console.log(chalk.bold.blue('🚀 Planning Mode Activated'));
|
|
142
|
+
console.log(chalk.dim('────────────────────────────────────────'));
|
|
143
|
+
console.log(`1. Context loaded into: ${chalk.bold('.rigstate/CURRENT_CONTEXT.md')}`);
|
|
144
|
+
console.log(`2. Plan template ready: ${chalk.bold('IMPLEMENTATION_PLAN.md')}`);
|
|
145
|
+
console.log('');
|
|
146
|
+
console.log(chalk.yellow('👉 NEXT STEP:'));
|
|
147
|
+
console.log(` Open ${chalk.bold('IMPLEMENTATION_PLAN.md')} in your IDE.`);
|
|
148
|
+
console.log(` Tell Frank: ${chalk.italic('"Read the context and draft the plan."')}`);
|
|
149
|
+
console.log('');
|
|
150
|
+
|
|
151
|
+
} catch (e: any) {
|
|
152
|
+
spinner.fail(chalk.red(`Planning failed: ${e.message}`));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function getContext() {
|
|
157
|
+
const apiKey = getApiKey();
|
|
158
|
+
const apiUrl = getApiUrl();
|
|
159
|
+
const projectId = getProjectId();
|
|
160
|
+
|
|
161
|
+
if (!projectId) {
|
|
162
|
+
throw new Error('Project ID missing. Run rigstate link.');
|
|
163
|
+
}
|
|
164
|
+
return { projectId, apiKey, apiUrl };
|
|
165
|
+
}
|
package/src/commands/work.ts
CHANGED
|
@@ -6,6 +6,7 @@ import inquirer from 'inquirer';
|
|
|
6
6
|
import fs from 'fs/promises';
|
|
7
7
|
import { getApiKey, getApiUrl, getProjectId } from '../utils/config.js';
|
|
8
8
|
import { suggestNextMove } from './suggest.js';
|
|
9
|
+
import { executePlan } from './plan.js';
|
|
9
10
|
|
|
10
11
|
export function createWorkCommand(): Command {
|
|
11
12
|
const work = new Command('work');
|
|
@@ -92,12 +93,17 @@ async function listInteractive() {
|
|
|
92
93
|
name: 'action',
|
|
93
94
|
message: 'Action:',
|
|
94
95
|
choices: [
|
|
96
|
+
{ name: 'Plan (Draft Blueprint - RECOMMENDED)', value: 'plan' },
|
|
95
97
|
{ name: 'Start (Set IN_PROGRESS)', value: 'start' },
|
|
96
98
|
{ name: 'Finish (Audit & Complete)', value: 'finish' },
|
|
97
99
|
{ name: 'Cancel', value: 'cancel' }
|
|
98
100
|
]
|
|
99
101
|
}]);
|
|
100
102
|
|
|
103
|
+
if (action === 'plan') {
|
|
104
|
+
await executePlan(taskId);
|
|
105
|
+
// After planning, maybe ask to start? For now, just exit as plan command does sufficient logging.
|
|
106
|
+
}
|
|
101
107
|
if (action === 'start') await setTaskStatus(taskId, 'IN_PROGRESS');
|
|
102
108
|
if (action === 'finish') await finishTask(taskId);
|
|
103
109
|
|
package/src/daemon/core.ts
CHANGED
|
@@ -160,6 +160,8 @@ export class GuardianDaemon extends EventEmitter {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
private violationsMap = new Map<string, any[]>();
|
|
164
|
+
|
|
163
165
|
private async runIntegrityCheck(filePath: string) {
|
|
164
166
|
if (!this.guardianMonitor) return;
|
|
165
167
|
|
|
@@ -170,25 +172,37 @@ export class GuardianDaemon extends EventEmitter {
|
|
|
170
172
|
if (result.violations.length > 0) {
|
|
171
173
|
this.handleViolations(filePath, result.violations);
|
|
172
174
|
} else {
|
|
173
|
-
// Success -
|
|
174
|
-
|
|
175
|
+
// Success - clear violations for this file
|
|
176
|
+
if (this.violationsMap.has(filePath)) {
|
|
177
|
+
this.violationsMap.delete(filePath);
|
|
178
|
+
this.updateViolationReport(); // Update the aggregate report
|
|
179
|
+
}
|
|
175
180
|
}
|
|
176
181
|
}
|
|
177
182
|
|
|
178
|
-
private async updateViolationReport(violations
|
|
183
|
+
private async updateViolationReport(violations?: any[]) {
|
|
184
|
+
// If violations arg is passed (from handleViolations), update the map first
|
|
185
|
+
// But usually handleViolations calls this without args to just refresh
|
|
186
|
+
|
|
179
187
|
const reportPath = path.join(process.cwd(), '.rigstate', 'ACTIVE_VIOLATIONS.md');
|
|
188
|
+
const allViolations = Array.from(this.violationsMap.entries());
|
|
189
|
+
const totalCount = allViolations.reduce((acc, [, v]) => acc + v.length, 0);
|
|
180
190
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
content += `*Last check: ${new Date().toLocaleString()}*\n\n`;
|
|
191
|
+
let content = `# 🛡️ Guardian Status: ${totalCount > 0 ? '⚠️ ATTENTION' : '✅ PASS'}\n\n`;
|
|
192
|
+
content += `*Last check: ${new Date().toLocaleString()}*\n`;
|
|
193
|
+
content += `*Files with issues: ${allViolations.length}*\n\n`;
|
|
185
194
|
|
|
186
|
-
if (
|
|
195
|
+
if (totalCount === 0) {
|
|
187
196
|
content += "All systems within architectural limits. Frank is satisfied. 🤫\n";
|
|
188
197
|
} else {
|
|
189
198
|
content += "### 🚨 Active Violations\n\n";
|
|
190
|
-
for (const
|
|
191
|
-
|
|
199
|
+
for (const [file, fileViolations] of allViolations) {
|
|
200
|
+
const relPath = path.relative(process.cwd(), file);
|
|
201
|
+
content += `#### 📄 ${relPath}\n`;
|
|
202
|
+
for (const v of fileViolations) {
|
|
203
|
+
content += `- **[${v.severity.toUpperCase()}]**: ${v.message}\n`;
|
|
204
|
+
}
|
|
205
|
+
content += '\n';
|
|
192
206
|
}
|
|
193
207
|
content += "\n---\n*Rigstate Daemon is watching. Fix violations to clear this report.*";
|
|
194
208
|
}
|
|
@@ -202,7 +216,10 @@ export class GuardianDaemon extends EventEmitter {
|
|
|
202
216
|
this.state.violationsFound += violations.length;
|
|
203
217
|
this.emit('violation', { file: filePath, violations });
|
|
204
218
|
|
|
205
|
-
|
|
219
|
+
// Update state map
|
|
220
|
+
this.violationsMap.set(filePath, violations);
|
|
221
|
+
|
|
222
|
+
this.updateViolationReport(); // Push to IDE dashboard
|
|
206
223
|
|
|
207
224
|
for (const v of violations) {
|
|
208
225
|
const level = v.severity === 'critical' ? 'error' : v.severity === 'warning' ? 'warn' : 'info';
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { createIdeaCommand } from './commands/idea.js';
|
|
|
22
22
|
import { createReleaseCommand } from './commands/release.js';
|
|
23
23
|
import { createRoadmapCommand } from './commands/roadmap.js';
|
|
24
24
|
import { createCouncilCommand } from './commands/council.js';
|
|
25
|
+
import { createPlanCommand } from './commands/plan.js';
|
|
25
26
|
import { checkVersion } from './utils/version.js';
|
|
26
27
|
import dotenv from 'dotenv';
|
|
27
28
|
|
|
@@ -60,7 +61,9 @@ program.addCommand(createOverrideCommand());
|
|
|
60
61
|
program.addCommand(createIdeaCommand());
|
|
61
62
|
program.addCommand(createReleaseCommand());
|
|
62
63
|
program.addCommand(createRoadmapCommand());
|
|
64
|
+
program.addCommand(createRoadmapCommand());
|
|
63
65
|
program.addCommand(createCouncilCommand());
|
|
66
|
+
program.addCommand(createPlanCommand());
|
|
64
67
|
|
|
65
68
|
program.hook('preAction', async () => {
|
|
66
69
|
await checkVersion();
|