@rigstate/cli 0.6.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.
- package/.env.example +5 -0
- package/IMPLEMENTATION.md +239 -0
- package/QUICK_START.md +220 -0
- package/README.md +150 -0
- package/dist/index.cjs +3987 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3964 -0
- package/dist/index.js.map +1 -0
- package/install.sh +15 -0
- package/package.json +53 -0
- package/src/commands/check.ts +329 -0
- package/src/commands/config.ts +81 -0
- package/src/commands/daemon.ts +197 -0
- package/src/commands/env.ts +158 -0
- package/src/commands/fix.ts +140 -0
- package/src/commands/focus.ts +134 -0
- package/src/commands/hooks.ts +163 -0
- package/src/commands/init.ts +282 -0
- package/src/commands/link.ts +45 -0
- package/src/commands/login.ts +35 -0
- package/src/commands/mcp.ts +73 -0
- package/src/commands/nexus.ts +81 -0
- package/src/commands/override.ts +65 -0
- package/src/commands/scan.ts +242 -0
- package/src/commands/sync-rules.ts +191 -0
- package/src/commands/sync.ts +339 -0
- package/src/commands/watch.ts +283 -0
- package/src/commands/work.ts +172 -0
- package/src/daemon/bridge-listener.ts +127 -0
- package/src/daemon/core.ts +184 -0
- package/src/daemon/factory.ts +45 -0
- package/src/daemon/file-watcher.ts +97 -0
- package/src/daemon/guardian-monitor.ts +133 -0
- package/src/daemon/heuristic-engine.ts +203 -0
- package/src/daemon/intervention-protocol.ts +128 -0
- package/src/daemon/telemetry.ts +23 -0
- package/src/daemon/types.ts +18 -0
- package/src/hive/gateway.ts +74 -0
- package/src/hive/protocol.ts +29 -0
- package/src/hive/scrubber.ts +72 -0
- package/src/index.ts +85 -0
- package/src/nexus/council.ts +103 -0
- package/src/nexus/dispatcher.ts +133 -0
- package/src/utils/config.ts +83 -0
- package/src/utils/files.ts +95 -0
- package/src/utils/governance.ts +128 -0
- package/src/utils/logger.ts +66 -0
- package/src/utils/manifest.ts +18 -0
- package/src/utils/rule-engine.ts +292 -0
- package/src/utils/skills-provisioner.ts +153 -0
- package/src/utils/version.ts +1 -0
- package/src/utils/watchdog.ts +215 -0
- package/tsconfig.json +29 -0
- package/tsup.config.ts +11 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3987 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
|
|
33
|
+
// node_modules/tsup/assets/cjs_shims.js
|
|
34
|
+
var getImportMetaUrl, importMetaUrl;
|
|
35
|
+
var init_cjs_shims = __esm({
|
|
36
|
+
"node_modules/tsup/assets/cjs_shims.js"() {
|
|
37
|
+
"use strict";
|
|
38
|
+
getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
39
|
+
importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// src/utils/config.ts
|
|
44
|
+
function getApiKey() {
|
|
45
|
+
const apiKey = config.get("apiKey");
|
|
46
|
+
if (!apiKey) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
'\u274C Not logged in. Please run "rigstate login <your-api-key>" first.'
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return apiKey;
|
|
52
|
+
}
|
|
53
|
+
function setApiKey(key) {
|
|
54
|
+
config.set("apiKey", key);
|
|
55
|
+
}
|
|
56
|
+
function getProjectId() {
|
|
57
|
+
return config.get("projectId");
|
|
58
|
+
}
|
|
59
|
+
function setProjectId(projectId) {
|
|
60
|
+
config.set("projectId", projectId);
|
|
61
|
+
}
|
|
62
|
+
function getApiUrl() {
|
|
63
|
+
if (process.env.RIGSTATE_API_URL) {
|
|
64
|
+
return process.env.RIGSTATE_API_URL;
|
|
65
|
+
}
|
|
66
|
+
const storedUrl = config.get("apiUrl");
|
|
67
|
+
if (storedUrl) {
|
|
68
|
+
return storedUrl;
|
|
69
|
+
}
|
|
70
|
+
return "https://app.rigstate.com";
|
|
71
|
+
}
|
|
72
|
+
var import_conf, config;
|
|
73
|
+
var init_config = __esm({
|
|
74
|
+
"src/utils/config.ts"() {
|
|
75
|
+
"use strict";
|
|
76
|
+
init_cjs_shims();
|
|
77
|
+
import_conf = __toESM(require("conf"), 1);
|
|
78
|
+
config = new import_conf.default({
|
|
79
|
+
projectName: "rigstate-cli",
|
|
80
|
+
defaults: {
|
|
81
|
+
apiUrl: "http://localhost:3000"
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// src/utils/skills-provisioner.ts
|
|
88
|
+
var skills_provisioner_exports = {};
|
|
89
|
+
__export(skills_provisioner_exports, {
|
|
90
|
+
generateSkillsDiscoveryBlock: () => generateSkillsDiscoveryBlock,
|
|
91
|
+
jitProvisionSkill: () => jitProvisionSkill,
|
|
92
|
+
provisionSkills: () => provisionSkills
|
|
93
|
+
});
|
|
94
|
+
async function provisionSkills(apiUrl, apiKey, projectId, rootDir) {
|
|
95
|
+
const skills = [];
|
|
96
|
+
try {
|
|
97
|
+
const response = await import_axios3.default.get(`${apiUrl}/api/v1/skills`, {
|
|
98
|
+
params: { project_id: projectId },
|
|
99
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
100
|
+
});
|
|
101
|
+
if (response.data.success && response.data.data) {
|
|
102
|
+
for (const dbSkill of response.data.data) {
|
|
103
|
+
skills.push({
|
|
104
|
+
name: dbSkill.name,
|
|
105
|
+
description: dbSkill.description,
|
|
106
|
+
specialist: dbSkill.specialist || "General",
|
|
107
|
+
version: dbSkill.version || "1.0.0",
|
|
108
|
+
governance: dbSkill.governance || "OPEN",
|
|
109
|
+
content: dbSkill.content
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
const msg = e.response?.data?.error || e.message;
|
|
115
|
+
console.log(import_chalk5.default.dim(` (Skills API not available: ${msg}, using core library)`));
|
|
116
|
+
}
|
|
117
|
+
if (skills.length === 0) {
|
|
118
|
+
const { getRigstateStandardSkills } = await import("@rigstate/rules-engine");
|
|
119
|
+
const coreSkills = getRigstateStandardSkills();
|
|
120
|
+
skills.push(...coreSkills);
|
|
121
|
+
}
|
|
122
|
+
const skillsDir = import_path5.default.join(rootDir, ".agent", "skills");
|
|
123
|
+
await import_promises5.default.mkdir(skillsDir, { recursive: true });
|
|
124
|
+
for (const skill of skills) {
|
|
125
|
+
const skillDir = import_path5.default.join(skillsDir, skill.name);
|
|
126
|
+
await import_promises5.default.mkdir(skillDir, { recursive: true });
|
|
127
|
+
const skillContent = `---
|
|
128
|
+
name: ${skill.name}
|
|
129
|
+
description: ${skill.description}
|
|
130
|
+
version: "${skill.version}"
|
|
131
|
+
specialist: ${skill.specialist}
|
|
132
|
+
governance: ${skill.governance}
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
${skill.content}
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
*Provisioned by Rigstate CLI. Do not modify manually.*`;
|
|
139
|
+
const skillPath = import_path5.default.join(skillDir, "SKILL.md");
|
|
140
|
+
await import_promises5.default.writeFile(skillPath, skillContent, "utf-8");
|
|
141
|
+
}
|
|
142
|
+
console.log(import_chalk5.default.green(` \u2705 Provisioned ${skills.length} skill(s) to .agent/skills/`));
|
|
143
|
+
return skills;
|
|
144
|
+
}
|
|
145
|
+
function generateSkillsDiscoveryBlock(skills) {
|
|
146
|
+
if (skills.length === 0) return "";
|
|
147
|
+
const skillBlocks = skills.map((skill) => ` <skill>
|
|
148
|
+
<name>${skill.name}</name>
|
|
149
|
+
<description>${skill.description}</description>
|
|
150
|
+
<location>.agent/skills/${skill.name}/SKILL.md</location>
|
|
151
|
+
</skill>`).join("\n");
|
|
152
|
+
return `<available_skills>
|
|
153
|
+
${skillBlocks}
|
|
154
|
+
</available_skills>`;
|
|
155
|
+
}
|
|
156
|
+
async function jitProvisionSkill(skillId, apiUrl, apiKey, projectId, rootDir) {
|
|
157
|
+
const rulesPath = import_path5.default.join(rootDir, ".cursorrules");
|
|
158
|
+
let rulesContent = "";
|
|
159
|
+
try {
|
|
160
|
+
rulesContent = await import_promises5.default.readFile(rulesPath, "utf-8");
|
|
161
|
+
} catch (e) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const isProvisioned = rulesContent.includes(`<name>${skillId}</name>`) || rulesContent.includes(`.agent/skills/${skillId}`);
|
|
165
|
+
if (isProvisioned) return false;
|
|
166
|
+
console.log(import_chalk5.default.yellow(` \u26A1 JIT PROVISIONING: Injecting ${skillId}...`));
|
|
167
|
+
try {
|
|
168
|
+
const skills = await provisionSkills(apiUrl, apiKey, projectId, rootDir);
|
|
169
|
+
const skillsBlock = generateSkillsDiscoveryBlock(skills);
|
|
170
|
+
if (rulesContent.includes("<available_skills>")) {
|
|
171
|
+
rulesContent = rulesContent.replace(
|
|
172
|
+
/<available_skills>[\s\S]*?<\/available_skills>/,
|
|
173
|
+
skillsBlock
|
|
174
|
+
);
|
|
175
|
+
} else if (rulesContent.includes("## \u{1F9E0} PROJECT CONTEXT")) {
|
|
176
|
+
const insertPoint = rulesContent.indexOf("---", rulesContent.indexOf("## \u{1F9E0} PROJECT CONTEXT"));
|
|
177
|
+
if (insertPoint !== -1) {
|
|
178
|
+
rulesContent = rulesContent.slice(0, insertPoint + 3) + "\n\n" + skillsBlock + "\n" + rulesContent.slice(insertPoint + 3);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
await import_promises5.default.writeFile(rulesPath, rulesContent, "utf-8");
|
|
182
|
+
return true;
|
|
183
|
+
} catch (e) {
|
|
184
|
+
console.log(import_chalk5.default.red(` Failed to provision skill: ${e.message}`));
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
var import_axios3, import_promises5, import_path5, import_chalk5;
|
|
189
|
+
var init_skills_provisioner = __esm({
|
|
190
|
+
"src/utils/skills-provisioner.ts"() {
|
|
191
|
+
"use strict";
|
|
192
|
+
init_cjs_shims();
|
|
193
|
+
import_axios3 = __toESM(require("axios"), 1);
|
|
194
|
+
import_promises5 = __toESM(require("fs/promises"), 1);
|
|
195
|
+
import_path5 = __toESM(require("path"), 1);
|
|
196
|
+
import_chalk5 = __toESM(require("chalk"), 1);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// src/utils/governance.ts
|
|
201
|
+
var governance_exports = {};
|
|
202
|
+
__export(governance_exports, {
|
|
203
|
+
InterventionLevel: () => InterventionLevel,
|
|
204
|
+
clearSoftLock: () => clearSoftLock,
|
|
205
|
+
getGovernanceConfig: () => getGovernanceConfig,
|
|
206
|
+
getSessionState: () => getSessionState,
|
|
207
|
+
performOverride: () => performOverride,
|
|
208
|
+
setSoftLock: () => setSoftLock
|
|
209
|
+
});
|
|
210
|
+
async function getGovernanceConfig(rootDir = process.cwd()) {
|
|
211
|
+
try {
|
|
212
|
+
const configPath = import_path6.default.join(rootDir, "rigstate.config.json");
|
|
213
|
+
const content = await import_promises6.default.readFile(configPath, "utf-8");
|
|
214
|
+
const userConfig = JSON.parse(content);
|
|
215
|
+
return {
|
|
216
|
+
governance: {
|
|
217
|
+
...DEFAULT_CONFIG.governance,
|
|
218
|
+
...userConfig.governance
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
} catch (e) {
|
|
222
|
+
return DEFAULT_CONFIG;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function getSessionState(rootDir = process.cwd()) {
|
|
226
|
+
try {
|
|
227
|
+
const sessionPath = import_path6.default.join(rootDir, ".rigstate", "session.json");
|
|
228
|
+
const content = await import_promises6.default.readFile(sessionPath, "utf-8");
|
|
229
|
+
return JSON.parse(content);
|
|
230
|
+
} catch (e) {
|
|
231
|
+
return DEFAULT_SESSION;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function setSoftLock(reason, violationId, rootDir = process.cwd()) {
|
|
235
|
+
const sessionPath = import_path6.default.join(rootDir, ".rigstate", "session.json");
|
|
236
|
+
const state = {
|
|
237
|
+
status: "SOFT_LOCK",
|
|
238
|
+
active_violation: violationId,
|
|
239
|
+
lock_reason: reason,
|
|
240
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
241
|
+
};
|
|
242
|
+
await import_promises6.default.mkdir(import_path6.default.dirname(sessionPath), { recursive: true });
|
|
243
|
+
await import_promises6.default.writeFile(sessionPath, JSON.stringify(state, null, 2), "utf-8");
|
|
244
|
+
}
|
|
245
|
+
async function clearSoftLock(rootDir = process.cwd()) {
|
|
246
|
+
const sessionPath = import_path6.default.join(rootDir, ".rigstate", "session.json");
|
|
247
|
+
const state = {
|
|
248
|
+
...DEFAULT_SESSION,
|
|
249
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
250
|
+
};
|
|
251
|
+
await import_promises6.default.mkdir(import_path6.default.dirname(sessionPath), { recursive: true });
|
|
252
|
+
await import_promises6.default.writeFile(sessionPath, JSON.stringify(state, null, 2), "utf-8");
|
|
253
|
+
}
|
|
254
|
+
async function performOverride(violationId, reason, rootDir = process.cwd()) {
|
|
255
|
+
const config2 = await getGovernanceConfig(rootDir);
|
|
256
|
+
if (!config2.governance.allow_overrides) {
|
|
257
|
+
console.log(import_chalk6.default.red("\u274C Overrides are disabled for this project."));
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
await clearSoftLock(rootDir);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
var import_promises6, import_path6, import_chalk6, InterventionLevel, DEFAULT_CONFIG, DEFAULT_SESSION;
|
|
264
|
+
var init_governance = __esm({
|
|
265
|
+
"src/utils/governance.ts"() {
|
|
266
|
+
"use strict";
|
|
267
|
+
init_cjs_shims();
|
|
268
|
+
import_promises6 = __toESM(require("fs/promises"), 1);
|
|
269
|
+
import_path6 = __toESM(require("path"), 1);
|
|
270
|
+
import_chalk6 = __toESM(require("chalk"), 1);
|
|
271
|
+
InterventionLevel = /* @__PURE__ */ ((InterventionLevel2) => {
|
|
272
|
+
InterventionLevel2[InterventionLevel2["GHOST"] = 0] = "GHOST";
|
|
273
|
+
InterventionLevel2[InterventionLevel2["NUDGE"] = 1] = "NUDGE";
|
|
274
|
+
InterventionLevel2[InterventionLevel2["SENTINEL"] = 2] = "SENTINEL";
|
|
275
|
+
return InterventionLevel2;
|
|
276
|
+
})(InterventionLevel || {});
|
|
277
|
+
DEFAULT_CONFIG = {
|
|
278
|
+
governance: {
|
|
279
|
+
intervention_level: 0 /* GHOST */,
|
|
280
|
+
allow_overrides: true
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
DEFAULT_SESSION = {
|
|
284
|
+
status: "OPEN",
|
|
285
|
+
active_violation: null,
|
|
286
|
+
lock_reason: null,
|
|
287
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// src/utils/watchdog.ts
|
|
293
|
+
var watchdog_exports = {};
|
|
294
|
+
__export(watchdog_exports, {
|
|
295
|
+
runGuardianWatchdog: () => runGuardianWatchdog
|
|
296
|
+
});
|
|
297
|
+
async function countLines(filePath) {
|
|
298
|
+
try {
|
|
299
|
+
const content = await import_promises7.default.readFile(filePath, "utf-8");
|
|
300
|
+
return content.split("\n").length;
|
|
301
|
+
} catch (e) {
|
|
302
|
+
return 0;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async function getFiles(dir, extension) {
|
|
306
|
+
const entries = await import_promises7.default.readdir(dir, { withFileTypes: true });
|
|
307
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
308
|
+
const res = import_path7.default.resolve(dir, entry.name);
|
|
309
|
+
if (entry.isDirectory()) {
|
|
310
|
+
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".next" || entry.name === "dist") return [];
|
|
311
|
+
return getFiles(res, extension);
|
|
312
|
+
} else {
|
|
313
|
+
return extension.some((ext) => entry.name.endsWith(ext)) ? res : [];
|
|
314
|
+
}
|
|
315
|
+
}));
|
|
316
|
+
return files.flat();
|
|
317
|
+
}
|
|
318
|
+
async function fetchRulesFromApi(projectId) {
|
|
319
|
+
try {
|
|
320
|
+
const apiUrl = getApiUrl();
|
|
321
|
+
const apiKey = getApiKey();
|
|
322
|
+
const response = await import_axios4.default.get(`${apiUrl}/api/v1/guardian/rules`, {
|
|
323
|
+
params: { project_id: projectId },
|
|
324
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
325
|
+
timeout: 1e4
|
|
326
|
+
});
|
|
327
|
+
if (response.data.success && response.data.data.settings) {
|
|
328
|
+
return {
|
|
329
|
+
lmax: response.data.data.settings.lmax || DEFAULT_LMAX,
|
|
330
|
+
lmaxWarning: response.data.data.settings.lmax_warning || DEFAULT_LMAX_WARNING,
|
|
331
|
+
source: "API (Dynamic)"
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
} catch (error) {
|
|
335
|
+
try {
|
|
336
|
+
const cachePath = import_path7.default.join(process.cwd(), CACHE_FILE);
|
|
337
|
+
const content = await import_promises7.default.readFile(cachePath, "utf-8");
|
|
338
|
+
const cached = JSON.parse(content);
|
|
339
|
+
if (cached.settings) {
|
|
340
|
+
return {
|
|
341
|
+
lmax: cached.settings.lmax || DEFAULT_LMAX,
|
|
342
|
+
lmaxWarning: cached.settings.lmax_warning || DEFAULT_LMAX_WARNING,
|
|
343
|
+
source: "Cache (Fallback)"
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
} catch {
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
lmax: DEFAULT_LMAX,
|
|
351
|
+
lmaxWarning: DEFAULT_LMAX_WARNING,
|
|
352
|
+
source: "Default (Hardcoded)"
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async function runGuardianWatchdog(rootPath, settings = {}, projectId) {
|
|
356
|
+
console.log(import_chalk7.default.bold("\n\u{1F6E1}\uFE0F Active Guardian Watchdog Initiated..."));
|
|
357
|
+
let lmax = settings.lmax || DEFAULT_LMAX;
|
|
358
|
+
let lmaxWarning = settings.lmax_warning || DEFAULT_LMAX_WARNING;
|
|
359
|
+
let ruleSource = settings.lmax ? "Settings (Passed)" : "Default";
|
|
360
|
+
if (projectId) {
|
|
361
|
+
const apiRules = await fetchRulesFromApi(projectId);
|
|
362
|
+
lmax = apiRules.lmax;
|
|
363
|
+
lmaxWarning = apiRules.lmaxWarning;
|
|
364
|
+
ruleSource = apiRules.source;
|
|
365
|
+
}
|
|
366
|
+
console.log(import_chalk7.default.dim(`Governance Rules: L_max=${lmax}, L_max_warning=${lmaxWarning}, Source: ${ruleSource}`));
|
|
367
|
+
const targetExtensions = [".ts", ".tsx"];
|
|
368
|
+
let scanTarget = rootPath;
|
|
369
|
+
const webSrc = import_path7.default.join(rootPath, "apps", "web", "src");
|
|
370
|
+
try {
|
|
371
|
+
await import_promises7.default.access(webSrc);
|
|
372
|
+
scanTarget = webSrc;
|
|
373
|
+
} catch {
|
|
374
|
+
}
|
|
375
|
+
console.log(import_chalk7.default.dim(`Scanning target: ${import_path7.default.relative(process.cwd(), scanTarget)}`));
|
|
376
|
+
const files = await getFiles(scanTarget, targetExtensions);
|
|
377
|
+
let violations = 0;
|
|
378
|
+
let warnings = 0;
|
|
379
|
+
const results = [];
|
|
380
|
+
for (const file of files) {
|
|
381
|
+
const lines = await countLines(file);
|
|
382
|
+
const relPath = import_path7.default.relative(rootPath, file);
|
|
383
|
+
if (lines > lmax) {
|
|
384
|
+
results.push({ file: relPath, lines, status: "VIOLATION" });
|
|
385
|
+
violations++;
|
|
386
|
+
console.log(import_chalk7.default.red(`[VIOLATION] ${relPath}: ${lines} lines (Limit: ${lmax})`));
|
|
387
|
+
} else if (lines > lmaxWarning) {
|
|
388
|
+
results.push({ file: relPath, lines, status: "WARNING" });
|
|
389
|
+
warnings++;
|
|
390
|
+
console.log(import_chalk7.default.yellow(`[WARNING] ${relPath}: ${lines} lines (Threshold: ${lmaxWarning})`));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (violations === 0 && warnings === 0) {
|
|
394
|
+
console.log(import_chalk7.default.green(`\u2714 All ${files.length} files are within governance limits.`));
|
|
395
|
+
} else {
|
|
396
|
+
console.log("\n" + import_chalk7.default.bold("Summary:"));
|
|
397
|
+
console.log(import_chalk7.default.red(`Violations: ${violations}`));
|
|
398
|
+
console.log(import_chalk7.default.yellow(`Warnings: ${warnings}`));
|
|
399
|
+
const { getGovernanceConfig: getGovernanceConfig2, setSoftLock: setSoftLock2, InterventionLevel: InterventionLevel2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
400
|
+
const { governance } = await getGovernanceConfig2(rootPath);
|
|
401
|
+
console.log(import_chalk7.default.dim(`Intervention Level: ${InterventionLevel2[governance.intervention_level] || "UNKNOWN"} (${governance.intervention_level})`));
|
|
402
|
+
if (violations > 0) {
|
|
403
|
+
console.log(import_chalk7.default.red.bold("\nCRITICAL: Governance violations detected. Immediate refactoring required."));
|
|
404
|
+
if (governance.intervention_level >= InterventionLevel2.SENTINEL) {
|
|
405
|
+
console.log(import_chalk7.default.red.bold("\u{1F6D1} SENTINEL MODE: Session SOFT_LOCKED until resolved."));
|
|
406
|
+
console.log(import_chalk7.default.red(' Run "rigstate override <id> --reason \\"...\\"" if this is an emergency.'));
|
|
407
|
+
await setSoftLock2("Sentinel Mode: Governance Violations Detected", "ARC-VIOLATION", rootPath);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (projectId) {
|
|
412
|
+
try {
|
|
413
|
+
const apiUrl = getApiUrl();
|
|
414
|
+
const apiKey = getApiKey();
|
|
415
|
+
const payloadViolations = results.filter((r) => r.status === "VIOLATION").map((v) => ({
|
|
416
|
+
uid: "V-" + Buffer.from(v.file).toString("base64").replace(/=/g, ""),
|
|
417
|
+
filePath: v.file,
|
|
418
|
+
lineCount: v.lines,
|
|
419
|
+
limitValue: lmax,
|
|
420
|
+
severity: "CRITICAL"
|
|
421
|
+
}));
|
|
422
|
+
await import_axios4.default.post(`${apiUrl}/api/v1/guardian/sync`, {
|
|
423
|
+
projectId,
|
|
424
|
+
violations: payloadViolations,
|
|
425
|
+
warnings
|
|
426
|
+
}, {
|
|
427
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
428
|
+
});
|
|
429
|
+
console.log(import_chalk7.default.dim("\u2714 Violations synced to Rigstate Cloud."));
|
|
430
|
+
} catch (e) {
|
|
431
|
+
console.log(import_chalk7.default.dim("\u26A0 Cloud sync skipped: " + (e.message || "Unknown")));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
var import_promises7, import_path7, import_chalk7, import_axios4, DEFAULT_LMAX, DEFAULT_LMAX_WARNING, CACHE_FILE;
|
|
436
|
+
var init_watchdog = __esm({
|
|
437
|
+
"src/utils/watchdog.ts"() {
|
|
438
|
+
"use strict";
|
|
439
|
+
init_cjs_shims();
|
|
440
|
+
import_promises7 = __toESM(require("fs/promises"), 1);
|
|
441
|
+
import_path7 = __toESM(require("path"), 1);
|
|
442
|
+
import_chalk7 = __toESM(require("chalk"), 1);
|
|
443
|
+
import_axios4 = __toESM(require("axios"), 1);
|
|
444
|
+
init_config();
|
|
445
|
+
DEFAULT_LMAX = 400;
|
|
446
|
+
DEFAULT_LMAX_WARNING = 350;
|
|
447
|
+
CACHE_FILE = ".rigstate/rules-cache.json";
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// src/index.ts
|
|
452
|
+
init_cjs_shims();
|
|
453
|
+
var import_commander19 = require("commander");
|
|
454
|
+
var import_chalk27 = __toESM(require("chalk"), 1);
|
|
455
|
+
|
|
456
|
+
// src/commands/login.ts
|
|
457
|
+
init_cjs_shims();
|
|
458
|
+
var import_commander = require("commander");
|
|
459
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
460
|
+
init_config();
|
|
461
|
+
function createLoginCommand() {
|
|
462
|
+
return new import_commander.Command("login").description("Authenticate with your Rigstate API key").argument("<api-key>", "Your Rigstate API key (starts with sk_)").action(async (apiKey) => {
|
|
463
|
+
try {
|
|
464
|
+
if (!apiKey || !apiKey.startsWith("sk_rigstate_")) {
|
|
465
|
+
console.error(import_chalk.default.red("\u274C Invalid API key format"));
|
|
466
|
+
console.error(import_chalk.default.dim('API keys must start with "sk_rigstate_"'));
|
|
467
|
+
process.exit(1);
|
|
468
|
+
}
|
|
469
|
+
setApiKey(apiKey);
|
|
470
|
+
console.log(import_chalk.default.green("\u2705 Successfully logged in!"));
|
|
471
|
+
console.log(
|
|
472
|
+
import_chalk.default.dim(
|
|
473
|
+
`
|
|
474
|
+
Your API key has been securely stored. You can now use "rigstate scan" to audit your code.`
|
|
475
|
+
)
|
|
476
|
+
);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
console.error(
|
|
479
|
+
import_chalk.default.red("\u274C Login failed:"),
|
|
480
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
481
|
+
);
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/commands/link.ts
|
|
488
|
+
init_cjs_shims();
|
|
489
|
+
var import_commander2 = require("commander");
|
|
490
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
491
|
+
var import_path = __toESM(require("path"), 1);
|
|
492
|
+
var import_chalk2 = __toESM(require("chalk"), 1);
|
|
493
|
+
var import_os = __toESM(require("os"), 1);
|
|
494
|
+
function createLinkCommand() {
|
|
495
|
+
return new import_commander2.Command("link").description("Link current directory to a Rigstate project").argument("<projectId>", "Project ID to link").action(async (projectId) => {
|
|
496
|
+
try {
|
|
497
|
+
const globalPath = import_path.default.join(import_os.default.homedir(), ".rigstate", "config.json");
|
|
498
|
+
const globalData = await import_promises.default.readFile(globalPath, "utf-8").catch(() => null);
|
|
499
|
+
if (globalData) {
|
|
500
|
+
const config2 = JSON.parse(globalData);
|
|
501
|
+
const cwd = process.cwd();
|
|
502
|
+
if (config2.overrides && config2.overrides[cwd]) {
|
|
503
|
+
const overrideId = config2.overrides[cwd];
|
|
504
|
+
if (overrideId !== projectId) {
|
|
505
|
+
console.warn(import_chalk2.default.yellow(`Global override detected. Enforcing project ID: ${overrideId}`));
|
|
506
|
+
projectId = overrideId;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
} catch (e) {
|
|
511
|
+
}
|
|
512
|
+
const manifestPath = import_path.default.join(process.cwd(), ".rigstate");
|
|
513
|
+
const content = {
|
|
514
|
+
project_id: projectId,
|
|
515
|
+
api_url: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
|
|
516
|
+
linked_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
517
|
+
};
|
|
518
|
+
try {
|
|
519
|
+
await import_promises.default.writeFile(manifestPath, JSON.stringify(content, null, 2), "utf-8");
|
|
520
|
+
console.log(import_chalk2.default.green(`\u2714 Linked to project ID: ${projectId}`));
|
|
521
|
+
console.log(import_chalk2.default.dim(`Created local context manifest at .rigstate`));
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.error(import_chalk2.default.red(`Failed to link project: ${error.message}`));
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/commands/scan.ts
|
|
529
|
+
init_cjs_shims();
|
|
530
|
+
var import_commander3 = require("commander");
|
|
531
|
+
var import_chalk3 = __toESM(require("chalk"), 1);
|
|
532
|
+
var import_ora = __toESM(require("ora"), 1);
|
|
533
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
534
|
+
var import_glob = require("glob");
|
|
535
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
536
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
537
|
+
init_config();
|
|
538
|
+
|
|
539
|
+
// src/utils/files.ts
|
|
540
|
+
init_cjs_shims();
|
|
541
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
542
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
543
|
+
async function readGitignore(dir) {
|
|
544
|
+
const gitignorePath = import_path2.default.join(dir, ".gitignore");
|
|
545
|
+
try {
|
|
546
|
+
const content = await import_promises2.default.readFile(gitignorePath, "utf-8");
|
|
547
|
+
return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
548
|
+
} catch (error) {
|
|
549
|
+
return [];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function shouldIgnore(filePath, patterns) {
|
|
553
|
+
const relativePath = filePath.replace(/^\.\//, "");
|
|
554
|
+
const defaultIgnores = [
|
|
555
|
+
"node_modules",
|
|
556
|
+
".git",
|
|
557
|
+
"dist",
|
|
558
|
+
"build",
|
|
559
|
+
".next",
|
|
560
|
+
".turbo",
|
|
561
|
+
"coverage",
|
|
562
|
+
".env",
|
|
563
|
+
".env.local"
|
|
564
|
+
];
|
|
565
|
+
const allPatterns = [...defaultIgnores, ...patterns];
|
|
566
|
+
for (const pattern of allPatterns) {
|
|
567
|
+
if (pattern.endsWith("/")) {
|
|
568
|
+
const dir = pattern.slice(0, -1);
|
|
569
|
+
if (relativePath.includes(`${dir}/`) || relativePath === dir) {
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
} else if (pattern.includes("*")) {
|
|
573
|
+
const regex = new RegExp(
|
|
574
|
+
"^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$"
|
|
575
|
+
);
|
|
576
|
+
if (regex.test(relativePath)) {
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
if (relativePath.includes(pattern)) {
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
function isCodeFile(filePath) {
|
|
588
|
+
const codeExtensions = [
|
|
589
|
+
".js",
|
|
590
|
+
".jsx",
|
|
591
|
+
".ts",
|
|
592
|
+
".tsx",
|
|
593
|
+
".py",
|
|
594
|
+
".java",
|
|
595
|
+
".go",
|
|
596
|
+
".rb",
|
|
597
|
+
".php",
|
|
598
|
+
".c",
|
|
599
|
+
".cpp",
|
|
600
|
+
".h",
|
|
601
|
+
".cs",
|
|
602
|
+
".swift",
|
|
603
|
+
".kt",
|
|
604
|
+
".rs",
|
|
605
|
+
".vue",
|
|
606
|
+
".svelte"
|
|
607
|
+
];
|
|
608
|
+
const ext = import_path2.default.extname(filePath).toLowerCase();
|
|
609
|
+
return codeExtensions.includes(ext);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/commands/scan.ts
|
|
613
|
+
function createScanCommand() {
|
|
614
|
+
return new import_commander3.Command("scan").description("Scan code files for security and quality issues").argument("[path]", "Directory or file to scan", ".").option("--json", "Output results as JSON").option("--project <id>", "Project ID to associate with this scan").action(async (targetPath, options) => {
|
|
615
|
+
const spinner = (0, import_ora.default)();
|
|
616
|
+
try {
|
|
617
|
+
const apiKey = getApiKey();
|
|
618
|
+
const apiUrl = getApiUrl();
|
|
619
|
+
const projectId = options.project || getProjectId();
|
|
620
|
+
if (!projectId) {
|
|
621
|
+
console.warn(
|
|
622
|
+
import_chalk3.default.yellow(
|
|
623
|
+
"\u26A0\uFE0F No project ID specified. Use --project <id> or set a default."
|
|
624
|
+
)
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
const scanPath = import_path3.default.resolve(process.cwd(), targetPath);
|
|
628
|
+
spinner.start(`Scanning ${import_chalk3.default.cyan(scanPath)}...`);
|
|
629
|
+
const gitignorePatterns = await readGitignore(scanPath);
|
|
630
|
+
const pattern = import_path3.default.join(scanPath, "**/*");
|
|
631
|
+
const allFiles = await (0, import_glob.glob)(pattern, {
|
|
632
|
+
nodir: true,
|
|
633
|
+
dot: false,
|
|
634
|
+
ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"]
|
|
635
|
+
});
|
|
636
|
+
const codeFiles = allFiles.filter((file) => {
|
|
637
|
+
const relativePath = import_path3.default.relative(scanPath, file);
|
|
638
|
+
return isCodeFile(file) && !shouldIgnore(relativePath, gitignorePatterns);
|
|
639
|
+
});
|
|
640
|
+
if (codeFiles.length === 0) {
|
|
641
|
+
spinner.warn(import_chalk3.default.yellow("No code files found to scan."));
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
spinner.text = `Found ${codeFiles.length} files. Scanning...`;
|
|
645
|
+
const results = [];
|
|
646
|
+
let totalIssues = 0;
|
|
647
|
+
const severityCounts = {};
|
|
648
|
+
for (let i = 0; i < codeFiles.length; i++) {
|
|
649
|
+
const filePath = codeFiles[i];
|
|
650
|
+
const relativePath = import_path3.default.relative(scanPath, filePath);
|
|
651
|
+
spinner.text = `Scanning ${i + 1}/${codeFiles.length}: ${relativePath}`;
|
|
652
|
+
try {
|
|
653
|
+
const content = await import_promises3.default.readFile(filePath, "utf-8");
|
|
654
|
+
const response = await import_axios.default.post(
|
|
655
|
+
`${apiUrl}/api/v1/audit`,
|
|
656
|
+
{
|
|
657
|
+
content,
|
|
658
|
+
file_path: relativePath,
|
|
659
|
+
project_id: projectId
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
headers: {
|
|
663
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
664
|
+
"Content-Type": "application/json"
|
|
665
|
+
},
|
|
666
|
+
timeout: 6e4
|
|
667
|
+
// 1 minute per file
|
|
668
|
+
}
|
|
669
|
+
);
|
|
670
|
+
const vulnerabilities = response.data.vulnerabilities || [];
|
|
671
|
+
if (vulnerabilities.length > 0) {
|
|
672
|
+
results.push({
|
|
673
|
+
id: response.data.id || relativePath,
|
|
674
|
+
file_path: relativePath,
|
|
675
|
+
issues: vulnerabilities.map((v) => ({
|
|
676
|
+
type: v.type,
|
|
677
|
+
severity: v.severity,
|
|
678
|
+
message: v.description || v.title,
|
|
679
|
+
line: v.line_number
|
|
680
|
+
}))
|
|
681
|
+
});
|
|
682
|
+
totalIssues += vulnerabilities.length;
|
|
683
|
+
vulnerabilities.forEach((v) => {
|
|
684
|
+
severityCounts[v.severity] = (severityCounts[v.severity] || 0) + 1;
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
} catch (fileError) {
|
|
688
|
+
if (import_axios.default.isAxiosError(fileError)) {
|
|
689
|
+
console.warn(import_chalk3.default.yellow(`
|
|
690
|
+
\u26A0\uFE0F Skipping ${relativePath}: ${fileError.message}`));
|
|
691
|
+
} else {
|
|
692
|
+
console.warn(import_chalk3.default.yellow(`
|
|
693
|
+
\u26A0\uFE0F Error reading ${relativePath}`));
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
spinner.succeed(import_chalk3.default.green("\u2705 Scan completed!"));
|
|
698
|
+
const aggregatedResponse = {
|
|
699
|
+
results,
|
|
700
|
+
summary: {
|
|
701
|
+
total_files: codeFiles.length,
|
|
702
|
+
total_issues: totalIssues,
|
|
703
|
+
by_severity: severityCounts
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
if (options.json) {
|
|
707
|
+
console.log(JSON.stringify(aggregatedResponse, null, 2));
|
|
708
|
+
} else {
|
|
709
|
+
printPrettyResults(aggregatedResponse);
|
|
710
|
+
}
|
|
711
|
+
} catch (error) {
|
|
712
|
+
spinner.fail(import_chalk3.default.red("\u274C Scan failed"));
|
|
713
|
+
if (import_axios.default.isAxiosError(error)) {
|
|
714
|
+
if (error.response) {
|
|
715
|
+
console.error(import_chalk3.default.red("API Error:"), error.response.data);
|
|
716
|
+
} else if (error.request) {
|
|
717
|
+
console.error(
|
|
718
|
+
import_chalk3.default.red("Network Error:"),
|
|
719
|
+
"Could not reach the API. Is the server running?"
|
|
720
|
+
);
|
|
721
|
+
} else {
|
|
722
|
+
console.error(import_chalk3.default.red("Error:"), error.message);
|
|
723
|
+
}
|
|
724
|
+
} else {
|
|
725
|
+
console.error(
|
|
726
|
+
import_chalk3.default.red("Error:"),
|
|
727
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
process.exit(1);
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
function printPrettyResults(data) {
|
|
735
|
+
const { results, summary } = data;
|
|
736
|
+
console.log("\n" + import_chalk3.default.bold("\u{1F4CA} Scan Summary"));
|
|
737
|
+
console.log(import_chalk3.default.dim("\u2500".repeat(60)));
|
|
738
|
+
console.log(`Total Files Scanned: ${import_chalk3.default.cyan(summary.total_files)}`);
|
|
739
|
+
console.log(`Total Issues Found: ${import_chalk3.default.yellow(summary.total_issues)}`);
|
|
740
|
+
if (summary.by_severity) {
|
|
741
|
+
console.log("\nIssues by Severity:");
|
|
742
|
+
Object.entries(summary.by_severity).forEach(([severity, count]) => {
|
|
743
|
+
const color = getSeverityColor(severity);
|
|
744
|
+
console.log(` ${color(`${severity}:`)} ${count}`);
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
if (results && results.length > 0) {
|
|
748
|
+
console.log("\n" + import_chalk3.default.bold("\u{1F50D} Detailed Results"));
|
|
749
|
+
console.log(import_chalk3.default.dim("\u2500".repeat(60)));
|
|
750
|
+
results.forEach((result) => {
|
|
751
|
+
if (result.issues && result.issues.length > 0) {
|
|
752
|
+
console.log(`
|
|
753
|
+
${import_chalk3.default.bold(result.file_path)}`);
|
|
754
|
+
result.issues.forEach((issue) => {
|
|
755
|
+
const severityColor = getSeverityColor(issue.severity);
|
|
756
|
+
const lineInfo = issue.line ? import_chalk3.default.dim(`:${issue.line}`) : "";
|
|
757
|
+
console.log(
|
|
758
|
+
` ${severityColor(`[${issue.severity.toUpperCase()}]`)} ${issue.type}${lineInfo}`
|
|
759
|
+
);
|
|
760
|
+
console.log(` ${import_chalk3.default.dim(issue.message)}`);
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
console.log("\n" + import_chalk3.default.dim("\u2500".repeat(60)));
|
|
766
|
+
}
|
|
767
|
+
function getSeverityColor(severity) {
|
|
768
|
+
switch (severity.toLowerCase()) {
|
|
769
|
+
case "critical":
|
|
770
|
+
return import_chalk3.default.red.bold;
|
|
771
|
+
case "high":
|
|
772
|
+
return import_chalk3.default.red;
|
|
773
|
+
case "medium":
|
|
774
|
+
return import_chalk3.default.yellow;
|
|
775
|
+
case "low":
|
|
776
|
+
return import_chalk3.default.blue;
|
|
777
|
+
case "info":
|
|
778
|
+
return import_chalk3.default.gray;
|
|
779
|
+
default:
|
|
780
|
+
return import_chalk3.default.white;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/commands/fix.ts
|
|
785
|
+
init_cjs_shims();
|
|
786
|
+
var import_commander4 = require("commander");
|
|
787
|
+
var import_chalk4 = __toESM(require("chalk"), 1);
|
|
788
|
+
var import_ora2 = __toESM(require("ora"), 1);
|
|
789
|
+
var import_axios2 = __toESM(require("axios"), 1);
|
|
790
|
+
var import_glob2 = require("glob");
|
|
791
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
792
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
793
|
+
var import_inquirer = __toESM(require("inquirer"), 1);
|
|
794
|
+
var Diff = __toESM(require("diff"), 1);
|
|
795
|
+
init_config();
|
|
796
|
+
function createFixCommand() {
|
|
797
|
+
return new import_commander4.Command("fix").description("Scan and interactively FIX detected issues using Rigstate AI").argument("[path]", "Directory or file to scan", ".").option("--project <id>", "Project ID to context-aware audit").action(async (targetPath, options) => {
|
|
798
|
+
const spinner = (0, import_ora2.default)();
|
|
799
|
+
try {
|
|
800
|
+
const apiKey = getApiKey();
|
|
801
|
+
const apiUrl = getApiUrl();
|
|
802
|
+
const projectId = options.project || getProjectId();
|
|
803
|
+
if (!projectId) {
|
|
804
|
+
console.log(import_chalk4.default.yellow("\u26A0\uFE0F Project ID is required for fixing. Using default or pass --project <id>"));
|
|
805
|
+
}
|
|
806
|
+
const scanPath = import_path4.default.resolve(process.cwd(), targetPath);
|
|
807
|
+
const gitignorePatterns = await readGitignore(scanPath);
|
|
808
|
+
const pattern = import_path4.default.join(scanPath, "**/*");
|
|
809
|
+
const allFiles = await (0, import_glob2.glob)(pattern, { nodir: true, dot: false, ignore: ["**/node_modules/**", "**/.git/**"] });
|
|
810
|
+
const codeFiles = allFiles.filter((file) => {
|
|
811
|
+
const relativePath = import_path4.default.relative(scanPath, file);
|
|
812
|
+
return isCodeFile(file) && !shouldIgnore(relativePath, gitignorePatterns);
|
|
813
|
+
});
|
|
814
|
+
if (codeFiles.length === 0) {
|
|
815
|
+
console.log(import_chalk4.default.yellow("No code files found."));
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
console.log(import_chalk4.default.bold(`
|
|
819
|
+
\u{1F9E0} Rigstate Fix Mode`));
|
|
820
|
+
console.log(import_chalk4.default.dim(`Scanning ${codeFiles.length} files with Project Context...
|
|
821
|
+
`));
|
|
822
|
+
let fixedCount = 0;
|
|
823
|
+
for (let i = 0; i < codeFiles.length; i++) {
|
|
824
|
+
const filePath = codeFiles[i];
|
|
825
|
+
const relativePath = import_path4.default.relative(scanPath, filePath);
|
|
826
|
+
spinner.start(`Analyzing ${relativePath}...`);
|
|
827
|
+
try {
|
|
828
|
+
const content = await import_promises4.default.readFile(filePath, "utf-8");
|
|
829
|
+
const response = await import_axios2.default.post(
|
|
830
|
+
`${apiUrl}/api/v1/audit`,
|
|
831
|
+
{ content, file_path: relativePath, project_id: projectId },
|
|
832
|
+
{ headers: { "Authorization": `Bearer ${apiKey}` }, timeout: 12e4 }
|
|
833
|
+
);
|
|
834
|
+
const vulnerabilities = response.data.vulnerabilities || [];
|
|
835
|
+
const fixableIssues = vulnerabilities.filter((v) => v.fixed_content);
|
|
836
|
+
if (fixableIssues.length > 0) {
|
|
837
|
+
spinner.stop();
|
|
838
|
+
console.log(`
|
|
839
|
+
${import_chalk4.default.bold(relativePath)}: Found ${fixableIssues.length} fixable issues.`);
|
|
840
|
+
for (const issue of fixableIssues) {
|
|
841
|
+
console.log(import_chalk4.default.red(`
|
|
842
|
+
[${issue.type}] ${issue.title}`));
|
|
843
|
+
console.log(import_chalk4.default.dim(issue.suggestion || issue.message));
|
|
844
|
+
const diff = Diff.createTwoFilesPatch(relativePath, relativePath, content, issue.fixed_content, "Current", "Fixed");
|
|
845
|
+
console.log("\n" + diff.split("\n").slice(0, 15).join("\n") + (diff.split("\n").length > 15 ? "\n..." : ""));
|
|
846
|
+
const { apply } = await import_inquirer.default.prompt([{
|
|
847
|
+
type: "confirm",
|
|
848
|
+
name: "apply",
|
|
849
|
+
message: `Apply this fix to ${import_chalk4.default.cyan(relativePath)}?`,
|
|
850
|
+
default: true
|
|
851
|
+
}]);
|
|
852
|
+
if (apply) {
|
|
853
|
+
await import_promises4.default.writeFile(filePath, issue.fixed_content);
|
|
854
|
+
console.log(import_chalk4.default.green(`\u2705 Fixed applied!`));
|
|
855
|
+
fixedCount++;
|
|
856
|
+
if (issue.related_step_id) {
|
|
857
|
+
const { completeStep } = await import_inquirer.default.prompt([{
|
|
858
|
+
type: "confirm",
|
|
859
|
+
name: "completeStep",
|
|
860
|
+
message: `Frank thinks this fix completes a Roadmap Step. Mark as COMPLETED in Rigstate?`,
|
|
861
|
+
default: true
|
|
862
|
+
}]);
|
|
863
|
+
if (completeStep) {
|
|
864
|
+
try {
|
|
865
|
+
await import_axios2.default.post(
|
|
866
|
+
`${apiUrl}/api/v1/roadmap/update-status`,
|
|
867
|
+
{ step_id: issue.related_step_id, status: "COMPLETED", project_id: projectId },
|
|
868
|
+
{ headers: { "Authorization": `Bearer ${apiKey}` } }
|
|
869
|
+
);
|
|
870
|
+
console.log(import_chalk4.default.green(`\u{1F680} Roadmap updated! Mission Control is in sync.`));
|
|
871
|
+
} catch (err) {
|
|
872
|
+
console.error(import_chalk4.default.yellow(`Failed to update roadmap: ${err.message}`));
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
break;
|
|
877
|
+
} else {
|
|
878
|
+
console.log(import_chalk4.default.dim("Skipped."));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
} else {
|
|
882
|
+
spinner.text = `Checked ${relativePath} (No auto-fixes)`;
|
|
883
|
+
}
|
|
884
|
+
} catch (e) {
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
spinner.stop();
|
|
888
|
+
console.log(import_chalk4.default.bold.green(`
|
|
889
|
+
|
|
890
|
+
\u{1F680} Fix session complete!`));
|
|
891
|
+
console.log(`Frank fixed ${fixedCount} detected issues.`);
|
|
892
|
+
console.log(import_chalk4.default.dim(`Run 'rigstate scan' to verify remaining issues.`));
|
|
893
|
+
} catch (error) {
|
|
894
|
+
spinner.fail("Fix session failed");
|
|
895
|
+
console.error(error.message);
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// src/commands/sync.ts
|
|
901
|
+
init_cjs_shims();
|
|
902
|
+
var import_commander5 = require("commander");
|
|
903
|
+
var import_chalk8 = __toESM(require("chalk"), 1);
|
|
904
|
+
var import_ora3 = __toESM(require("ora"), 1);
|
|
905
|
+
init_config();
|
|
906
|
+
var import_axios5 = __toESM(require("axios"), 1);
|
|
907
|
+
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
908
|
+
var import_path8 = __toESM(require("path"), 1);
|
|
909
|
+
function createSyncCommand() {
|
|
910
|
+
const sync = new import_commander5.Command("sync");
|
|
911
|
+
sync.description("Synchronize local state with Rigstate Cloud").option("-p, --project <id>", "Specify Project ID (saves to config automatically)").action(async (options) => {
|
|
912
|
+
const spinner = (0, import_ora3.default)("Synchronizing project state...").start();
|
|
913
|
+
try {
|
|
914
|
+
let apiKey;
|
|
915
|
+
try {
|
|
916
|
+
apiKey = getApiKey();
|
|
917
|
+
} catch (e) {
|
|
918
|
+
spinner.fail('Not authenticated. Run "rigstate login" first.');
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
let projectId = options.project;
|
|
922
|
+
if (!projectId) {
|
|
923
|
+
try {
|
|
924
|
+
const manifestPath = import_path8.default.join(process.cwd(), ".rigstate");
|
|
925
|
+
const manifestContent = await import_promises8.default.readFile(manifestPath, "utf-8");
|
|
926
|
+
const manifest = JSON.parse(manifestContent);
|
|
927
|
+
if (manifest.project_id) projectId = manifest.project_id;
|
|
928
|
+
} catch (e) {
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (!projectId) projectId = getProjectId();
|
|
932
|
+
if (options.project) {
|
|
933
|
+
setProjectId(options.project);
|
|
934
|
+
}
|
|
935
|
+
if (!projectId) {
|
|
936
|
+
spinner.fail("No project context found.\n Run with --project <id> once to save context.");
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
const apiUrl = getApiUrl();
|
|
940
|
+
const response = await import_axios5.default.get(`${apiUrl}/api/v1/roadmap`, {
|
|
941
|
+
params: { project_id: projectId },
|
|
942
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
943
|
+
});
|
|
944
|
+
if (!response.data.success) {
|
|
945
|
+
throw new Error(response.data.error || "Unknown API failure");
|
|
946
|
+
}
|
|
947
|
+
const { roadmap, project } = response.data.data;
|
|
948
|
+
const timestamp = response.data.timestamp;
|
|
949
|
+
const targetPath = import_path8.default.join(process.cwd(), "roadmap.json");
|
|
950
|
+
const fileContent = JSON.stringify({
|
|
951
|
+
project,
|
|
952
|
+
last_synced: timestamp,
|
|
953
|
+
roadmap
|
|
954
|
+
}, null, 2);
|
|
955
|
+
await import_promises8.default.writeFile(targetPath, fileContent, "utf-8");
|
|
956
|
+
try {
|
|
957
|
+
const manifestPath = import_path8.default.join(process.cwd(), ".rigstate");
|
|
958
|
+
const manifestContent = {
|
|
959
|
+
project_id: projectId,
|
|
960
|
+
project_name: project,
|
|
961
|
+
last_synced: timestamp,
|
|
962
|
+
api_url: apiUrl
|
|
963
|
+
};
|
|
964
|
+
await import_promises8.default.writeFile(manifestPath, JSON.stringify(manifestContent, null, 2), "utf-8");
|
|
965
|
+
} catch (e) {
|
|
966
|
+
}
|
|
967
|
+
console.log(import_chalk8.default.bold("\n\u{1F9E0} Agent Skills Provisioning..."));
|
|
968
|
+
try {
|
|
969
|
+
const { provisionSkills: provisionSkills2, generateSkillsDiscoveryBlock: generateSkillsDiscoveryBlock2 } = await Promise.resolve().then(() => (init_skills_provisioner(), skills_provisioner_exports));
|
|
970
|
+
const skills = await provisionSkills2(apiUrl, apiKey, projectId, process.cwd());
|
|
971
|
+
const cursorRulesPath = import_path8.default.join(process.cwd(), ".cursorrules");
|
|
972
|
+
try {
|
|
973
|
+
let rulesContent = await import_promises8.default.readFile(cursorRulesPath, "utf-8");
|
|
974
|
+
const skillsBlock = generateSkillsDiscoveryBlock2(skills);
|
|
975
|
+
if (rulesContent.includes("<available_skills>")) {
|
|
976
|
+
rulesContent = rulesContent.replace(
|
|
977
|
+
/<available_skills>[\s\S]*?<\/available_skills>/,
|
|
978
|
+
skillsBlock
|
|
979
|
+
);
|
|
980
|
+
} else if (rulesContent.includes("## \u{1F9E0} PROJECT CONTEXT")) {
|
|
981
|
+
const insertPoint = rulesContent.indexOf("---", rulesContent.indexOf("## \u{1F9E0} PROJECT CONTEXT"));
|
|
982
|
+
if (insertPoint !== -1) {
|
|
983
|
+
rulesContent = rulesContent.slice(0, insertPoint + 3) + "\n\n" + skillsBlock + "\n" + rulesContent.slice(insertPoint + 3);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
await import_promises8.default.writeFile(cursorRulesPath, rulesContent, "utf-8");
|
|
987
|
+
console.log(import_chalk8.default.dim(` Updated .cursorrules with skills discovery block`));
|
|
988
|
+
} catch (e) {
|
|
989
|
+
}
|
|
990
|
+
} catch (e) {
|
|
991
|
+
console.log(import_chalk8.default.yellow(` \u26A0 Skills provisioning skipped: ${e.message}`));
|
|
992
|
+
}
|
|
993
|
+
try {
|
|
994
|
+
const logPath = import_path8.default.join(process.cwd(), ".rigstate", "logs", "last_execution.json");
|
|
995
|
+
try {
|
|
996
|
+
const logContent = await import_promises8.default.readFile(logPath, "utf-8");
|
|
997
|
+
const logData = JSON.parse(logContent);
|
|
998
|
+
if (logData.task_summary) {
|
|
999
|
+
await import_axios5.default.post(`${apiUrl}/api/v1/execution-logs`, {
|
|
1000
|
+
project_id: projectId,
|
|
1001
|
+
...logData,
|
|
1002
|
+
agent_role: process.env.RIGSTATE_MODE === "SUPERVISOR" ? "SUPERVISOR" : "WORKER"
|
|
1003
|
+
}, {
|
|
1004
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1005
|
+
});
|
|
1006
|
+
await import_promises8.default.unlink(logPath);
|
|
1007
|
+
console.log(import_chalk8.default.dim(`\u2714 Mission Report uploaded.`));
|
|
1008
|
+
}
|
|
1009
|
+
} catch (e) {
|
|
1010
|
+
if (e.code !== "ENOENT") {
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
} catch (e) {
|
|
1014
|
+
}
|
|
1015
|
+
spinner.succeed(import_chalk8.default.green(`Synced ${roadmap.length} roadmap steps for project "${project}"`));
|
|
1016
|
+
console.log(import_chalk8.default.dim(`Local files updated: roadmap.json`));
|
|
1017
|
+
const { runGuardianWatchdog: runGuardianWatchdog2 } = await Promise.resolve().then(() => (init_watchdog(), watchdog_exports));
|
|
1018
|
+
const settings = response.data.data.settings || {};
|
|
1019
|
+
await runGuardianWatchdog2(process.cwd(), settings, projectId);
|
|
1020
|
+
console.log(import_chalk8.default.bold("\n\u{1F4E1} Agent Bridge Heartbeat..."));
|
|
1021
|
+
try {
|
|
1022
|
+
const bridgeResponse = await import_axios5.default.get(`${apiUrl}/api/v1/agent/bridge`, {
|
|
1023
|
+
params: { project_id: projectId },
|
|
1024
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1025
|
+
});
|
|
1026
|
+
if (bridgeResponse.data.success) {
|
|
1027
|
+
const tasks = bridgeResponse.data.tasks;
|
|
1028
|
+
const pending = tasks.filter((t) => t.status === "PENDING");
|
|
1029
|
+
const approved = tasks.filter((t) => t.status === "APPROVED");
|
|
1030
|
+
if (pending.length > 0 || approved.length > 0) {
|
|
1031
|
+
console.log(import_chalk8.default.yellow(`\u26A0 Bridge Alert: ${pending.length} pending, ${approved.length} approved tasks found.`));
|
|
1032
|
+
console.log(import_chalk8.default.dim('Run "rigstate fix" to process these tasks or ensure your IDE MCP server is active.'));
|
|
1033
|
+
} else {
|
|
1034
|
+
console.log(import_chalk8.default.green("\u2714 Heartbeat healthy. No pending bridge tasks."));
|
|
1035
|
+
}
|
|
1036
|
+
const pings = pending.filter((t) => t.proposal?.startsWith("ping"));
|
|
1037
|
+
for (const ping of pings) {
|
|
1038
|
+
await import_axios5.default.post(`${apiUrl}/api/v1/agent/bridge`, {
|
|
1039
|
+
bridge_id: ping.id,
|
|
1040
|
+
status: "COMPLETED",
|
|
1041
|
+
summary: "Pong! CLI Sync Heartbeat confirmed."
|
|
1042
|
+
}, {
|
|
1043
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1044
|
+
});
|
|
1045
|
+
console.log(import_chalk8.default.cyan(`\u{1F3D3} Pong! Acknowledged heartbeat signal [${ping.id}]`));
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
} catch (e) {
|
|
1049
|
+
console.log(import_chalk8.default.yellow(`\u26A0 Could not verify Bridge status: ${e.message}`));
|
|
1050
|
+
}
|
|
1051
|
+
if (options.project) {
|
|
1052
|
+
console.log(import_chalk8.default.blue(`Project context saved. Future commands will use this project.`));
|
|
1053
|
+
}
|
|
1054
|
+
try {
|
|
1055
|
+
const migrationDir = import_path8.default.join(process.cwd(), "supabase", "migrations");
|
|
1056
|
+
const files = await import_promises8.default.readdir(migrationDir);
|
|
1057
|
+
const sqlFiles = files.filter((f) => f.endsWith(".sql")).sort();
|
|
1058
|
+
if (sqlFiles.length > 0) {
|
|
1059
|
+
const latestMigration = sqlFiles[sqlFiles.length - 1];
|
|
1060
|
+
console.log(import_chalk8.default.dim(`
|
|
1061
|
+
\u{1F6E1} Migration Guard:`));
|
|
1062
|
+
console.log(import_chalk8.default.dim(` Latest Local: ${latestMigration}`));
|
|
1063
|
+
console.log(import_chalk8.default.yellow(` \u26A0 Ensure DB schema matches this version. CLI cannot verify Remote RLS policies directly.`));
|
|
1064
|
+
}
|
|
1065
|
+
} catch (e) {
|
|
1066
|
+
}
|
|
1067
|
+
try {
|
|
1068
|
+
const vaultResponse = await import_axios5.default.post(
|
|
1069
|
+
`${apiUrl}/api/v1/vault/sync`,
|
|
1070
|
+
{ project_id: projectId },
|
|
1071
|
+
{ headers: { Authorization: `Bearer ${apiKey}` } }
|
|
1072
|
+
);
|
|
1073
|
+
if (vaultResponse.data.success) {
|
|
1074
|
+
const vaultContent = vaultResponse.data.data.content || "";
|
|
1075
|
+
const localEnvPath = import_path8.default.join(process.cwd(), ".env.local");
|
|
1076
|
+
let localContent = "";
|
|
1077
|
+
try {
|
|
1078
|
+
localContent = await import_promises8.default.readFile(localEnvPath, "utf-8");
|
|
1079
|
+
} catch (e) {
|
|
1080
|
+
}
|
|
1081
|
+
if (vaultContent.trim() !== localContent.trim()) {
|
|
1082
|
+
console.log(import_chalk8.default.bold("\n\u{1F510} Sovereign Foundation (Vault):"));
|
|
1083
|
+
console.log(import_chalk8.default.yellow(" Status: Drift Detected / Update Available"));
|
|
1084
|
+
const { syncVault } = await import("inquirer").then((m) => m.default.prompt([{
|
|
1085
|
+
type: "confirm",
|
|
1086
|
+
name: "syncVault",
|
|
1087
|
+
message: "Synchronize local .env.local with Vault secrets?",
|
|
1088
|
+
default: false
|
|
1089
|
+
}]));
|
|
1090
|
+
if (syncVault) {
|
|
1091
|
+
await import_promises8.default.writeFile(localEnvPath, vaultContent, "utf-8");
|
|
1092
|
+
console.log(import_chalk8.default.green(" \u2705 .env.local synchronized with Vault."));
|
|
1093
|
+
} else {
|
|
1094
|
+
console.log(import_chalk8.default.dim(" Skipped vault sync."));
|
|
1095
|
+
}
|
|
1096
|
+
} else {
|
|
1097
|
+
console.log(import_chalk8.default.dim("\n\u{1F510} Sovereign Foundation: Synced."));
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
} catch (e) {
|
|
1101
|
+
}
|
|
1102
|
+
console.log(import_chalk8.default.dim("\n\u{1F6E1}\uFE0F System Integrity Check..."));
|
|
1103
|
+
await checkSystemIntegrity(apiUrl, apiKey, projectId);
|
|
1104
|
+
} catch (error) {
|
|
1105
|
+
if (import_axios5.default.isAxiosError(error)) {
|
|
1106
|
+
const message = error.response?.data?.error || error.message;
|
|
1107
|
+
spinner.fail(import_chalk8.default.red(`Sync failed: ${message}`));
|
|
1108
|
+
} else {
|
|
1109
|
+
spinner.fail(import_chalk8.default.red("Sync failed: " + (error.message || "Unknown error")));
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
return sync;
|
|
1114
|
+
}
|
|
1115
|
+
async function checkSystemIntegrity(apiUrl, apiKey, projectId) {
|
|
1116
|
+
try {
|
|
1117
|
+
const response = await import_axios5.default.get(`${apiUrl}/api/v1/system/integrity`, {
|
|
1118
|
+
params: { project_id: projectId },
|
|
1119
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1120
|
+
});
|
|
1121
|
+
if (response.data.success) {
|
|
1122
|
+
const { migrations, rls, guardian_violations } = response.data.data;
|
|
1123
|
+
if (migrations) {
|
|
1124
|
+
if (migrations.in_sync) {
|
|
1125
|
+
console.log(import_chalk8.default.green(` \u2705 Migrations synced (${migrations.count} versions)`));
|
|
1126
|
+
} else {
|
|
1127
|
+
console.log(import_chalk8.default.red(` \u{1F6D1} CRITICAL: DB Schema out of sync! ${migrations.missing?.length || 0} migrations not applied.`));
|
|
1128
|
+
if (migrations.missing?.length > 0) {
|
|
1129
|
+
console.log(import_chalk8.default.dim(` Missing: ${migrations.missing.slice(0, 3).join(", ")}${migrations.missing.length > 3 ? "..." : ""}`));
|
|
1130
|
+
}
|
|
1131
|
+
console.log(import_chalk8.default.yellow(` Run 'supabase db push' or apply migrations immediately.`));
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
if (rls) {
|
|
1135
|
+
if (rls.all_secured) {
|
|
1136
|
+
console.log(import_chalk8.default.green(` \u2705 RLS Audit Passed (${rls.table_count} tables secured)`));
|
|
1137
|
+
} else {
|
|
1138
|
+
console.log(import_chalk8.default.red(` \u{1F6D1} CRITICAL: Security Vulnerability! ${rls.unsecured?.length || 0} tables have RLS disabled.`));
|
|
1139
|
+
rls.unsecured?.forEach((table) => {
|
|
1140
|
+
console.log(import_chalk8.default.red(` - ${table}`));
|
|
1141
|
+
});
|
|
1142
|
+
console.log(import_chalk8.default.yellow(' Enable RLS immediately: ALTER TABLE "table" ENABLE ROW LEVEL SECURITY;'));
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (guardian_violations) {
|
|
1146
|
+
if (guardian_violations.count === 0) {
|
|
1147
|
+
console.log(import_chalk8.default.green(" \u2705 Guardian: No active violations"));
|
|
1148
|
+
} else {
|
|
1149
|
+
console.log(import_chalk8.default.yellow(` \u26A0\uFE0F Guardian: ${guardian_violations.count} active violations`));
|
|
1150
|
+
console.log(import_chalk8.default.dim(' Run "rigstate check" for details.'));
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
} catch (e) {
|
|
1155
|
+
console.log(import_chalk8.default.dim(" (System integrity check skipped - API endpoint not available)"));
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// src/commands/init.ts
|
|
1160
|
+
init_cjs_shims();
|
|
1161
|
+
var import_commander6 = require("commander");
|
|
1162
|
+
var import_chalk9 = __toESM(require("chalk"), 1);
|
|
1163
|
+
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
1164
|
+
var import_path10 = __toESM(require("path"), 1);
|
|
1165
|
+
var import_ora4 = __toESM(require("ora"), 1);
|
|
1166
|
+
var import_child_process = require("child_process");
|
|
1167
|
+
|
|
1168
|
+
// src/utils/manifest.ts
|
|
1169
|
+
init_cjs_shims();
|
|
1170
|
+
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
1171
|
+
var import_path9 = __toESM(require("path"), 1);
|
|
1172
|
+
async function loadManifest() {
|
|
1173
|
+
try {
|
|
1174
|
+
const manifestPath = import_path9.default.join(process.cwd(), ".rigstate");
|
|
1175
|
+
const content = await import_promises9.default.readFile(manifestPath, "utf-8");
|
|
1176
|
+
return JSON.parse(content);
|
|
1177
|
+
} catch {
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/commands/init.ts
|
|
1183
|
+
init_config();
|
|
1184
|
+
var import_axios6 = __toESM(require("axios"), 1);
|
|
1185
|
+
function createInitCommand() {
|
|
1186
|
+
return new import_commander6.Command("init").description("Initialize or link a Rigstate project (interactive mode available)").argument("[project-id]", "ID of the project to link (optional, prompts if not provided)").option("-f, --force", "Overwrite existing .cursorrules file").option("--rules-only", "Only regenerate .cursorrules without interactive setup").action(async (projectIdArg, options) => {
|
|
1187
|
+
const spinner = (0, import_ora4.default)("Initializing Rigstate project...").start();
|
|
1188
|
+
let apiKey;
|
|
1189
|
+
try {
|
|
1190
|
+
apiKey = getApiKey();
|
|
1191
|
+
} catch (e) {
|
|
1192
|
+
spinner.fail(import_chalk9.default.red('Not authenticated. Run "rigstate login" first.'));
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
const apiUrl = getApiUrl();
|
|
1196
|
+
let projectId = projectIdArg;
|
|
1197
|
+
try {
|
|
1198
|
+
if (options.rulesOnly) {
|
|
1199
|
+
const manifest = await loadManifest();
|
|
1200
|
+
if (!manifest) {
|
|
1201
|
+
spinner.fail('No .rigstate manifest found. Run "rigstate init" first.');
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
projectId = manifest.project_id;
|
|
1205
|
+
await generateRules(apiUrl, apiKey, projectId, options.force, spinner);
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
if (!projectId) {
|
|
1209
|
+
spinner.stop();
|
|
1210
|
+
const inquirer4 = (await import("inquirer")).default;
|
|
1211
|
+
spinner.start("Fetching your projects...");
|
|
1212
|
+
let projects = [];
|
|
1213
|
+
try {
|
|
1214
|
+
const projectsResponse = await import_axios6.default.get(`${apiUrl}/api/v1/projects`, {
|
|
1215
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1216
|
+
});
|
|
1217
|
+
if (projectsResponse.data.success) {
|
|
1218
|
+
projects = projectsResponse.data.data.projects || [];
|
|
1219
|
+
}
|
|
1220
|
+
} catch (e) {
|
|
1221
|
+
spinner.info("Projects API not available. Using manual entry mode.");
|
|
1222
|
+
}
|
|
1223
|
+
spinner.stop();
|
|
1224
|
+
if (projects.length === 0) {
|
|
1225
|
+
const { manualProjectId } = await inquirer4.prompt([
|
|
1226
|
+
{
|
|
1227
|
+
type: "input",
|
|
1228
|
+
name: "manualProjectId",
|
|
1229
|
+
message: "Enter Project ID (from Rigstate dashboard):",
|
|
1230
|
+
validate: (input) => input.trim() ? true : "Project ID is required"
|
|
1231
|
+
}
|
|
1232
|
+
]);
|
|
1233
|
+
projectId = manualProjectId;
|
|
1234
|
+
} else {
|
|
1235
|
+
const choices = [
|
|
1236
|
+
{ name: "\u2795 Create New Project", value: "NEW" },
|
|
1237
|
+
new inquirer4.Separator()
|
|
1238
|
+
];
|
|
1239
|
+
projects.forEach((p) => {
|
|
1240
|
+
choices.push({
|
|
1241
|
+
name: `[${p.organization_name || "Personal"}] ${p.name} (${p.status})`,
|
|
1242
|
+
value: p.id
|
|
1243
|
+
});
|
|
1244
|
+
});
|
|
1245
|
+
const { selectedId } = await inquirer4.prompt([
|
|
1246
|
+
{
|
|
1247
|
+
type: "list",
|
|
1248
|
+
name: "selectedId",
|
|
1249
|
+
message: "Select a project to link:",
|
|
1250
|
+
choices,
|
|
1251
|
+
pageSize: 15
|
|
1252
|
+
}
|
|
1253
|
+
]);
|
|
1254
|
+
if (selectedId === "NEW") {
|
|
1255
|
+
const { newName } = await inquirer4.prompt([
|
|
1256
|
+
{
|
|
1257
|
+
type: "input",
|
|
1258
|
+
name: "newName",
|
|
1259
|
+
message: "Enter project name:",
|
|
1260
|
+
validate: (input) => input.trim() ? true : "Name is required"
|
|
1261
|
+
}
|
|
1262
|
+
]);
|
|
1263
|
+
spinner.start("Fetching organizations...");
|
|
1264
|
+
let orgs = [];
|
|
1265
|
+
try {
|
|
1266
|
+
const orgsResponse = await import_axios6.default.get(`${apiUrl}/api/v1/organizations`, {
|
|
1267
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1268
|
+
});
|
|
1269
|
+
orgs = orgsResponse.data.data?.organizations || [];
|
|
1270
|
+
} catch (e) {
|
|
1271
|
+
}
|
|
1272
|
+
spinner.stop();
|
|
1273
|
+
let selectedOrgId = orgs[0]?.id;
|
|
1274
|
+
if (orgs.length > 1) {
|
|
1275
|
+
const { orgId } = await inquirer4.prompt([
|
|
1276
|
+
{
|
|
1277
|
+
type: "list",
|
|
1278
|
+
name: "orgId",
|
|
1279
|
+
message: "Which organization does this belong to?",
|
|
1280
|
+
choices: orgs.map((org) => ({
|
|
1281
|
+
name: `${org.name} (${org.role || "member"})`,
|
|
1282
|
+
value: org.id
|
|
1283
|
+
}))
|
|
1284
|
+
}
|
|
1285
|
+
]);
|
|
1286
|
+
selectedOrgId = orgId;
|
|
1287
|
+
}
|
|
1288
|
+
if (!selectedOrgId) {
|
|
1289
|
+
console.log(import_chalk9.default.yellow("No organization available. Please create the project via the Rigstate dashboard."));
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
spinner.start("Creating new project...");
|
|
1293
|
+
try {
|
|
1294
|
+
const createResponse = await import_axios6.default.post(`${apiUrl}/api/v1/projects`, {
|
|
1295
|
+
name: newName,
|
|
1296
|
+
organization_id: selectedOrgId
|
|
1297
|
+
}, {
|
|
1298
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1299
|
+
});
|
|
1300
|
+
if (!createResponse.data.success) {
|
|
1301
|
+
spinner.fail(import_chalk9.default.red("Failed to create project: " + createResponse.data.error));
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
projectId = createResponse.data.data.project.id;
|
|
1305
|
+
spinner.succeed(import_chalk9.default.green(`Created new project: ${newName}`));
|
|
1306
|
+
} catch (e) {
|
|
1307
|
+
spinner.fail(import_chalk9.default.red("Project creation API not available. Please create via dashboard."));
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
} else {
|
|
1311
|
+
projectId = selectedId;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
spinner.start(`Linking to project ID: ${projectId}...`);
|
|
1315
|
+
}
|
|
1316
|
+
setProjectId(projectId);
|
|
1317
|
+
const manifestPath = import_path10.default.join(process.cwd(), ".rigstate");
|
|
1318
|
+
const manifestContent = {
|
|
1319
|
+
project_id: projectId,
|
|
1320
|
+
last_linked: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1321
|
+
api_url: apiUrl
|
|
1322
|
+
};
|
|
1323
|
+
await import_promises10.default.writeFile(manifestPath, JSON.stringify(manifestContent, null, 2), "utf-8");
|
|
1324
|
+
try {
|
|
1325
|
+
await import_promises10.default.access(".git");
|
|
1326
|
+
} catch {
|
|
1327
|
+
spinner.text = "Initializing git repository...";
|
|
1328
|
+
(0, import_child_process.execSync)("git init", { stdio: "ignore" });
|
|
1329
|
+
}
|
|
1330
|
+
spinner.succeed(import_chalk9.default.green(`\u2705 Linked to project: ${projectId}`));
|
|
1331
|
+
await generateRules(apiUrl, apiKey, projectId, options.force, spinner);
|
|
1332
|
+
console.log("");
|
|
1333
|
+
console.log(import_chalk9.default.blue("Next steps:"));
|
|
1334
|
+
console.log(import_chalk9.default.dim(" rigstate sync - Sync roadmap and context"));
|
|
1335
|
+
console.log(import_chalk9.default.dim(" rigstate watch - Start development loop"));
|
|
1336
|
+
console.log(import_chalk9.default.dim(" rigstate focus - Get current task"));
|
|
1337
|
+
} catch (e) {
|
|
1338
|
+
spinner.fail(import_chalk9.default.red("Initialization failed: " + e.message));
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
async function generateRules(apiUrl, apiKey, projectId, force, spinner) {
|
|
1343
|
+
spinner.start("Generating AI rules (MDC + AGENTS.md)...");
|
|
1344
|
+
try {
|
|
1345
|
+
const response = await import_axios6.default.post(`${apiUrl}/api/v1/rules/generate`, {
|
|
1346
|
+
project_id: projectId
|
|
1347
|
+
}, {
|
|
1348
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1349
|
+
});
|
|
1350
|
+
if (response.data.success || response.data.files) {
|
|
1351
|
+
const files = response.data.files || [];
|
|
1352
|
+
if (files.length === 0 && response.data.rules) {
|
|
1353
|
+
const rulesPath = import_path10.default.join(process.cwd(), ".cursorrules");
|
|
1354
|
+
await import_promises10.default.writeFile(rulesPath, response.data.rules, "utf-8");
|
|
1355
|
+
spinner.succeed(import_chalk9.default.green("\u2714 Generated .cursorrules (legacy mode)"));
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
for (const file of files) {
|
|
1359
|
+
const targetPath = import_path10.default.join(process.cwd(), file.path);
|
|
1360
|
+
const targetDir = import_path10.default.dirname(targetPath);
|
|
1361
|
+
await import_promises10.default.mkdir(targetDir, { recursive: true });
|
|
1362
|
+
try {
|
|
1363
|
+
await import_promises10.default.access(targetPath);
|
|
1364
|
+
if (!force && !file.path.startsWith(".cursor/rules/")) {
|
|
1365
|
+
console.log(import_chalk9.default.dim(` ${file.path} already exists. Skipping.`));
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
} catch {
|
|
1369
|
+
}
|
|
1370
|
+
await import_promises10.default.writeFile(targetPath, file.content, "utf-8");
|
|
1371
|
+
}
|
|
1372
|
+
if (files.length > 0) {
|
|
1373
|
+
const legacyPath = import_path10.default.join(process.cwd(), ".cursorrules");
|
|
1374
|
+
try {
|
|
1375
|
+
const stats = await import_promises10.default.stat(legacyPath);
|
|
1376
|
+
if (stats.isFile()) {
|
|
1377
|
+
await import_promises10.default.rename(legacyPath, `${legacyPath}.bak`);
|
|
1378
|
+
console.log(import_chalk9.default.dim(" Moved legacy .cursorrules to .cursorrules.bak"));
|
|
1379
|
+
}
|
|
1380
|
+
} catch (e) {
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
spinner.succeed(import_chalk9.default.green(`\u2714 Generated ${files.length} rule files (v${response.data.version || "3.0"})`));
|
|
1384
|
+
} else {
|
|
1385
|
+
spinner.info(import_chalk9.default.dim(" Rules generation skipped (API response invalid)"));
|
|
1386
|
+
}
|
|
1387
|
+
} catch (e) {
|
|
1388
|
+
spinner.info(import_chalk9.default.dim(` Rules generation failed: ${e.message}`));
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// src/commands/check.ts
|
|
1393
|
+
init_cjs_shims();
|
|
1394
|
+
var import_commander7 = require("commander");
|
|
1395
|
+
var import_chalk11 = __toESM(require("chalk"), 1);
|
|
1396
|
+
var import_ora5 = __toESM(require("ora"), 1);
|
|
1397
|
+
var import_axios7 = __toESM(require("axios"), 1);
|
|
1398
|
+
var import_glob3 = require("glob");
|
|
1399
|
+
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
1400
|
+
var import_path12 = __toESM(require("path"), 1);
|
|
1401
|
+
var import_child_process2 = require("child_process");
|
|
1402
|
+
init_config();
|
|
1403
|
+
|
|
1404
|
+
// src/utils/rule-engine.ts
|
|
1405
|
+
init_cjs_shims();
|
|
1406
|
+
var import_promises11 = __toESM(require("fs/promises"), 1);
|
|
1407
|
+
var import_path11 = __toESM(require("path"), 1);
|
|
1408
|
+
var import_chalk10 = __toESM(require("chalk"), 1);
|
|
1409
|
+
async function checkFile(filePath, rules, rootPath) {
|
|
1410
|
+
const violations = [];
|
|
1411
|
+
const relativePath = import_path11.default.relative(rootPath, filePath);
|
|
1412
|
+
try {
|
|
1413
|
+
const content = await import_promises11.default.readFile(filePath, "utf-8");
|
|
1414
|
+
const lines = content.split("\n");
|
|
1415
|
+
for (const rule of rules) {
|
|
1416
|
+
const ruleViolations = await evaluateRule(rule, content, lines, relativePath);
|
|
1417
|
+
violations.push(...ruleViolations);
|
|
1418
|
+
}
|
|
1419
|
+
} catch (error) {
|
|
1420
|
+
violations.push({
|
|
1421
|
+
file: relativePath,
|
|
1422
|
+
rule: "FILE_READ_ERROR",
|
|
1423
|
+
ruleType: "SYSTEM",
|
|
1424
|
+
severity: "warning",
|
|
1425
|
+
message: `Could not read file: ${error.message}`
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
return {
|
|
1429
|
+
file: relativePath,
|
|
1430
|
+
violations,
|
|
1431
|
+
passed: violations.length === 0
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
async function evaluateRule(rule, content, lines, filePath) {
|
|
1435
|
+
const violations = [];
|
|
1436
|
+
switch (rule.rule_type) {
|
|
1437
|
+
case "MAX_FILE_LINES": {
|
|
1438
|
+
const value = rule.value;
|
|
1439
|
+
const lineCount = lines.length;
|
|
1440
|
+
if (lineCount > value.limit) {
|
|
1441
|
+
violations.push({
|
|
1442
|
+
file: filePath,
|
|
1443
|
+
rule: rule.rule_name,
|
|
1444
|
+
ruleType: rule.rule_type,
|
|
1445
|
+
severity: "critical",
|
|
1446
|
+
message: `File exceeds ${value.limit} lines`,
|
|
1447
|
+
details: `Current: ${lineCount} lines (limit: ${value.limit})`
|
|
1448
|
+
});
|
|
1449
|
+
} else if (value.warning_threshold && lineCount > value.warning_threshold) {
|
|
1450
|
+
violations.push({
|
|
1451
|
+
file: filePath,
|
|
1452
|
+
rule: rule.rule_name,
|
|
1453
|
+
ruleType: rule.rule_type,
|
|
1454
|
+
severity: "warning",
|
|
1455
|
+
message: `File approaching line limit`,
|
|
1456
|
+
details: `Current: ${lineCount} lines (warning at: ${value.warning_threshold}, limit: ${value.limit})`
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
break;
|
|
1460
|
+
}
|
|
1461
|
+
case "MAX_FUNCTION_LINES": {
|
|
1462
|
+
const value = rule.value;
|
|
1463
|
+
const functionViolations = checkFunctionLines(content, lines, filePath, rule, value.limit);
|
|
1464
|
+
violations.push(...functionViolations);
|
|
1465
|
+
break;
|
|
1466
|
+
}
|
|
1467
|
+
case "PATTERN_FORBIDDEN": {
|
|
1468
|
+
const value = rule.value;
|
|
1469
|
+
const pattern = new RegExp(value.pattern, "g");
|
|
1470
|
+
let match;
|
|
1471
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1472
|
+
const lineNumber = content.substring(0, match.index).split("\n").length;
|
|
1473
|
+
violations.push({
|
|
1474
|
+
file: filePath,
|
|
1475
|
+
rule: rule.rule_name,
|
|
1476
|
+
ruleType: rule.rule_type,
|
|
1477
|
+
severity: rule.severity,
|
|
1478
|
+
message: value.message || `Forbidden pattern found: ${value.pattern}`,
|
|
1479
|
+
line: lineNumber,
|
|
1480
|
+
details: `Found: "${match[0].substring(0, 50)}${match[0].length > 50 ? "..." : ""}"`
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
break;
|
|
1484
|
+
}
|
|
1485
|
+
case "PATTERN_REQUIRED": {
|
|
1486
|
+
const value = rule.value;
|
|
1487
|
+
const pattern = new RegExp(value.pattern);
|
|
1488
|
+
const shouldCheck = !value.context || filePath.includes(value.context);
|
|
1489
|
+
if (shouldCheck && !pattern.test(content)) {
|
|
1490
|
+
violations.push({
|
|
1491
|
+
file: filePath,
|
|
1492
|
+
rule: rule.rule_name,
|
|
1493
|
+
ruleType: rule.rule_type,
|
|
1494
|
+
severity: rule.severity,
|
|
1495
|
+
message: `Required pattern not found: ${value.pattern}`,
|
|
1496
|
+
details: value.context ? `Expected in files matching: ${value.context}` : void 0
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
break;
|
|
1500
|
+
}
|
|
1501
|
+
case "NAMING_CONVENTION": {
|
|
1502
|
+
const value = rule.value;
|
|
1503
|
+
const pattern = new RegExp(value.pattern);
|
|
1504
|
+
const fileName = import_path11.default.basename(filePath);
|
|
1505
|
+
if (filePath.includes(value.context) && !pattern.test(fileName)) {
|
|
1506
|
+
violations.push({
|
|
1507
|
+
file: filePath,
|
|
1508
|
+
rule: rule.rule_name,
|
|
1509
|
+
ruleType: rule.rule_type,
|
|
1510
|
+
severity: rule.severity,
|
|
1511
|
+
message: `File name does not match naming convention`,
|
|
1512
|
+
details: `Expected pattern: ${value.pattern}`
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
break;
|
|
1516
|
+
}
|
|
1517
|
+
default:
|
|
1518
|
+
break;
|
|
1519
|
+
}
|
|
1520
|
+
return violations;
|
|
1521
|
+
}
|
|
1522
|
+
function checkFunctionLines(content, lines, filePath, rule, limit) {
|
|
1523
|
+
const violations = [];
|
|
1524
|
+
const functionPatterns = [
|
|
1525
|
+
/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)\s*\{/g,
|
|
1526
|
+
/(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>\s*\{/g,
|
|
1527
|
+
/(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?function\s*\([^)]*\)\s*\{/g,
|
|
1528
|
+
/(\w+)\s*\([^)]*\)\s*\{/g
|
|
1529
|
+
// Method in class/object
|
|
1530
|
+
];
|
|
1531
|
+
for (const pattern of functionPatterns) {
|
|
1532
|
+
let match;
|
|
1533
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1534
|
+
const functionName = match[1];
|
|
1535
|
+
const startIndex = match.index + match[0].length - 1;
|
|
1536
|
+
let braceCount = 1;
|
|
1537
|
+
let endIndex = startIndex + 1;
|
|
1538
|
+
while (braceCount > 0 && endIndex < content.length) {
|
|
1539
|
+
if (content[endIndex] === "{") braceCount++;
|
|
1540
|
+
else if (content[endIndex] === "}") braceCount--;
|
|
1541
|
+
endIndex++;
|
|
1542
|
+
}
|
|
1543
|
+
const functionContent = content.substring(startIndex, endIndex);
|
|
1544
|
+
const functionLines = functionContent.split("\n").length;
|
|
1545
|
+
if (functionLines > limit) {
|
|
1546
|
+
const lineNumber = content.substring(0, match.index).split("\n").length;
|
|
1547
|
+
violations.push({
|
|
1548
|
+
file: filePath,
|
|
1549
|
+
rule: rule.rule_name,
|
|
1550
|
+
ruleType: rule.rule_type,
|
|
1551
|
+
severity: rule.severity,
|
|
1552
|
+
message: `Function "${functionName}" exceeds ${limit} lines`,
|
|
1553
|
+
line: lineNumber,
|
|
1554
|
+
details: `Current: ${functionLines} lines (limit: ${limit})`
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
return violations;
|
|
1560
|
+
}
|
|
1561
|
+
function formatViolations(violations) {
|
|
1562
|
+
for (const v of violations) {
|
|
1563
|
+
const severityColor = v.severity === "critical" ? import_chalk10.default.red : v.severity === "warning" ? import_chalk10.default.yellow : import_chalk10.default.blue;
|
|
1564
|
+
const lineInfo = v.line ? import_chalk10.default.dim(`:${v.line}`) : "";
|
|
1565
|
+
console.log(` ${severityColor(`[${v.severity.toUpperCase()}]`)} ${v.file}${lineInfo}`);
|
|
1566
|
+
console.log(` ${v.message}`);
|
|
1567
|
+
if (v.details) {
|
|
1568
|
+
console.log(` ${import_chalk10.default.dim(v.details)}`);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
function summarizeResults(results) {
|
|
1573
|
+
let criticalCount = 0;
|
|
1574
|
+
let warningCount = 0;
|
|
1575
|
+
let infoCount = 0;
|
|
1576
|
+
for (const result of results) {
|
|
1577
|
+
for (const v of result.violations) {
|
|
1578
|
+
if (v.severity === "critical") criticalCount++;
|
|
1579
|
+
else if (v.severity === "warning") warningCount++;
|
|
1580
|
+
else infoCount++;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
return {
|
|
1584
|
+
totalFiles: results.length,
|
|
1585
|
+
totalViolations: criticalCount + warningCount + infoCount,
|
|
1586
|
+
criticalCount,
|
|
1587
|
+
warningCount,
|
|
1588
|
+
infoCount
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
// src/commands/check.ts
|
|
1593
|
+
var CACHE_FILE2 = ".rigstate/rules-cache.json";
|
|
1594
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1595
|
+
var CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
1596
|
+
function createCheckCommand() {
|
|
1597
|
+
return new import_commander7.Command("check").description("Validate code against Guardian architectural rules").argument("[path]", "Directory or file to check", ".").option("--project <id>", "Project ID (or use .rigstate manifest)").option("--strict [level]", 'Exit 1 on violations. Level: "all" (default) or "critical"').option("--staged", "Only check git staged files (for pre-commit hooks)").option("--json", "Output results as JSON").option("--no-cache", "Skip rule cache and fetch fresh from API").action(async (targetPath, options) => {
|
|
1598
|
+
const spinner = (0, import_ora5.default)();
|
|
1599
|
+
try {
|
|
1600
|
+
let projectId = options.project;
|
|
1601
|
+
let apiUrl = getApiUrl();
|
|
1602
|
+
if (!projectId) {
|
|
1603
|
+
const manifest = await loadManifest();
|
|
1604
|
+
if (manifest) {
|
|
1605
|
+
projectId = manifest.project_id;
|
|
1606
|
+
if (manifest.api_url) apiUrl = manifest.api_url;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
if (!projectId) {
|
|
1610
|
+
projectId = getProjectId();
|
|
1611
|
+
}
|
|
1612
|
+
if (!projectId) {
|
|
1613
|
+
console.log(import_chalk11.default.red("\u274C No project context found."));
|
|
1614
|
+
console.log(import_chalk11.default.dim(' Run "rigstate link" or pass --project <id>'));
|
|
1615
|
+
process.exit(2);
|
|
1616
|
+
}
|
|
1617
|
+
let apiKey;
|
|
1618
|
+
try {
|
|
1619
|
+
apiKey = getApiKey();
|
|
1620
|
+
} catch {
|
|
1621
|
+
console.log(import_chalk11.default.red('\u274C Not authenticated. Run "rigstate login" first.'));
|
|
1622
|
+
process.exit(2);
|
|
1623
|
+
}
|
|
1624
|
+
spinner.start("Fetching Guardian rules...");
|
|
1625
|
+
let rules;
|
|
1626
|
+
let settings;
|
|
1627
|
+
try {
|
|
1628
|
+
const cached = options.cache !== false ? await loadCachedRules(projectId) : null;
|
|
1629
|
+
if (cached && !isStale(cached.timestamp, CACHE_TTL_MS)) {
|
|
1630
|
+
rules = cached.rules;
|
|
1631
|
+
settings = cached.settings;
|
|
1632
|
+
spinner.text = "Using cached rules...";
|
|
1633
|
+
} else {
|
|
1634
|
+
const response = await import_axios7.default.get(`${apiUrl}/api/v1/guardian/rules`, {
|
|
1635
|
+
params: { project_id: projectId },
|
|
1636
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
1637
|
+
timeout: 1e4
|
|
1638
|
+
});
|
|
1639
|
+
if (!response.data.success) {
|
|
1640
|
+
throw new Error(response.data.error || "Unknown API error");
|
|
1641
|
+
}
|
|
1642
|
+
rules = response.data.data.rules;
|
|
1643
|
+
settings = response.data.data.settings;
|
|
1644
|
+
await saveCachedRules(projectId, rules, settings);
|
|
1645
|
+
}
|
|
1646
|
+
} catch (apiError) {
|
|
1647
|
+
const cached = await loadCachedRules(projectId);
|
|
1648
|
+
if (cached && !isStale(cached.timestamp, CACHE_MAX_AGE_MS)) {
|
|
1649
|
+
spinner.warn(import_chalk11.default.yellow("Using cached rules (API unavailable)"));
|
|
1650
|
+
rules = cached.rules;
|
|
1651
|
+
settings = cached.settings;
|
|
1652
|
+
} else {
|
|
1653
|
+
spinner.fail(import_chalk11.default.red("Failed to fetch rules and no valid cache"));
|
|
1654
|
+
console.log(import_chalk11.default.dim(` Error: ${apiError.message}`));
|
|
1655
|
+
process.exit(2);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
spinner.succeed(`Loaded ${rules.length} Guardian rules`);
|
|
1659
|
+
const scanPath = import_path12.default.resolve(process.cwd(), targetPath);
|
|
1660
|
+
let filesToCheck;
|
|
1661
|
+
if (options.staged) {
|
|
1662
|
+
spinner.start("Getting staged files...");
|
|
1663
|
+
try {
|
|
1664
|
+
const stagedOutput = (0, import_child_process2.execSync)("git diff --cached --name-only --diff-filter=ACMR", {
|
|
1665
|
+
encoding: "utf-8",
|
|
1666
|
+
cwd: process.cwd()
|
|
1667
|
+
});
|
|
1668
|
+
filesToCheck = stagedOutput.split("\n").filter((f) => f.trim()).filter((f) => isCodeFile2(f)).map((f) => import_path12.default.resolve(process.cwd(), f));
|
|
1669
|
+
} catch {
|
|
1670
|
+
spinner.fail("Not a git repository or no staged files");
|
|
1671
|
+
process.exit(2);
|
|
1672
|
+
}
|
|
1673
|
+
} else {
|
|
1674
|
+
spinner.start(`Scanning ${import_chalk11.default.cyan(targetPath)}...`);
|
|
1675
|
+
const pattern = import_path12.default.join(scanPath, "**/*");
|
|
1676
|
+
const allFiles = await (0, import_glob3.glob)(pattern, {
|
|
1677
|
+
nodir: true,
|
|
1678
|
+
dot: false,
|
|
1679
|
+
ignore: [
|
|
1680
|
+
"**/node_modules/**",
|
|
1681
|
+
"**/.git/**",
|
|
1682
|
+
"**/dist/**",
|
|
1683
|
+
"**/build/**",
|
|
1684
|
+
"**/.next/**",
|
|
1685
|
+
"**/coverage/**"
|
|
1686
|
+
]
|
|
1687
|
+
});
|
|
1688
|
+
filesToCheck = allFiles.filter((f) => isCodeFile2(f));
|
|
1689
|
+
}
|
|
1690
|
+
if (filesToCheck.length === 0) {
|
|
1691
|
+
spinner.warn(import_chalk11.default.yellow("No code files found to check."));
|
|
1692
|
+
outputResults([], !!options.json);
|
|
1693
|
+
process.exit(0);
|
|
1694
|
+
}
|
|
1695
|
+
spinner.succeed(`Found ${filesToCheck.length} files to check`);
|
|
1696
|
+
spinner.start("Running Guardian checks...");
|
|
1697
|
+
const results = [];
|
|
1698
|
+
for (let i = 0; i < filesToCheck.length; i++) {
|
|
1699
|
+
const file = filesToCheck[i];
|
|
1700
|
+
spinner.text = `Checking ${i + 1}/${filesToCheck.length}: ${import_path12.default.basename(file)}`;
|
|
1701
|
+
const result = await checkFile(file, rules, process.cwd());
|
|
1702
|
+
results.push(result);
|
|
1703
|
+
}
|
|
1704
|
+
spinner.stop();
|
|
1705
|
+
const summary = summarizeResults(results);
|
|
1706
|
+
if (options.json) {
|
|
1707
|
+
outputResults(results, true);
|
|
1708
|
+
} else {
|
|
1709
|
+
outputResults(results, false);
|
|
1710
|
+
console.log("\n" + import_chalk11.default.bold("\u{1F4CA} Summary"));
|
|
1711
|
+
console.log(import_chalk11.default.dim("\u2500".repeat(50)));
|
|
1712
|
+
console.log(`Files checked: ${import_chalk11.default.cyan(summary.totalFiles)}`);
|
|
1713
|
+
console.log(`Total violations: ${summary.totalViolations > 0 ? import_chalk11.default.red(summary.totalViolations) : import_chalk11.default.green(0)}`);
|
|
1714
|
+
if (summary.totalViolations > 0) {
|
|
1715
|
+
console.log(` ${import_chalk11.default.red("Critical:")} ${summary.criticalCount}`);
|
|
1716
|
+
console.log(` ${import_chalk11.default.yellow("Warning:")} ${summary.warningCount}`);
|
|
1717
|
+
console.log(` ${import_chalk11.default.blue("Info:")} ${summary.infoCount}`);
|
|
1718
|
+
}
|
|
1719
|
+
console.log(import_chalk11.default.dim("\u2500".repeat(50)));
|
|
1720
|
+
}
|
|
1721
|
+
if (options.strict !== void 0) {
|
|
1722
|
+
const strictLevel = typeof options.strict === "string" ? options.strict : "all";
|
|
1723
|
+
if (strictLevel === "critical" && summary.criticalCount > 0) {
|
|
1724
|
+
console.log(import_chalk11.default.red("\n\u274C Check failed: Critical violations found"));
|
|
1725
|
+
process.exit(1);
|
|
1726
|
+
} else if (strictLevel === "all" && summary.totalViolations > 0) {
|
|
1727
|
+
console.log(import_chalk11.default.red("\n\u274C Check failed: Violations found"));
|
|
1728
|
+
process.exit(1);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
if (summary.totalViolations === 0) {
|
|
1732
|
+
console.log(import_chalk11.default.green("\n\u2705 All checks passed!"));
|
|
1733
|
+
}
|
|
1734
|
+
process.exit(0);
|
|
1735
|
+
} catch (error) {
|
|
1736
|
+
spinner.fail(import_chalk11.default.red("Check failed"));
|
|
1737
|
+
console.error(import_chalk11.default.red("Error:"), error.message);
|
|
1738
|
+
process.exit(2);
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
function isCodeFile2(filePath) {
|
|
1743
|
+
const codeExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1744
|
+
const ext = import_path12.default.extname(filePath).toLowerCase();
|
|
1745
|
+
return codeExtensions.includes(ext);
|
|
1746
|
+
}
|
|
1747
|
+
async function loadCachedRules(projectId) {
|
|
1748
|
+
try {
|
|
1749
|
+
const cachePath = import_path12.default.join(process.cwd(), CACHE_FILE2);
|
|
1750
|
+
const content = await import_promises12.default.readFile(cachePath, "utf-8");
|
|
1751
|
+
const cached = JSON.parse(content);
|
|
1752
|
+
if (cached.projectId !== projectId) {
|
|
1753
|
+
return null;
|
|
1754
|
+
}
|
|
1755
|
+
return cached;
|
|
1756
|
+
} catch {
|
|
1757
|
+
return null;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
async function saveCachedRules(projectId, rules, settings) {
|
|
1761
|
+
try {
|
|
1762
|
+
const cacheDir = import_path12.default.join(process.cwd(), ".rigstate");
|
|
1763
|
+
await import_promises12.default.mkdir(cacheDir, { recursive: true });
|
|
1764
|
+
const cached = {
|
|
1765
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1766
|
+
projectId,
|
|
1767
|
+
rules,
|
|
1768
|
+
settings
|
|
1769
|
+
};
|
|
1770
|
+
await import_promises12.default.writeFile(
|
|
1771
|
+
import_path12.default.join(cacheDir, "rules-cache.json"),
|
|
1772
|
+
JSON.stringify(cached, null, 2)
|
|
1773
|
+
);
|
|
1774
|
+
} catch {
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
function isStale(timestamp, maxAge) {
|
|
1778
|
+
const age = Date.now() - new Date(timestamp).getTime();
|
|
1779
|
+
return age > maxAge;
|
|
1780
|
+
}
|
|
1781
|
+
function outputResults(results, json) {
|
|
1782
|
+
if (json) {
|
|
1783
|
+
console.log(JSON.stringify({
|
|
1784
|
+
results,
|
|
1785
|
+
summary: summarizeResults(results)
|
|
1786
|
+
}, null, 2));
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
const hasViolations = results.some((r) => r.violations.length > 0);
|
|
1790
|
+
if (!hasViolations) {
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
console.log("\n" + import_chalk11.default.bold("\u{1F50D} Violations Found"));
|
|
1794
|
+
console.log(import_chalk11.default.dim("\u2500".repeat(50)));
|
|
1795
|
+
for (const result of results) {
|
|
1796
|
+
if (result.violations.length > 0) {
|
|
1797
|
+
formatViolations(result.violations);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// src/commands/hooks.ts
|
|
1803
|
+
init_cjs_shims();
|
|
1804
|
+
var import_commander8 = require("commander");
|
|
1805
|
+
var import_chalk12 = __toESM(require("chalk"), 1);
|
|
1806
|
+
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
1807
|
+
var import_path13 = __toESM(require("path"), 1);
|
|
1808
|
+
var PRE_COMMIT_SCRIPT = `#!/bin/sh
|
|
1809
|
+
# Rigstate Guardian Pre-commit Hook
|
|
1810
|
+
# Installed by: rigstate hooks install
|
|
1811
|
+
|
|
1812
|
+
# 1. Silent Sentinel Check (Phase 5)
|
|
1813
|
+
if [ -f .rigstate/guardian.lock ]; then
|
|
1814
|
+
echo "\u{1F6D1} INTERVENTION ACTIVE: Commit blocked by Silent Sentinel."
|
|
1815
|
+
echo " A critical violation ('HARD_LOCK') was detected by the Guardian Daemon."
|
|
1816
|
+
echo " Please fix the violation to unlock the repo."
|
|
1817
|
+
echo ""
|
|
1818
|
+
if grep -q "HARD_LOCK_ACTIVE" .rigstate/guardian.lock; then
|
|
1819
|
+
cat .rigstate/guardian.lock
|
|
1820
|
+
fi
|
|
1821
|
+
exit 1
|
|
1822
|
+
fi
|
|
1823
|
+
|
|
1824
|
+
echo "\u{1F6E1}\uFE0F Running Guardian checks..."
|
|
1825
|
+
|
|
1826
|
+
# Run check with strict mode for critical violations
|
|
1827
|
+
rigstate check --staged --strict=critical
|
|
1828
|
+
|
|
1829
|
+
# Exit with the same code as rigstate check
|
|
1830
|
+
exit $?
|
|
1831
|
+
`;
|
|
1832
|
+
function createHooksCommand() {
|
|
1833
|
+
const hooks = new import_commander8.Command("hooks").description("Manage git hooks for Guardian integration");
|
|
1834
|
+
hooks.command("install").description("Install pre-commit hook to run Guardian checks").option("--strict [level]", 'Strict level: "all" or "critical" (default)', "critical").action(async (options) => {
|
|
1835
|
+
try {
|
|
1836
|
+
const gitDir = import_path13.default.join(process.cwd(), ".git");
|
|
1837
|
+
try {
|
|
1838
|
+
await import_promises13.default.access(gitDir);
|
|
1839
|
+
} catch {
|
|
1840
|
+
console.log(import_chalk12.default.red("\u274C Not a git repository."));
|
|
1841
|
+
console.log(import_chalk12.default.dim(' Initialize with "git init" first.'));
|
|
1842
|
+
process.exit(1);
|
|
1843
|
+
}
|
|
1844
|
+
const hooksDir = import_path13.default.join(gitDir, "hooks");
|
|
1845
|
+
await import_promises13.default.mkdir(hooksDir, { recursive: true });
|
|
1846
|
+
const preCommitPath = import_path13.default.join(hooksDir, "pre-commit");
|
|
1847
|
+
let existingContent = "";
|
|
1848
|
+
try {
|
|
1849
|
+
existingContent = await import_promises13.default.readFile(preCommitPath, "utf-8");
|
|
1850
|
+
if (existingContent.includes("rigstate")) {
|
|
1851
|
+
console.log(import_chalk12.default.yellow("\u26A0 Rigstate pre-commit hook already installed."));
|
|
1852
|
+
console.log(import_chalk12.default.dim(' Use "rigstate hooks uninstall" to remove first.'));
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
} catch {
|
|
1856
|
+
}
|
|
1857
|
+
let script = PRE_COMMIT_SCRIPT;
|
|
1858
|
+
if (options.strict === "all") {
|
|
1859
|
+
script = script.replace("--strict=critical", "--strict");
|
|
1860
|
+
}
|
|
1861
|
+
if (existingContent && !existingContent.includes("rigstate")) {
|
|
1862
|
+
const combinedScript = existingContent + "\n\n" + script.replace("#!/bin/sh\n", "");
|
|
1863
|
+
await import_promises13.default.writeFile(preCommitPath, combinedScript, { mode: 493 });
|
|
1864
|
+
console.log(import_chalk12.default.green("\u2705 Rigstate hook appended to existing pre-commit."));
|
|
1865
|
+
} else {
|
|
1866
|
+
await import_promises13.default.writeFile(preCommitPath, script, { mode: 493 });
|
|
1867
|
+
console.log(import_chalk12.default.green("\u2705 Pre-commit hook installed!"));
|
|
1868
|
+
}
|
|
1869
|
+
console.log(import_chalk12.default.dim(` Path: ${preCommitPath}`));
|
|
1870
|
+
console.log(import_chalk12.default.dim(` Strict level: ${options.strict}`));
|
|
1871
|
+
console.log("");
|
|
1872
|
+
console.log(import_chalk12.default.cyan("Guardian will now check your code before each commit."));
|
|
1873
|
+
console.log(import_chalk12.default.dim('Use "rigstate hooks uninstall" to remove the hook.'));
|
|
1874
|
+
} catch (error) {
|
|
1875
|
+
console.error(import_chalk12.default.red("Failed to install hook:"), error.message);
|
|
1876
|
+
process.exit(1);
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1879
|
+
hooks.command("uninstall").description("Remove Rigstate pre-commit hook").action(async () => {
|
|
1880
|
+
try {
|
|
1881
|
+
const preCommitPath = import_path13.default.join(process.cwd(), ".git", "hooks", "pre-commit");
|
|
1882
|
+
try {
|
|
1883
|
+
const content = await import_promises13.default.readFile(preCommitPath, "utf-8");
|
|
1884
|
+
if (!content.includes("rigstate")) {
|
|
1885
|
+
console.log(import_chalk12.default.yellow("\u26A0 No Rigstate hook found in pre-commit."));
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
if (content.includes("# Rigstate Guardian Pre-commit Hook") && content.trim().split("\n").filter((l) => l && !l.startsWith("#")).length <= 4) {
|
|
1889
|
+
await import_promises13.default.unlink(preCommitPath);
|
|
1890
|
+
console.log(import_chalk12.default.green("\u2705 Pre-commit hook removed."));
|
|
1891
|
+
} else {
|
|
1892
|
+
const lines = content.split("\n");
|
|
1893
|
+
const filteredLines = [];
|
|
1894
|
+
let inRigstateSection = false;
|
|
1895
|
+
for (const line of lines) {
|
|
1896
|
+
if (line.includes("Rigstate Guardian Pre-commit Hook")) {
|
|
1897
|
+
inRigstateSection = true;
|
|
1898
|
+
continue;
|
|
1899
|
+
}
|
|
1900
|
+
if (inRigstateSection && line.includes("exit $?")) {
|
|
1901
|
+
inRigstateSection = false;
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1904
|
+
if (!inRigstateSection && !line.includes("rigstate check")) {
|
|
1905
|
+
filteredLines.push(line);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
await import_promises13.default.writeFile(preCommitPath, filteredLines.join("\n"), { mode: 493 });
|
|
1909
|
+
console.log(import_chalk12.default.green("\u2705 Rigstate section removed from pre-commit hook."));
|
|
1910
|
+
}
|
|
1911
|
+
} catch {
|
|
1912
|
+
console.log(import_chalk12.default.yellow("\u26A0 No pre-commit hook found."));
|
|
1913
|
+
}
|
|
1914
|
+
} catch (error) {
|
|
1915
|
+
console.error(import_chalk12.default.red("Failed to uninstall hook:"), error.message);
|
|
1916
|
+
process.exit(1);
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
return hooks;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
// src/commands/daemon.ts
|
|
1923
|
+
init_cjs_shims();
|
|
1924
|
+
var import_commander9 = require("commander");
|
|
1925
|
+
var import_chalk15 = __toESM(require("chalk"), 1);
|
|
1926
|
+
var import_ora6 = __toESM(require("ora"), 1);
|
|
1927
|
+
var import_promises16 = __toESM(require("fs/promises"), 1);
|
|
1928
|
+
var import_path18 = __toESM(require("path"), 1);
|
|
1929
|
+
|
|
1930
|
+
// src/daemon/factory.ts
|
|
1931
|
+
init_cjs_shims();
|
|
1932
|
+
|
|
1933
|
+
// src/daemon/core.ts
|
|
1934
|
+
init_cjs_shims();
|
|
1935
|
+
var import_chalk14 = __toESM(require("chalk"), 1);
|
|
1936
|
+
var fs16 = __toESM(require("fs/promises"), 1);
|
|
1937
|
+
var import_events3 = require("events");
|
|
1938
|
+
|
|
1939
|
+
// src/daemon/file-watcher.ts
|
|
1940
|
+
init_cjs_shims();
|
|
1941
|
+
var chokidar = __toESM(require("chokidar"), 1);
|
|
1942
|
+
var import_path14 = __toESM(require("path"), 1);
|
|
1943
|
+
var import_events = require("events");
|
|
1944
|
+
var CODE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1945
|
+
function isCodeFile3(filePath) {
|
|
1946
|
+
const ext = import_path14.default.extname(filePath).toLowerCase();
|
|
1947
|
+
return CODE_EXTENSIONS.includes(ext);
|
|
1948
|
+
}
|
|
1949
|
+
function createFileWatcher(watchPath) {
|
|
1950
|
+
const emitter = new import_events.EventEmitter();
|
|
1951
|
+
let watcher = null;
|
|
1952
|
+
emitter.start = () => {
|
|
1953
|
+
const absolutePath = import_path14.default.resolve(process.cwd(), watchPath);
|
|
1954
|
+
watcher = chokidar.watch(absolutePath, {
|
|
1955
|
+
ignored: (path23) => {
|
|
1956
|
+
if (path23.includes("node_modules")) return true;
|
|
1957
|
+
if (path23.includes(".git")) return true;
|
|
1958
|
+
if (path23.includes(".next")) return true;
|
|
1959
|
+
if (path23.includes("dist")) return true;
|
|
1960
|
+
if (path23.includes("build")) return true;
|
|
1961
|
+
if (path23.includes(".rigstate")) return true;
|
|
1962
|
+
if (path23.includes("coverage")) return true;
|
|
1963
|
+
return false;
|
|
1964
|
+
},
|
|
1965
|
+
persistent: true,
|
|
1966
|
+
ignoreInitial: true,
|
|
1967
|
+
depth: 15,
|
|
1968
|
+
awaitWriteFinish: {
|
|
1969
|
+
stabilityThreshold: 200,
|
|
1970
|
+
pollInterval: 100
|
|
1971
|
+
},
|
|
1972
|
+
usePolling: false,
|
|
1973
|
+
// Use native events when possible
|
|
1974
|
+
interval: 300,
|
|
1975
|
+
binaryInterval: 1e3
|
|
1976
|
+
});
|
|
1977
|
+
watcher.on("change", (filePath) => {
|
|
1978
|
+
if (isCodeFile3(filePath)) {
|
|
1979
|
+
emitter.emit("change", import_path14.default.relative(process.cwd(), filePath));
|
|
1980
|
+
}
|
|
1981
|
+
});
|
|
1982
|
+
watcher.on("add", (filePath) => {
|
|
1983
|
+
if (isCodeFile3(filePath)) {
|
|
1984
|
+
emitter.emit("add", import_path14.default.relative(process.cwd(), filePath));
|
|
1985
|
+
}
|
|
1986
|
+
});
|
|
1987
|
+
watcher.on("unlink", (filePath) => {
|
|
1988
|
+
if (isCodeFile3(filePath)) {
|
|
1989
|
+
emitter.emit("unlink", import_path14.default.relative(process.cwd(), filePath));
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
watcher.on("error", (error) => {
|
|
1993
|
+
emitter.emit("error", error);
|
|
1994
|
+
});
|
|
1995
|
+
watcher.on("ready", () => {
|
|
1996
|
+
emitter.emit("ready");
|
|
1997
|
+
});
|
|
1998
|
+
};
|
|
1999
|
+
emitter.stop = async () => {
|
|
2000
|
+
if (watcher) {
|
|
2001
|
+
await watcher.close();
|
|
2002
|
+
watcher = null;
|
|
2003
|
+
}
|
|
2004
|
+
};
|
|
2005
|
+
return emitter;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
// src/daemon/heuristic-engine.ts
|
|
2009
|
+
init_cjs_shims();
|
|
2010
|
+
var import_promises14 = require("fs/promises");
|
|
2011
|
+
var import_path15 = require("path");
|
|
2012
|
+
var import_path16 = __toESM(require("path"), 1);
|
|
2013
|
+
var import_axios8 = __toESM(require("axios"), 1);
|
|
2014
|
+
var GLOBAL_HEURISTICS = [
|
|
2015
|
+
{
|
|
2016
|
+
skillId: "payment-expert",
|
|
2017
|
+
patterns: {
|
|
2018
|
+
imports: ["@stripe/", "stripe"],
|
|
2019
|
+
content: ["PaymentIntent", "CheckoutSession"]
|
|
2020
|
+
},
|
|
2021
|
+
confidence: "high"
|
|
2022
|
+
},
|
|
2023
|
+
{
|
|
2024
|
+
skillId: "rigstate-integrity-gate",
|
|
2025
|
+
patterns: {
|
|
2026
|
+
files: ["**/release.config.js", "**/manifest.json", "**/.rigstate/release/*"],
|
|
2027
|
+
content: ["[CORE INTEGRITY]", "prepare_release"]
|
|
2028
|
+
},
|
|
2029
|
+
confidence: "high"
|
|
2030
|
+
},
|
|
2031
|
+
{
|
|
2032
|
+
skillId: "database-architect",
|
|
2033
|
+
patterns: {
|
|
2034
|
+
files: ["**/*.sql", "**/schema.prisma", "**/migrations/*"],
|
|
2035
|
+
imports: ["@supabase/supabase-js", "drizzle-orm", "prisma"]
|
|
2036
|
+
},
|
|
2037
|
+
confidence: "medium"
|
|
2038
|
+
}
|
|
2039
|
+
];
|
|
2040
|
+
var HeuristicEngine = class {
|
|
2041
|
+
rules = [];
|
|
2042
|
+
cachePath;
|
|
2043
|
+
constructor() {
|
|
2044
|
+
this.cachePath = import_path16.default.join(process.cwd(), ".rigstate", "cache", "heuristics.json");
|
|
2045
|
+
this.loadRules();
|
|
2046
|
+
}
|
|
2047
|
+
async loadRules() {
|
|
2048
|
+
try {
|
|
2049
|
+
const cached = await (0, import_promises14.readFile)(this.cachePath, "utf-8");
|
|
2050
|
+
const data = JSON.parse(cached);
|
|
2051
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
2052
|
+
this.rules = data;
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
} catch (e) {
|
|
2056
|
+
}
|
|
2057
|
+
this.rules = GLOBAL_HEURISTICS;
|
|
2058
|
+
}
|
|
2059
|
+
async refreshRules(projectId, apiUrl, apiKey) {
|
|
2060
|
+
try {
|
|
2061
|
+
await (0, import_promises14.mkdir)((0, import_path15.dirname)(this.cachePath), { recursive: true });
|
|
2062
|
+
const endpoint = `${apiUrl}/api/v1/skills/triggers`;
|
|
2063
|
+
const response = await import_axios8.default.get(endpoint, {
|
|
2064
|
+
headers: {
|
|
2065
|
+
"x-api-key": apiKey,
|
|
2066
|
+
"Content-Type": "application/json"
|
|
2067
|
+
}
|
|
2068
|
+
});
|
|
2069
|
+
if (response.data && Array.isArray(response.data.triggers)) {
|
|
2070
|
+
const cloudRules = response.data.triggers;
|
|
2071
|
+
await (0, import_promises14.writeFile)(this.cachePath, JSON.stringify(cloudRules, null, 2));
|
|
2072
|
+
this.rules = cloudRules;
|
|
2073
|
+
return true;
|
|
2074
|
+
}
|
|
2075
|
+
} catch (error) {
|
|
2076
|
+
return false;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
async analyzeFile(filePath, metrics) {
|
|
2080
|
+
try {
|
|
2081
|
+
const content = await (0, import_promises14.readFile)(filePath, "utf-8");
|
|
2082
|
+
const matches = [];
|
|
2083
|
+
const activeRules = this.rules.length > 0 ? this.rules : GLOBAL_HEURISTICS;
|
|
2084
|
+
for (const heuristic of activeRules) {
|
|
2085
|
+
const match = this.checkHeuristic(filePath, content, heuristic, metrics);
|
|
2086
|
+
if (match) {
|
|
2087
|
+
matches.push(match);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
return matches;
|
|
2091
|
+
} catch (error) {
|
|
2092
|
+
return [];
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
checkHeuristic(filePath, content, heuristic, metrics) {
|
|
2096
|
+
if (heuristic.patterns.metric_threshold && metrics) {
|
|
2097
|
+
const lineLimitRule = metrics.rules.find((r) => r.rule_type === "MAX_FILE_LINES");
|
|
2098
|
+
if (lineLimitRule) {
|
|
2099
|
+
const limit = lineLimitRule.value.limit;
|
|
2100
|
+
const threshold = limit * heuristic.patterns.metric_threshold;
|
|
2101
|
+
if (metrics.lineCount >= threshold && metrics.lineCount < limit) {
|
|
2102
|
+
return {
|
|
2103
|
+
skillId: heuristic.skillId,
|
|
2104
|
+
file: filePath,
|
|
2105
|
+
reason: `File reached ${Math.round(metrics.lineCount / limit * 100)}% of its line limit (${metrics.lineCount}/${limit})`,
|
|
2106
|
+
confidence: "high"
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
if (heuristic.patterns.files) {
|
|
2112
|
+
const matchesFile = heuristic.patterns.files.some((pattern) => {
|
|
2113
|
+
if (pattern.startsWith("**/*")) return filePath.endsWith(pattern.replace("**/*", ""));
|
|
2114
|
+
return filePath.includes(pattern);
|
|
2115
|
+
});
|
|
2116
|
+
if (matchesFile) {
|
|
2117
|
+
return {
|
|
2118
|
+
skillId: heuristic.skillId,
|
|
2119
|
+
file: filePath,
|
|
2120
|
+
reason: `Matches file pattern: ${heuristic.patterns.files.join(", ")}`,
|
|
2121
|
+
confidence: heuristic.confidence
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
if (heuristic.patterns.imports) {
|
|
2126
|
+
for (const imp of heuristic.patterns.imports) {
|
|
2127
|
+
const importRegex = new RegExp(`(import .* from ['"]${imp}|require\\(['"]${imp})`, "i");
|
|
2128
|
+
if (importRegex.test(content)) {
|
|
2129
|
+
return {
|
|
2130
|
+
skillId: heuristic.skillId,
|
|
2131
|
+
file: filePath,
|
|
2132
|
+
reason: `Detected import: ${imp}`,
|
|
2133
|
+
confidence: heuristic.confidence
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
if (heuristic.patterns.content) {
|
|
2139
|
+
for (const pattern of heuristic.patterns.content) {
|
|
2140
|
+
if (content.includes(pattern)) {
|
|
2141
|
+
return {
|
|
2142
|
+
skillId: heuristic.skillId,
|
|
2143
|
+
file: filePath,
|
|
2144
|
+
reason: `Detected content pattern: ${pattern}`,
|
|
2145
|
+
confidence: heuristic.confidence
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
return null;
|
|
2151
|
+
}
|
|
2152
|
+
};
|
|
2153
|
+
function createHeuristicEngine() {
|
|
2154
|
+
return new HeuristicEngine();
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// src/daemon/intervention-protocol.ts
|
|
2158
|
+
init_cjs_shims();
|
|
2159
|
+
var import_chalk13 = __toESM(require("chalk"), 1);
|
|
2160
|
+
var fs14 = __toESM(require("fs"), 1);
|
|
2161
|
+
var path16 = __toESM(require("path"), 1);
|
|
2162
|
+
var InterventionProtocol = class {
|
|
2163
|
+
activeViolators = /* @__PURE__ */ new Set();
|
|
2164
|
+
/**
|
|
2165
|
+
* Registers a violation outcome to update the global lock state.
|
|
2166
|
+
*/
|
|
2167
|
+
registerViolation(filePath, decision) {
|
|
2168
|
+
if (decision.mode === "HARD_LOCK") {
|
|
2169
|
+
this.activeViolators.add(filePath);
|
|
2170
|
+
this.syncLockFile();
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
/**
|
|
2174
|
+
* Clears any active locks for a specific file (e.g. after a fix).
|
|
2175
|
+
*/
|
|
2176
|
+
clear(filePath) {
|
|
2177
|
+
if (this.activeViolators.has(filePath)) {
|
|
2178
|
+
this.activeViolators.delete(filePath);
|
|
2179
|
+
this.syncLockFile();
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
syncLockFile() {
|
|
2183
|
+
try {
|
|
2184
|
+
const lockDir = path16.join(process.cwd(), ".rigstate");
|
|
2185
|
+
if (!fs14.existsSync(lockDir)) fs14.mkdirSync(lockDir, { recursive: true });
|
|
2186
|
+
const lockPath = path16.join(lockDir, "guardian.lock");
|
|
2187
|
+
if (this.activeViolators.size > 0) {
|
|
2188
|
+
const content = `HARD_LOCK_ACTIVE
|
|
2189
|
+
Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
2190
|
+
|
|
2191
|
+
Blocking Files:
|
|
2192
|
+
${Array.from(this.activeViolators).join("\n")}`;
|
|
2193
|
+
fs14.writeFileSync(lockPath, content, "utf-8");
|
|
2194
|
+
} else {
|
|
2195
|
+
if (fs14.existsSync(lockPath)) fs14.unlinkSync(lockPath);
|
|
2196
|
+
}
|
|
2197
|
+
} catch (e) {
|
|
2198
|
+
console.error("Failed to sync guardian lock file:", e);
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Evaluate a Heuristic Trigger (Preventative)
|
|
2203
|
+
*/
|
|
2204
|
+
evaluateTrigger(skillId, confidence) {
|
|
2205
|
+
if (skillId === "rigstate-integrity-gate") {
|
|
2206
|
+
return {
|
|
2207
|
+
mode: "SOFT_LOCK",
|
|
2208
|
+
message: "Integrity Gate detected. Release Manifest required before final push.",
|
|
2209
|
+
blockCommit: false
|
|
2210
|
+
// Soft lock just warns
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
return {
|
|
2214
|
+
mode: "OPEN",
|
|
2215
|
+
message: `Predictive activation: ${skillId}`,
|
|
2216
|
+
blockCommit: false
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Evaluate a Guardian Violation (Corrective)
|
|
2221
|
+
*/
|
|
2222
|
+
evaluateViolation(ruleId, severity) {
|
|
2223
|
+
if (severity === "critical" || severity === "error") {
|
|
2224
|
+
return {
|
|
2225
|
+
mode: "HARD_LOCK",
|
|
2226
|
+
message: `CRITICAL VIOLATION: ${ruleId}. System Integrity Compromised.`,
|
|
2227
|
+
blockCommit: true
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
if (severity === "warning") {
|
|
2231
|
+
return {
|
|
2232
|
+
mode: "SOFT_LOCK",
|
|
2233
|
+
message: `Warning: ${ruleId}. Review recommended.`,
|
|
2234
|
+
blockCommit: false
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
return {
|
|
2238
|
+
mode: "OPEN",
|
|
2239
|
+
message: "Info notice.",
|
|
2240
|
+
blockCommit: false
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* Logs the intervention to the console with appropriate visual weight
|
|
2245
|
+
*/
|
|
2246
|
+
enforce(decision) {
|
|
2247
|
+
if (decision.mode === "OPEN") return;
|
|
2248
|
+
const icon = decision.mode === "HARD_LOCK" ? "\u{1F6AB}" : "\u26A0\uFE0F";
|
|
2249
|
+
const color = decision.mode === "HARD_LOCK" ? import_chalk13.default.bgRed.white.bold : import_chalk13.default.yellow.bold;
|
|
2250
|
+
console.log("\n" + color(` ${icon} [${decision.mode}] INTERVENTION `));
|
|
2251
|
+
console.log(import_chalk13.default.redBright(` ${decision.message}`));
|
|
2252
|
+
if (decision.blockCommit) {
|
|
2253
|
+
console.log(import_chalk13.default.dim(" \u{1F512} Commit functionality is logically suspended until fixed."));
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
function createInterventionProtocol() {
|
|
2258
|
+
return new InterventionProtocol();
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
// src/daemon/guardian-monitor.ts
|
|
2262
|
+
init_cjs_shims();
|
|
2263
|
+
var import_axios9 = __toESM(require("axios"), 1);
|
|
2264
|
+
var import_promises15 = __toESM(require("fs/promises"), 1);
|
|
2265
|
+
var import_path17 = __toESM(require("path"), 1);
|
|
2266
|
+
var CACHE_FILE3 = ".rigstate/rules-cache.json";
|
|
2267
|
+
var CACHE_TTL_MS2 = 5 * 60 * 1e3;
|
|
2268
|
+
function createGuardianMonitor(projectId, apiUrl, apiKey) {
|
|
2269
|
+
let rules = [];
|
|
2270
|
+
let lastFetch = 0;
|
|
2271
|
+
const loadRules = async () => {
|
|
2272
|
+
if (rules.length > 0 && Date.now() - lastFetch < CACHE_TTL_MS2) {
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
try {
|
|
2276
|
+
const response = await import_axios9.default.get(`${apiUrl}/api/v1/guardian/rules`, {
|
|
2277
|
+
params: { project_id: projectId },
|
|
2278
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
2279
|
+
timeout: 1e4
|
|
2280
|
+
});
|
|
2281
|
+
if (response.data.success && response.data.data.rules) {
|
|
2282
|
+
rules = response.data.data.rules;
|
|
2283
|
+
lastFetch = Date.now();
|
|
2284
|
+
await saveCachedRules2(projectId, rules);
|
|
2285
|
+
return;
|
|
2286
|
+
}
|
|
2287
|
+
} catch (error) {
|
|
2288
|
+
const cached = await loadCachedRules2(projectId);
|
|
2289
|
+
if (cached) {
|
|
2290
|
+
rules = cached.rules;
|
|
2291
|
+
lastFetch = Date.now();
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
rules = [];
|
|
2296
|
+
};
|
|
2297
|
+
const checkFileImpl = async (filePath) => {
|
|
2298
|
+
await loadRules();
|
|
2299
|
+
if (rules.length === 0) {
|
|
2300
|
+
return {
|
|
2301
|
+
file: filePath,
|
|
2302
|
+
violations: [],
|
|
2303
|
+
passed: true
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
const absolutePath = import_path17.default.resolve(process.cwd(), filePath);
|
|
2307
|
+
return checkFile(absolutePath, rules, process.cwd());
|
|
2308
|
+
};
|
|
2309
|
+
const getRuleCount = () => rules.length;
|
|
2310
|
+
const getRules = () => rules;
|
|
2311
|
+
return {
|
|
2312
|
+
loadRules,
|
|
2313
|
+
checkFile: checkFileImpl,
|
|
2314
|
+
getRuleCount,
|
|
2315
|
+
getRules
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
async function loadCachedRules2(projectId) {
|
|
2319
|
+
try {
|
|
2320
|
+
const cachePath = import_path17.default.join(process.cwd(), CACHE_FILE3);
|
|
2321
|
+
const content = await import_promises15.default.readFile(cachePath, "utf-8");
|
|
2322
|
+
const cached = JSON.parse(content);
|
|
2323
|
+
if (cached.projectId !== projectId) {
|
|
2324
|
+
return null;
|
|
2325
|
+
}
|
|
2326
|
+
return cached;
|
|
2327
|
+
} catch {
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
async function saveCachedRules2(projectId, rules) {
|
|
2332
|
+
try {
|
|
2333
|
+
const cacheDir = import_path17.default.join(process.cwd(), ".rigstate");
|
|
2334
|
+
await import_promises15.default.mkdir(cacheDir, { recursive: true });
|
|
2335
|
+
const cached = {
|
|
2336
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2337
|
+
projectId,
|
|
2338
|
+
rules,
|
|
2339
|
+
settings: { lmax: 400, lmax_warning: 350 }
|
|
2340
|
+
};
|
|
2341
|
+
await import_promises15.default.writeFile(
|
|
2342
|
+
import_path17.default.join(cacheDir, "rules-cache.json"),
|
|
2343
|
+
JSON.stringify(cached, null, 2)
|
|
2344
|
+
);
|
|
2345
|
+
} catch {
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
// src/daemon/bridge-listener.ts
|
|
2350
|
+
init_cjs_shims();
|
|
2351
|
+
var import_axios10 = __toESM(require("axios"), 1);
|
|
2352
|
+
var import_events2 = require("events");
|
|
2353
|
+
var POLL_INTERVAL_MS = 5e3;
|
|
2354
|
+
function createBridgeListener(projectId, apiUrl, apiKey) {
|
|
2355
|
+
const emitter = new import_events2.EventEmitter();
|
|
2356
|
+
let pollInterval = null;
|
|
2357
|
+
let isConnected = false;
|
|
2358
|
+
let lastCheckedId = null;
|
|
2359
|
+
const checkBridge = async () => {
|
|
2360
|
+
try {
|
|
2361
|
+
const response = await import_axios10.default.get(`${apiUrl}/api/v1/agent/bridge`, {
|
|
2362
|
+
params: {
|
|
2363
|
+
project_id: projectId,
|
|
2364
|
+
action: "check"
|
|
2365
|
+
},
|
|
2366
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
2367
|
+
timeout: 1e4
|
|
2368
|
+
});
|
|
2369
|
+
if (response.data.success && response.data.data?.task) {
|
|
2370
|
+
const task = response.data.data.task;
|
|
2371
|
+
if (task.id !== lastCheckedId) {
|
|
2372
|
+
lastCheckedId = task.id;
|
|
2373
|
+
if (task.proposal?.startsWith("ping")) {
|
|
2374
|
+
emitter.emit("ping");
|
|
2375
|
+
await acknowledgePing(task.id);
|
|
2376
|
+
} else {
|
|
2377
|
+
emitter.emit("task", task);
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
} catch (error) {
|
|
2382
|
+
if (error.code !== "ECONNREFUSED" && error.code !== "ETIMEDOUT") {
|
|
2383
|
+
emitter.emit("error", error);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
};
|
|
2387
|
+
const acknowledgePing = async (taskId) => {
|
|
2388
|
+
try {
|
|
2389
|
+
await import_axios10.default.post(`${apiUrl}/api/v1/agent/bridge`, {
|
|
2390
|
+
project_id: projectId,
|
|
2391
|
+
action: "update",
|
|
2392
|
+
bridge_id: taskId,
|
|
2393
|
+
status: "COMPLETED",
|
|
2394
|
+
summary: "Pong! Guardian Daemon is active."
|
|
2395
|
+
}, {
|
|
2396
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
2397
|
+
timeout: 5e3
|
|
2398
|
+
});
|
|
2399
|
+
} catch {
|
|
2400
|
+
}
|
|
2401
|
+
};
|
|
2402
|
+
emitter.connect = async () => {
|
|
2403
|
+
if (isConnected) return;
|
|
2404
|
+
await checkBridge();
|
|
2405
|
+
pollInterval = setInterval(checkBridge, POLL_INTERVAL_MS);
|
|
2406
|
+
isConnected = true;
|
|
2407
|
+
emitter.emit("connected");
|
|
2408
|
+
};
|
|
2409
|
+
emitter.disconnect = async () => {
|
|
2410
|
+
if (pollInterval) {
|
|
2411
|
+
clearInterval(pollInterval);
|
|
2412
|
+
pollInterval = null;
|
|
2413
|
+
}
|
|
2414
|
+
isConnected = false;
|
|
2415
|
+
emitter.emit("disconnected");
|
|
2416
|
+
};
|
|
2417
|
+
return emitter;
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
// src/daemon/telemetry.ts
|
|
2421
|
+
init_cjs_shims();
|
|
2422
|
+
var import_axios11 = __toESM(require("axios"), 1);
|
|
2423
|
+
async function trackSkillUsage(apiUrl, apiKey, projectId, skillId) {
|
|
2424
|
+
try {
|
|
2425
|
+
await import_axios11.default.post(`${apiUrl}/api/v1/skills/usage`, {
|
|
2426
|
+
projectId,
|
|
2427
|
+
skillName: skillId,
|
|
2428
|
+
status: "ACTIVATED"
|
|
2429
|
+
}, {
|
|
2430
|
+
headers: { "x-api-key": apiKey }
|
|
2431
|
+
});
|
|
2432
|
+
} catch (e) {
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
// src/daemon/core.ts
|
|
2437
|
+
init_skills_provisioner();
|
|
2438
|
+
var GuardianDaemon = class extends import_events3.EventEmitter {
|
|
2439
|
+
config;
|
|
2440
|
+
state;
|
|
2441
|
+
fileWatcher = null;
|
|
2442
|
+
guardianMonitor = null;
|
|
2443
|
+
heuristicEngine = null;
|
|
2444
|
+
interventionProtocol = null;
|
|
2445
|
+
bridgeListener = null;
|
|
2446
|
+
constructor(config2) {
|
|
2447
|
+
super();
|
|
2448
|
+
this.config = config2;
|
|
2449
|
+
this.state = {
|
|
2450
|
+
isRunning: false,
|
|
2451
|
+
startedAt: null,
|
|
2452
|
+
filesChecked: 0,
|
|
2453
|
+
violationsFound: 0,
|
|
2454
|
+
tasksProcessed: 0,
|
|
2455
|
+
lastActivity: null
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
async start() {
|
|
2459
|
+
if (this.state.isRunning) {
|
|
2460
|
+
console.log(import_chalk14.default.yellow("Daemon is already running."));
|
|
2461
|
+
return;
|
|
2462
|
+
}
|
|
2463
|
+
this.printWelcome();
|
|
2464
|
+
this.state.isRunning = true;
|
|
2465
|
+
this.state.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2466
|
+
this.heuristicEngine = createHeuristicEngine();
|
|
2467
|
+
this.interventionProtocol = createInterventionProtocol();
|
|
2468
|
+
this.guardianMonitor = createGuardianMonitor(this.config.projectId, this.config.apiUrl, this.config.apiKey);
|
|
2469
|
+
await this.guardianMonitor.loadRules();
|
|
2470
|
+
console.log(import_chalk14.default.green(` \u2713 Loaded ${this.guardianMonitor.getRuleCount()} rules`));
|
|
2471
|
+
await this.syncHeuristics();
|
|
2472
|
+
if (this.config.checkOnChange) {
|
|
2473
|
+
this.setupFileWatcher();
|
|
2474
|
+
}
|
|
2475
|
+
if (this.config.bridgeEnabled) {
|
|
2476
|
+
await this.setupBridge();
|
|
2477
|
+
}
|
|
2478
|
+
this.printActive();
|
|
2479
|
+
this.emit("started", this.state);
|
|
2480
|
+
}
|
|
2481
|
+
printWelcome() {
|
|
2482
|
+
console.log(import_chalk14.default.bold.blue("\n\u{1F6E1}\uFE0F Guardian Daemon Starting..."));
|
|
2483
|
+
console.log(import_chalk14.default.dim(`Project: ${this.config.projectId}`));
|
|
2484
|
+
console.log(import_chalk14.default.dim(`Watch Path: ${this.config.watchPath}`));
|
|
2485
|
+
console.log(import_chalk14.default.dim("\u2500".repeat(50)));
|
|
2486
|
+
}
|
|
2487
|
+
printActive() {
|
|
2488
|
+
console.log(import_chalk14.default.dim("\u2500".repeat(50)));
|
|
2489
|
+
console.log(import_chalk14.default.green.bold("\u2705 Guardian Daemon is now active"));
|
|
2490
|
+
console.log(import_chalk14.default.dim("Press Ctrl+C to stop\n"));
|
|
2491
|
+
}
|
|
2492
|
+
async syncHeuristics() {
|
|
2493
|
+
if (!this.heuristicEngine) return;
|
|
2494
|
+
const synced = await this.heuristicEngine.refreshRules(this.config.projectId, this.config.apiUrl, this.config.apiKey);
|
|
2495
|
+
if (synced) console.log(import_chalk14.default.green(" \u2713 Synced heuristic rules"));
|
|
2496
|
+
}
|
|
2497
|
+
setupFileWatcher() {
|
|
2498
|
+
console.log(import_chalk14.default.dim("\u{1F4C2} Starting file watcher..."));
|
|
2499
|
+
this.fileWatcher = createFileWatcher(this.config.watchPath);
|
|
2500
|
+
this.fileWatcher.on("change", (path23) => this.handleFileChange(path23));
|
|
2501
|
+
this.fileWatcher.start();
|
|
2502
|
+
console.log(import_chalk14.default.green(" \u2713 File watcher active"));
|
|
2503
|
+
}
|
|
2504
|
+
async handleFileChange(filePath) {
|
|
2505
|
+
this.state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
2506
|
+
if (this.config.verbose) console.log(import_chalk14.default.dim(` \u{1F4DD} File changed: ${filePath}`));
|
|
2507
|
+
let lineCount = 0;
|
|
2508
|
+
try {
|
|
2509
|
+
const content = await fs16.readFile(filePath, "utf-8");
|
|
2510
|
+
lineCount = content.split("\n").length;
|
|
2511
|
+
} catch (e) {
|
|
2512
|
+
}
|
|
2513
|
+
if (this.heuristicEngine && this.interventionProtocol && this.guardianMonitor) {
|
|
2514
|
+
const matches = await this.heuristicEngine.analyzeFile(filePath, {
|
|
2515
|
+
lineCount,
|
|
2516
|
+
rules: this.guardianMonitor.getRules()
|
|
2517
|
+
});
|
|
2518
|
+
for (const match of matches) {
|
|
2519
|
+
console.log(import_chalk14.default.magenta(` \u{1F4A1} PREDICTIVE ACTIVATION: ${match.skillId}`));
|
|
2520
|
+
console.log(import_chalk14.default.dim(` Reason: ${match.reason}`));
|
|
2521
|
+
const decision = this.interventionProtocol.evaluateTrigger(match.skillId, match.confidence);
|
|
2522
|
+
this.interventionProtocol.enforce(decision);
|
|
2523
|
+
await jitProvisionSkill(match.skillId, this.config.apiUrl, this.config.apiKey, this.config.projectId, process.cwd());
|
|
2524
|
+
await trackSkillUsage(this.config.apiUrl, this.config.apiKey, this.config.projectId, match.skillId);
|
|
2525
|
+
this.emit("skill:suggestion", match);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
if (this.guardianMonitor) {
|
|
2529
|
+
if (this.interventionProtocol) this.interventionProtocol.clear(filePath);
|
|
2530
|
+
const result = await this.guardianMonitor.checkFile(filePath);
|
|
2531
|
+
this.state.filesChecked++;
|
|
2532
|
+
if (result.violations.length > 0) {
|
|
2533
|
+
this.state.violationsFound += result.violations.length;
|
|
2534
|
+
this.emit("violation", { file: filePath, violations: result.violations });
|
|
2535
|
+
for (const v of result.violations) {
|
|
2536
|
+
const color = v.severity === "critical" ? import_chalk14.default.red : v.severity === "warning" ? import_chalk14.default.yellow : import_chalk14.default.blue;
|
|
2537
|
+
console.log(color(` [${v.severity.toUpperCase()}] ${filePath}: ${v.message}`));
|
|
2538
|
+
if (this.interventionProtocol) {
|
|
2539
|
+
const decision = this.interventionProtocol.evaluateViolation(v.message, v.severity);
|
|
2540
|
+
this.interventionProtocol.enforce(decision);
|
|
2541
|
+
this.interventionProtocol.registerViolation(filePath, decision);
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
async setupBridge() {
|
|
2548
|
+
console.log(import_chalk14.default.dim("\u{1F309} Connecting to Agent Bridge..."));
|
|
2549
|
+
this.bridgeListener = createBridgeListener(this.config.projectId, this.config.apiUrl, this.config.apiKey);
|
|
2550
|
+
this.bridgeListener.on("task", (task) => {
|
|
2551
|
+
this.state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
2552
|
+
this.state.tasksProcessed++;
|
|
2553
|
+
console.log(import_chalk14.default.cyan(`
|
|
2554
|
+
\u{1F4E5} New task received: ${task.id}`));
|
|
2555
|
+
this.emit("task", task);
|
|
2556
|
+
});
|
|
2557
|
+
await this.bridgeListener.connect();
|
|
2558
|
+
console.log(import_chalk14.default.green(" \u2713 Agent Bridge connected"));
|
|
2559
|
+
}
|
|
2560
|
+
async stop() {
|
|
2561
|
+
if (!this.state.isRunning) return;
|
|
2562
|
+
console.log(import_chalk14.default.dim("\n\u{1F6D1} Stopping Guardian Daemon..."));
|
|
2563
|
+
if (this.fileWatcher) await this.fileWatcher.stop();
|
|
2564
|
+
if (this.bridgeListener) await this.bridgeListener.disconnect();
|
|
2565
|
+
this.state.isRunning = false;
|
|
2566
|
+
console.log(import_chalk14.default.green("\u2713 Daemon stopped."));
|
|
2567
|
+
this.emit("stopped", this.state);
|
|
2568
|
+
}
|
|
2569
|
+
getState() {
|
|
2570
|
+
return { ...this.state };
|
|
2571
|
+
}
|
|
2572
|
+
};
|
|
2573
|
+
|
|
2574
|
+
// src/daemon/factory.ts
|
|
2575
|
+
init_config();
|
|
2576
|
+
async function createDaemon(options) {
|
|
2577
|
+
const apiUrl = getApiUrl();
|
|
2578
|
+
let projectId = options.project;
|
|
2579
|
+
if (!projectId) {
|
|
2580
|
+
const manifest = await loadManifest();
|
|
2581
|
+
if (manifest) projectId = manifest.project_id;
|
|
2582
|
+
}
|
|
2583
|
+
if (!projectId) projectId = getProjectId();
|
|
2584
|
+
if (!projectId) {
|
|
2585
|
+
throw new Error('No project ID found. Run "rigstate link" or use --project <id>.');
|
|
2586
|
+
}
|
|
2587
|
+
const apiKey = getApiKey();
|
|
2588
|
+
if (!apiKey) {
|
|
2589
|
+
throw new Error('Not authenticated. Run "rigstate login" first.');
|
|
2590
|
+
}
|
|
2591
|
+
const config2 = {
|
|
2592
|
+
projectId,
|
|
2593
|
+
apiUrl,
|
|
2594
|
+
apiKey,
|
|
2595
|
+
watchPath: options.path || process.cwd(),
|
|
2596
|
+
checkOnChange: true,
|
|
2597
|
+
bridgeEnabled: !options.noBridge,
|
|
2598
|
+
verbose: !!options.verbose
|
|
2599
|
+
};
|
|
2600
|
+
return new GuardianDaemon(config2);
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// src/commands/daemon.ts
|
|
2604
|
+
var PID_FILE = ".rigstate/daemon.pid";
|
|
2605
|
+
var STATE_FILE = ".rigstate/daemon.state.json";
|
|
2606
|
+
function createDaemonCommand() {
|
|
2607
|
+
const daemon = new import_commander9.Command("daemon").description("Start the Guardian daemon for continuous monitoring");
|
|
2608
|
+
daemon.argument("[action]", "Action: start (default) or status", "start").option("--project <id>", "Project ID (or use .rigstate manifest)").option("--path <path>", "Path to watch", ".").option("--no-bridge", "Disable Agent Bridge connection").option("--verbose", "Enable verbose output").action(async (action, options) => {
|
|
2609
|
+
if (action === "status") {
|
|
2610
|
+
await showStatus();
|
|
2611
|
+
return;
|
|
2612
|
+
}
|
|
2613
|
+
const spinner = (0, import_ora6.default)();
|
|
2614
|
+
try {
|
|
2615
|
+
if (await isRunning()) {
|
|
2616
|
+
console.log(import_chalk15.default.yellow("\u26A0 Another daemon instance may be running."));
|
|
2617
|
+
console.log(import_chalk15.default.dim(` Check ${PID_FILE} or run "rigstate daemon status"`));
|
|
2618
|
+
console.log(import_chalk15.default.dim(" Use Ctrl+C to stop the running daemon first.\n"));
|
|
2619
|
+
}
|
|
2620
|
+
spinner.start("Initializing Guardian Daemon...");
|
|
2621
|
+
const daemonInstance = await createDaemon({
|
|
2622
|
+
project: options.project,
|
|
2623
|
+
path: options.path,
|
|
2624
|
+
noBridge: options.bridge === false,
|
|
2625
|
+
verbose: options.verbose
|
|
2626
|
+
});
|
|
2627
|
+
spinner.stop();
|
|
2628
|
+
await writePidFile();
|
|
2629
|
+
process.on("SIGINT", async () => {
|
|
2630
|
+
console.log(import_chalk15.default.dim("\n\nShutting down..."));
|
|
2631
|
+
await daemonInstance.stop();
|
|
2632
|
+
await cleanupPidFile();
|
|
2633
|
+
process.exit(0);
|
|
2634
|
+
});
|
|
2635
|
+
process.on("SIGTERM", async () => {
|
|
2636
|
+
await daemonInstance.stop();
|
|
2637
|
+
await cleanupPidFile();
|
|
2638
|
+
process.exit(0);
|
|
2639
|
+
});
|
|
2640
|
+
const stateInterval = setInterval(async () => {
|
|
2641
|
+
await writeStateFile(daemonInstance.getState());
|
|
2642
|
+
}, 5e3);
|
|
2643
|
+
daemonInstance.on("stopped", () => {
|
|
2644
|
+
clearInterval(stateInterval);
|
|
2645
|
+
});
|
|
2646
|
+
await daemonInstance.start();
|
|
2647
|
+
await new Promise(() => {
|
|
2648
|
+
});
|
|
2649
|
+
} catch (error) {
|
|
2650
|
+
spinner.fail(import_chalk15.default.red("Failed to start daemon"));
|
|
2651
|
+
console.error(import_chalk15.default.red("Error:"), error.message);
|
|
2652
|
+
process.exit(1);
|
|
2653
|
+
}
|
|
2654
|
+
});
|
|
2655
|
+
return daemon;
|
|
2656
|
+
}
|
|
2657
|
+
async function isRunning() {
|
|
2658
|
+
try {
|
|
2659
|
+
const pidPath = import_path18.default.join(process.cwd(), PID_FILE);
|
|
2660
|
+
const content = await import_promises16.default.readFile(pidPath, "utf-8");
|
|
2661
|
+
const pid = parseInt(content.trim(), 10);
|
|
2662
|
+
try {
|
|
2663
|
+
process.kill(pid, 0);
|
|
2664
|
+
return true;
|
|
2665
|
+
} catch {
|
|
2666
|
+
await import_promises16.default.unlink(pidPath);
|
|
2667
|
+
return false;
|
|
2668
|
+
}
|
|
2669
|
+
} catch {
|
|
2670
|
+
return false;
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
async function writePidFile() {
|
|
2674
|
+
try {
|
|
2675
|
+
const dir = import_path18.default.join(process.cwd(), ".rigstate");
|
|
2676
|
+
await import_promises16.default.mkdir(dir, { recursive: true });
|
|
2677
|
+
await import_promises16.default.writeFile(import_path18.default.join(dir, "daemon.pid"), process.pid.toString());
|
|
2678
|
+
} catch {
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
async function cleanupPidFile() {
|
|
2682
|
+
try {
|
|
2683
|
+
await import_promises16.default.unlink(import_path18.default.join(process.cwd(), PID_FILE));
|
|
2684
|
+
await import_promises16.default.unlink(import_path18.default.join(process.cwd(), STATE_FILE));
|
|
2685
|
+
} catch {
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
async function writeStateFile(state) {
|
|
2689
|
+
try {
|
|
2690
|
+
const dir = import_path18.default.join(process.cwd(), ".rigstate");
|
|
2691
|
+
await import_promises16.default.mkdir(dir, { recursive: true });
|
|
2692
|
+
await import_promises16.default.writeFile(
|
|
2693
|
+
import_path18.default.join(dir, "daemon.state.json"),
|
|
2694
|
+
JSON.stringify(state, null, 2)
|
|
2695
|
+
);
|
|
2696
|
+
} catch {
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
async function showStatus() {
|
|
2700
|
+
console.log(import_chalk15.default.bold("\n\u{1F6E1}\uFE0F Guardian Daemon Status\n"));
|
|
2701
|
+
const running = await isRunning();
|
|
2702
|
+
if (!running) {
|
|
2703
|
+
console.log(import_chalk15.default.yellow("Status: Not running"));
|
|
2704
|
+
console.log(import_chalk15.default.dim('Use "rigstate daemon" to start.\n'));
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2707
|
+
console.log(import_chalk15.default.green("Status: Running"));
|
|
2708
|
+
try {
|
|
2709
|
+
const statePath = import_path18.default.join(process.cwd(), STATE_FILE);
|
|
2710
|
+
const content = await import_promises16.default.readFile(statePath, "utf-8");
|
|
2711
|
+
const state = JSON.parse(content);
|
|
2712
|
+
console.log(import_chalk15.default.dim("\u2500".repeat(40)));
|
|
2713
|
+
console.log(`Started at: ${state.startedAt || "Unknown"}`);
|
|
2714
|
+
console.log(`Files checked: ${state.filesChecked || 0}`);
|
|
2715
|
+
console.log(`Violations: ${state.violationsFound || 0}`);
|
|
2716
|
+
console.log(`Tasks processed: ${state.tasksProcessed || 0}`);
|
|
2717
|
+
console.log(`Last activity: ${state.lastActivity || "None"}`);
|
|
2718
|
+
console.log(import_chalk15.default.dim("\u2500".repeat(40)));
|
|
2719
|
+
} catch {
|
|
2720
|
+
console.log(import_chalk15.default.dim("(State file not found)"));
|
|
2721
|
+
}
|
|
2722
|
+
try {
|
|
2723
|
+
const pidPath = import_path18.default.join(process.cwd(), PID_FILE);
|
|
2724
|
+
const pid = await import_promises16.default.readFile(pidPath, "utf-8");
|
|
2725
|
+
console.log(import_chalk15.default.dim(`PID: ${pid.trim()}`));
|
|
2726
|
+
} catch {
|
|
2727
|
+
}
|
|
2728
|
+
console.log("");
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// src/commands/work.ts
|
|
2732
|
+
init_cjs_shims();
|
|
2733
|
+
var import_commander10 = require("commander");
|
|
2734
|
+
var import_chalk16 = __toESM(require("chalk"), 1);
|
|
2735
|
+
var import_ora7 = __toESM(require("ora"), 1);
|
|
2736
|
+
var import_axios12 = __toESM(require("axios"), 1);
|
|
2737
|
+
var import_inquirer2 = __toESM(require("inquirer"), 1);
|
|
2738
|
+
var import_promises17 = __toESM(require("fs/promises"), 1);
|
|
2739
|
+
init_config();
|
|
2740
|
+
function createWorkCommand() {
|
|
2741
|
+
return new import_commander10.Command("work").alias("start").description("Select and execute a Roadmap Task (fetches IDE Prompt)").argument("[taskId]", "Optional Task ID (e.g., T-1021) to start immediately").option("--project <id>", "Project ID").action(async (taskId, options) => {
|
|
2742
|
+
const spinner = (0, import_ora7.default)();
|
|
2743
|
+
try {
|
|
2744
|
+
const apiKey = getApiKey();
|
|
2745
|
+
const apiUrl = getApiUrl();
|
|
2746
|
+
const projectId = options.project || getProjectId();
|
|
2747
|
+
if (!projectId) {
|
|
2748
|
+
console.log(import_chalk16.default.red("\u274C Project ID is required. Run `rigstate link` or pass --project <id>"));
|
|
2749
|
+
process.exit(1);
|
|
2750
|
+
}
|
|
2751
|
+
if (!taskId) {
|
|
2752
|
+
spinner.start("Fetching active roadmap tasks...");
|
|
2753
|
+
}
|
|
2754
|
+
const response = await import_axios12.default.get(
|
|
2755
|
+
`${apiUrl}/api/v1/roadmap?project_id=${projectId}`,
|
|
2756
|
+
{ headers: { "Authorization": `Bearer ${apiKey}` }, timeout: 1e4 }
|
|
2757
|
+
);
|
|
2758
|
+
if (!response.data.success) {
|
|
2759
|
+
throw new Error(response.data.error || "Failed to fetch roadmap");
|
|
2760
|
+
}
|
|
2761
|
+
const allTasks = response.data.data.roadmap || [];
|
|
2762
|
+
const actionableTasks = allTasks.filter((t) => ["ACTIVE", "LOCKED"].includes(t.status)).sort((a, b) => {
|
|
2763
|
+
if (a.status === "ACTIVE" && b.status !== "ACTIVE") return -1;
|
|
2764
|
+
if (b.status === "ACTIVE" && a.status !== "ACTIVE") return 1;
|
|
2765
|
+
return a.step_number - b.step_number;
|
|
2766
|
+
});
|
|
2767
|
+
spinner.stop();
|
|
2768
|
+
let selectedTask;
|
|
2769
|
+
if (taskId) {
|
|
2770
|
+
selectedTask = allTasks.find(
|
|
2771
|
+
(t) => t.id === taskId || `T-${t.step_number}` === taskId || t.step_number.toString() === taskId
|
|
2772
|
+
);
|
|
2773
|
+
if (!selectedTask) {
|
|
2774
|
+
console.log(import_chalk16.default.red(`\u274C Task '${taskId}' not found in roadmap.`));
|
|
2775
|
+
return;
|
|
2776
|
+
}
|
|
2777
|
+
} else {
|
|
2778
|
+
if (actionableTasks.length === 0) {
|
|
2779
|
+
console.log(import_chalk16.default.yellow("No active or locked tasks found. The Roadmap is clear! \u{1F389}"));
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2782
|
+
const choices = actionableTasks.map((t) => {
|
|
2783
|
+
const id = `T-${t.step_number}`;
|
|
2784
|
+
const statusIcon = t.status === "ACTIVE" ? "\u25B6\uFE0F" : "\u{1F512}";
|
|
2785
|
+
const priority = t.priority === "MVP" ? import_chalk16.default.magenta("[MVP]") : import_chalk16.default.blue(`[${t.priority}]`);
|
|
2786
|
+
return {
|
|
2787
|
+
name: `${statusIcon} ${import_chalk16.default.bold(id)}: ${t.title} ${priority}`,
|
|
2788
|
+
value: t,
|
|
2789
|
+
short: `${id}: ${t.title}`
|
|
2790
|
+
};
|
|
2791
|
+
});
|
|
2792
|
+
const answer = await import_inquirer2.default.prompt([{
|
|
2793
|
+
type: "list",
|
|
2794
|
+
name: "task",
|
|
2795
|
+
message: "Which task are you working on?",
|
|
2796
|
+
choices,
|
|
2797
|
+
pageSize: 15
|
|
2798
|
+
}]);
|
|
2799
|
+
selectedTask = answer.task;
|
|
2800
|
+
}
|
|
2801
|
+
console.log("\n" + import_chalk16.default.bold.underline(`\u{1F680} WORK MODE: ${selectedTask.title}`));
|
|
2802
|
+
console.log(import_chalk16.default.dim(`ID: T-${selectedTask.step_number} | Status: ${selectedTask.status}`));
|
|
2803
|
+
if (selectedTask.prompt_content) {
|
|
2804
|
+
console.log(import_chalk16.default.yellow.bold("\n\u{1F4CB} IDE EXECUTION SIGNAL (Prompt):"));
|
|
2805
|
+
console.log(import_chalk16.default.gray("--------------------------------------------------"));
|
|
2806
|
+
console.log(selectedTask.prompt_content);
|
|
2807
|
+
console.log(import_chalk16.default.gray("--------------------------------------------------"));
|
|
2808
|
+
const { action } = await import_inquirer2.default.prompt([{
|
|
2809
|
+
type: "list",
|
|
2810
|
+
name: "action",
|
|
2811
|
+
message: "What do you want to do?",
|
|
2812
|
+
choices: [
|
|
2813
|
+
{ name: "Copy Prompt (Print clean)", value: "print" },
|
|
2814
|
+
{ name: "Create .cursorrules (Agent Context)", value: "cursorrules" },
|
|
2815
|
+
{ name: "Mark as ACTIVE (if LOCKED)", value: "activate" },
|
|
2816
|
+
{ name: "Mark as COMPLETED", value: "complete" },
|
|
2817
|
+
{ name: "Cancel", value: "cancel" }
|
|
2818
|
+
]
|
|
2819
|
+
}]);
|
|
2820
|
+
if (action === "cursorrules") {
|
|
2821
|
+
await import_promises17.default.writeFile(".rigstate-prompt.md", selectedTask.prompt_content);
|
|
2822
|
+
console.log(import_chalk16.default.green(`\u2705 Prompt saved to ${import_chalk16.default.bold(".rigstate-prompt.md")}`));
|
|
2823
|
+
console.log(import_chalk16.default.dim("You can now reference this file in your IDE chat (@.rigstate-prompt.md)"));
|
|
2824
|
+
} else if (action === "print") {
|
|
2825
|
+
console.log("\n" + selectedTask.prompt_content + "\n");
|
|
2826
|
+
} else if (action === "activate" && selectedTask.status !== "ACTIVE") {
|
|
2827
|
+
try {
|
|
2828
|
+
await import_axios12.default.post(
|
|
2829
|
+
`${apiUrl}/api/v1/roadmap/update-status`,
|
|
2830
|
+
{ step_id: selectedTask.id, status: "ACTIVE", project_id: projectId },
|
|
2831
|
+
{ headers: { "Authorization": `Bearer ${apiKey}` } }
|
|
2832
|
+
);
|
|
2833
|
+
console.log(import_chalk16.default.green(`\u2705 Task marked as ACTIVE.`));
|
|
2834
|
+
} catch (e) {
|
|
2835
|
+
console.error(import_chalk16.default.red(`Failed to update status: ${e.message}`));
|
|
2836
|
+
}
|
|
2837
|
+
} else if (action === "complete") {
|
|
2838
|
+
try {
|
|
2839
|
+
await import_axios12.default.post(
|
|
2840
|
+
`${apiUrl}/api/v1/roadmap/update-status`,
|
|
2841
|
+
{ step_id: selectedTask.id, status: "COMPLETED", project_id: projectId },
|
|
2842
|
+
{ headers: { "Authorization": `Bearer ${apiKey}` } }
|
|
2843
|
+
);
|
|
2844
|
+
console.log(import_chalk16.default.green(`\u2705 Task marked as COMPLETED. Great job!`));
|
|
2845
|
+
} catch (e) {
|
|
2846
|
+
console.error(import_chalk16.default.red(`Failed to update status: ${e.message}`));
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
} else {
|
|
2850
|
+
console.log(import_chalk16.default.yellow("\n\u26A0\uFE0F No specific IDE Prompt found for this task (Legacy Task?)."));
|
|
2851
|
+
console.log(import_chalk16.default.dim("Objective: " + (selectedTask.summary || selectedTask.description || "Check web UI for details.")));
|
|
2852
|
+
}
|
|
2853
|
+
} catch (error) {
|
|
2854
|
+
spinner.stop();
|
|
2855
|
+
console.error(import_chalk16.default.red(`
|
|
2856
|
+
Command failed: ${error.message}`));
|
|
2857
|
+
}
|
|
2858
|
+
});
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
// src/commands/watch.ts
|
|
2862
|
+
init_cjs_shims();
|
|
2863
|
+
var import_commander11 = require("commander");
|
|
2864
|
+
var import_chalk17 = __toESM(require("chalk"), 1);
|
|
2865
|
+
var import_ora8 = __toESM(require("ora"), 1);
|
|
2866
|
+
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
2867
|
+
var import_promises18 = __toESM(require("fs/promises"), 1);
|
|
2868
|
+
var import_path19 = __toESM(require("path"), 1);
|
|
2869
|
+
var import_child_process3 = require("child_process");
|
|
2870
|
+
init_config();
|
|
2871
|
+
var import_axios13 = __toESM(require("axios"), 1);
|
|
2872
|
+
function createWatchCommand() {
|
|
2873
|
+
const watch2 = new import_commander11.Command("watch");
|
|
2874
|
+
watch2.description("Watch for changes and auto-verify roadmap tasks").option("--no-auto-commit", "Disable auto-commit on verification").option("--no-auto-push", "Disable auto-push after commit").option("--run-tests", "Run tests before committing").option("--test-command <cmd>", "Custom test command (default: npm test)").action(async (options) => {
|
|
2875
|
+
console.log(import_chalk17.default.bold.blue("\u{1F52D} Rigstate Watch Mode"));
|
|
2876
|
+
console.log(import_chalk17.default.dim("Monitoring for task completion..."));
|
|
2877
|
+
console.log("");
|
|
2878
|
+
let apiKey;
|
|
2879
|
+
let projectId;
|
|
2880
|
+
try {
|
|
2881
|
+
apiKey = getApiKey();
|
|
2882
|
+
} catch (e) {
|
|
2883
|
+
console.log(import_chalk17.default.red('Not authenticated. Run "rigstate login" first.'));
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
projectId = getProjectId();
|
|
2887
|
+
if (!projectId) {
|
|
2888
|
+
try {
|
|
2889
|
+
const manifestPath = import_path19.default.join(process.cwd(), ".rigstate");
|
|
2890
|
+
const content = await import_promises18.default.readFile(manifestPath, "utf-8");
|
|
2891
|
+
const manifest = JSON.parse(content);
|
|
2892
|
+
projectId = manifest.project_id;
|
|
2893
|
+
} catch (e) {
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
if (!projectId) {
|
|
2897
|
+
console.log(import_chalk17.default.red('No project context. Run "rigstate link" or "rigstate sync --project <id>" first.'));
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
const apiUrl = getApiUrl();
|
|
2901
|
+
const config2 = {
|
|
2902
|
+
autoCommit: options.autoCommit !== false,
|
|
2903
|
+
autoPush: options.autoPush !== false,
|
|
2904
|
+
runTests: options.runTests || false,
|
|
2905
|
+
testCommand: options.testCommand || "npm test"
|
|
2906
|
+
};
|
|
2907
|
+
console.log(import_chalk17.default.dim(`Auto-commit: ${config2.autoCommit ? "ON" : "OFF"}`));
|
|
2908
|
+
console.log(import_chalk17.default.dim(`Auto-push: ${config2.autoPush ? "ON" : "OFF"}`));
|
|
2909
|
+
console.log("");
|
|
2910
|
+
const fetchActiveTask = async () => {
|
|
2911
|
+
try {
|
|
2912
|
+
const response = await import_axios13.default.get(`${apiUrl}/api/v1/roadmap`, {
|
|
2913
|
+
params: { project_id: projectId },
|
|
2914
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
2915
|
+
});
|
|
2916
|
+
if (!response.data.success) return null;
|
|
2917
|
+
const roadmap = response.data.data.roadmap || [];
|
|
2918
|
+
const statusPriority = {
|
|
2919
|
+
"IN_PROGRESS": 0,
|
|
2920
|
+
"ACTIVE": 1,
|
|
2921
|
+
"LOCKED": 2
|
|
2922
|
+
};
|
|
2923
|
+
const activeTasks = roadmap.filter((t) => ["IN_PROGRESS", "ACTIVE", "LOCKED"].includes(t.status)).sort((a, b) => {
|
|
2924
|
+
const pA = statusPriority[a.status] ?? 99;
|
|
2925
|
+
const pB = statusPriority[b.status] ?? 99;
|
|
2926
|
+
if (pA !== pB) return pA - pB;
|
|
2927
|
+
return (a.step_number || 0) - (b.step_number || 0);
|
|
2928
|
+
});
|
|
2929
|
+
return activeTasks[0] || null;
|
|
2930
|
+
} catch (e) {
|
|
2931
|
+
return null;
|
|
2932
|
+
}
|
|
2933
|
+
};
|
|
2934
|
+
const checkCriteria = async (criteria) => {
|
|
2935
|
+
try {
|
|
2936
|
+
const fullPath = import_path19.default.resolve(process.cwd(), criteria.path);
|
|
2937
|
+
switch (criteria.type) {
|
|
2938
|
+
case "file_exists":
|
|
2939
|
+
await import_promises18.default.access(fullPath);
|
|
2940
|
+
return true;
|
|
2941
|
+
case "file_content":
|
|
2942
|
+
const content = await import_promises18.default.readFile(fullPath, "utf-8");
|
|
2943
|
+
return content.length > 0;
|
|
2944
|
+
case "content_match":
|
|
2945
|
+
if (!criteria.match) return false;
|
|
2946
|
+
const fileContent = await import_promises18.default.readFile(fullPath, "utf-8");
|
|
2947
|
+
return fileContent.includes(criteria.match);
|
|
2948
|
+
default:
|
|
2949
|
+
return false;
|
|
2950
|
+
}
|
|
2951
|
+
} catch (e) {
|
|
2952
|
+
return false;
|
|
2953
|
+
}
|
|
2954
|
+
};
|
|
2955
|
+
const completeTask = async (taskId, task) => {
|
|
2956
|
+
const spinner = (0, import_ora8.default)("Completing task...").start();
|
|
2957
|
+
try {
|
|
2958
|
+
if (config2.runTests) {
|
|
2959
|
+
spinner.text = "Running tests...";
|
|
2960
|
+
try {
|
|
2961
|
+
(0, import_child_process3.execSync)(config2.testCommand, { stdio: "pipe" });
|
|
2962
|
+
spinner.text = "Tests passed!";
|
|
2963
|
+
} catch (e) {
|
|
2964
|
+
spinner.fail("Tests failed. Task not completed.");
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
await import_axios13.default.post(`${apiUrl}/api/v1/roadmap/update-status`, {
|
|
2969
|
+
project_id: projectId,
|
|
2970
|
+
chunk_id: taskId,
|
|
2971
|
+
status: "COMPLETED"
|
|
2972
|
+
}, {
|
|
2973
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
2974
|
+
});
|
|
2975
|
+
spinner.succeed(import_chalk17.default.green(`\u2705 Task #${task.step_number} completed: ${task.title}`));
|
|
2976
|
+
if (config2.autoCommit) {
|
|
2977
|
+
spinner.start("Committing changes...");
|
|
2978
|
+
try {
|
|
2979
|
+
(0, import_child_process3.execSync)("git add -A", { stdio: "pipe" });
|
|
2980
|
+
const commitMsg = `feat: Complete task #${task.step_number} - ${task.title}`;
|
|
2981
|
+
(0, import_child_process3.execSync)(`git commit -m "${commitMsg}"`, { stdio: "pipe" });
|
|
2982
|
+
spinner.succeed("Changes committed");
|
|
2983
|
+
if (config2.autoPush) {
|
|
2984
|
+
spinner.start("Pushing to remote...");
|
|
2985
|
+
try {
|
|
2986
|
+
(0, import_child_process3.execSync)("git push", { stdio: "pipe" });
|
|
2987
|
+
spinner.succeed("Pushed to remote");
|
|
2988
|
+
} catch (e) {
|
|
2989
|
+
spinner.warn("Push failed (no remote or conflict)");
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
} catch (e) {
|
|
2993
|
+
spinner.warn("Nothing to commit or commit failed");
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
console.log("");
|
|
2997
|
+
console.log(import_chalk17.default.blue("Watching for next task..."));
|
|
2998
|
+
} catch (e) {
|
|
2999
|
+
spinner.fail(`Failed to complete task: ${e.message}`);
|
|
3000
|
+
}
|
|
3001
|
+
};
|
|
3002
|
+
let currentTask = null;
|
|
3003
|
+
let isProcessing = false;
|
|
3004
|
+
const processActiveTask = async () => {
|
|
3005
|
+
if (isProcessing) return;
|
|
3006
|
+
isProcessing = true;
|
|
3007
|
+
const task = await fetchActiveTask();
|
|
3008
|
+
if (!task) {
|
|
3009
|
+
if (currentTask) {
|
|
3010
|
+
console.log(import_chalk17.default.green("\u{1F389} All tasks completed! Watching for new tasks..."));
|
|
3011
|
+
currentTask = null;
|
|
3012
|
+
}
|
|
3013
|
+
isProcessing = false;
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
if (!currentTask || currentTask.id !== task.id) {
|
|
3017
|
+
currentTask = task;
|
|
3018
|
+
console.log("");
|
|
3019
|
+
console.log(import_chalk17.default.bold.yellow(`\u{1F4CC} Active Task #${task.step_number}: ${task.title}`));
|
|
3020
|
+
console.log(import_chalk17.default.dim(`Status: ${task.status}`));
|
|
3021
|
+
if (task.verification_criteria) {
|
|
3022
|
+
console.log(import_chalk17.default.dim("Verification: Auto-checking criteria..."));
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
if (task.verification_criteria && Array.isArray(task.verification_criteria)) {
|
|
3026
|
+
let allPassed = true;
|
|
3027
|
+
for (const criteria of task.verification_criteria) {
|
|
3028
|
+
const passed = await checkCriteria(criteria);
|
|
3029
|
+
if (!passed) {
|
|
3030
|
+
allPassed = false;
|
|
3031
|
+
break;
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
if (allPassed) {
|
|
3035
|
+
console.log(import_chalk17.default.green("\u2713 All verification criteria passed!"));
|
|
3036
|
+
await completeTask(task.id, task);
|
|
3037
|
+
currentTask = null;
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
isProcessing = false;
|
|
3041
|
+
};
|
|
3042
|
+
await processActiveTask();
|
|
3043
|
+
const watcher = import_chokidar.default.watch(".", {
|
|
3044
|
+
ignored: [
|
|
3045
|
+
/(^|[\/\\])\../,
|
|
3046
|
+
// dotfiles
|
|
3047
|
+
"**/node_modules/**",
|
|
3048
|
+
"**/.git/**",
|
|
3049
|
+
"**/.next/**",
|
|
3050
|
+
"**/dist/**"
|
|
3051
|
+
],
|
|
3052
|
+
persistent: true,
|
|
3053
|
+
ignoreInitial: true
|
|
3054
|
+
});
|
|
3055
|
+
watcher.on("all", async (event, filePath) => {
|
|
3056
|
+
if (["add", "change", "unlink"].includes(event)) {
|
|
3057
|
+
setTimeout(() => processActiveTask(), 500);
|
|
3058
|
+
}
|
|
3059
|
+
});
|
|
3060
|
+
console.log(import_chalk17.default.dim("Watching for file changes... (Ctrl+C to exit)"));
|
|
3061
|
+
setInterval(() => processActiveTask(), 3e4);
|
|
3062
|
+
process.on("SIGINT", () => {
|
|
3063
|
+
console.log("");
|
|
3064
|
+
console.log(import_chalk17.default.dim("Watch mode stopped."));
|
|
3065
|
+
watcher.close();
|
|
3066
|
+
process.exit(0);
|
|
3067
|
+
});
|
|
3068
|
+
});
|
|
3069
|
+
return watch2;
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
// src/commands/focus.ts
|
|
3073
|
+
init_cjs_shims();
|
|
3074
|
+
var import_commander12 = require("commander");
|
|
3075
|
+
var import_chalk18 = __toESM(require("chalk"), 1);
|
|
3076
|
+
var import_ora9 = __toESM(require("ora"), 1);
|
|
3077
|
+
init_config();
|
|
3078
|
+
var import_axios14 = __toESM(require("axios"), 1);
|
|
3079
|
+
var import_child_process4 = require("child_process");
|
|
3080
|
+
var import_promises19 = __toESM(require("fs/promises"), 1);
|
|
3081
|
+
var import_path20 = __toESM(require("path"), 1);
|
|
3082
|
+
function createFocusCommand() {
|
|
3083
|
+
const focus = new import_commander12.Command("focus");
|
|
3084
|
+
focus.alias("task").description("Get the next active roadmap task and copy its prompt to clipboard").option("--no-copy", "Do not copy to clipboard").action(async (options) => {
|
|
3085
|
+
const spinner = (0, import_ora9.default)("Fetching next objective...").start();
|
|
3086
|
+
let apiKey;
|
|
3087
|
+
let projectId;
|
|
3088
|
+
try {
|
|
3089
|
+
apiKey = getApiKey();
|
|
3090
|
+
} catch (e) {
|
|
3091
|
+
spinner.fail(import_chalk18.default.red('Not authenticated. Run "rigstate login" first.'));
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
projectId = getProjectId();
|
|
3095
|
+
if (!projectId) {
|
|
3096
|
+
try {
|
|
3097
|
+
const manifestPath = import_path20.default.join(process.cwd(), ".rigstate");
|
|
3098
|
+
const content = await import_promises19.default.readFile(manifestPath, "utf-8");
|
|
3099
|
+
const manifest = JSON.parse(content);
|
|
3100
|
+
projectId = manifest.project_id;
|
|
3101
|
+
} catch (e) {
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
if (!projectId) {
|
|
3105
|
+
spinner.fail(import_chalk18.default.red('No project context. Run "rigstate link" first.'));
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
const apiUrl = getApiUrl();
|
|
3109
|
+
try {
|
|
3110
|
+
const response = await import_axios14.default.get(`${apiUrl}/api/v1/roadmap`, {
|
|
3111
|
+
params: { project_id: projectId },
|
|
3112
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3113
|
+
});
|
|
3114
|
+
if (!response.data.success) {
|
|
3115
|
+
throw new Error(response.data.error || "Failed to fetch roadmap");
|
|
3116
|
+
}
|
|
3117
|
+
const roadmap = response.data.data.roadmap || [];
|
|
3118
|
+
const statusPriority = {
|
|
3119
|
+
"IN_PROGRESS": 0,
|
|
3120
|
+
"ACTIVE": 1,
|
|
3121
|
+
"LOCKED": 2
|
|
3122
|
+
};
|
|
3123
|
+
const activeTasks = roadmap.filter((t) => ["IN_PROGRESS", "ACTIVE", "LOCKED"].includes(t.status)).sort((a, b) => {
|
|
3124
|
+
const pA = statusPriority[a.status] ?? 99;
|
|
3125
|
+
const pB = statusPriority[b.status] ?? 99;
|
|
3126
|
+
if (pA !== pB) return pA - pB;
|
|
3127
|
+
return (a.step_number || 0) - (b.step_number || 0);
|
|
3128
|
+
});
|
|
3129
|
+
if (activeTasks.length === 0) {
|
|
3130
|
+
spinner.succeed("All caught up! No active tasks found.");
|
|
3131
|
+
return;
|
|
3132
|
+
}
|
|
3133
|
+
const nextTask = activeTasks[0];
|
|
3134
|
+
spinner.stop();
|
|
3135
|
+
console.log("");
|
|
3136
|
+
console.log(import_chalk18.default.bold.blue(`\u{1F4CC} Task #${nextTask.step_number || "?"}: ${nextTask.title}`));
|
|
3137
|
+
const statusColor = nextTask.status === "IN_PROGRESS" ? import_chalk18.default.yellow : nextTask.status === "ACTIVE" ? import_chalk18.default.green : import_chalk18.default.dim;
|
|
3138
|
+
console.log(import_chalk18.default.dim("Status: ") + statusColor(nextTask.status));
|
|
3139
|
+
console.log(import_chalk18.default.dim("\u2500".repeat(60)));
|
|
3140
|
+
if (nextTask.prompt_content) {
|
|
3141
|
+
console.log(import_chalk18.default.white(nextTask.prompt_content));
|
|
3142
|
+
console.log(import_chalk18.default.dim("\u2500".repeat(60)));
|
|
3143
|
+
if (options.copy !== false) {
|
|
3144
|
+
try {
|
|
3145
|
+
if (process.platform === "darwin") {
|
|
3146
|
+
(0, import_child_process4.execSync)("pbcopy", { input: nextTask.prompt_content });
|
|
3147
|
+
console.log(import_chalk18.default.green("\u2705 Prompt copied to clipboard! Ready to paste (Cmd+V)."));
|
|
3148
|
+
} else if (process.platform === "linux") {
|
|
3149
|
+
try {
|
|
3150
|
+
(0, import_child_process4.execSync)("xclip -selection clipboard", { input: nextTask.prompt_content });
|
|
3151
|
+
console.log(import_chalk18.default.green("\u2705 Prompt copied to clipboard!"));
|
|
3152
|
+
} catch (e) {
|
|
3153
|
+
console.log(import_chalk18.default.yellow("\u2139\uFE0F Copy prompt manually (xclip not available)"));
|
|
3154
|
+
}
|
|
3155
|
+
} else {
|
|
3156
|
+
console.log(import_chalk18.default.yellow("\u2139\uFE0F Copy prompt manually (Auto-copy not supported on this OS)"));
|
|
3157
|
+
}
|
|
3158
|
+
} catch (e) {
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
} else {
|
|
3162
|
+
console.log(import_chalk18.default.yellow("No prompt instructions available."));
|
|
3163
|
+
if (nextTask.architectural_brief) {
|
|
3164
|
+
console.log(import_chalk18.default.bold("Brief:"));
|
|
3165
|
+
console.log(nextTask.architectural_brief);
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
console.log("");
|
|
3169
|
+
} catch (e) {
|
|
3170
|
+
spinner.fail(import_chalk18.default.red(`Failed to fetch task: ${e.message}`));
|
|
3171
|
+
}
|
|
3172
|
+
});
|
|
3173
|
+
return focus;
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3176
|
+
// src/commands/env.ts
|
|
3177
|
+
init_cjs_shims();
|
|
3178
|
+
var import_commander13 = require("commander");
|
|
3179
|
+
var import_chalk19 = __toESM(require("chalk"), 1);
|
|
3180
|
+
var import_ora10 = __toESM(require("ora"), 1);
|
|
3181
|
+
var import_promises20 = __toESM(require("fs/promises"), 1);
|
|
3182
|
+
var import_path21 = __toESM(require("path"), 1);
|
|
3183
|
+
init_config();
|
|
3184
|
+
var import_axios15 = __toESM(require("axios"), 1);
|
|
3185
|
+
function createEnvPullCommand() {
|
|
3186
|
+
const envPull = new import_commander13.Command("env");
|
|
3187
|
+
envPull.command("pull").description("Pull environment variables from project vault").action(async () => {
|
|
3188
|
+
console.log("");
|
|
3189
|
+
console.log(import_chalk19.default.bold.yellow("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
3190
|
+
console.log(import_chalk19.default.bold.yellow("\u2551") + import_chalk19.default.bold.white(" \u{1F6E1}\uFE0F RIGSTATE SOVEREIGN VAULT SYNC \u{1F6E1}\uFE0F ") + import_chalk19.default.bold.yellow("\u2551"));
|
|
3191
|
+
console.log(import_chalk19.default.bold.yellow("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
3192
|
+
console.log("");
|
|
3193
|
+
const spinner = (0, import_ora10.default)("Authenticating with Vault...").start();
|
|
3194
|
+
let apiKey;
|
|
3195
|
+
let projectId;
|
|
3196
|
+
try {
|
|
3197
|
+
apiKey = getApiKey();
|
|
3198
|
+
} catch (e) {
|
|
3199
|
+
spinner.fail(import_chalk19.default.red('Not authenticated. Run "rigstate login" first.'));
|
|
3200
|
+
return;
|
|
3201
|
+
}
|
|
3202
|
+
spinner.succeed("Authenticated");
|
|
3203
|
+
spinner.start("Reading project configuration...");
|
|
3204
|
+
projectId = getProjectId();
|
|
3205
|
+
if (!projectId) {
|
|
3206
|
+
try {
|
|
3207
|
+
const manifestPath = import_path21.default.join(process.cwd(), ".rigstate");
|
|
3208
|
+
const content = await import_promises20.default.readFile(manifestPath, "utf-8");
|
|
3209
|
+
const manifest = JSON.parse(content);
|
|
3210
|
+
projectId = manifest.project_id;
|
|
3211
|
+
} catch (e) {
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
if (!projectId) {
|
|
3215
|
+
spinner.fail(import_chalk19.default.red('No project context. Run "rigstate link" first.'));
|
|
3216
|
+
return;
|
|
3217
|
+
}
|
|
3218
|
+
spinner.succeed(`Project: ${import_chalk19.default.cyan(projectId.substring(0, 8))}...`);
|
|
3219
|
+
const apiUrl = getApiUrl();
|
|
3220
|
+
spinner.start("Fetching secrets from Vault...");
|
|
3221
|
+
try {
|
|
3222
|
+
const response = await import_axios15.default.post(`${apiUrl}/api/v1/vault/sync`, {
|
|
3223
|
+
project_id: projectId
|
|
3224
|
+
}, {
|
|
3225
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3226
|
+
});
|
|
3227
|
+
if (!response.data.success) {
|
|
3228
|
+
throw new Error(response.data.error || "Failed to fetch secrets");
|
|
3229
|
+
}
|
|
3230
|
+
const vaultContent = response.data.data.content || "";
|
|
3231
|
+
const secretCount = response.data.data.count || 0;
|
|
3232
|
+
if (secretCount === 0) {
|
|
3233
|
+
spinner.info("No secrets found in Vault for this project.");
|
|
3234
|
+
console.log(import_chalk19.default.dim(" Add secrets via the Rigstate web interface."));
|
|
3235
|
+
return;
|
|
3236
|
+
}
|
|
3237
|
+
spinner.succeed(`Retrieved ${import_chalk19.default.bold(secretCount)} secret(s)`);
|
|
3238
|
+
const envFile = import_path21.default.resolve(process.cwd(), ".env.local");
|
|
3239
|
+
let existingContent = "";
|
|
3240
|
+
let existingKeys = /* @__PURE__ */ new Set();
|
|
3241
|
+
try {
|
|
3242
|
+
existingContent = await import_promises20.default.readFile(envFile, "utf-8");
|
|
3243
|
+
existingContent.split("\n").forEach((line) => {
|
|
3244
|
+
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
3245
|
+
if (match) existingKeys.add(match[1]);
|
|
3246
|
+
});
|
|
3247
|
+
} catch (e) {
|
|
3248
|
+
}
|
|
3249
|
+
const vaultKeys = /* @__PURE__ */ new Set();
|
|
3250
|
+
vaultContent.split("\n").forEach((line) => {
|
|
3251
|
+
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
3252
|
+
if (match) vaultKeys.add(match[1]);
|
|
3253
|
+
});
|
|
3254
|
+
let newCount = 0;
|
|
3255
|
+
let updatedCount = 0;
|
|
3256
|
+
vaultKeys.forEach((key) => {
|
|
3257
|
+
if (!existingKeys.has(key)) {
|
|
3258
|
+
newCount++;
|
|
3259
|
+
} else {
|
|
3260
|
+
updatedCount++;
|
|
3261
|
+
}
|
|
3262
|
+
});
|
|
3263
|
+
const unchangedCount = existingKeys.size - updatedCount;
|
|
3264
|
+
spinner.start("Writing .env.local...");
|
|
3265
|
+
const header = [
|
|
3266
|
+
"# ==========================================",
|
|
3267
|
+
"# RIGSTATE SOVEREIGN FOUNDATION",
|
|
3268
|
+
"# Authenticated Environment Configuration",
|
|
3269
|
+
`# Synced at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
3270
|
+
`# Project: ${projectId}`,
|
|
3271
|
+
"# ==========================================",
|
|
3272
|
+
""
|
|
3273
|
+
].join("\n");
|
|
3274
|
+
await import_promises20.default.writeFile(envFile, header + vaultContent + "\n");
|
|
3275
|
+
spinner.succeed("Written to .env.local");
|
|
3276
|
+
console.log("");
|
|
3277
|
+
console.log(import_chalk19.default.bold.green("\u2705 Environment synchronized successfully"));
|
|
3278
|
+
console.log("");
|
|
3279
|
+
console.log(import_chalk19.default.dim(" Summary:"));
|
|
3280
|
+
console.log(import_chalk19.default.green(` + ${newCount} new`));
|
|
3281
|
+
console.log(import_chalk19.default.yellow(` ~ ${updatedCount} updated`));
|
|
3282
|
+
console.log(import_chalk19.default.dim(` = ${unchangedCount} unchanged`));
|
|
3283
|
+
console.log("");
|
|
3284
|
+
console.log(import_chalk19.default.bold.yellow("\u26A0\uFE0F Security Reminder:"));
|
|
3285
|
+
console.log(import_chalk19.default.dim(" - Never commit .env.local to version control."));
|
|
3286
|
+
console.log(import_chalk19.default.dim(" - Ensure .gitignore includes .env.local"));
|
|
3287
|
+
console.log("");
|
|
3288
|
+
} catch (e) {
|
|
3289
|
+
spinner.fail(import_chalk19.default.red(`Failed to fetch secrets: ${e.message}`));
|
|
3290
|
+
}
|
|
3291
|
+
});
|
|
3292
|
+
return envPull;
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3295
|
+
// src/commands/config.ts
|
|
3296
|
+
init_cjs_shims();
|
|
3297
|
+
var import_commander14 = require("commander");
|
|
3298
|
+
var import_chalk20 = __toESM(require("chalk"), 1);
|
|
3299
|
+
init_config();
|
|
3300
|
+
function createConfigCommand() {
|
|
3301
|
+
const config2 = new import_commander14.Command("config");
|
|
3302
|
+
config2.description("View or modify Rigstate configuration").argument("[key]", "Configuration key to view/set (api_key, project_id, api_url)").argument("[value]", "Value to set").action(async (key, value) => {
|
|
3303
|
+
if (!key) {
|
|
3304
|
+
console.log(import_chalk20.default.bold("Rigstate Configuration"));
|
|
3305
|
+
console.log(import_chalk20.default.dim("\u2500".repeat(40)));
|
|
3306
|
+
try {
|
|
3307
|
+
const apiKey = getApiKey();
|
|
3308
|
+
console.log(`${import_chalk20.default.cyan("api_key")}: ${apiKey.substring(0, 20)}...`);
|
|
3309
|
+
} catch (e) {
|
|
3310
|
+
console.log(`${import_chalk20.default.cyan("api_key")}: ${import_chalk20.default.dim("(not set)")}`);
|
|
3311
|
+
}
|
|
3312
|
+
const projectId = getProjectId();
|
|
3313
|
+
console.log(`${import_chalk20.default.cyan("project_id")}: ${projectId || import_chalk20.default.dim("(not set)")}`);
|
|
3314
|
+
const apiUrl = getApiUrl();
|
|
3315
|
+
console.log(`${import_chalk20.default.cyan("api_url")}: ${apiUrl}`);
|
|
3316
|
+
console.log("");
|
|
3317
|
+
console.log(import_chalk20.default.dim('Use "rigstate config <key> <value>" to set a value.'));
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
if (!value) {
|
|
3321
|
+
switch (key) {
|
|
3322
|
+
case "api_key":
|
|
3323
|
+
try {
|
|
3324
|
+
const apiKey = getApiKey();
|
|
3325
|
+
console.log(apiKey);
|
|
3326
|
+
} catch (e) {
|
|
3327
|
+
console.log(import_chalk20.default.dim("(not set)"));
|
|
3328
|
+
}
|
|
3329
|
+
break;
|
|
3330
|
+
case "project_id":
|
|
3331
|
+
console.log(getProjectId() || import_chalk20.default.dim("(not set)"));
|
|
3332
|
+
break;
|
|
3333
|
+
case "api_url":
|
|
3334
|
+
console.log(getApiUrl());
|
|
3335
|
+
break;
|
|
3336
|
+
default:
|
|
3337
|
+
console.log(import_chalk20.default.red(`Unknown config key: ${key}`));
|
|
3338
|
+
console.log(import_chalk20.default.dim("Valid keys: api_key, project_id, api_url"));
|
|
3339
|
+
}
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
switch (key) {
|
|
3343
|
+
case "api_key":
|
|
3344
|
+
setApiKey(value);
|
|
3345
|
+
console.log(import_chalk20.default.green(`\u2705 api_key updated`));
|
|
3346
|
+
break;
|
|
3347
|
+
case "project_id":
|
|
3348
|
+
setProjectId(value);
|
|
3349
|
+
console.log(import_chalk20.default.green(`\u2705 project_id updated`));
|
|
3350
|
+
break;
|
|
3351
|
+
case "api_url":
|
|
3352
|
+
console.log(import_chalk20.default.yellow("api_url is set via RIGSTATE_API_URL environment variable"));
|
|
3353
|
+
break;
|
|
3354
|
+
default:
|
|
3355
|
+
console.log(import_chalk20.default.red(`Unknown config key: ${key}`));
|
|
3356
|
+
console.log(import_chalk20.default.dim("Valid keys: api_key, project_id"));
|
|
3357
|
+
}
|
|
3358
|
+
});
|
|
3359
|
+
return config2;
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
// src/commands/mcp.ts
|
|
3363
|
+
init_cjs_shims();
|
|
3364
|
+
var import_commander15 = require("commander");
|
|
3365
|
+
var import_chalk21 = __toESM(require("chalk"), 1);
|
|
3366
|
+
var import_child_process5 = require("child_process");
|
|
3367
|
+
var import_path22 = __toESM(require("path"), 1);
|
|
3368
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
3369
|
+
var import_url = require("url");
|
|
3370
|
+
var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
|
|
3371
|
+
var __dirname = import_path22.default.dirname(__filename2);
|
|
3372
|
+
function createMcpCommand() {
|
|
3373
|
+
const mcp = new import_commander15.Command("mcp");
|
|
3374
|
+
mcp.description("Run the Rigstate MCP server for AI editors").action(async () => {
|
|
3375
|
+
const possiblePaths = [
|
|
3376
|
+
// From packages/cli -> packages/mcp (sibling package)
|
|
3377
|
+
import_path22.default.resolve(__dirname, "../../mcp/dist/index.js"),
|
|
3378
|
+
// If installed globally or via npm
|
|
3379
|
+
import_path22.default.resolve(__dirname, "../../../mcp/dist/index.js"),
|
|
3380
|
+
// Development path from packages/cli/dist
|
|
3381
|
+
import_path22.default.resolve(__dirname, "../../../packages/mcp/dist/index.js")
|
|
3382
|
+
];
|
|
3383
|
+
let serverPath = "";
|
|
3384
|
+
for (const p of possiblePaths) {
|
|
3385
|
+
if (import_fs.default.existsSync(p)) {
|
|
3386
|
+
serverPath = p;
|
|
3387
|
+
break;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
if (!serverPath) {
|
|
3391
|
+
console.error(import_chalk21.default.red("\u274C Error: Rigstate MCP Server binary not found."));
|
|
3392
|
+
console.error(import_chalk21.default.yellow("Please ensure that the mcp package is built:"));
|
|
3393
|
+
console.error(import_chalk21.default.white(" cd packages/mcp && npm run build"));
|
|
3394
|
+
console.error("");
|
|
3395
|
+
console.error(import_chalk21.default.dim("Or run directly with:"));
|
|
3396
|
+
console.error(import_chalk21.default.white(" npx @rigstate/mcp"));
|
|
3397
|
+
process.exit(1);
|
|
3398
|
+
}
|
|
3399
|
+
console.log(import_chalk21.default.dim(`Starting MCP server from: ${serverPath}`));
|
|
3400
|
+
if (process.env.VIBE_API_KEY && !process.env.RIGSTATE_API_KEY) {
|
|
3401
|
+
process.env.RIGSTATE_API_KEY = process.env.VIBE_API_KEY;
|
|
3402
|
+
}
|
|
3403
|
+
const worker = (0, import_child_process5.spawn)("node", [serverPath], {
|
|
3404
|
+
env: process.env,
|
|
3405
|
+
stdio: ["inherit", "inherit", "inherit"]
|
|
3406
|
+
});
|
|
3407
|
+
worker.on("error", (err) => {
|
|
3408
|
+
console.error(import_chalk21.default.red(`\u274C Failed to start MCP server: ${err.message}`));
|
|
3409
|
+
process.exit(1);
|
|
3410
|
+
});
|
|
3411
|
+
worker.on("exit", (code) => {
|
|
3412
|
+
if (code !== 0 && code !== null) {
|
|
3413
|
+
process.exit(code);
|
|
3414
|
+
}
|
|
3415
|
+
});
|
|
3416
|
+
});
|
|
3417
|
+
return mcp;
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3420
|
+
// src/commands/nexus.ts
|
|
3421
|
+
init_cjs_shims();
|
|
3422
|
+
var import_commander16 = require("commander");
|
|
3423
|
+
var import_chalk24 = __toESM(require("chalk"), 1);
|
|
3424
|
+
|
|
3425
|
+
// src/nexus/dispatcher.ts
|
|
3426
|
+
init_cjs_shims();
|
|
3427
|
+
var import_events4 = __toESM(require("events"), 1);
|
|
3428
|
+
var import_uuid = require("uuid");
|
|
3429
|
+
|
|
3430
|
+
// src/hive/gateway.ts
|
|
3431
|
+
init_cjs_shims();
|
|
3432
|
+
var import_axios16 = __toESM(require("axios"), 1);
|
|
3433
|
+
|
|
3434
|
+
// src/hive/scrubber.ts
|
|
3435
|
+
init_cjs_shims();
|
|
3436
|
+
var HiveScrubber = class {
|
|
3437
|
+
// Patterns that definitely identify a project and MUST be removed
|
|
3438
|
+
static SENSITIVE_PATTERNS = [
|
|
3439
|
+
/(api_key|secret|token|password)[\s]*[:=][\s]*['"][a-zA-Z0-9_\-]+['"]/gi,
|
|
3440
|
+
// Secrets
|
|
3441
|
+
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
3442
|
+
// Emails
|
|
3443
|
+
/(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\. (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/g,
|
|
3444
|
+
// IPs
|
|
3445
|
+
/postgres:\/\/[^:]+:[^@]+@/g
|
|
3446
|
+
// DB Connection Strings
|
|
3447
|
+
];
|
|
3448
|
+
// Generic replacements for project-specific terms to keep the rule abstract
|
|
3449
|
+
static ABSTRACTION_MAP = {
|
|
3450
|
+
"Vibeline": "{PROJECT_NAME}",
|
|
3451
|
+
"Rigstate": "{FRAMEWORK}",
|
|
3452
|
+
"Steinhofve": "{USER}"
|
|
3453
|
+
};
|
|
3454
|
+
/**
|
|
3455
|
+
* Scrubs a string (rule content or log excerpt) of sensitive data.
|
|
3456
|
+
*/
|
|
3457
|
+
static scrub(content, customTerms = []) {
|
|
3458
|
+
let scrubbed = content;
|
|
3459
|
+
let count = 0;
|
|
3460
|
+
let risk = 0;
|
|
3461
|
+
this.SENSITIVE_PATTERNS.forEach((pattern) => {
|
|
3462
|
+
if (pattern.test(scrubbed)) {
|
|
3463
|
+
scrubbed = scrubbed.replace(pattern, "[REDACTED_CREDENTIAL]");
|
|
3464
|
+
count++;
|
|
3465
|
+
risk += 50;
|
|
3466
|
+
}
|
|
3467
|
+
});
|
|
3468
|
+
Object.entries(this.ABSTRACTION_MAP).forEach(([term, replacement]) => {
|
|
3469
|
+
const regex = new RegExp(term, "gi");
|
|
3470
|
+
if (regex.test(scrubbed)) {
|
|
3471
|
+
scrubbed = scrubbed.replace(regex, replacement);
|
|
3472
|
+
count++;
|
|
3473
|
+
}
|
|
3474
|
+
});
|
|
3475
|
+
customTerms.forEach((term) => {
|
|
3476
|
+
const regex = new RegExp(term, "gi");
|
|
3477
|
+
if (regex.test(scrubbed)) {
|
|
3478
|
+
scrubbed = scrubbed.replace(regex, "{ENTITY}");
|
|
3479
|
+
count++;
|
|
3480
|
+
}
|
|
3481
|
+
});
|
|
3482
|
+
return {
|
|
3483
|
+
sanitizedContent: scrubbed,
|
|
3484
|
+
redactionCount: count,
|
|
3485
|
+
riskScore: Math.min(risk, 100)
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
};
|
|
3489
|
+
|
|
3490
|
+
// src/hive/gateway.ts
|
|
3491
|
+
var import_chalk22 = __toESM(require("chalk"), 1);
|
|
3492
|
+
var HiveGateway = class {
|
|
3493
|
+
client;
|
|
3494
|
+
enabled;
|
|
3495
|
+
lastSignalTime = 0;
|
|
3496
|
+
MIN_INTERVAL_MS = 5e3;
|
|
3497
|
+
// Throttle: Max 1 signal per 5s
|
|
3498
|
+
constructor(baseUrl, token) {
|
|
3499
|
+
this.enabled = !!token;
|
|
3500
|
+
if (!this.enabled) {
|
|
3501
|
+
console.log(import_chalk22.default.dim("\u26A0\uFE0F Hive Gateway disabled (No Token provided). Running in localized mode."));
|
|
3502
|
+
}
|
|
3503
|
+
this.client = import_axios16.default.create({
|
|
3504
|
+
baseURL: baseUrl,
|
|
3505
|
+
headers: {
|
|
3506
|
+
"Authorization": `Bearer ${token}`,
|
|
3507
|
+
"Content-Type": "application/json",
|
|
3508
|
+
"X-Rigstate-Client": "CLI-0.2.0"
|
|
3509
|
+
},
|
|
3510
|
+
timeout: 5e3
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
/**
|
|
3514
|
+
* Transmit an Immune Signal to the Hive.
|
|
3515
|
+
* Includes Pre-Flight Scrubbing and Throttling.
|
|
3516
|
+
*/
|
|
3517
|
+
async transmit(signal) {
|
|
3518
|
+
if (!this.enabled) return false;
|
|
3519
|
+
const now = Date.now();
|
|
3520
|
+
if (now - this.lastSignalTime < this.MIN_INTERVAL_MS) {
|
|
3521
|
+
console.warn(import_chalk22.default.yellow("\u23F3 Hive Gateway Throttled. Signal dropped to preventing spam."));
|
|
3522
|
+
return false;
|
|
3523
|
+
}
|
|
3524
|
+
const scrubResult = HiveScrubber.scrub(signal.ruleContent);
|
|
3525
|
+
if (scrubResult.riskScore > 20) {
|
|
3526
|
+
console.error(import_chalk22.default.red(`\u{1F6D1} HIVE BLOCKED: Signal contains sensitive data (Risk: ${scrubResult.riskScore})`));
|
|
3527
|
+
return false;
|
|
3528
|
+
}
|
|
3529
|
+
try {
|
|
3530
|
+
console.log(import_chalk22.default.blue(`\u{1F4E1} Uplinking to Hive... [${signal.vector}]`));
|
|
3531
|
+
const payload = { ...signal, ruleContent: scrubResult.sanitizedContent };
|
|
3532
|
+
await this.client.post("/signal", payload);
|
|
3533
|
+
this.lastSignalTime = now;
|
|
3534
|
+
console.log(import_chalk22.default.green("\u2705 Signal Received by Hive Core. Knowledge Shared."));
|
|
3535
|
+
return true;
|
|
3536
|
+
} catch (error) {
|
|
3537
|
+
console.error(import_chalk22.default.red(`\u274C Hive Transmission Failed: ${error.message}`));
|
|
3538
|
+
return false;
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
};
|
|
3542
|
+
|
|
3543
|
+
// src/utils/logger.ts
|
|
3544
|
+
init_cjs_shims();
|
|
3545
|
+
var import_chalk23 = __toESM(require("chalk"), 1);
|
|
3546
|
+
var Logger = class {
|
|
3547
|
+
static formatMessage(level, message, context) {
|
|
3548
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3549
|
+
let prefix = "";
|
|
3550
|
+
switch (level) {
|
|
3551
|
+
case "INFO" /* INFO */:
|
|
3552
|
+
prefix = import_chalk23.default.blue(`[${"INFO" /* INFO */}]`);
|
|
3553
|
+
break;
|
|
3554
|
+
case "WARN" /* WARN */:
|
|
3555
|
+
prefix = import_chalk23.default.yellow(`[${"WARN" /* WARN */}]`);
|
|
3556
|
+
break;
|
|
3557
|
+
case "ERROR" /* ERROR */:
|
|
3558
|
+
prefix = import_chalk23.default.red(`[${"ERROR" /* ERROR */}]`);
|
|
3559
|
+
break;
|
|
3560
|
+
case "DEBUG" /* DEBUG */:
|
|
3561
|
+
prefix = import_chalk23.default.gray(`[${"DEBUG" /* DEBUG */}]`);
|
|
3562
|
+
break;
|
|
3563
|
+
}
|
|
3564
|
+
let output = `${import_chalk23.default.gray(timestamp)} ${prefix} ${message}`;
|
|
3565
|
+
if (context) {
|
|
3566
|
+
if (context instanceof Error) {
|
|
3567
|
+
output += `
|
|
3568
|
+
${import_chalk23.default.red(context.stack || context.message)}`;
|
|
3569
|
+
} else if (typeof context === "object") {
|
|
3570
|
+
try {
|
|
3571
|
+
output += `
|
|
3572
|
+
${import_chalk23.default.gray(JSON.stringify(context, null, 2))}`;
|
|
3573
|
+
} catch (e) {
|
|
3574
|
+
output += `
|
|
3575
|
+
${import_chalk23.default.gray("[Circular or invalid object]")}`;
|
|
3576
|
+
}
|
|
3577
|
+
} else {
|
|
3578
|
+
output += ` ${String(context)}`;
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
return output;
|
|
3582
|
+
}
|
|
3583
|
+
static info(message, context) {
|
|
3584
|
+
console.log(this.formatMessage("INFO" /* INFO */, message, context));
|
|
3585
|
+
}
|
|
3586
|
+
static warn(message, context) {
|
|
3587
|
+
console.warn(this.formatMessage("WARN" /* WARN */, message, context));
|
|
3588
|
+
}
|
|
3589
|
+
static error(message, error) {
|
|
3590
|
+
console.error(this.formatMessage("ERROR" /* ERROR */, message, error));
|
|
3591
|
+
}
|
|
3592
|
+
static debug(message, context) {
|
|
3593
|
+
if (process.env.DEBUG || process.env.RIGSTATE_DEBUG) {
|
|
3594
|
+
console.debug(this.formatMessage("DEBUG" /* DEBUG */, message, context));
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
};
|
|
3598
|
+
|
|
3599
|
+
// src/nexus/dispatcher.ts
|
|
3600
|
+
var NexusDispatcher = class extends import_events4.default {
|
|
3601
|
+
context;
|
|
3602
|
+
orderQueue = [];
|
|
3603
|
+
orderHistory = [];
|
|
3604
|
+
gateway;
|
|
3605
|
+
constructor(context) {
|
|
3606
|
+
super();
|
|
3607
|
+
this.context = context;
|
|
3608
|
+
this.gateway = new HiveGateway(
|
|
3609
|
+
process.env.RIGSTATE_HIVE_URL || "https://rigstate.com/api/hive",
|
|
3610
|
+
process.env.RIGSTATE_HIVE_TOKEN
|
|
3611
|
+
);
|
|
3612
|
+
Logger.info(`\u{1F9E0} NEXUS DISPATCHER ONLINE. Context: ${context.projectId} (DryRun: ${context.dryRun})`);
|
|
3613
|
+
}
|
|
3614
|
+
/**
|
|
3615
|
+
* Creates a new Service Order and routes it.
|
|
3616
|
+
*/
|
|
3617
|
+
async dispatch(source, target, intent, action, params, constraints = []) {
|
|
3618
|
+
const order = {
|
|
3619
|
+
id: (0, import_uuid.v4)(),
|
|
3620
|
+
traceId: (0, import_uuid.v4)(),
|
|
3621
|
+
// TODO: Inherit traceId if chained
|
|
3622
|
+
sourceAgent: source,
|
|
3623
|
+
targetAgent: target,
|
|
3624
|
+
priority: "NORMAL",
|
|
3625
|
+
intent,
|
|
3626
|
+
action,
|
|
3627
|
+
parameters: params,
|
|
3628
|
+
constraints,
|
|
3629
|
+
status: "PENDING",
|
|
3630
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3631
|
+
};
|
|
3632
|
+
this.orderQueue.push(order);
|
|
3633
|
+
this.emit("order:created", order);
|
|
3634
|
+
if (target === "EITRI" && order.action.startsWith("fs.write")) {
|
|
3635
|
+
if (this.context.dryRun) {
|
|
3636
|
+
Logger.info(`\u{1F6D1} NEXUS KILL-SWITCH: Order ${order.id} blocked by Dry-Run protocol.`);
|
|
3637
|
+
order.status = "PENDING";
|
|
3638
|
+
this.emit("order:blocked", order);
|
|
3639
|
+
return order;
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
return this.executeOrder(order);
|
|
3643
|
+
}
|
|
3644
|
+
/**
|
|
3645
|
+
* Executes the order (simulated for now, essentially "Sending" it)
|
|
3646
|
+
*/
|
|
3647
|
+
async executeOrder(order) {
|
|
3648
|
+
order.status = "EXECUTING";
|
|
3649
|
+
order.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3650
|
+
this.emit("order:started", order);
|
|
3651
|
+
try {
|
|
3652
|
+
Logger.info(`\u{1F680} NEXUS: Routing Order ${order.id} [${order.sourceAgent} -> ${order.targetAgent}]: ${order.intent}`);
|
|
3653
|
+
if (order.targetAgent === "MAJA" && order.action === "HIVE_TRANSMIT") {
|
|
3654
|
+
const signal = order.parameters.signal;
|
|
3655
|
+
if (signal) {
|
|
3656
|
+
await this.gateway.transmit(signal);
|
|
3657
|
+
order.status = "COMPLETED";
|
|
3658
|
+
return order;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
this.emit(`agent:${order.targetAgent}`, order);
|
|
3662
|
+
return order;
|
|
3663
|
+
} catch (error) {
|
|
3664
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
3665
|
+
Logger.error(`Dispatch failed for order ${order.id}`, error);
|
|
3666
|
+
order.status = "FAILED";
|
|
3667
|
+
order.error = {
|
|
3668
|
+
code: "DISPATCH_ERROR",
|
|
3669
|
+
message: errorMessage
|
|
3670
|
+
};
|
|
3671
|
+
this.emit("order:failed", order);
|
|
3672
|
+
return order;
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
/**
|
|
3676
|
+
* Human Approval (The "Red Button")
|
|
3677
|
+
*/
|
|
3678
|
+
async approveOrder(orderId) {
|
|
3679
|
+
const order = this.orderQueue.find((o) => o.id === orderId);
|
|
3680
|
+
if (!order) throw new Error(`Order ${orderId} not found`);
|
|
3681
|
+
if (order.status !== "AWAITING_APPROVAL") {
|
|
3682
|
+
Logger.warn(`Order ${orderId} is not awaiting approval (Status: ${order.status})`);
|
|
3683
|
+
return;
|
|
3684
|
+
}
|
|
3685
|
+
Logger.info(`\u2705 HUMAN APPROVED Order ${orderId}`);
|
|
3686
|
+
await this.executeOrder(order);
|
|
3687
|
+
}
|
|
3688
|
+
};
|
|
3689
|
+
|
|
3690
|
+
// src/commands/nexus.ts
|
|
3691
|
+
var import_inquirer3 = __toESM(require("inquirer"), 1);
|
|
3692
|
+
function createNexusCommand() {
|
|
3693
|
+
const command = new import_commander16.Command("nexus");
|
|
3694
|
+
command.description("Interact with The Multi-Agent Nexus (Phase 8)").argument("<intent>", "The natural language instruction for the swarm").option("--dry-run", "Enable Dry-Run mode (Kill-Switch Active)", true).option("--force", "Disable Dry-Run mode (DANGEROUS)", false).action(async (intent, options) => {
|
|
3695
|
+
console.log(import_chalk24.default.bold.magenta("\n\u{1F981} Welcome to The Nexus (Phase 8)\n"));
|
|
3696
|
+
const dryRun = !options.force;
|
|
3697
|
+
if (!dryRun) {
|
|
3698
|
+
console.log(import_chalk24.default.black.bgYellow(" WARNING ") + import_chalk24.default.yellow(" Dry-Run disabled! Eitri is authorized to write code."));
|
|
3699
|
+
const { confirm } = await import_inquirer3.default.prompt([{
|
|
3700
|
+
type: "confirm",
|
|
3701
|
+
name: "confirm",
|
|
3702
|
+
message: "Are you absolutely sure you want to bypass the Kill-Switch?",
|
|
3703
|
+
default: false
|
|
3704
|
+
}]);
|
|
3705
|
+
if (!confirm) {
|
|
3706
|
+
console.log("Aborting.");
|
|
3707
|
+
process.exit(0);
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
const context = {
|
|
3711
|
+
projectId: process.env.RIGSTATE_PROJECT_ID || "local",
|
|
3712
|
+
rootPath: process.cwd(),
|
|
3713
|
+
sessionUser: "cli-user",
|
|
3714
|
+
// Should strictly be pulled from auth
|
|
3715
|
+
dryRun
|
|
3716
|
+
};
|
|
3717
|
+
const dispatcher = new NexusDispatcher(context);
|
|
3718
|
+
dispatcher.on("order:created", (o) => {
|
|
3719
|
+
console.log(import_chalk24.default.blue(`\u{1F195} [${o.id.slice(0, 6)}] Order Created: `) + o.intent);
|
|
3720
|
+
});
|
|
3721
|
+
dispatcher.on("order:started", (o) => {
|
|
3722
|
+
console.log(import_chalk24.default.yellow(`\u23F3 [${o.id.slice(0, 6)}] Processing...`));
|
|
3723
|
+
});
|
|
3724
|
+
dispatcher.on("order:blocked", (o) => {
|
|
3725
|
+
console.log(import_chalk24.default.red(`\u{1F6D1} [${o.id.slice(0, 6)}] BLOCKED by Kill-Switch`));
|
|
3726
|
+
console.log(import_chalk24.default.dim(` Target: ${o.targetAgent} | Action: ${o.action}`));
|
|
3727
|
+
console.log(import_chalk24.default.dim(" Run with --force to execute automatically (NOT RECOMMENDED)."));
|
|
3728
|
+
});
|
|
3729
|
+
dispatcher.on("agent:SINDRE", (o) => console.log(import_chalk24.default.cyan(`\u{1F916} Sindre (Vault): I'm on it! (${o.action})`)));
|
|
3730
|
+
dispatcher.on("agent:EITRI", (o) => console.log(import_chalk24.default.green(`\u{1F477} Eitri (Smith): Ready to build! (${o.action})`)));
|
|
3731
|
+
console.log(import_chalk24.default.dim("\u{1F9E0} Frank is analyzing your intent..."));
|
|
3732
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
3733
|
+
if (intent.toLowerCase().includes("db") || intent.toLowerCase().includes("database")) {
|
|
3734
|
+
await dispatcher.dispatch("FRANK", "SINDRE", intent, "db.analyze", { raw: intent });
|
|
3735
|
+
} else if (intent.toLowerCase().includes("create") || intent.toLowerCase().includes("code")) {
|
|
3736
|
+
await dispatcher.dispatch("FRANK", "EITRI", intent, "fs.write", { path: "src/demo.ts", content: "// demo" });
|
|
3737
|
+
} else {
|
|
3738
|
+
console.log(import_chalk24.default.gray("Frank didn't understand. Try 'create file' or 'check database'."));
|
|
3739
|
+
}
|
|
3740
|
+
});
|
|
3741
|
+
return command;
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3744
|
+
// src/commands/sync-rules.ts
|
|
3745
|
+
init_cjs_shims();
|
|
3746
|
+
var import_commander17 = require("commander");
|
|
3747
|
+
var import_chalk25 = __toESM(require("chalk"), 1);
|
|
3748
|
+
var import_ora11 = __toESM(require("ora"), 1);
|
|
3749
|
+
init_config();
|
|
3750
|
+
var import_axios17 = __toESM(require("axios"), 1);
|
|
3751
|
+
function createSyncRulesCommand() {
|
|
3752
|
+
const syncRules = new import_commander17.Command("sync-rules");
|
|
3753
|
+
syncRules.description("\u{1F6E1}\uFE0F Push Frank Protocol v1.0 to all existing projects").option("--dry-run", "Preview changes without pushing to GitHub").option("--project <id>", "Sync a specific project only").action(async (options) => {
|
|
3754
|
+
const spinner = (0, import_ora11.default)("\u{1F6E1}\uFE0F Frank Protocol: Initializing retroactive sync...").start();
|
|
3755
|
+
const results = [];
|
|
3756
|
+
let apiKey;
|
|
3757
|
+
try {
|
|
3758
|
+
apiKey = getApiKey();
|
|
3759
|
+
} catch (e) {
|
|
3760
|
+
spinner.fail(import_chalk25.default.red('Not authenticated. Run "rigstate login" first.'));
|
|
3761
|
+
return;
|
|
3762
|
+
}
|
|
3763
|
+
const apiUrl = getApiUrl();
|
|
3764
|
+
try {
|
|
3765
|
+
spinner.text = "Fetching projects...";
|
|
3766
|
+
const projectsResponse = await import_axios17.default.get(`${apiUrl}/api/v1/projects`, {
|
|
3767
|
+
params: options.project ? { project_id: options.project } : {},
|
|
3768
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3769
|
+
});
|
|
3770
|
+
if (!projectsResponse.data.success) {
|
|
3771
|
+
throw new Error(projectsResponse.data.error || "Failed to fetch projects");
|
|
3772
|
+
}
|
|
3773
|
+
let projects = projectsResponse.data.data.projects || [];
|
|
3774
|
+
if (projects.length === 0) {
|
|
3775
|
+
spinner.fail(import_chalk25.default.red("No projects found."));
|
|
3776
|
+
return;
|
|
3777
|
+
}
|
|
3778
|
+
if (projects.length > 1 && !options.project) {
|
|
3779
|
+
spinner.stop();
|
|
3780
|
+
const inquirer4 = (await import("inquirer")).default;
|
|
3781
|
+
const { selectedProjectId } = await inquirer4.prompt([{
|
|
3782
|
+
type: "list",
|
|
3783
|
+
name: "selectedProjectId",
|
|
3784
|
+
message: "Multiple projects found. Which one do you want to sync?",
|
|
3785
|
+
choices: projects.map((p) => ({
|
|
3786
|
+
name: `${p.name} [${p.id}]`,
|
|
3787
|
+
value: p.id
|
|
3788
|
+
}))
|
|
3789
|
+
}]);
|
|
3790
|
+
projects = projects.filter((p) => p.id === selectedProjectId);
|
|
3791
|
+
options.project = selectedProjectId;
|
|
3792
|
+
try {
|
|
3793
|
+
const fs23 = await import("fs/promises");
|
|
3794
|
+
const path23 = await import("path");
|
|
3795
|
+
const envPath = path23.join(process.cwd(), ".env");
|
|
3796
|
+
const envLocalPath = path23.join(process.cwd(), ".env.local");
|
|
3797
|
+
let targetEnv = envLocalPath;
|
|
3798
|
+
try {
|
|
3799
|
+
await fs23.access(envLocalPath);
|
|
3800
|
+
} catch {
|
|
3801
|
+
try {
|
|
3802
|
+
await fs23.access(envPath);
|
|
3803
|
+
targetEnv = envPath;
|
|
3804
|
+
} catch {
|
|
3805
|
+
targetEnv = envPath;
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
let content = "";
|
|
3809
|
+
try {
|
|
3810
|
+
content = await fs23.readFile(targetEnv, "utf-8");
|
|
3811
|
+
} catch {
|
|
3812
|
+
}
|
|
3813
|
+
if (!content.includes("RIGSTATE_PROJECT_ID")) {
|
|
3814
|
+
const newContent = content.endsWith("\n") || content === "" ? `${content}RIGSTATE_PROJECT_ID=${selectedProjectId}
|
|
3815
|
+
` : `${content}
|
|
3816
|
+
RIGSTATE_PROJECT_ID=${selectedProjectId}
|
|
3817
|
+
`;
|
|
3818
|
+
await fs23.writeFile(targetEnv, newContent, "utf-8");
|
|
3819
|
+
console.log(import_chalk25.default.dim(` \u{1F4BE} Saved default project to ${path23.basename(targetEnv)}`));
|
|
3820
|
+
}
|
|
3821
|
+
} catch (e) {
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
spinner.succeed(`Syncing project: ${projects[0].name}`);
|
|
3825
|
+
for (const project of projects) {
|
|
3826
|
+
const projectSpinner = (0, import_ora11.default)(` Syncing: ${project.name}...`).start();
|
|
3827
|
+
try {
|
|
3828
|
+
if (options.dryRun) {
|
|
3829
|
+
projectSpinner.succeed(import_chalk25.default.yellow(` [DRY-RUN] Would sync: ${project.name}`));
|
|
3830
|
+
results.push({ projectId: project.id, projectName: project.name, status: "success" });
|
|
3831
|
+
continue;
|
|
3832
|
+
}
|
|
3833
|
+
const syncResponse = await import_axios17.default.post(`${apiUrl}/api/v1/rules/sync`, {
|
|
3834
|
+
project_id: project.id
|
|
3835
|
+
}, {
|
|
3836
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3837
|
+
});
|
|
3838
|
+
if (syncResponse.data.success) {
|
|
3839
|
+
if (syncResponse.data.data.github_synced) {
|
|
3840
|
+
projectSpinner.succeed(import_chalk25.default.green(` \u2705 ${project.name} [${project.id}] \u2192 GitHub synced`));
|
|
3841
|
+
} else {
|
|
3842
|
+
projectSpinner.info(import_chalk25.default.blue(` \u2139\uFE0F ${project.name} [${project.id}] \u2192 Rules generated (no GitHub)`));
|
|
3843
|
+
}
|
|
3844
|
+
const files = syncResponse.data.data.files;
|
|
3845
|
+
if (files && Array.isArray(files) && (projects.length === 1 || options.project)) {
|
|
3846
|
+
const fs23 = await import("fs/promises");
|
|
3847
|
+
const path23 = await import("path");
|
|
3848
|
+
for (const file of files) {
|
|
3849
|
+
const filePath = path23.join(process.cwd(), file.path);
|
|
3850
|
+
await fs23.mkdir(path23.dirname(filePath), { recursive: true });
|
|
3851
|
+
await fs23.writeFile(filePath, file.content, "utf-8");
|
|
3852
|
+
}
|
|
3853
|
+
console.log(import_chalk25.default.dim(` \u{1F4BE} Wrote ${files.length} rule files to local .cursor/rules/`));
|
|
3854
|
+
}
|
|
3855
|
+
results.push({ projectId: project.id, projectName: project.name, status: "success" });
|
|
3856
|
+
} else {
|
|
3857
|
+
projectSpinner.warn(import_chalk25.default.yellow(` \u26A0\uFE0F ${project.name} \u2192 ${syncResponse.data.error || "Unknown error"}`));
|
|
3858
|
+
results.push({ projectId: project.id, projectName: project.name, status: "failed", error: syncResponse.data.error });
|
|
3859
|
+
}
|
|
3860
|
+
} catch (e) {
|
|
3861
|
+
projectSpinner.fail(import_chalk25.default.red(` \u274C ${project.name}: ${e.message}`));
|
|
3862
|
+
results.push({ projectId: project.id, projectName: project.name, status: "failed", error: e.message });
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
console.log("");
|
|
3866
|
+
console.log(import_chalk25.default.bold("\u{1F4CA} Sync Summary:"));
|
|
3867
|
+
const successful = results.filter((r) => r.status === "success").length;
|
|
3868
|
+
const failed = results.filter((r) => r.status === "failed").length;
|
|
3869
|
+
console.log(import_chalk25.default.green(` \u2705 Successful: ${successful}`));
|
|
3870
|
+
if (failed > 0) {
|
|
3871
|
+
console.log(import_chalk25.default.red(` \u274C Failed: ${failed}`));
|
|
3872
|
+
}
|
|
3873
|
+
console.log("");
|
|
3874
|
+
console.log(import_chalk25.default.cyan("\u{1F6E1}\uFE0F Frank Protocol v1.0 has been injected into the rules engine."));
|
|
3875
|
+
console.log(import_chalk25.default.dim(" All new chats will now boot with mandatory governance checks."));
|
|
3876
|
+
} catch (e) {
|
|
3877
|
+
spinner.fail(import_chalk25.default.red("Sync failed: " + e.message));
|
|
3878
|
+
}
|
|
3879
|
+
});
|
|
3880
|
+
return syncRules;
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
// src/commands/override.ts
|
|
3884
|
+
init_cjs_shims();
|
|
3885
|
+
var import_commander18 = require("commander");
|
|
3886
|
+
var import_chalk26 = __toESM(require("chalk"), 1);
|
|
3887
|
+
init_governance();
|
|
3888
|
+
init_config();
|
|
3889
|
+
var import_axios18 = __toESM(require("axios"), 1);
|
|
3890
|
+
function createOverrideCommand() {
|
|
3891
|
+
const override = new import_commander18.Command("override");
|
|
3892
|
+
override.description("Emergency Override for Governance Soft Locks").argument("<violationId>", 'ID of the violation to override (or "all")').requiredOption("-r, --reason <reason>", "Description of why this override is necessary").action(async (violationId, options) => {
|
|
3893
|
+
const { reason } = options;
|
|
3894
|
+
console.log(import_chalk26.default.bold(`
|
|
3895
|
+
\u{1F513} Initiating Governance Override Protocol...`));
|
|
3896
|
+
const session = await getSessionState(process.cwd());
|
|
3897
|
+
if (session.status !== "SOFT_LOCK") {
|
|
3898
|
+
console.log(import_chalk26.default.yellow(" Info: Session is not currently locked."));
|
|
3899
|
+
return;
|
|
3900
|
+
}
|
|
3901
|
+
console.log(import_chalk26.default.dim(` Active Violation: ${session.active_violation}`));
|
|
3902
|
+
console.log(import_chalk26.default.dim(` Reason Provided: "${reason}"`));
|
|
3903
|
+
const success = await performOverride(violationId, reason, process.cwd());
|
|
3904
|
+
if (success) {
|
|
3905
|
+
console.log(import_chalk26.default.green(` \u2705 Session UNLOCKED.`));
|
|
3906
|
+
console.log(import_chalk26.default.dim(` This event has been logged to the Mission Report.`));
|
|
3907
|
+
try {
|
|
3908
|
+
const projectId = getProjectId();
|
|
3909
|
+
if (projectId) {
|
|
3910
|
+
const apiUrl = getApiUrl();
|
|
3911
|
+
const apiKey = getApiKey();
|
|
3912
|
+
await import_axios18.default.post(`${apiUrl}/api/v1/execution-logs`, {
|
|
3913
|
+
project_id: projectId,
|
|
3914
|
+
task_id: "OVERRIDE-" + Date.now(),
|
|
3915
|
+
task_title: `Governance Override: ${violationId}`,
|
|
3916
|
+
status: "COMPLETED",
|
|
3917
|
+
execution_summary: `Manual override executed. Reason: ${reason}`,
|
|
3918
|
+
agent_role: "SUPERVISOR"
|
|
3919
|
+
// Override is a supervisor action
|
|
3920
|
+
}, {
|
|
3921
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3922
|
+
});
|
|
3923
|
+
console.log(import_chalk26.default.dim(` \u2601 Audit log synced to Cloud.`));
|
|
3924
|
+
}
|
|
3925
|
+
} catch (e) {
|
|
3926
|
+
console.log(import_chalk26.default.dim(` (Cloud audit sync failed: ${e.message})`));
|
|
3927
|
+
}
|
|
3928
|
+
} else {
|
|
3929
|
+
console.log(import_chalk26.default.red(` \u{1F6D1} Override Failed. Check project configuration.`));
|
|
3930
|
+
}
|
|
3931
|
+
});
|
|
3932
|
+
return override;
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3935
|
+
// src/utils/version.ts
|
|
3936
|
+
init_cjs_shims();
|
|
3937
|
+
async function checkVersion() {
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
// src/index.ts
|
|
3941
|
+
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
3942
|
+
import_dotenv.default.config();
|
|
3943
|
+
var program = new import_commander19.Command();
|
|
3944
|
+
program.name("rigstate").description("CLI for Rigstate - The AI-Native Dev Studio").version("0.2.0");
|
|
3945
|
+
program.addCommand(createLoginCommand());
|
|
3946
|
+
program.addCommand(createLinkCommand());
|
|
3947
|
+
program.addCommand(createScanCommand());
|
|
3948
|
+
program.addCommand(createFixCommand());
|
|
3949
|
+
program.addCommand(createSyncCommand());
|
|
3950
|
+
program.addCommand(createInitCommand());
|
|
3951
|
+
program.addCommand(createCheckCommand());
|
|
3952
|
+
program.addCommand(createHooksCommand());
|
|
3953
|
+
program.addCommand(createDaemonCommand());
|
|
3954
|
+
program.addCommand(createWorkCommand());
|
|
3955
|
+
program.addCommand(createWatchCommand());
|
|
3956
|
+
program.addCommand(createFocusCommand());
|
|
3957
|
+
program.addCommand(createEnvPullCommand());
|
|
3958
|
+
program.addCommand(createConfigCommand());
|
|
3959
|
+
program.addCommand(createMcpCommand());
|
|
3960
|
+
program.addCommand(createNexusCommand());
|
|
3961
|
+
program.addCommand(createSyncRulesCommand());
|
|
3962
|
+
program.addCommand(createOverrideCommand());
|
|
3963
|
+
program.hook("preAction", async () => {
|
|
3964
|
+
await checkVersion();
|
|
3965
|
+
});
|
|
3966
|
+
program.on("--help", () => {
|
|
3967
|
+
console.log("");
|
|
3968
|
+
console.log(import_chalk27.default.bold("Examples:"));
|
|
3969
|
+
console.log("");
|
|
3970
|
+
console.log(import_chalk27.default.cyan(" $ rigstate login sk_rigstate_your_api_key"));
|
|
3971
|
+
console.log(import_chalk27.default.dim(" Authenticate with your Rigstate API key"));
|
|
3972
|
+
console.log("");
|
|
3973
|
+
console.log(import_chalk27.default.cyan(" $ rigstate scan"));
|
|
3974
|
+
console.log(import_chalk27.default.dim(" Scan the current directory"));
|
|
3975
|
+
console.log("");
|
|
3976
|
+
console.log(import_chalk27.default.cyan(" $ rigstate scan ./src --project abc123"));
|
|
3977
|
+
console.log(import_chalk27.default.dim(" Scan a specific directory with project ID"));
|
|
3978
|
+
console.log("");
|
|
3979
|
+
console.log(import_chalk27.default.cyan(" $ rigstate scan --json"));
|
|
3980
|
+
console.log(import_chalk27.default.dim(" Output results in JSON format (useful for IDE extensions)"));
|
|
3981
|
+
console.log("");
|
|
3982
|
+
});
|
|
3983
|
+
program.parse(process.argv);
|
|
3984
|
+
if (!process.argv.slice(2).length) {
|
|
3985
|
+
program.outputHelp();
|
|
3986
|
+
}
|
|
3987
|
+
//# sourceMappingURL=index.cjs.map
|