@rigstate/cli 0.6.3 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1158 -979
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1157 -970
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/daemon.ts +129 -0
- package/src/commands/env.ts +116 -112
- package/src/commands/link.ts +72 -1
- package/src/commands/login.ts +46 -27
- package/src/commands/sync-rules.ts +77 -153
- package/src/daemon/file-watcher.ts +37 -14
package/dist/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
4
10
|
var __esm = (fn, res) => function __init() {
|
|
5
11
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
12
|
};
|
|
@@ -12,13 +18,28 @@ var __export = (target, all) => {
|
|
|
12
18
|
// node_modules/tsup/assets/esm_shims.js
|
|
13
19
|
import path from "path";
|
|
14
20
|
import { fileURLToPath } from "url";
|
|
21
|
+
var getFilename, getDirname, __dirname;
|
|
15
22
|
var init_esm_shims = __esm({
|
|
16
23
|
"node_modules/tsup/assets/esm_shims.js"() {
|
|
17
24
|
"use strict";
|
|
25
|
+
getFilename = () => fileURLToPath(import.meta.url);
|
|
26
|
+
getDirname = () => path.dirname(getFilename());
|
|
27
|
+
__dirname = /* @__PURE__ */ getDirname();
|
|
18
28
|
}
|
|
19
29
|
});
|
|
20
30
|
|
|
21
31
|
// src/utils/config.ts
|
|
32
|
+
var config_exports = {};
|
|
33
|
+
__export(config_exports, {
|
|
34
|
+
clearConfig: () => clearConfig,
|
|
35
|
+
config: () => config,
|
|
36
|
+
getApiKey: () => getApiKey,
|
|
37
|
+
getApiUrl: () => getApiUrl,
|
|
38
|
+
getProjectId: () => getProjectId,
|
|
39
|
+
setApiKey: () => setApiKey,
|
|
40
|
+
setApiUrl: () => setApiUrl,
|
|
41
|
+
setProjectId: () => setProjectId
|
|
42
|
+
});
|
|
22
43
|
import Conf from "conf";
|
|
23
44
|
function getApiKey() {
|
|
24
45
|
const apiKey = config.get("apiKey");
|
|
@@ -48,6 +69,12 @@ function getApiUrl() {
|
|
|
48
69
|
}
|
|
49
70
|
return "https://app.rigstate.com";
|
|
50
71
|
}
|
|
72
|
+
function setApiUrl(url) {
|
|
73
|
+
config.set("apiUrl", url);
|
|
74
|
+
}
|
|
75
|
+
function clearConfig() {
|
|
76
|
+
config.clear();
|
|
77
|
+
}
|
|
51
78
|
var config;
|
|
52
79
|
var init_config = __esm({
|
|
53
80
|
"src/utils/config.ts"() {
|
|
@@ -62,6 +89,360 @@ var init_config = __esm({
|
|
|
62
89
|
}
|
|
63
90
|
});
|
|
64
91
|
|
|
92
|
+
// src/commands/env.ts
|
|
93
|
+
var env_exports = {};
|
|
94
|
+
__export(env_exports, {
|
|
95
|
+
createEnvPullCommand: () => createEnvPullCommand,
|
|
96
|
+
syncEnv: () => syncEnv
|
|
97
|
+
});
|
|
98
|
+
import { Command as Command2 } from "commander";
|
|
99
|
+
import chalk2 from "chalk";
|
|
100
|
+
import ora from "ora";
|
|
101
|
+
import fs from "fs/promises";
|
|
102
|
+
import path2 from "path";
|
|
103
|
+
import axios from "axios";
|
|
104
|
+
async function syncEnv(projectId, apiKey, apiUrl, silent = false) {
|
|
105
|
+
if (!silent) {
|
|
106
|
+
console.log("");
|
|
107
|
+
console.log(chalk2.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"));
|
|
108
|
+
console.log(chalk2.bold.yellow("\u2551") + chalk2.bold.white(" \u{1F6E1}\uFE0F RIGSTATE SOVEREIGN VAULT SYNC \u{1F6E1}\uFE0F ") + chalk2.bold.yellow("\u2551"));
|
|
109
|
+
console.log(chalk2.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"));
|
|
110
|
+
console.log("");
|
|
111
|
+
}
|
|
112
|
+
const spinner = ora("Fetching secrets from Vault...").start();
|
|
113
|
+
try {
|
|
114
|
+
const response = await axios.post(`${apiUrl}/api/v1/vault/sync`, {
|
|
115
|
+
project_id: projectId
|
|
116
|
+
}, {
|
|
117
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
118
|
+
});
|
|
119
|
+
if (!response.data.success) {
|
|
120
|
+
throw new Error(response.data.error || "Failed to fetch secrets");
|
|
121
|
+
}
|
|
122
|
+
const vaultContent = response.data.data.content || "";
|
|
123
|
+
const secretCount = response.data.data.count || 0;
|
|
124
|
+
if (secretCount === 0) {
|
|
125
|
+
spinner.info("No secrets found in Vault for this project.");
|
|
126
|
+
if (!silent) console.log(chalk2.dim(" Add secrets via the Rigstate web interface."));
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
spinner.succeed(`Retrieved ${chalk2.bold(secretCount)} secret(s)`);
|
|
130
|
+
const envFile = path2.resolve(process.cwd(), ".env.local");
|
|
131
|
+
let existingContent = "";
|
|
132
|
+
let existingKeys = /* @__PURE__ */ new Set();
|
|
133
|
+
try {
|
|
134
|
+
existingContent = await fs.readFile(envFile, "utf-8");
|
|
135
|
+
existingContent.split("\n").forEach((line) => {
|
|
136
|
+
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
137
|
+
if (match) existingKeys.add(match[1]);
|
|
138
|
+
});
|
|
139
|
+
} catch (e) {
|
|
140
|
+
}
|
|
141
|
+
const vaultKeys = /* @__PURE__ */ new Set();
|
|
142
|
+
vaultContent.split("\n").forEach((line) => {
|
|
143
|
+
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
144
|
+
if (match) vaultKeys.add(match[1]);
|
|
145
|
+
});
|
|
146
|
+
let newCount = 0;
|
|
147
|
+
let updatedCount = 0;
|
|
148
|
+
vaultKeys.forEach((key) => {
|
|
149
|
+
if (!existingKeys.has(key)) {
|
|
150
|
+
newCount++;
|
|
151
|
+
} else {
|
|
152
|
+
updatedCount++;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const unchangedCount = existingKeys.size - updatedCount;
|
|
156
|
+
spinner.start("Writing .env.local...");
|
|
157
|
+
const header = [
|
|
158
|
+
"# ==========================================",
|
|
159
|
+
"# RIGSTATE SOVEREIGN FOUNDATION",
|
|
160
|
+
"# Authenticated Environment Configuration",
|
|
161
|
+
`# Synced at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
162
|
+
`# Project: ${projectId}`,
|
|
163
|
+
"# ==========================================",
|
|
164
|
+
""
|
|
165
|
+
].join("\n");
|
|
166
|
+
await fs.writeFile(envFile, header + vaultContent + "\n");
|
|
167
|
+
spinner.succeed("Written to .env.local");
|
|
168
|
+
if (!silent) {
|
|
169
|
+
console.log("");
|
|
170
|
+
console.log(chalk2.bold.green("\u2705 Environment synchronized successfully"));
|
|
171
|
+
console.log("");
|
|
172
|
+
console.log(chalk2.dim(" Summary:"));
|
|
173
|
+
console.log(chalk2.green(` + ${newCount} new`));
|
|
174
|
+
console.log(chalk2.yellow(` ~ ${updatedCount} updated`));
|
|
175
|
+
console.log(chalk2.dim(` = ${unchangedCount} unchanged`));
|
|
176
|
+
console.log("");
|
|
177
|
+
console.log(chalk2.bold.yellow("\u26A0\uFE0F Security Reminder:"));
|
|
178
|
+
console.log(chalk2.dim(" - Never commit .env.local to version control."));
|
|
179
|
+
console.log(chalk2.dim(" - Ensure .gitignore includes .env.local"));
|
|
180
|
+
console.log("");
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
} catch (e) {
|
|
184
|
+
spinner.fail(chalk2.red(`Failed to fetch secrets: ${e.message}`));
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function createEnvPullCommand() {
|
|
189
|
+
const envPull = new Command2("env");
|
|
190
|
+
envPull.command("pull").description("Pull environment variables from project vault").action(async () => {
|
|
191
|
+
let apiKey;
|
|
192
|
+
let projectId;
|
|
193
|
+
try {
|
|
194
|
+
apiKey = getApiKey();
|
|
195
|
+
} catch (e) {
|
|
196
|
+
console.error(chalk2.red('Not authenticated. Run "rigstate login" first.'));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
projectId = getProjectId();
|
|
200
|
+
if (!projectId) {
|
|
201
|
+
try {
|
|
202
|
+
const manifestPath = path2.join(process.cwd(), ".rigstate");
|
|
203
|
+
const content = await fs.readFile(manifestPath, "utf-8");
|
|
204
|
+
const manifest = JSON.parse(content);
|
|
205
|
+
projectId = manifest.project_id;
|
|
206
|
+
} catch (e) {
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!projectId) {
|
|
210
|
+
console.error(chalk2.red('No project context. Run "rigstate link" first.'));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const apiUrl = getApiUrl();
|
|
214
|
+
await syncEnv(projectId, apiKey, apiUrl);
|
|
215
|
+
});
|
|
216
|
+
return envPull;
|
|
217
|
+
}
|
|
218
|
+
var init_env = __esm({
|
|
219
|
+
"src/commands/env.ts"() {
|
|
220
|
+
"use strict";
|
|
221
|
+
init_esm_shims();
|
|
222
|
+
init_config();
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// src/commands/sync-rules.ts
|
|
227
|
+
var sync_rules_exports = {};
|
|
228
|
+
__export(sync_rules_exports, {
|
|
229
|
+
createSyncRulesCommand: () => createSyncRulesCommand,
|
|
230
|
+
syncProjectRules: () => syncProjectRules
|
|
231
|
+
});
|
|
232
|
+
import { Command as Command3 } from "commander";
|
|
233
|
+
import chalk3 from "chalk";
|
|
234
|
+
import ora2 from "ora";
|
|
235
|
+
import axios2 from "axios";
|
|
236
|
+
async function syncProjectRules(projectId, apiKey, apiUrl, dryRun = false) {
|
|
237
|
+
const spinner = ora2("\u{1F6E1}\uFE0F Frank Protocol: Initializing retroactive sync...").start();
|
|
238
|
+
let success = true;
|
|
239
|
+
try {
|
|
240
|
+
spinner.text = "Fetching project info...";
|
|
241
|
+
const projectRes = await axios2.get(`${apiUrl}/api/v1/projects`, {
|
|
242
|
+
params: { project_id: projectId },
|
|
243
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
244
|
+
});
|
|
245
|
+
if (!projectRes.data.success || !projectRes.data.data.projects?.length) {
|
|
246
|
+
throw new Error("Project not found");
|
|
247
|
+
}
|
|
248
|
+
const project = projectRes.data.data.projects[0];
|
|
249
|
+
spinner.text = `Syncing rules for ${project.name}...`;
|
|
250
|
+
if (dryRun) {
|
|
251
|
+
spinner.succeed(chalk3.yellow(` [DRY-RUN] Would sync: ${project.name}`));
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
const syncResponse = await axios2.post(`${apiUrl}/api/v1/rules/sync`, {
|
|
255
|
+
project_id: project.id
|
|
256
|
+
}, {
|
|
257
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
258
|
+
});
|
|
259
|
+
if (syncResponse.data.success) {
|
|
260
|
+
if (syncResponse.data.data.github_synced) {
|
|
261
|
+
spinner.succeed(chalk3.green(` \u2705 ${project.name} [${project.id}] \u2192 GitHub synced`));
|
|
262
|
+
} else {
|
|
263
|
+
spinner.info(chalk3.blue(` \u2139\uFE0F ${project.name} [${project.id}] \u2192 Rules generated (no GitHub)`));
|
|
264
|
+
}
|
|
265
|
+
const files = syncResponse.data.data.files;
|
|
266
|
+
if (files && Array.isArray(files)) {
|
|
267
|
+
const fs23 = await import("fs/promises");
|
|
268
|
+
const path24 = await import("path");
|
|
269
|
+
for (const file of files) {
|
|
270
|
+
const filePath = path24.join(process.cwd(), file.path);
|
|
271
|
+
await fs23.mkdir(path24.dirname(filePath), { recursive: true });
|
|
272
|
+
await fs23.writeFile(filePath, file.content, "utf-8");
|
|
273
|
+
}
|
|
274
|
+
console.log(chalk3.dim(` \u{1F4BE} Wrote ${files.length} rule files to local .cursor/rules/`));
|
|
275
|
+
}
|
|
276
|
+
console.log("");
|
|
277
|
+
console.log(chalk3.cyan("\u{1F6E1}\uFE0F Frank Protocol v1.0 has been injected into the rules engine."));
|
|
278
|
+
console.log(chalk3.dim(" All new chats will now boot with mandatory governance checks."));
|
|
279
|
+
} else {
|
|
280
|
+
spinner.warn(chalk3.yellow(` \u26A0\uFE0F ${project.name} \u2192 ${syncResponse.data.error || "Unknown error"}`));
|
|
281
|
+
success = false;
|
|
282
|
+
}
|
|
283
|
+
} catch (e) {
|
|
284
|
+
spinner.fail(chalk3.red(`Sync failed: ${e.message}`));
|
|
285
|
+
success = false;
|
|
286
|
+
}
|
|
287
|
+
return success;
|
|
288
|
+
}
|
|
289
|
+
function createSyncRulesCommand() {
|
|
290
|
+
const syncRules = new Command3("sync-rules");
|
|
291
|
+
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) => {
|
|
292
|
+
let apiKey;
|
|
293
|
+
try {
|
|
294
|
+
apiKey = getApiKey();
|
|
295
|
+
} catch (e) {
|
|
296
|
+
console.error(chalk3.red('Not authenticated. Run "rigstate login" first.'));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const apiUrl = getApiUrl();
|
|
300
|
+
if (options.project) {
|
|
301
|
+
await syncProjectRules(options.project, apiKey, apiUrl, options.dryRun);
|
|
302
|
+
} else {
|
|
303
|
+
console.log(chalk3.yellow("Use --project <id> for now. (Mass sync logic awaiting migration)"));
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
return syncRules;
|
|
307
|
+
}
|
|
308
|
+
var init_sync_rules = __esm({
|
|
309
|
+
"src/commands/sync-rules.ts"() {
|
|
310
|
+
"use strict";
|
|
311
|
+
init_esm_shims();
|
|
312
|
+
init_config();
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// src/commands/hooks.ts
|
|
317
|
+
var hooks_exports = {};
|
|
318
|
+
__export(hooks_exports, {
|
|
319
|
+
createHooksCommand: () => createHooksCommand
|
|
320
|
+
});
|
|
321
|
+
import { Command as Command4 } from "commander";
|
|
322
|
+
import chalk4 from "chalk";
|
|
323
|
+
import fs2 from "fs/promises";
|
|
324
|
+
import path3 from "path";
|
|
325
|
+
function createHooksCommand() {
|
|
326
|
+
const hooks = new Command4("hooks").description("Manage git hooks for Guardian integration");
|
|
327
|
+
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) => {
|
|
328
|
+
try {
|
|
329
|
+
const gitDir = path3.join(process.cwd(), ".git");
|
|
330
|
+
try {
|
|
331
|
+
await fs2.access(gitDir);
|
|
332
|
+
} catch {
|
|
333
|
+
console.log(chalk4.red("\u274C Not a git repository."));
|
|
334
|
+
console.log(chalk4.dim(' Initialize with "git init" first.'));
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
const hooksDir = path3.join(gitDir, "hooks");
|
|
338
|
+
await fs2.mkdir(hooksDir, { recursive: true });
|
|
339
|
+
const preCommitPath = path3.join(hooksDir, "pre-commit");
|
|
340
|
+
let existingContent = "";
|
|
341
|
+
try {
|
|
342
|
+
existingContent = await fs2.readFile(preCommitPath, "utf-8");
|
|
343
|
+
if (existingContent.includes("rigstate")) {
|
|
344
|
+
console.log(chalk4.yellow("\u26A0 Rigstate pre-commit hook already installed."));
|
|
345
|
+
console.log(chalk4.dim(' Use "rigstate hooks uninstall" to remove first.'));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
350
|
+
let script = PRE_COMMIT_SCRIPT;
|
|
351
|
+
if (options.strict === "all") {
|
|
352
|
+
script = script.replace("--strict=critical", "--strict");
|
|
353
|
+
}
|
|
354
|
+
if (existingContent && !existingContent.includes("rigstate")) {
|
|
355
|
+
const combinedScript = existingContent + "\n\n" + script.replace("#!/bin/sh\n", "");
|
|
356
|
+
await fs2.writeFile(preCommitPath, combinedScript, { mode: 493 });
|
|
357
|
+
console.log(chalk4.green("\u2705 Rigstate hook appended to existing pre-commit."));
|
|
358
|
+
} else {
|
|
359
|
+
await fs2.writeFile(preCommitPath, script, { mode: 493 });
|
|
360
|
+
console.log(chalk4.green("\u2705 Pre-commit hook installed!"));
|
|
361
|
+
}
|
|
362
|
+
console.log(chalk4.dim(` Path: ${preCommitPath}`));
|
|
363
|
+
console.log(chalk4.dim(` Strict level: ${options.strict}`));
|
|
364
|
+
console.log("");
|
|
365
|
+
console.log(chalk4.cyan("Guardian will now check your code before each commit."));
|
|
366
|
+
console.log(chalk4.dim('Use "rigstate hooks uninstall" to remove the hook.'));
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error(chalk4.red("Failed to install hook:"), error.message);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
hooks.command("uninstall").description("Remove Rigstate pre-commit hook").action(async () => {
|
|
373
|
+
try {
|
|
374
|
+
const preCommitPath = path3.join(process.cwd(), ".git", "hooks", "pre-commit");
|
|
375
|
+
try {
|
|
376
|
+
const content = await fs2.readFile(preCommitPath, "utf-8");
|
|
377
|
+
if (!content.includes("rigstate")) {
|
|
378
|
+
console.log(chalk4.yellow("\u26A0 No Rigstate hook found in pre-commit."));
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (content.includes("# Rigstate Guardian Pre-commit Hook") && content.trim().split("\n").filter((l) => l && !l.startsWith("#")).length <= 4) {
|
|
382
|
+
await fs2.unlink(preCommitPath);
|
|
383
|
+
console.log(chalk4.green("\u2705 Pre-commit hook removed."));
|
|
384
|
+
} else {
|
|
385
|
+
const lines = content.split("\n");
|
|
386
|
+
const filteredLines = [];
|
|
387
|
+
let inRigstateSection = false;
|
|
388
|
+
for (const line of lines) {
|
|
389
|
+
if (line.includes("Rigstate Guardian Pre-commit Hook")) {
|
|
390
|
+
inRigstateSection = true;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (inRigstateSection && line.includes("exit $?")) {
|
|
394
|
+
inRigstateSection = false;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (!inRigstateSection && !line.includes("rigstate check")) {
|
|
398
|
+
filteredLines.push(line);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
await fs2.writeFile(preCommitPath, filteredLines.join("\n"), { mode: 493 });
|
|
402
|
+
console.log(chalk4.green("\u2705 Rigstate section removed from pre-commit hook."));
|
|
403
|
+
}
|
|
404
|
+
} catch {
|
|
405
|
+
console.log(chalk4.yellow("\u26A0 No pre-commit hook found."));
|
|
406
|
+
}
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error(chalk4.red("Failed to uninstall hook:"), error.message);
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
return hooks;
|
|
413
|
+
}
|
|
414
|
+
var PRE_COMMIT_SCRIPT;
|
|
415
|
+
var init_hooks = __esm({
|
|
416
|
+
"src/commands/hooks.ts"() {
|
|
417
|
+
"use strict";
|
|
418
|
+
init_esm_shims();
|
|
419
|
+
PRE_COMMIT_SCRIPT = `#!/bin/sh
|
|
420
|
+
# Rigstate Guardian Pre-commit Hook
|
|
421
|
+
# Installed by: rigstate hooks install
|
|
422
|
+
|
|
423
|
+
# 1. Silent Sentinel Check (Phase 5)
|
|
424
|
+
if [ -f .rigstate/guardian.lock ]; then
|
|
425
|
+
echo "\u{1F6D1} INTERVENTION ACTIVE: Commit blocked by Silent Sentinel."
|
|
426
|
+
echo " A critical violation ('HARD_LOCK') was detected by the Guardian Daemon."
|
|
427
|
+
echo " Please fix the violation to unlock the repo."
|
|
428
|
+
echo ""
|
|
429
|
+
if grep -q "HARD_LOCK_ACTIVE" .rigstate/guardian.lock; then
|
|
430
|
+
cat .rigstate/guardian.lock
|
|
431
|
+
fi
|
|
432
|
+
exit 1
|
|
433
|
+
fi
|
|
434
|
+
|
|
435
|
+
echo "\u{1F6E1}\uFE0F Running Guardian checks..."
|
|
436
|
+
|
|
437
|
+
# Run check with strict mode for critical violations
|
|
438
|
+
rigstate check --staged --strict=critical
|
|
439
|
+
|
|
440
|
+
# Exit with the same code as rigstate check
|
|
441
|
+
exit $?
|
|
442
|
+
`;
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
65
446
|
// src/utils/skills-provisioner.ts
|
|
66
447
|
var skills_provisioner_exports = {};
|
|
67
448
|
__export(skills_provisioner_exports, {
|
|
@@ -69,14 +450,14 @@ __export(skills_provisioner_exports, {
|
|
|
69
450
|
jitProvisionSkill: () => jitProvisionSkill,
|
|
70
451
|
provisionSkills: () => provisionSkills
|
|
71
452
|
});
|
|
72
|
-
import
|
|
73
|
-
import
|
|
74
|
-
import
|
|
75
|
-
import
|
|
453
|
+
import axios5 from "axios";
|
|
454
|
+
import fs7 from "fs/promises";
|
|
455
|
+
import path8 from "path";
|
|
456
|
+
import chalk8 from "chalk";
|
|
76
457
|
async function provisionSkills(apiUrl, apiKey, projectId, rootDir) {
|
|
77
458
|
const skills = [];
|
|
78
459
|
try {
|
|
79
|
-
const response = await
|
|
460
|
+
const response = await axios5.get(`${apiUrl}/api/v1/skills`, {
|
|
80
461
|
params: { project_id: projectId },
|
|
81
462
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
82
463
|
});
|
|
@@ -94,18 +475,18 @@ async function provisionSkills(apiUrl, apiKey, projectId, rootDir) {
|
|
|
94
475
|
}
|
|
95
476
|
} catch (e) {
|
|
96
477
|
const msg = e.response?.data?.error || e.message;
|
|
97
|
-
console.log(
|
|
478
|
+
console.log(chalk8.dim(` (Skills API not available: ${msg}, using core library)`));
|
|
98
479
|
}
|
|
99
480
|
if (skills.length === 0) {
|
|
100
481
|
const { getRigstateStandardSkills } = await import("@rigstate/rules-engine");
|
|
101
482
|
const coreSkills = getRigstateStandardSkills();
|
|
102
483
|
skills.push(...coreSkills);
|
|
103
484
|
}
|
|
104
|
-
const skillsDir =
|
|
105
|
-
await
|
|
485
|
+
const skillsDir = path8.join(rootDir, ".agent", "skills");
|
|
486
|
+
await fs7.mkdir(skillsDir, { recursive: true });
|
|
106
487
|
for (const skill of skills) {
|
|
107
|
-
const skillDir =
|
|
108
|
-
await
|
|
488
|
+
const skillDir = path8.join(skillsDir, skill.name);
|
|
489
|
+
await fs7.mkdir(skillDir, { recursive: true });
|
|
109
490
|
const skillContent = `---
|
|
110
491
|
name: ${skill.name}
|
|
111
492
|
description: ${skill.description}
|
|
@@ -118,10 +499,10 @@ ${skill.content}
|
|
|
118
499
|
|
|
119
500
|
---
|
|
120
501
|
*Provisioned by Rigstate CLI. Do not modify manually.*`;
|
|
121
|
-
const skillPath =
|
|
122
|
-
await
|
|
502
|
+
const skillPath = path8.join(skillDir, "SKILL.md");
|
|
503
|
+
await fs7.writeFile(skillPath, skillContent, "utf-8");
|
|
123
504
|
}
|
|
124
|
-
console.log(
|
|
505
|
+
console.log(chalk8.green(` \u2705 Provisioned ${skills.length} skill(s) to .agent/skills/`));
|
|
125
506
|
return skills;
|
|
126
507
|
}
|
|
127
508
|
function generateSkillsDiscoveryBlock(skills) {
|
|
@@ -136,16 +517,16 @@ ${skillBlocks}
|
|
|
136
517
|
</available_skills>`;
|
|
137
518
|
}
|
|
138
519
|
async function jitProvisionSkill(skillId, apiUrl, apiKey, projectId, rootDir) {
|
|
139
|
-
const rulesPath =
|
|
520
|
+
const rulesPath = path8.join(rootDir, ".cursorrules");
|
|
140
521
|
let rulesContent = "";
|
|
141
522
|
try {
|
|
142
|
-
rulesContent = await
|
|
523
|
+
rulesContent = await fs7.readFile(rulesPath, "utf-8");
|
|
143
524
|
} catch (e) {
|
|
144
525
|
return false;
|
|
145
526
|
}
|
|
146
527
|
const isProvisioned = rulesContent.includes(`<name>${skillId}</name>`) || rulesContent.includes(`.agent/skills/${skillId}`);
|
|
147
528
|
if (isProvisioned) return false;
|
|
148
|
-
console.log(
|
|
529
|
+
console.log(chalk8.yellow(` \u26A1 JIT PROVISIONING: Injecting ${skillId}...`));
|
|
149
530
|
try {
|
|
150
531
|
const skills = await provisionSkills(apiUrl, apiKey, projectId, rootDir);
|
|
151
532
|
const skillsBlock = generateSkillsDiscoveryBlock(skills);
|
|
@@ -160,10 +541,10 @@ async function jitProvisionSkill(skillId, apiUrl, apiKey, projectId, rootDir) {
|
|
|
160
541
|
rulesContent = rulesContent.slice(0, insertPoint + 3) + "\n\n" + skillsBlock + "\n" + rulesContent.slice(insertPoint + 3);
|
|
161
542
|
}
|
|
162
543
|
}
|
|
163
|
-
await
|
|
544
|
+
await fs7.writeFile(rulesPath, rulesContent, "utf-8");
|
|
164
545
|
return true;
|
|
165
546
|
} catch (e) {
|
|
166
|
-
console.log(
|
|
547
|
+
console.log(chalk8.red(` Failed to provision skill: ${e.message}`));
|
|
167
548
|
return false;
|
|
168
549
|
}
|
|
169
550
|
}
|
|
@@ -184,13 +565,13 @@ __export(governance_exports, {
|
|
|
184
565
|
performOverride: () => performOverride,
|
|
185
566
|
setSoftLock: () => setSoftLock
|
|
186
567
|
});
|
|
187
|
-
import
|
|
188
|
-
import
|
|
189
|
-
import
|
|
568
|
+
import fs8 from "fs/promises";
|
|
569
|
+
import path9 from "path";
|
|
570
|
+
import chalk9 from "chalk";
|
|
190
571
|
async function getGovernanceConfig(rootDir = process.cwd()) {
|
|
191
572
|
try {
|
|
192
|
-
const configPath =
|
|
193
|
-
const content = await
|
|
573
|
+
const configPath = path9.join(rootDir, "rigstate.config.json");
|
|
574
|
+
const content = await fs8.readFile(configPath, "utf-8");
|
|
194
575
|
const userConfig = JSON.parse(content);
|
|
195
576
|
return {
|
|
196
577
|
governance: {
|
|
@@ -204,37 +585,37 @@ async function getGovernanceConfig(rootDir = process.cwd()) {
|
|
|
204
585
|
}
|
|
205
586
|
async function getSessionState(rootDir = process.cwd()) {
|
|
206
587
|
try {
|
|
207
|
-
const sessionPath =
|
|
208
|
-
const content = await
|
|
588
|
+
const sessionPath = path9.join(rootDir, ".rigstate", "session.json");
|
|
589
|
+
const content = await fs8.readFile(sessionPath, "utf-8");
|
|
209
590
|
return JSON.parse(content);
|
|
210
591
|
} catch (e) {
|
|
211
592
|
return DEFAULT_SESSION;
|
|
212
593
|
}
|
|
213
594
|
}
|
|
214
595
|
async function setSoftLock(reason, violationId, rootDir = process.cwd()) {
|
|
215
|
-
const sessionPath =
|
|
596
|
+
const sessionPath = path9.join(rootDir, ".rigstate", "session.json");
|
|
216
597
|
const state = {
|
|
217
598
|
status: "SOFT_LOCK",
|
|
218
599
|
active_violation: violationId,
|
|
219
600
|
lock_reason: reason,
|
|
220
601
|
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
221
602
|
};
|
|
222
|
-
await
|
|
223
|
-
await
|
|
603
|
+
await fs8.mkdir(path9.dirname(sessionPath), { recursive: true });
|
|
604
|
+
await fs8.writeFile(sessionPath, JSON.stringify(state, null, 2), "utf-8");
|
|
224
605
|
}
|
|
225
606
|
async function clearSoftLock(rootDir = process.cwd()) {
|
|
226
|
-
const sessionPath =
|
|
607
|
+
const sessionPath = path9.join(rootDir, ".rigstate", "session.json");
|
|
227
608
|
const state = {
|
|
228
609
|
...DEFAULT_SESSION,
|
|
229
610
|
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
230
611
|
};
|
|
231
|
-
await
|
|
232
|
-
await
|
|
612
|
+
await fs8.mkdir(path9.dirname(sessionPath), { recursive: true });
|
|
613
|
+
await fs8.writeFile(sessionPath, JSON.stringify(state, null, 2), "utf-8");
|
|
233
614
|
}
|
|
234
615
|
async function performOverride(violationId, reason, rootDir = process.cwd()) {
|
|
235
616
|
const config2 = await getGovernanceConfig(rootDir);
|
|
236
617
|
if (!config2.governance.allow_overrides) {
|
|
237
|
-
console.log(
|
|
618
|
+
console.log(chalk9.red("\u274C Overrides are disabled for this project."));
|
|
238
619
|
return false;
|
|
239
620
|
}
|
|
240
621
|
await clearSoftLock(rootDir);
|
|
@@ -271,22 +652,22 @@ var watchdog_exports = {};
|
|
|
271
652
|
__export(watchdog_exports, {
|
|
272
653
|
runGuardianWatchdog: () => runGuardianWatchdog
|
|
273
654
|
});
|
|
274
|
-
import
|
|
275
|
-
import
|
|
276
|
-
import
|
|
277
|
-
import
|
|
655
|
+
import fs9 from "fs/promises";
|
|
656
|
+
import path10 from "path";
|
|
657
|
+
import chalk10 from "chalk";
|
|
658
|
+
import axios6 from "axios";
|
|
278
659
|
async function countLines(filePath) {
|
|
279
660
|
try {
|
|
280
|
-
const content = await
|
|
661
|
+
const content = await fs9.readFile(filePath, "utf-8");
|
|
281
662
|
return content.split("\n").length;
|
|
282
663
|
} catch (e) {
|
|
283
664
|
return 0;
|
|
284
665
|
}
|
|
285
666
|
}
|
|
286
667
|
async function getFiles(dir, extension) {
|
|
287
|
-
const entries = await
|
|
668
|
+
const entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
288
669
|
const files = await Promise.all(entries.map(async (entry) => {
|
|
289
|
-
const res =
|
|
670
|
+
const res = path10.resolve(dir, entry.name);
|
|
290
671
|
if (entry.isDirectory()) {
|
|
291
672
|
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".next" || entry.name === "dist") return [];
|
|
292
673
|
return getFiles(res, extension);
|
|
@@ -300,7 +681,7 @@ async function fetchRulesFromApi(projectId) {
|
|
|
300
681
|
try {
|
|
301
682
|
const apiUrl = getApiUrl();
|
|
302
683
|
const apiKey = getApiKey();
|
|
303
|
-
const response = await
|
|
684
|
+
const response = await axios6.get(`${apiUrl}/api/v1/guardian/rules`, {
|
|
304
685
|
params: { project_id: projectId },
|
|
305
686
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
306
687
|
timeout: 1e4
|
|
@@ -314,8 +695,8 @@ async function fetchRulesFromApi(projectId) {
|
|
|
314
695
|
}
|
|
315
696
|
} catch (error) {
|
|
316
697
|
try {
|
|
317
|
-
const cachePath =
|
|
318
|
-
const content = await
|
|
698
|
+
const cachePath = path10.join(process.cwd(), CACHE_FILE);
|
|
699
|
+
const content = await fs9.readFile(cachePath, "utf-8");
|
|
319
700
|
const cached = JSON.parse(content);
|
|
320
701
|
if (cached.settings) {
|
|
321
702
|
return {
|
|
@@ -334,7 +715,7 @@ async function fetchRulesFromApi(projectId) {
|
|
|
334
715
|
};
|
|
335
716
|
}
|
|
336
717
|
async function runGuardianWatchdog(rootPath, settings = {}, projectId) {
|
|
337
|
-
console.log(
|
|
718
|
+
console.log(chalk10.bold("\n\u{1F6E1}\uFE0F Active Guardian Watchdog Initiated..."));
|
|
338
719
|
let lmax = settings.lmax || DEFAULT_LMAX;
|
|
339
720
|
let lmaxWarning = settings.lmax_warning || DEFAULT_LMAX_WARNING;
|
|
340
721
|
let ruleSource = settings.lmax ? "Settings (Passed)" : "Default";
|
|
@@ -344,47 +725,47 @@ async function runGuardianWatchdog(rootPath, settings = {}, projectId) {
|
|
|
344
725
|
lmaxWarning = apiRules.lmaxWarning;
|
|
345
726
|
ruleSource = apiRules.source;
|
|
346
727
|
}
|
|
347
|
-
console.log(
|
|
728
|
+
console.log(chalk10.dim(`Governance Rules: L_max=${lmax}, L_max_warning=${lmaxWarning}, Source: ${ruleSource}`));
|
|
348
729
|
const targetExtensions = [".ts", ".tsx"];
|
|
349
730
|
let scanTarget = rootPath;
|
|
350
|
-
const webSrc =
|
|
731
|
+
const webSrc = path10.join(rootPath, "apps", "web", "src");
|
|
351
732
|
try {
|
|
352
|
-
await
|
|
733
|
+
await fs9.access(webSrc);
|
|
353
734
|
scanTarget = webSrc;
|
|
354
735
|
} catch {
|
|
355
736
|
}
|
|
356
|
-
console.log(
|
|
737
|
+
console.log(chalk10.dim(`Scanning target: ${path10.relative(process.cwd(), scanTarget)}`));
|
|
357
738
|
const files = await getFiles(scanTarget, targetExtensions);
|
|
358
739
|
let violations = 0;
|
|
359
740
|
let warnings = 0;
|
|
360
741
|
const results = [];
|
|
361
742
|
for (const file of files) {
|
|
362
743
|
const lines = await countLines(file);
|
|
363
|
-
const relPath =
|
|
744
|
+
const relPath = path10.relative(rootPath, file);
|
|
364
745
|
if (lines > lmax) {
|
|
365
746
|
results.push({ file: relPath, lines, status: "VIOLATION" });
|
|
366
747
|
violations++;
|
|
367
|
-
console.log(
|
|
748
|
+
console.log(chalk10.red(`[VIOLATION] ${relPath}: ${lines} lines (Limit: ${lmax})`));
|
|
368
749
|
} else if (lines > lmaxWarning) {
|
|
369
750
|
results.push({ file: relPath, lines, status: "WARNING" });
|
|
370
751
|
warnings++;
|
|
371
|
-
console.log(
|
|
752
|
+
console.log(chalk10.yellow(`[WARNING] ${relPath}: ${lines} lines (Threshold: ${lmaxWarning})`));
|
|
372
753
|
}
|
|
373
754
|
}
|
|
374
755
|
if (violations === 0 && warnings === 0) {
|
|
375
|
-
console.log(
|
|
756
|
+
console.log(chalk10.green(`\u2714 All ${files.length} files are within governance limits.`));
|
|
376
757
|
} else {
|
|
377
|
-
console.log("\n" +
|
|
378
|
-
console.log(
|
|
379
|
-
console.log(
|
|
758
|
+
console.log("\n" + chalk10.bold("Summary:"));
|
|
759
|
+
console.log(chalk10.red(`Violations: ${violations}`));
|
|
760
|
+
console.log(chalk10.yellow(`Warnings: ${warnings}`));
|
|
380
761
|
const { getGovernanceConfig: getGovernanceConfig2, setSoftLock: setSoftLock2, InterventionLevel: InterventionLevel2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
381
762
|
const { governance } = await getGovernanceConfig2(rootPath);
|
|
382
|
-
console.log(
|
|
763
|
+
console.log(chalk10.dim(`Intervention Level: ${InterventionLevel2[governance.intervention_level] || "UNKNOWN"} (${governance.intervention_level})`));
|
|
383
764
|
if (violations > 0) {
|
|
384
|
-
console.log(
|
|
765
|
+
console.log(chalk10.red.bold("\nCRITICAL: Governance violations detected. Immediate refactoring required."));
|
|
385
766
|
if (governance.intervention_level >= InterventionLevel2.SENTINEL) {
|
|
386
|
-
console.log(
|
|
387
|
-
console.log(
|
|
767
|
+
console.log(chalk10.red.bold("\u{1F6D1} SENTINEL MODE: Session SOFT_LOCKED until resolved."));
|
|
768
|
+
console.log(chalk10.red(' Run "rigstate override <id> --reason \\"...\\"" if this is an emergency.'));
|
|
388
769
|
await setSoftLock2("Sentinel Mode: Governance Violations Detected", "ARC-VIOLATION", rootPath);
|
|
389
770
|
}
|
|
390
771
|
}
|
|
@@ -400,16 +781,16 @@ async function runGuardianWatchdog(rootPath, settings = {}, projectId) {
|
|
|
400
781
|
limitValue: lmax,
|
|
401
782
|
severity: "CRITICAL"
|
|
402
783
|
}));
|
|
403
|
-
await
|
|
784
|
+
await axios6.post(`${apiUrl}/api/v1/guardian/sync`, {
|
|
404
785
|
projectId,
|
|
405
786
|
violations: payloadViolations,
|
|
406
787
|
warnings
|
|
407
788
|
}, {
|
|
408
789
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
409
790
|
});
|
|
410
|
-
console.log(
|
|
791
|
+
console.log(chalk10.dim("\u2714 Violations synced to Rigstate Cloud."));
|
|
411
792
|
} catch (e) {
|
|
412
|
-
console.log(
|
|
793
|
+
console.log(chalk10.dim("\u26A0 Cloud sync skipped: " + (e.message || "Unknown")));
|
|
413
794
|
}
|
|
414
795
|
}
|
|
415
796
|
}
|
|
@@ -451,6 +832,23 @@ function createLoginCommand() {
|
|
|
451
832
|
Your API key has been securely stored. You can now use "rigstate scan" to audit your code.`
|
|
452
833
|
)
|
|
453
834
|
);
|
|
835
|
+
console.log(chalk.bold("\n\u{1F916} Cursor MCP Configuration"));
|
|
836
|
+
console.log(chalk.dim("Copy and paste this into Cursor Settings -> Features -> MCP:"));
|
|
837
|
+
console.log(chalk.cyan(`
|
|
838
|
+
{
|
|
839
|
+
"mcpServers": {
|
|
840
|
+
"rigstate": {
|
|
841
|
+
"command": "npx",
|
|
842
|
+
"args": [
|
|
843
|
+
"-y",
|
|
844
|
+
"@rigstate/mcp@latest"
|
|
845
|
+
],
|
|
846
|
+
"env": {
|
|
847
|
+
"RIGSTATE_API_KEY": "${apiKey}"
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}`));
|
|
454
852
|
} catch (error) {
|
|
455
853
|
console.error(
|
|
456
854
|
chalk.red("\u274C Login failed:"),
|
|
@@ -463,64 +861,108 @@ Your API key has been securely stored. You can now use "rigstate scan" to audit
|
|
|
463
861
|
|
|
464
862
|
// src/commands/link.ts
|
|
465
863
|
init_esm_shims();
|
|
466
|
-
import { Command as
|
|
467
|
-
import
|
|
468
|
-
import
|
|
469
|
-
import
|
|
864
|
+
import { Command as Command5 } from "commander";
|
|
865
|
+
import fs3 from "fs/promises";
|
|
866
|
+
import path4 from "path";
|
|
867
|
+
import chalk5 from "chalk";
|
|
470
868
|
import os from "os";
|
|
471
869
|
function createLinkCommand() {
|
|
472
|
-
return new
|
|
870
|
+
return new Command5("link").description("Link current directory to a Rigstate project").argument("<projectId>", "Project ID to link").action(async (projectId) => {
|
|
473
871
|
try {
|
|
474
|
-
const globalPath =
|
|
475
|
-
const globalData = await
|
|
872
|
+
const globalPath = path4.join(os.homedir(), ".rigstate", "config.json");
|
|
873
|
+
const globalData = await fs3.readFile(globalPath, "utf-8").catch(() => null);
|
|
476
874
|
if (globalData) {
|
|
477
875
|
const config2 = JSON.parse(globalData);
|
|
478
876
|
const cwd = process.cwd();
|
|
479
877
|
if (config2.overrides && config2.overrides[cwd]) {
|
|
480
878
|
const overrideId = config2.overrides[cwd];
|
|
481
879
|
if (overrideId !== projectId) {
|
|
482
|
-
console.warn(
|
|
880
|
+
console.warn(chalk5.yellow(`Global override detected. Enforcing project ID: ${overrideId}`));
|
|
483
881
|
projectId = overrideId;
|
|
484
882
|
}
|
|
485
883
|
}
|
|
486
884
|
}
|
|
487
885
|
} catch (e) {
|
|
488
886
|
}
|
|
489
|
-
const manifestPath =
|
|
887
|
+
const manifestPath = path4.join(process.cwd(), ".rigstate");
|
|
490
888
|
const content = {
|
|
491
889
|
project_id: projectId,
|
|
492
890
|
api_url: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
|
|
493
891
|
linked_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
494
892
|
};
|
|
495
893
|
try {
|
|
496
|
-
await
|
|
497
|
-
console.log(
|
|
498
|
-
console.log(
|
|
894
|
+
await fs3.writeFile(manifestPath, JSON.stringify(content, null, 2), "utf-8");
|
|
895
|
+
console.log(chalk5.green(`\u2714 Linked to project ID: ${projectId}`));
|
|
896
|
+
console.log(chalk5.dim(`Created local context manifest at .rigstate`));
|
|
897
|
+
console.log("");
|
|
898
|
+
console.log(chalk5.bold("\u{1F916} Rigstate Automation Detected"));
|
|
899
|
+
console.log("");
|
|
900
|
+
const { getApiKey: getApiKey2, getApiUrl: getApiUrl2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
901
|
+
const apiKey = getApiKey2();
|
|
902
|
+
const apiUrl = getApiUrl2();
|
|
903
|
+
if (apiKey) {
|
|
904
|
+
console.log(chalk5.blue("\u{1F510} Checking Vault for secrets..."));
|
|
905
|
+
const { syncEnv: syncEnv2 } = await Promise.resolve().then(() => (init_env(), env_exports));
|
|
906
|
+
await syncEnv2(projectId, apiKey, apiUrl, true);
|
|
907
|
+
console.log(chalk5.blue("\u{1F9E0} Syncing neural instructions..."));
|
|
908
|
+
const { syncProjectRules: syncProjectRules2 } = await Promise.resolve().then(() => (init_sync_rules(), sync_rules_exports));
|
|
909
|
+
await syncProjectRules2(projectId, apiKey, apiUrl);
|
|
910
|
+
console.log(chalk5.blue("\u{1F6E1}\uFE0F Checking immunity system..."));
|
|
911
|
+
await installHooks(process.cwd());
|
|
912
|
+
}
|
|
913
|
+
console.log("");
|
|
914
|
+
console.log(chalk5.bold.green("\u{1F680} Link Complete! Your environment is ready."));
|
|
499
915
|
} catch (error) {
|
|
500
|
-
|
|
916
|
+
if (error.message.includes("Not authenticated")) {
|
|
917
|
+
console.warn(chalk5.yellow('\u26A0\uFE0F Not authenticated. Run "rigstate login" to enable automation features.'));
|
|
918
|
+
} else {
|
|
919
|
+
console.error(chalk5.red(`Failed to link project: ${error.message}`));
|
|
920
|
+
}
|
|
501
921
|
}
|
|
502
922
|
});
|
|
503
923
|
}
|
|
924
|
+
async function installHooks(cwd) {
|
|
925
|
+
const fs23 = await import("fs/promises");
|
|
926
|
+
const path24 = await import("path");
|
|
927
|
+
try {
|
|
928
|
+
await fs23.access(path24.join(cwd, ".git"));
|
|
929
|
+
} catch {
|
|
930
|
+
console.log(chalk5.dim(" (Not a git repository, skipping hooks)"));
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
const hooksDir = path24.join(cwd, ".husky");
|
|
934
|
+
try {
|
|
935
|
+
const { installHooks: runInstall } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
936
|
+
const preCommitPath = path24.join(cwd, ".git/hooks/pre-commit");
|
|
937
|
+
try {
|
|
938
|
+
await fs23.access(preCommitPath);
|
|
939
|
+
console.log(chalk5.green(" \u2714 Git hooks already active"));
|
|
940
|
+
} catch {
|
|
941
|
+
console.log(chalk5.yellow(' \u26A0\uFE0F Git hooks missing. Run "rigstate hooks install" to secure repo.'));
|
|
942
|
+
}
|
|
943
|
+
} catch (e) {
|
|
944
|
+
}
|
|
945
|
+
}
|
|
504
946
|
|
|
505
947
|
// src/commands/scan.ts
|
|
506
948
|
init_esm_shims();
|
|
507
949
|
init_config();
|
|
508
|
-
import { Command as
|
|
509
|
-
import
|
|
510
|
-
import
|
|
511
|
-
import
|
|
950
|
+
import { Command as Command6 } from "commander";
|
|
951
|
+
import chalk6 from "chalk";
|
|
952
|
+
import ora3 from "ora";
|
|
953
|
+
import axios3 from "axios";
|
|
512
954
|
import { glob } from "glob";
|
|
513
|
-
import
|
|
514
|
-
import
|
|
955
|
+
import fs5 from "fs/promises";
|
|
956
|
+
import path6 from "path";
|
|
515
957
|
|
|
516
958
|
// src/utils/files.ts
|
|
517
959
|
init_esm_shims();
|
|
518
|
-
import
|
|
519
|
-
import
|
|
960
|
+
import fs4 from "fs/promises";
|
|
961
|
+
import path5 from "path";
|
|
520
962
|
async function readGitignore(dir) {
|
|
521
|
-
const gitignorePath =
|
|
963
|
+
const gitignorePath = path5.join(dir, ".gitignore");
|
|
522
964
|
try {
|
|
523
|
-
const content = await
|
|
965
|
+
const content = await fs4.readFile(gitignorePath, "utf-8");
|
|
524
966
|
return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
525
967
|
} catch (error) {
|
|
526
968
|
return [];
|
|
@@ -582,40 +1024,40 @@ function isCodeFile(filePath) {
|
|
|
582
1024
|
".vue",
|
|
583
1025
|
".svelte"
|
|
584
1026
|
];
|
|
585
|
-
const ext =
|
|
1027
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
586
1028
|
return codeExtensions.includes(ext);
|
|
587
1029
|
}
|
|
588
1030
|
|
|
589
1031
|
// src/commands/scan.ts
|
|
590
1032
|
function createScanCommand() {
|
|
591
|
-
return new
|
|
592
|
-
const spinner =
|
|
1033
|
+
return new Command6("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) => {
|
|
1034
|
+
const spinner = ora3();
|
|
593
1035
|
try {
|
|
594
1036
|
const apiKey = getApiKey();
|
|
595
1037
|
const apiUrl = getApiUrl();
|
|
596
1038
|
const projectId = options.project || getProjectId();
|
|
597
1039
|
if (!projectId) {
|
|
598
1040
|
console.warn(
|
|
599
|
-
|
|
1041
|
+
chalk6.yellow(
|
|
600
1042
|
"\u26A0\uFE0F No project ID specified. Use --project <id> or set a default."
|
|
601
1043
|
)
|
|
602
1044
|
);
|
|
603
1045
|
}
|
|
604
|
-
const scanPath =
|
|
605
|
-
spinner.start(`Scanning ${
|
|
1046
|
+
const scanPath = path6.resolve(process.cwd(), targetPath);
|
|
1047
|
+
spinner.start(`Scanning ${chalk6.cyan(scanPath)}...`);
|
|
606
1048
|
const gitignorePatterns = await readGitignore(scanPath);
|
|
607
|
-
const pattern =
|
|
1049
|
+
const pattern = path6.join(scanPath, "**/*");
|
|
608
1050
|
const allFiles = await glob(pattern, {
|
|
609
1051
|
nodir: true,
|
|
610
1052
|
dot: false,
|
|
611
1053
|
ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"]
|
|
612
1054
|
});
|
|
613
1055
|
const codeFiles = allFiles.filter((file) => {
|
|
614
|
-
const relativePath =
|
|
1056
|
+
const relativePath = path6.relative(scanPath, file);
|
|
615
1057
|
return isCodeFile(file) && !shouldIgnore(relativePath, gitignorePatterns);
|
|
616
1058
|
});
|
|
617
1059
|
if (codeFiles.length === 0) {
|
|
618
|
-
spinner.warn(
|
|
1060
|
+
spinner.warn(chalk6.yellow("No code files found to scan."));
|
|
619
1061
|
return;
|
|
620
1062
|
}
|
|
621
1063
|
spinner.text = `Found ${codeFiles.length} files. Scanning...`;
|
|
@@ -624,11 +1066,11 @@ function createScanCommand() {
|
|
|
624
1066
|
const severityCounts = {};
|
|
625
1067
|
for (let i = 0; i < codeFiles.length; i++) {
|
|
626
1068
|
const filePath = codeFiles[i];
|
|
627
|
-
const relativePath =
|
|
1069
|
+
const relativePath = path6.relative(scanPath, filePath);
|
|
628
1070
|
spinner.text = `Scanning ${i + 1}/${codeFiles.length}: ${relativePath}`;
|
|
629
1071
|
try {
|
|
630
|
-
const content = await
|
|
631
|
-
const response = await
|
|
1072
|
+
const content = await fs5.readFile(filePath, "utf-8");
|
|
1073
|
+
const response = await axios3.post(
|
|
632
1074
|
`${apiUrl}/api/v1/audit`,
|
|
633
1075
|
{
|
|
634
1076
|
content,
|
|
@@ -662,16 +1104,16 @@ function createScanCommand() {
|
|
|
662
1104
|
});
|
|
663
1105
|
}
|
|
664
1106
|
} catch (fileError) {
|
|
665
|
-
if (
|
|
666
|
-
console.warn(
|
|
1107
|
+
if (axios3.isAxiosError(fileError)) {
|
|
1108
|
+
console.warn(chalk6.yellow(`
|
|
667
1109
|
\u26A0\uFE0F Skipping ${relativePath}: ${fileError.message}`));
|
|
668
1110
|
} else {
|
|
669
|
-
console.warn(
|
|
1111
|
+
console.warn(chalk6.yellow(`
|
|
670
1112
|
\u26A0\uFE0F Error reading ${relativePath}`));
|
|
671
1113
|
}
|
|
672
1114
|
}
|
|
673
1115
|
}
|
|
674
|
-
spinner.succeed(
|
|
1116
|
+
spinner.succeed(chalk6.green("\u2705 Scan completed!"));
|
|
675
1117
|
const aggregatedResponse = {
|
|
676
1118
|
results,
|
|
677
1119
|
summary: {
|
|
@@ -686,21 +1128,21 @@ function createScanCommand() {
|
|
|
686
1128
|
printPrettyResults(aggregatedResponse);
|
|
687
1129
|
}
|
|
688
1130
|
} catch (error) {
|
|
689
|
-
spinner.fail(
|
|
690
|
-
if (
|
|
1131
|
+
spinner.fail(chalk6.red("\u274C Scan failed"));
|
|
1132
|
+
if (axios3.isAxiosError(error)) {
|
|
691
1133
|
if (error.response) {
|
|
692
|
-
console.error(
|
|
1134
|
+
console.error(chalk6.red("API Error:"), error.response.data);
|
|
693
1135
|
} else if (error.request) {
|
|
694
1136
|
console.error(
|
|
695
|
-
|
|
1137
|
+
chalk6.red("Network Error:"),
|
|
696
1138
|
"Could not reach the API. Is the server running?"
|
|
697
1139
|
);
|
|
698
1140
|
} else {
|
|
699
|
-
console.error(
|
|
1141
|
+
console.error(chalk6.red("Error:"), error.message);
|
|
700
1142
|
}
|
|
701
1143
|
} else {
|
|
702
1144
|
console.error(
|
|
703
|
-
|
|
1145
|
+
chalk6.red("Error:"),
|
|
704
1146
|
error instanceof Error ? error.message : "Unknown error"
|
|
705
1147
|
);
|
|
706
1148
|
}
|
|
@@ -710,10 +1152,10 @@ function createScanCommand() {
|
|
|
710
1152
|
}
|
|
711
1153
|
function printPrettyResults(data) {
|
|
712
1154
|
const { results, summary } = data;
|
|
713
|
-
console.log("\n" +
|
|
714
|
-
console.log(
|
|
715
|
-
console.log(`Total Files Scanned: ${
|
|
716
|
-
console.log(`Total Issues Found: ${
|
|
1155
|
+
console.log("\n" + chalk6.bold("\u{1F4CA} Scan Summary"));
|
|
1156
|
+
console.log(chalk6.dim("\u2500".repeat(60)));
|
|
1157
|
+
console.log(`Total Files Scanned: ${chalk6.cyan(summary.total_files)}`);
|
|
1158
|
+
console.log(`Total Issues Found: ${chalk6.yellow(summary.total_issues)}`);
|
|
717
1159
|
if (summary.by_severity) {
|
|
718
1160
|
console.log("\nIssues by Severity:");
|
|
719
1161
|
Object.entries(summary.by_severity).forEach(([severity, count]) => {
|
|
@@ -722,88 +1164,88 @@ function printPrettyResults(data) {
|
|
|
722
1164
|
});
|
|
723
1165
|
}
|
|
724
1166
|
if (results && results.length > 0) {
|
|
725
|
-
console.log("\n" +
|
|
726
|
-
console.log(
|
|
1167
|
+
console.log("\n" + chalk6.bold("\u{1F50D} Detailed Results"));
|
|
1168
|
+
console.log(chalk6.dim("\u2500".repeat(60)));
|
|
727
1169
|
results.forEach((result) => {
|
|
728
1170
|
if (result.issues && result.issues.length > 0) {
|
|
729
1171
|
console.log(`
|
|
730
|
-
${
|
|
1172
|
+
${chalk6.bold(result.file_path)}`);
|
|
731
1173
|
result.issues.forEach((issue) => {
|
|
732
1174
|
const severityColor = getSeverityColor(issue.severity);
|
|
733
|
-
const lineInfo = issue.line ?
|
|
1175
|
+
const lineInfo = issue.line ? chalk6.dim(`:${issue.line}`) : "";
|
|
734
1176
|
console.log(
|
|
735
1177
|
` ${severityColor(`[${issue.severity.toUpperCase()}]`)} ${issue.type}${lineInfo}`
|
|
736
1178
|
);
|
|
737
|
-
console.log(` ${
|
|
1179
|
+
console.log(` ${chalk6.dim(issue.message)}`);
|
|
738
1180
|
});
|
|
739
1181
|
}
|
|
740
1182
|
});
|
|
741
1183
|
}
|
|
742
|
-
console.log("\n" +
|
|
1184
|
+
console.log("\n" + chalk6.dim("\u2500".repeat(60)));
|
|
743
1185
|
}
|
|
744
1186
|
function getSeverityColor(severity) {
|
|
745
1187
|
switch (severity.toLowerCase()) {
|
|
746
1188
|
case "critical":
|
|
747
|
-
return
|
|
1189
|
+
return chalk6.red.bold;
|
|
748
1190
|
case "high":
|
|
749
|
-
return
|
|
1191
|
+
return chalk6.red;
|
|
750
1192
|
case "medium":
|
|
751
|
-
return
|
|
1193
|
+
return chalk6.yellow;
|
|
752
1194
|
case "low":
|
|
753
|
-
return
|
|
1195
|
+
return chalk6.blue;
|
|
754
1196
|
case "info":
|
|
755
|
-
return
|
|
1197
|
+
return chalk6.gray;
|
|
756
1198
|
default:
|
|
757
|
-
return
|
|
1199
|
+
return chalk6.white;
|
|
758
1200
|
}
|
|
759
1201
|
}
|
|
760
1202
|
|
|
761
1203
|
// src/commands/fix.ts
|
|
762
1204
|
init_esm_shims();
|
|
763
1205
|
init_config();
|
|
764
|
-
import { Command as
|
|
765
|
-
import
|
|
766
|
-
import
|
|
767
|
-
import
|
|
1206
|
+
import { Command as Command7 } from "commander";
|
|
1207
|
+
import chalk7 from "chalk";
|
|
1208
|
+
import ora4 from "ora";
|
|
1209
|
+
import axios4 from "axios";
|
|
768
1210
|
import { glob as glob2 } from "glob";
|
|
769
|
-
import
|
|
770
|
-
import
|
|
1211
|
+
import fs6 from "fs/promises";
|
|
1212
|
+
import path7 from "path";
|
|
771
1213
|
import inquirer from "inquirer";
|
|
772
1214
|
import * as Diff from "diff";
|
|
773
1215
|
function createFixCommand() {
|
|
774
|
-
return new
|
|
775
|
-
const spinner =
|
|
1216
|
+
return new Command7("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) => {
|
|
1217
|
+
const spinner = ora4();
|
|
776
1218
|
try {
|
|
777
1219
|
const apiKey = getApiKey();
|
|
778
1220
|
const apiUrl = getApiUrl();
|
|
779
1221
|
const projectId = options.project || getProjectId();
|
|
780
1222
|
if (!projectId) {
|
|
781
|
-
console.log(
|
|
1223
|
+
console.log(chalk7.yellow("\u26A0\uFE0F Project ID is required for fixing. Using default or pass --project <id>"));
|
|
782
1224
|
}
|
|
783
|
-
const scanPath =
|
|
1225
|
+
const scanPath = path7.resolve(process.cwd(), targetPath);
|
|
784
1226
|
const gitignorePatterns = await readGitignore(scanPath);
|
|
785
|
-
const pattern =
|
|
1227
|
+
const pattern = path7.join(scanPath, "**/*");
|
|
786
1228
|
const allFiles = await glob2(pattern, { nodir: true, dot: false, ignore: ["**/node_modules/**", "**/.git/**"] });
|
|
787
1229
|
const codeFiles = allFiles.filter((file) => {
|
|
788
|
-
const relativePath =
|
|
1230
|
+
const relativePath = path7.relative(scanPath, file);
|
|
789
1231
|
return isCodeFile(file) && !shouldIgnore(relativePath, gitignorePatterns);
|
|
790
1232
|
});
|
|
791
1233
|
if (codeFiles.length === 0) {
|
|
792
|
-
console.log(
|
|
1234
|
+
console.log(chalk7.yellow("No code files found."));
|
|
793
1235
|
return;
|
|
794
1236
|
}
|
|
795
|
-
console.log(
|
|
1237
|
+
console.log(chalk7.bold(`
|
|
796
1238
|
\u{1F9E0} Rigstate Fix Mode`));
|
|
797
|
-
console.log(
|
|
1239
|
+
console.log(chalk7.dim(`Scanning ${codeFiles.length} files with Project Context...
|
|
798
1240
|
`));
|
|
799
1241
|
let fixedCount = 0;
|
|
800
1242
|
for (let i = 0; i < codeFiles.length; i++) {
|
|
801
1243
|
const filePath = codeFiles[i];
|
|
802
|
-
const relativePath =
|
|
1244
|
+
const relativePath = path7.relative(scanPath, filePath);
|
|
803
1245
|
spinner.start(`Analyzing ${relativePath}...`);
|
|
804
1246
|
try {
|
|
805
|
-
const content = await
|
|
806
|
-
const response = await
|
|
1247
|
+
const content = await fs6.readFile(filePath, "utf-8");
|
|
1248
|
+
const response = await axios4.post(
|
|
807
1249
|
`${apiUrl}/api/v1/audit`,
|
|
808
1250
|
{ content, file_path: relativePath, project_id: projectId },
|
|
809
1251
|
{ headers: { "Authorization": `Bearer ${apiKey}` }, timeout: 12e4 }
|
|
@@ -813,22 +1255,22 @@ function createFixCommand() {
|
|
|
813
1255
|
if (fixableIssues.length > 0) {
|
|
814
1256
|
spinner.stop();
|
|
815
1257
|
console.log(`
|
|
816
|
-
${
|
|
1258
|
+
${chalk7.bold(relativePath)}: Found ${fixableIssues.length} fixable issues.`);
|
|
817
1259
|
for (const issue of fixableIssues) {
|
|
818
|
-
console.log(
|
|
1260
|
+
console.log(chalk7.red(`
|
|
819
1261
|
[${issue.type}] ${issue.title}`));
|
|
820
|
-
console.log(
|
|
1262
|
+
console.log(chalk7.dim(issue.suggestion || issue.message));
|
|
821
1263
|
const diff = Diff.createTwoFilesPatch(relativePath, relativePath, content, issue.fixed_content, "Current", "Fixed");
|
|
822
1264
|
console.log("\n" + diff.split("\n").slice(0, 15).join("\n") + (diff.split("\n").length > 15 ? "\n..." : ""));
|
|
823
1265
|
const { apply } = await inquirer.prompt([{
|
|
824
1266
|
type: "confirm",
|
|
825
1267
|
name: "apply",
|
|
826
|
-
message: `Apply this fix to ${
|
|
1268
|
+
message: `Apply this fix to ${chalk7.cyan(relativePath)}?`,
|
|
827
1269
|
default: true
|
|
828
1270
|
}]);
|
|
829
1271
|
if (apply) {
|
|
830
|
-
await
|
|
831
|
-
console.log(
|
|
1272
|
+
await fs6.writeFile(filePath, issue.fixed_content);
|
|
1273
|
+
console.log(chalk7.green(`\u2705 Fixed applied!`));
|
|
832
1274
|
fixedCount++;
|
|
833
1275
|
if (issue.related_step_id) {
|
|
834
1276
|
const { completeStep } = await inquirer.prompt([{
|
|
@@ -839,20 +1281,20 @@ ${chalk4.bold(relativePath)}: Found ${fixableIssues.length} fixable issues.`);
|
|
|
839
1281
|
}]);
|
|
840
1282
|
if (completeStep) {
|
|
841
1283
|
try {
|
|
842
|
-
await
|
|
1284
|
+
await axios4.post(
|
|
843
1285
|
`${apiUrl}/api/v1/roadmap/update-status`,
|
|
844
1286
|
{ step_id: issue.related_step_id, status: "COMPLETED", project_id: projectId },
|
|
845
1287
|
{ headers: { "Authorization": `Bearer ${apiKey}` } }
|
|
846
1288
|
);
|
|
847
|
-
console.log(
|
|
1289
|
+
console.log(chalk7.green(`\u{1F680} Roadmap updated! Mission Control is in sync.`));
|
|
848
1290
|
} catch (err) {
|
|
849
|
-
console.error(
|
|
1291
|
+
console.error(chalk7.yellow(`Failed to update roadmap: ${err.message}`));
|
|
850
1292
|
}
|
|
851
1293
|
}
|
|
852
1294
|
}
|
|
853
1295
|
break;
|
|
854
1296
|
} else {
|
|
855
|
-
console.log(
|
|
1297
|
+
console.log(chalk7.dim("Skipped."));
|
|
856
1298
|
}
|
|
857
1299
|
}
|
|
858
1300
|
} else {
|
|
@@ -862,11 +1304,11 @@ ${chalk4.bold(relativePath)}: Found ${fixableIssues.length} fixable issues.`);
|
|
|
862
1304
|
}
|
|
863
1305
|
}
|
|
864
1306
|
spinner.stop();
|
|
865
|
-
console.log(
|
|
1307
|
+
console.log(chalk7.bold.green(`
|
|
866
1308
|
|
|
867
1309
|
\u{1F680} Fix session complete!`));
|
|
868
1310
|
console.log(`Frank fixed ${fixedCount} detected issues.`);
|
|
869
|
-
console.log(
|
|
1311
|
+
console.log(chalk7.dim(`Run 'rigstate scan' to verify remaining issues.`));
|
|
870
1312
|
} catch (error) {
|
|
871
1313
|
spinner.fail("Fix session failed");
|
|
872
1314
|
console.error(error.message);
|
|
@@ -877,16 +1319,16 @@ ${chalk4.bold(relativePath)}: Found ${fixableIssues.length} fixable issues.`);
|
|
|
877
1319
|
// src/commands/sync.ts
|
|
878
1320
|
init_esm_shims();
|
|
879
1321
|
init_config();
|
|
880
|
-
import { Command as
|
|
881
|
-
import
|
|
882
|
-
import
|
|
883
|
-
import
|
|
884
|
-
import
|
|
885
|
-
import
|
|
1322
|
+
import { Command as Command8 } from "commander";
|
|
1323
|
+
import chalk11 from "chalk";
|
|
1324
|
+
import ora5 from "ora";
|
|
1325
|
+
import axios7 from "axios";
|
|
1326
|
+
import fs10 from "fs/promises";
|
|
1327
|
+
import path11 from "path";
|
|
886
1328
|
function createSyncCommand() {
|
|
887
|
-
const sync = new
|
|
1329
|
+
const sync = new Command8("sync");
|
|
888
1330
|
sync.description("Synchronize local state with Rigstate Cloud").option("-p, --project <id>", "Specify Project ID (saves to config automatically)").action(async (options) => {
|
|
889
|
-
const spinner =
|
|
1331
|
+
const spinner = ora5("Synchronizing project state...").start();
|
|
890
1332
|
try {
|
|
891
1333
|
let apiKey;
|
|
892
1334
|
try {
|
|
@@ -898,8 +1340,8 @@ function createSyncCommand() {
|
|
|
898
1340
|
let projectId = options.project;
|
|
899
1341
|
if (!projectId) {
|
|
900
1342
|
try {
|
|
901
|
-
const manifestPath =
|
|
902
|
-
const manifestContent = await
|
|
1343
|
+
const manifestPath = path11.join(process.cwd(), ".rigstate");
|
|
1344
|
+
const manifestContent = await fs10.readFile(manifestPath, "utf-8");
|
|
903
1345
|
const manifest = JSON.parse(manifestContent);
|
|
904
1346
|
if (manifest.project_id) projectId = manifest.project_id;
|
|
905
1347
|
} catch (e) {
|
|
@@ -914,7 +1356,7 @@ function createSyncCommand() {
|
|
|
914
1356
|
return;
|
|
915
1357
|
}
|
|
916
1358
|
const apiUrl = getApiUrl();
|
|
917
|
-
const response = await
|
|
1359
|
+
const response = await axios7.get(`${apiUrl}/api/v1/roadmap`, {
|
|
918
1360
|
params: { project_id: projectId },
|
|
919
1361
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
920
1362
|
});
|
|
@@ -923,31 +1365,31 @@ function createSyncCommand() {
|
|
|
923
1365
|
}
|
|
924
1366
|
const { roadmap, project } = response.data.data;
|
|
925
1367
|
const timestamp = response.data.timestamp;
|
|
926
|
-
const targetPath =
|
|
1368
|
+
const targetPath = path11.join(process.cwd(), "roadmap.json");
|
|
927
1369
|
const fileContent = JSON.stringify({
|
|
928
1370
|
project,
|
|
929
1371
|
last_synced: timestamp,
|
|
930
1372
|
roadmap
|
|
931
1373
|
}, null, 2);
|
|
932
|
-
await
|
|
1374
|
+
await fs10.writeFile(targetPath, fileContent, "utf-8");
|
|
933
1375
|
try {
|
|
934
|
-
const manifestPath =
|
|
1376
|
+
const manifestPath = path11.join(process.cwd(), ".rigstate");
|
|
935
1377
|
const manifestContent = {
|
|
936
1378
|
project_id: projectId,
|
|
937
1379
|
project_name: project,
|
|
938
1380
|
last_synced: timestamp,
|
|
939
1381
|
api_url: apiUrl
|
|
940
1382
|
};
|
|
941
|
-
await
|
|
1383
|
+
await fs10.writeFile(manifestPath, JSON.stringify(manifestContent, null, 2), "utf-8");
|
|
942
1384
|
} catch (e) {
|
|
943
1385
|
}
|
|
944
|
-
console.log(
|
|
1386
|
+
console.log(chalk11.bold("\n\u{1F9E0} Agent Skills Provisioning..."));
|
|
945
1387
|
try {
|
|
946
1388
|
const { provisionSkills: provisionSkills2, generateSkillsDiscoveryBlock: generateSkillsDiscoveryBlock2 } = await Promise.resolve().then(() => (init_skills_provisioner(), skills_provisioner_exports));
|
|
947
1389
|
const skills = await provisionSkills2(apiUrl, apiKey, projectId, process.cwd());
|
|
948
|
-
const cursorRulesPath =
|
|
1390
|
+
const cursorRulesPath = path11.join(process.cwd(), ".cursorrules");
|
|
949
1391
|
try {
|
|
950
|
-
let rulesContent = await
|
|
1392
|
+
let rulesContent = await fs10.readFile(cursorRulesPath, "utf-8");
|
|
951
1393
|
const skillsBlock = generateSkillsDiscoveryBlock2(skills);
|
|
952
1394
|
if (rulesContent.includes("<available_skills>")) {
|
|
953
1395
|
rulesContent = rulesContent.replace(
|
|
@@ -960,28 +1402,28 @@ function createSyncCommand() {
|
|
|
960
1402
|
rulesContent = rulesContent.slice(0, insertPoint + 3) + "\n\n" + skillsBlock + "\n" + rulesContent.slice(insertPoint + 3);
|
|
961
1403
|
}
|
|
962
1404
|
}
|
|
963
|
-
await
|
|
964
|
-
console.log(
|
|
1405
|
+
await fs10.writeFile(cursorRulesPath, rulesContent, "utf-8");
|
|
1406
|
+
console.log(chalk11.dim(` Updated .cursorrules with skills discovery block`));
|
|
965
1407
|
} catch (e) {
|
|
966
1408
|
}
|
|
967
1409
|
} catch (e) {
|
|
968
|
-
console.log(
|
|
1410
|
+
console.log(chalk11.yellow(` \u26A0 Skills provisioning skipped: ${e.message}`));
|
|
969
1411
|
}
|
|
970
1412
|
try {
|
|
971
|
-
const logPath =
|
|
1413
|
+
const logPath = path11.join(process.cwd(), ".rigstate", "logs", "last_execution.json");
|
|
972
1414
|
try {
|
|
973
|
-
const logContent = await
|
|
1415
|
+
const logContent = await fs10.readFile(logPath, "utf-8");
|
|
974
1416
|
const logData = JSON.parse(logContent);
|
|
975
1417
|
if (logData.task_summary) {
|
|
976
|
-
await
|
|
1418
|
+
await axios7.post(`${apiUrl}/api/v1/execution-logs`, {
|
|
977
1419
|
project_id: projectId,
|
|
978
1420
|
...logData,
|
|
979
1421
|
agent_role: process.env.RIGSTATE_MODE === "SUPERVISOR" ? "SUPERVISOR" : "WORKER"
|
|
980
1422
|
}, {
|
|
981
1423
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
982
1424
|
});
|
|
983
|
-
await
|
|
984
|
-
console.log(
|
|
1425
|
+
await fs10.unlink(logPath);
|
|
1426
|
+
console.log(chalk11.dim(`\u2714 Mission Report uploaded.`));
|
|
985
1427
|
}
|
|
986
1428
|
} catch (e) {
|
|
987
1429
|
if (e.code !== "ENOENT") {
|
|
@@ -989,14 +1431,14 @@ function createSyncCommand() {
|
|
|
989
1431
|
}
|
|
990
1432
|
} catch (e) {
|
|
991
1433
|
}
|
|
992
|
-
spinner.succeed(
|
|
993
|
-
console.log(
|
|
1434
|
+
spinner.succeed(chalk11.green(`Synced ${roadmap.length} roadmap steps for project "${project}"`));
|
|
1435
|
+
console.log(chalk11.dim(`Local files updated: roadmap.json`));
|
|
994
1436
|
const { runGuardianWatchdog: runGuardianWatchdog2 } = await Promise.resolve().then(() => (init_watchdog(), watchdog_exports));
|
|
995
1437
|
const settings = response.data.data.settings || {};
|
|
996
1438
|
await runGuardianWatchdog2(process.cwd(), settings, projectId);
|
|
997
|
-
console.log(
|
|
1439
|
+
console.log(chalk11.bold("\n\u{1F4E1} Agent Bridge Heartbeat..."));
|
|
998
1440
|
try {
|
|
999
|
-
const bridgeResponse = await
|
|
1441
|
+
const bridgeResponse = await axios7.get(`${apiUrl}/api/v1/agent/bridge`, {
|
|
1000
1442
|
params: { project_id: projectId },
|
|
1001
1443
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1002
1444
|
});
|
|
@@ -1005,59 +1447,59 @@ function createSyncCommand() {
|
|
|
1005
1447
|
const pending = tasks.filter((t) => t.status === "PENDING");
|
|
1006
1448
|
const approved = tasks.filter((t) => t.status === "APPROVED");
|
|
1007
1449
|
if (pending.length > 0 || approved.length > 0) {
|
|
1008
|
-
console.log(
|
|
1009
|
-
console.log(
|
|
1450
|
+
console.log(chalk11.yellow(`\u26A0 Bridge Alert: ${pending.length} pending, ${approved.length} approved tasks found.`));
|
|
1451
|
+
console.log(chalk11.dim('Run "rigstate fix" to process these tasks or ensure your IDE MCP server is active.'));
|
|
1010
1452
|
} else {
|
|
1011
|
-
console.log(
|
|
1453
|
+
console.log(chalk11.green("\u2714 Heartbeat healthy. No pending bridge tasks."));
|
|
1012
1454
|
}
|
|
1013
1455
|
const pings = pending.filter((t) => t.proposal?.startsWith("ping"));
|
|
1014
1456
|
for (const ping of pings) {
|
|
1015
|
-
await
|
|
1457
|
+
await axios7.post(`${apiUrl}/api/v1/agent/bridge`, {
|
|
1016
1458
|
bridge_id: ping.id,
|
|
1017
1459
|
status: "COMPLETED",
|
|
1018
1460
|
summary: "Pong! CLI Sync Heartbeat confirmed."
|
|
1019
1461
|
}, {
|
|
1020
1462
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1021
1463
|
});
|
|
1022
|
-
console.log(
|
|
1464
|
+
console.log(chalk11.cyan(`\u{1F3D3} Pong! Acknowledged heartbeat signal [${ping.id}]`));
|
|
1023
1465
|
}
|
|
1024
1466
|
}
|
|
1025
1467
|
} catch (e) {
|
|
1026
|
-
console.log(
|
|
1468
|
+
console.log(chalk11.yellow(`\u26A0 Could not verify Bridge status: ${e.message}`));
|
|
1027
1469
|
}
|
|
1028
1470
|
if (options.project) {
|
|
1029
|
-
console.log(
|
|
1471
|
+
console.log(chalk11.blue(`Project context saved. Future commands will use this project.`));
|
|
1030
1472
|
}
|
|
1031
1473
|
try {
|
|
1032
|
-
const migrationDir =
|
|
1033
|
-
const files = await
|
|
1474
|
+
const migrationDir = path11.join(process.cwd(), "supabase", "migrations");
|
|
1475
|
+
const files = await fs10.readdir(migrationDir);
|
|
1034
1476
|
const sqlFiles = files.filter((f) => f.endsWith(".sql")).sort();
|
|
1035
1477
|
if (sqlFiles.length > 0) {
|
|
1036
1478
|
const latestMigration = sqlFiles[sqlFiles.length - 1];
|
|
1037
|
-
console.log(
|
|
1479
|
+
console.log(chalk11.dim(`
|
|
1038
1480
|
\u{1F6E1} Migration Guard:`));
|
|
1039
|
-
console.log(
|
|
1040
|
-
console.log(
|
|
1481
|
+
console.log(chalk11.dim(` Latest Local: ${latestMigration}`));
|
|
1482
|
+
console.log(chalk11.yellow(` \u26A0 Ensure DB schema matches this version. CLI cannot verify Remote RLS policies directly.`));
|
|
1041
1483
|
}
|
|
1042
1484
|
} catch (e) {
|
|
1043
1485
|
}
|
|
1044
1486
|
try {
|
|
1045
|
-
const vaultResponse = await
|
|
1487
|
+
const vaultResponse = await axios7.post(
|
|
1046
1488
|
`${apiUrl}/api/v1/vault/sync`,
|
|
1047
1489
|
{ project_id: projectId },
|
|
1048
1490
|
{ headers: { Authorization: `Bearer ${apiKey}` } }
|
|
1049
1491
|
);
|
|
1050
1492
|
if (vaultResponse.data.success) {
|
|
1051
1493
|
const vaultContent = vaultResponse.data.data.content || "";
|
|
1052
|
-
const localEnvPath =
|
|
1494
|
+
const localEnvPath = path11.join(process.cwd(), ".env.local");
|
|
1053
1495
|
let localContent = "";
|
|
1054
1496
|
try {
|
|
1055
|
-
localContent = await
|
|
1497
|
+
localContent = await fs10.readFile(localEnvPath, "utf-8");
|
|
1056
1498
|
} catch (e) {
|
|
1057
1499
|
}
|
|
1058
1500
|
if (vaultContent.trim() !== localContent.trim()) {
|
|
1059
|
-
console.log(
|
|
1060
|
-
console.log(
|
|
1501
|
+
console.log(chalk11.bold("\n\u{1F510} Sovereign Foundation (Vault):"));
|
|
1502
|
+
console.log(chalk11.yellow(" Status: Drift Detected / Update Available"));
|
|
1061
1503
|
const { syncVault } = await import("inquirer").then((m) => m.default.prompt([{
|
|
1062
1504
|
type: "confirm",
|
|
1063
1505
|
name: "syncVault",
|
|
@@ -1065,25 +1507,25 @@ function createSyncCommand() {
|
|
|
1065
1507
|
default: false
|
|
1066
1508
|
}]));
|
|
1067
1509
|
if (syncVault) {
|
|
1068
|
-
await
|
|
1069
|
-
console.log(
|
|
1510
|
+
await fs10.writeFile(localEnvPath, vaultContent, "utf-8");
|
|
1511
|
+
console.log(chalk11.green(" \u2705 .env.local synchronized with Vault."));
|
|
1070
1512
|
} else {
|
|
1071
|
-
console.log(
|
|
1513
|
+
console.log(chalk11.dim(" Skipped vault sync."));
|
|
1072
1514
|
}
|
|
1073
1515
|
} else {
|
|
1074
|
-
console.log(
|
|
1516
|
+
console.log(chalk11.dim("\n\u{1F510} Sovereign Foundation: Synced."));
|
|
1075
1517
|
}
|
|
1076
1518
|
}
|
|
1077
1519
|
} catch (e) {
|
|
1078
1520
|
}
|
|
1079
|
-
console.log(
|
|
1521
|
+
console.log(chalk11.dim("\n\u{1F6E1}\uFE0F System Integrity Check..."));
|
|
1080
1522
|
await checkSystemIntegrity(apiUrl, apiKey, projectId);
|
|
1081
1523
|
} catch (error) {
|
|
1082
|
-
if (
|
|
1524
|
+
if (axios7.isAxiosError(error)) {
|
|
1083
1525
|
const message = error.response?.data?.error || error.message;
|
|
1084
|
-
spinner.fail(
|
|
1526
|
+
spinner.fail(chalk11.red(`Sync failed: ${message}`));
|
|
1085
1527
|
} else {
|
|
1086
|
-
spinner.fail(
|
|
1528
|
+
spinner.fail(chalk11.red("Sync failed: " + (error.message || "Unknown error")));
|
|
1087
1529
|
}
|
|
1088
1530
|
}
|
|
1089
1531
|
});
|
|
@@ -1091,7 +1533,7 @@ function createSyncCommand() {
|
|
|
1091
1533
|
}
|
|
1092
1534
|
async function checkSystemIntegrity(apiUrl, apiKey, projectId) {
|
|
1093
1535
|
try {
|
|
1094
|
-
const response = await
|
|
1536
|
+
const response = await axios7.get(`${apiUrl}/api/v1/system/integrity`, {
|
|
1095
1537
|
params: { project_id: projectId },
|
|
1096
1538
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1097
1539
|
});
|
|
@@ -1099,57 +1541,57 @@ async function checkSystemIntegrity(apiUrl, apiKey, projectId) {
|
|
|
1099
1541
|
const { migrations, rls, guardian_violations } = response.data.data;
|
|
1100
1542
|
if (migrations) {
|
|
1101
1543
|
if (migrations.in_sync) {
|
|
1102
|
-
console.log(
|
|
1544
|
+
console.log(chalk11.green(` \u2705 Migrations synced (${migrations.count} versions)`));
|
|
1103
1545
|
} else {
|
|
1104
|
-
console.log(
|
|
1546
|
+
console.log(chalk11.red(` \u{1F6D1} CRITICAL: DB Schema out of sync! ${migrations.missing?.length || 0} migrations not applied.`));
|
|
1105
1547
|
if (migrations.missing?.length > 0) {
|
|
1106
|
-
console.log(
|
|
1548
|
+
console.log(chalk11.dim(` Missing: ${migrations.missing.slice(0, 3).join(", ")}${migrations.missing.length > 3 ? "..." : ""}`));
|
|
1107
1549
|
}
|
|
1108
|
-
console.log(
|
|
1550
|
+
console.log(chalk11.yellow(` Run 'supabase db push' or apply migrations immediately.`));
|
|
1109
1551
|
}
|
|
1110
1552
|
}
|
|
1111
1553
|
if (rls) {
|
|
1112
1554
|
if (rls.all_secured) {
|
|
1113
|
-
console.log(
|
|
1555
|
+
console.log(chalk11.green(` \u2705 RLS Audit Passed (${rls.table_count} tables secured)`));
|
|
1114
1556
|
} else {
|
|
1115
|
-
console.log(
|
|
1557
|
+
console.log(chalk11.red(` \u{1F6D1} CRITICAL: Security Vulnerability! ${rls.unsecured?.length || 0} tables have RLS disabled.`));
|
|
1116
1558
|
rls.unsecured?.forEach((table) => {
|
|
1117
|
-
console.log(
|
|
1559
|
+
console.log(chalk11.red(` - ${table}`));
|
|
1118
1560
|
});
|
|
1119
|
-
console.log(
|
|
1561
|
+
console.log(chalk11.yellow(' Enable RLS immediately: ALTER TABLE "table" ENABLE ROW LEVEL SECURITY;'));
|
|
1120
1562
|
}
|
|
1121
1563
|
}
|
|
1122
1564
|
if (guardian_violations) {
|
|
1123
1565
|
if (guardian_violations.count === 0) {
|
|
1124
|
-
console.log(
|
|
1566
|
+
console.log(chalk11.green(" \u2705 Guardian: No active violations"));
|
|
1125
1567
|
} else {
|
|
1126
|
-
console.log(
|
|
1127
|
-
console.log(
|
|
1568
|
+
console.log(chalk11.yellow(` \u26A0\uFE0F Guardian: ${guardian_violations.count} active violations`));
|
|
1569
|
+
console.log(chalk11.dim(' Run "rigstate check" for details.'));
|
|
1128
1570
|
}
|
|
1129
1571
|
}
|
|
1130
1572
|
}
|
|
1131
1573
|
} catch (e) {
|
|
1132
|
-
console.log(
|
|
1574
|
+
console.log(chalk11.dim(" (System integrity check skipped - API endpoint not available)"));
|
|
1133
1575
|
}
|
|
1134
1576
|
}
|
|
1135
1577
|
|
|
1136
1578
|
// src/commands/init.ts
|
|
1137
1579
|
init_esm_shims();
|
|
1138
|
-
import { Command as
|
|
1139
|
-
import
|
|
1140
|
-
import
|
|
1141
|
-
import
|
|
1142
|
-
import
|
|
1580
|
+
import { Command as Command9 } from "commander";
|
|
1581
|
+
import chalk12 from "chalk";
|
|
1582
|
+
import fs12 from "fs/promises";
|
|
1583
|
+
import path13 from "path";
|
|
1584
|
+
import ora6 from "ora";
|
|
1143
1585
|
import { execSync } from "child_process";
|
|
1144
1586
|
|
|
1145
1587
|
// src/utils/manifest.ts
|
|
1146
1588
|
init_esm_shims();
|
|
1147
|
-
import
|
|
1148
|
-
import
|
|
1589
|
+
import fs11 from "fs/promises";
|
|
1590
|
+
import path12 from "path";
|
|
1149
1591
|
async function loadManifest() {
|
|
1150
1592
|
try {
|
|
1151
|
-
const manifestPath =
|
|
1152
|
-
const content = await
|
|
1593
|
+
const manifestPath = path12.join(process.cwd(), ".rigstate");
|
|
1594
|
+
const content = await fs11.readFile(manifestPath, "utf-8");
|
|
1153
1595
|
return JSON.parse(content);
|
|
1154
1596
|
} catch {
|
|
1155
1597
|
return null;
|
|
@@ -1158,15 +1600,15 @@ async function loadManifest() {
|
|
|
1158
1600
|
|
|
1159
1601
|
// src/commands/init.ts
|
|
1160
1602
|
init_config();
|
|
1161
|
-
import
|
|
1603
|
+
import axios8 from "axios";
|
|
1162
1604
|
function createInitCommand() {
|
|
1163
|
-
return new
|
|
1164
|
-
const spinner =
|
|
1605
|
+
return new Command9("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) => {
|
|
1606
|
+
const spinner = ora6("Initializing Rigstate project...").start();
|
|
1165
1607
|
let apiKey;
|
|
1166
1608
|
try {
|
|
1167
1609
|
apiKey = getApiKey();
|
|
1168
1610
|
} catch (e) {
|
|
1169
|
-
spinner.fail(
|
|
1611
|
+
spinner.fail(chalk12.red('Not authenticated. Run "rigstate login" first.'));
|
|
1170
1612
|
return;
|
|
1171
1613
|
}
|
|
1172
1614
|
const apiUrl = getApiUrl();
|
|
@@ -1188,7 +1630,7 @@ function createInitCommand() {
|
|
|
1188
1630
|
spinner.start("Fetching your projects...");
|
|
1189
1631
|
let projects = [];
|
|
1190
1632
|
try {
|
|
1191
|
-
const projectsResponse = await
|
|
1633
|
+
const projectsResponse = await axios8.get(`${apiUrl}/api/v1/projects`, {
|
|
1192
1634
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1193
1635
|
});
|
|
1194
1636
|
if (projectsResponse.data.success) {
|
|
@@ -1240,7 +1682,7 @@ function createInitCommand() {
|
|
|
1240
1682
|
spinner.start("Fetching organizations...");
|
|
1241
1683
|
let orgs = [];
|
|
1242
1684
|
try {
|
|
1243
|
-
const orgsResponse = await
|
|
1685
|
+
const orgsResponse = await axios8.get(`${apiUrl}/api/v1/organizations`, {
|
|
1244
1686
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1245
1687
|
});
|
|
1246
1688
|
orgs = orgsResponse.data.data?.organizations || [];
|
|
@@ -1263,25 +1705,25 @@ function createInitCommand() {
|
|
|
1263
1705
|
selectedOrgId = orgId;
|
|
1264
1706
|
}
|
|
1265
1707
|
if (!selectedOrgId) {
|
|
1266
|
-
console.log(
|
|
1708
|
+
console.log(chalk12.yellow("No organization available. Please create the project via the Rigstate dashboard."));
|
|
1267
1709
|
return;
|
|
1268
1710
|
}
|
|
1269
1711
|
spinner.start("Creating new project...");
|
|
1270
1712
|
try {
|
|
1271
|
-
const createResponse = await
|
|
1713
|
+
const createResponse = await axios8.post(`${apiUrl}/api/v1/projects`, {
|
|
1272
1714
|
name: newName,
|
|
1273
1715
|
organization_id: selectedOrgId
|
|
1274
1716
|
}, {
|
|
1275
1717
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1276
1718
|
});
|
|
1277
1719
|
if (!createResponse.data.success) {
|
|
1278
|
-
spinner.fail(
|
|
1720
|
+
spinner.fail(chalk12.red("Failed to create project: " + createResponse.data.error));
|
|
1279
1721
|
return;
|
|
1280
1722
|
}
|
|
1281
1723
|
projectId = createResponse.data.data.project.id;
|
|
1282
|
-
spinner.succeed(
|
|
1724
|
+
spinner.succeed(chalk12.green(`Created new project: ${newName}`));
|
|
1283
1725
|
} catch (e) {
|
|
1284
|
-
spinner.fail(
|
|
1726
|
+
spinner.fail(chalk12.red("Project creation API not available. Please create via dashboard."));
|
|
1285
1727
|
return;
|
|
1286
1728
|
}
|
|
1287
1729
|
} else {
|
|
@@ -1291,35 +1733,35 @@ function createInitCommand() {
|
|
|
1291
1733
|
spinner.start(`Linking to project ID: ${projectId}...`);
|
|
1292
1734
|
}
|
|
1293
1735
|
setProjectId(projectId);
|
|
1294
|
-
const manifestPath =
|
|
1736
|
+
const manifestPath = path13.join(process.cwd(), ".rigstate");
|
|
1295
1737
|
const manifestContent = {
|
|
1296
1738
|
project_id: projectId,
|
|
1297
1739
|
last_linked: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1298
1740
|
api_url: apiUrl
|
|
1299
1741
|
};
|
|
1300
|
-
await
|
|
1742
|
+
await fs12.writeFile(manifestPath, JSON.stringify(manifestContent, null, 2), "utf-8");
|
|
1301
1743
|
try {
|
|
1302
|
-
await
|
|
1744
|
+
await fs12.access(".git");
|
|
1303
1745
|
} catch {
|
|
1304
1746
|
spinner.text = "Initializing git repository...";
|
|
1305
1747
|
execSync("git init", { stdio: "ignore" });
|
|
1306
1748
|
}
|
|
1307
|
-
spinner.succeed(
|
|
1749
|
+
spinner.succeed(chalk12.green(`\u2705 Linked to project: ${projectId}`));
|
|
1308
1750
|
await generateRules(apiUrl, apiKey, projectId, options.force, spinner);
|
|
1309
1751
|
console.log("");
|
|
1310
|
-
console.log(
|
|
1311
|
-
console.log(
|
|
1312
|
-
console.log(
|
|
1313
|
-
console.log(
|
|
1752
|
+
console.log(chalk12.blue("Next steps:"));
|
|
1753
|
+
console.log(chalk12.dim(" rigstate sync - Sync roadmap and context"));
|
|
1754
|
+
console.log(chalk12.dim(" rigstate watch - Start development loop"));
|
|
1755
|
+
console.log(chalk12.dim(" rigstate focus - Get current task"));
|
|
1314
1756
|
} catch (e) {
|
|
1315
|
-
spinner.fail(
|
|
1757
|
+
spinner.fail(chalk12.red("Initialization failed: " + e.message));
|
|
1316
1758
|
}
|
|
1317
1759
|
});
|
|
1318
1760
|
}
|
|
1319
1761
|
async function generateRules(apiUrl, apiKey, projectId, force, spinner) {
|
|
1320
1762
|
spinner.start("Generating AI rules (MDC + AGENTS.md)...");
|
|
1321
1763
|
try {
|
|
1322
|
-
const response = await
|
|
1764
|
+
const response = await axios8.post(`${apiUrl}/api/v1/rules/generate`, {
|
|
1323
1765
|
project_id: projectId
|
|
1324
1766
|
}, {
|
|
1325
1767
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
@@ -1327,67 +1769,67 @@ async function generateRules(apiUrl, apiKey, projectId, force, spinner) {
|
|
|
1327
1769
|
if (response.data.success || response.data.files) {
|
|
1328
1770
|
const files = response.data.files || [];
|
|
1329
1771
|
if (files.length === 0 && response.data.rules) {
|
|
1330
|
-
const rulesPath =
|
|
1331
|
-
await
|
|
1332
|
-
spinner.succeed(
|
|
1772
|
+
const rulesPath = path13.join(process.cwd(), ".cursorrules");
|
|
1773
|
+
await fs12.writeFile(rulesPath, response.data.rules, "utf-8");
|
|
1774
|
+
spinner.succeed(chalk12.green("\u2714 Generated .cursorrules (legacy mode)"));
|
|
1333
1775
|
return;
|
|
1334
1776
|
}
|
|
1335
1777
|
for (const file of files) {
|
|
1336
|
-
const targetPath =
|
|
1337
|
-
const targetDir =
|
|
1338
|
-
await
|
|
1778
|
+
const targetPath = path13.join(process.cwd(), file.path);
|
|
1779
|
+
const targetDir = path13.dirname(targetPath);
|
|
1780
|
+
await fs12.mkdir(targetDir, { recursive: true });
|
|
1339
1781
|
try {
|
|
1340
|
-
await
|
|
1782
|
+
await fs12.access(targetPath);
|
|
1341
1783
|
if (!force && !file.path.startsWith(".cursor/rules/")) {
|
|
1342
|
-
console.log(
|
|
1784
|
+
console.log(chalk12.dim(` ${file.path} already exists. Skipping.`));
|
|
1343
1785
|
continue;
|
|
1344
1786
|
}
|
|
1345
1787
|
} catch {
|
|
1346
1788
|
}
|
|
1347
|
-
await
|
|
1789
|
+
await fs12.writeFile(targetPath, file.content, "utf-8");
|
|
1348
1790
|
}
|
|
1349
1791
|
if (files.length > 0) {
|
|
1350
|
-
const legacyPath =
|
|
1792
|
+
const legacyPath = path13.join(process.cwd(), ".cursorrules");
|
|
1351
1793
|
try {
|
|
1352
|
-
const stats = await
|
|
1794
|
+
const stats = await fs12.stat(legacyPath);
|
|
1353
1795
|
if (stats.isFile()) {
|
|
1354
|
-
await
|
|
1355
|
-
console.log(
|
|
1796
|
+
await fs12.rename(legacyPath, `${legacyPath}.bak`);
|
|
1797
|
+
console.log(chalk12.dim(" Moved legacy .cursorrules to .cursorrules.bak"));
|
|
1356
1798
|
}
|
|
1357
1799
|
} catch (e) {
|
|
1358
1800
|
}
|
|
1359
1801
|
}
|
|
1360
|
-
spinner.succeed(
|
|
1802
|
+
spinner.succeed(chalk12.green(`\u2714 Generated ${files.length} rule files (v${response.data.version || "3.0"})`));
|
|
1361
1803
|
} else {
|
|
1362
|
-
spinner.info(
|
|
1804
|
+
spinner.info(chalk12.dim(" Rules generation skipped (API response invalid)"));
|
|
1363
1805
|
}
|
|
1364
1806
|
} catch (e) {
|
|
1365
|
-
spinner.info(
|
|
1807
|
+
spinner.info(chalk12.dim(` Rules generation failed: ${e.message}`));
|
|
1366
1808
|
}
|
|
1367
1809
|
}
|
|
1368
1810
|
|
|
1369
1811
|
// src/commands/check.ts
|
|
1370
1812
|
init_esm_shims();
|
|
1371
1813
|
init_config();
|
|
1372
|
-
import { Command as
|
|
1373
|
-
import
|
|
1374
|
-
import
|
|
1375
|
-
import
|
|
1814
|
+
import { Command as Command10 } from "commander";
|
|
1815
|
+
import chalk14 from "chalk";
|
|
1816
|
+
import ora7 from "ora";
|
|
1817
|
+
import axios9 from "axios";
|
|
1376
1818
|
import { glob as glob3 } from "glob";
|
|
1377
|
-
import
|
|
1378
|
-
import
|
|
1819
|
+
import fs14 from "fs/promises";
|
|
1820
|
+
import path15 from "path";
|
|
1379
1821
|
import { execSync as execSync2 } from "child_process";
|
|
1380
1822
|
|
|
1381
1823
|
// src/utils/rule-engine.ts
|
|
1382
1824
|
init_esm_shims();
|
|
1383
|
-
import
|
|
1384
|
-
import
|
|
1385
|
-
import
|
|
1825
|
+
import fs13 from "fs/promises";
|
|
1826
|
+
import path14 from "path";
|
|
1827
|
+
import chalk13 from "chalk";
|
|
1386
1828
|
async function checkFile(filePath, rules, rootPath) {
|
|
1387
1829
|
const violations = [];
|
|
1388
|
-
const relativePath =
|
|
1830
|
+
const relativePath = path14.relative(rootPath, filePath);
|
|
1389
1831
|
try {
|
|
1390
|
-
const content = await
|
|
1832
|
+
const content = await fs13.readFile(filePath, "utf-8");
|
|
1391
1833
|
const lines = content.split("\n");
|
|
1392
1834
|
for (const rule of rules) {
|
|
1393
1835
|
const ruleViolations = await evaluateRule(rule, content, lines, relativePath);
|
|
@@ -1478,7 +1920,7 @@ async function evaluateRule(rule, content, lines, filePath) {
|
|
|
1478
1920
|
case "NAMING_CONVENTION": {
|
|
1479
1921
|
const value = rule.value;
|
|
1480
1922
|
const pattern = new RegExp(value.pattern);
|
|
1481
|
-
const fileName =
|
|
1923
|
+
const fileName = path14.basename(filePath);
|
|
1482
1924
|
if (filePath.includes(value.context) && !pattern.test(fileName)) {
|
|
1483
1925
|
violations.push({
|
|
1484
1926
|
file: filePath,
|
|
@@ -1537,12 +1979,12 @@ function checkFunctionLines(content, lines, filePath, rule, limit) {
|
|
|
1537
1979
|
}
|
|
1538
1980
|
function formatViolations(violations) {
|
|
1539
1981
|
for (const v of violations) {
|
|
1540
|
-
const severityColor = v.severity === "critical" ?
|
|
1541
|
-
const lineInfo = v.line ?
|
|
1982
|
+
const severityColor = v.severity === "critical" ? chalk13.red : v.severity === "warning" ? chalk13.yellow : chalk13.blue;
|
|
1983
|
+
const lineInfo = v.line ? chalk13.dim(`:${v.line}`) : "";
|
|
1542
1984
|
console.log(` ${severityColor(`[${v.severity.toUpperCase()}]`)} ${v.file}${lineInfo}`);
|
|
1543
1985
|
console.log(` ${v.message}`);
|
|
1544
1986
|
if (v.details) {
|
|
1545
|
-
console.log(` ${
|
|
1987
|
+
console.log(` ${chalk13.dim(v.details)}`);
|
|
1546
1988
|
}
|
|
1547
1989
|
}
|
|
1548
1990
|
}
|
|
@@ -1571,8 +2013,8 @@ var CACHE_FILE2 = ".rigstate/rules-cache.json";
|
|
|
1571
2013
|
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1572
2014
|
var CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
1573
2015
|
function createCheckCommand() {
|
|
1574
|
-
return new
|
|
1575
|
-
const spinner =
|
|
2016
|
+
return new Command10("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) => {
|
|
2017
|
+
const spinner = ora7();
|
|
1576
2018
|
try {
|
|
1577
2019
|
let projectId = options.project;
|
|
1578
2020
|
let apiUrl = getApiUrl();
|
|
@@ -1587,15 +2029,15 @@ function createCheckCommand() {
|
|
|
1587
2029
|
projectId = getProjectId();
|
|
1588
2030
|
}
|
|
1589
2031
|
if (!projectId) {
|
|
1590
|
-
console.log(
|
|
1591
|
-
console.log(
|
|
2032
|
+
console.log(chalk14.red("\u274C No project context found."));
|
|
2033
|
+
console.log(chalk14.dim(' Run "rigstate link" or pass --project <id>'));
|
|
1592
2034
|
process.exit(2);
|
|
1593
2035
|
}
|
|
1594
2036
|
let apiKey;
|
|
1595
2037
|
try {
|
|
1596
2038
|
apiKey = getApiKey();
|
|
1597
2039
|
} catch {
|
|
1598
|
-
console.log(
|
|
2040
|
+
console.log(chalk14.red('\u274C Not authenticated. Run "rigstate login" first.'));
|
|
1599
2041
|
process.exit(2);
|
|
1600
2042
|
}
|
|
1601
2043
|
spinner.start("Fetching Guardian rules...");
|
|
@@ -1608,7 +2050,7 @@ function createCheckCommand() {
|
|
|
1608
2050
|
settings = cached.settings;
|
|
1609
2051
|
spinner.text = "Using cached rules...";
|
|
1610
2052
|
} else {
|
|
1611
|
-
const response = await
|
|
2053
|
+
const response = await axios9.get(`${apiUrl}/api/v1/guardian/rules`, {
|
|
1612
2054
|
params: { project_id: projectId },
|
|
1613
2055
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
1614
2056
|
timeout: 1e4
|
|
@@ -1623,17 +2065,17 @@ function createCheckCommand() {
|
|
|
1623
2065
|
} catch (apiError) {
|
|
1624
2066
|
const cached = await loadCachedRules(projectId);
|
|
1625
2067
|
if (cached && !isStale(cached.timestamp, CACHE_MAX_AGE_MS)) {
|
|
1626
|
-
spinner.warn(
|
|
2068
|
+
spinner.warn(chalk14.yellow("Using cached rules (API unavailable)"));
|
|
1627
2069
|
rules = cached.rules;
|
|
1628
2070
|
settings = cached.settings;
|
|
1629
2071
|
} else {
|
|
1630
|
-
spinner.fail(
|
|
1631
|
-
console.log(
|
|
2072
|
+
spinner.fail(chalk14.red("Failed to fetch rules and no valid cache"));
|
|
2073
|
+
console.log(chalk14.dim(` Error: ${apiError.message}`));
|
|
1632
2074
|
process.exit(2);
|
|
1633
2075
|
}
|
|
1634
2076
|
}
|
|
1635
2077
|
spinner.succeed(`Loaded ${rules.length} Guardian rules`);
|
|
1636
|
-
const scanPath =
|
|
2078
|
+
const scanPath = path15.resolve(process.cwd(), targetPath);
|
|
1637
2079
|
let filesToCheck;
|
|
1638
2080
|
if (options.staged) {
|
|
1639
2081
|
spinner.start("Getting staged files...");
|
|
@@ -1642,14 +2084,14 @@ function createCheckCommand() {
|
|
|
1642
2084
|
encoding: "utf-8",
|
|
1643
2085
|
cwd: process.cwd()
|
|
1644
2086
|
});
|
|
1645
|
-
filesToCheck = stagedOutput.split("\n").filter((f) => f.trim()).filter((f) => isCodeFile2(f)).map((f) =>
|
|
2087
|
+
filesToCheck = stagedOutput.split("\n").filter((f) => f.trim()).filter((f) => isCodeFile2(f)).map((f) => path15.resolve(process.cwd(), f));
|
|
1646
2088
|
} catch {
|
|
1647
2089
|
spinner.fail("Not a git repository or no staged files");
|
|
1648
2090
|
process.exit(2);
|
|
1649
2091
|
}
|
|
1650
2092
|
} else {
|
|
1651
|
-
spinner.start(`Scanning ${
|
|
1652
|
-
const pattern =
|
|
2093
|
+
spinner.start(`Scanning ${chalk14.cyan(targetPath)}...`);
|
|
2094
|
+
const pattern = path15.join(scanPath, "**/*");
|
|
1653
2095
|
const allFiles = await glob3(pattern, {
|
|
1654
2096
|
nodir: true,
|
|
1655
2097
|
dot: false,
|
|
@@ -1665,7 +2107,7 @@ function createCheckCommand() {
|
|
|
1665
2107
|
filesToCheck = allFiles.filter((f) => isCodeFile2(f));
|
|
1666
2108
|
}
|
|
1667
2109
|
if (filesToCheck.length === 0) {
|
|
1668
|
-
spinner.warn(
|
|
2110
|
+
spinner.warn(chalk14.yellow("No code files found to check."));
|
|
1669
2111
|
outputResults([], !!options.json);
|
|
1670
2112
|
process.exit(0);
|
|
1671
2113
|
}
|
|
@@ -1674,7 +2116,7 @@ function createCheckCommand() {
|
|
|
1674
2116
|
const results = [];
|
|
1675
2117
|
for (let i = 0; i < filesToCheck.length; i++) {
|
|
1676
2118
|
const file = filesToCheck[i];
|
|
1677
|
-
spinner.text = `Checking ${i + 1}/${filesToCheck.length}: ${
|
|
2119
|
+
spinner.text = `Checking ${i + 1}/${filesToCheck.length}: ${path15.basename(file)}`;
|
|
1678
2120
|
const result = await checkFile(file, rules, process.cwd());
|
|
1679
2121
|
results.push(result);
|
|
1680
2122
|
}
|
|
@@ -1684,47 +2126,47 @@ function createCheckCommand() {
|
|
|
1684
2126
|
outputResults(results, true);
|
|
1685
2127
|
} else {
|
|
1686
2128
|
outputResults(results, false);
|
|
1687
|
-
console.log("\n" +
|
|
1688
|
-
console.log(
|
|
1689
|
-
console.log(`Files checked: ${
|
|
1690
|
-
console.log(`Total violations: ${summary.totalViolations > 0 ?
|
|
2129
|
+
console.log("\n" + chalk14.bold("\u{1F4CA} Summary"));
|
|
2130
|
+
console.log(chalk14.dim("\u2500".repeat(50)));
|
|
2131
|
+
console.log(`Files checked: ${chalk14.cyan(summary.totalFiles)}`);
|
|
2132
|
+
console.log(`Total violations: ${summary.totalViolations > 0 ? chalk14.red(summary.totalViolations) : chalk14.green(0)}`);
|
|
1691
2133
|
if (summary.totalViolations > 0) {
|
|
1692
|
-
console.log(` ${
|
|
1693
|
-
console.log(` ${
|
|
1694
|
-
console.log(` ${
|
|
2134
|
+
console.log(` ${chalk14.red("Critical:")} ${summary.criticalCount}`);
|
|
2135
|
+
console.log(` ${chalk14.yellow("Warning:")} ${summary.warningCount}`);
|
|
2136
|
+
console.log(` ${chalk14.blue("Info:")} ${summary.infoCount}`);
|
|
1695
2137
|
}
|
|
1696
|
-
console.log(
|
|
2138
|
+
console.log(chalk14.dim("\u2500".repeat(50)));
|
|
1697
2139
|
}
|
|
1698
2140
|
if (options.strict !== void 0) {
|
|
1699
2141
|
const strictLevel = typeof options.strict === "string" ? options.strict : "all";
|
|
1700
2142
|
if (strictLevel === "critical" && summary.criticalCount > 0) {
|
|
1701
|
-
console.log(
|
|
2143
|
+
console.log(chalk14.red("\n\u274C Check failed: Critical violations found"));
|
|
1702
2144
|
process.exit(1);
|
|
1703
2145
|
} else if (strictLevel === "all" && summary.totalViolations > 0) {
|
|
1704
|
-
console.log(
|
|
2146
|
+
console.log(chalk14.red("\n\u274C Check failed: Violations found"));
|
|
1705
2147
|
process.exit(1);
|
|
1706
2148
|
}
|
|
1707
2149
|
}
|
|
1708
2150
|
if (summary.totalViolations === 0) {
|
|
1709
|
-
console.log(
|
|
2151
|
+
console.log(chalk14.green("\n\u2705 All checks passed!"));
|
|
1710
2152
|
}
|
|
1711
2153
|
process.exit(0);
|
|
1712
2154
|
} catch (error) {
|
|
1713
|
-
spinner.fail(
|
|
1714
|
-
console.error(
|
|
2155
|
+
spinner.fail(chalk14.red("Check failed"));
|
|
2156
|
+
console.error(chalk14.red("Error:"), error.message);
|
|
1715
2157
|
process.exit(2);
|
|
1716
2158
|
}
|
|
1717
2159
|
});
|
|
1718
2160
|
}
|
|
1719
2161
|
function isCodeFile2(filePath) {
|
|
1720
2162
|
const codeExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1721
|
-
const ext =
|
|
2163
|
+
const ext = path15.extname(filePath).toLowerCase();
|
|
1722
2164
|
return codeExtensions.includes(ext);
|
|
1723
2165
|
}
|
|
1724
2166
|
async function loadCachedRules(projectId) {
|
|
1725
2167
|
try {
|
|
1726
|
-
const cachePath =
|
|
1727
|
-
const content = await
|
|
2168
|
+
const cachePath = path15.join(process.cwd(), CACHE_FILE2);
|
|
2169
|
+
const content = await fs14.readFile(cachePath, "utf-8");
|
|
1728
2170
|
const cached = JSON.parse(content);
|
|
1729
2171
|
if (cached.projectId !== projectId) {
|
|
1730
2172
|
return null;
|
|
@@ -1736,16 +2178,16 @@ async function loadCachedRules(projectId) {
|
|
|
1736
2178
|
}
|
|
1737
2179
|
async function saveCachedRules(projectId, rules, settings) {
|
|
1738
2180
|
try {
|
|
1739
|
-
const cacheDir =
|
|
1740
|
-
await
|
|
2181
|
+
const cacheDir = path15.join(process.cwd(), ".rigstate");
|
|
2182
|
+
await fs14.mkdir(cacheDir, { recursive: true });
|
|
1741
2183
|
const cached = {
|
|
1742
2184
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1743
2185
|
projectId,
|
|
1744
2186
|
rules,
|
|
1745
2187
|
settings
|
|
1746
2188
|
};
|
|
1747
|
-
await
|
|
1748
|
-
|
|
2189
|
+
await fs14.writeFile(
|
|
2190
|
+
path15.join(cacheDir, "rules-cache.json"),
|
|
1749
2191
|
JSON.stringify(cached, null, 2)
|
|
1750
2192
|
);
|
|
1751
2193
|
} catch {
|
|
@@ -1754,216 +2196,115 @@ async function saveCachedRules(projectId, rules, settings) {
|
|
|
1754
2196
|
function isStale(timestamp, maxAge) {
|
|
1755
2197
|
const age = Date.now() - new Date(timestamp).getTime();
|
|
1756
2198
|
return age > maxAge;
|
|
1757
|
-
}
|
|
1758
|
-
function outputResults(results, json) {
|
|
1759
|
-
if (json) {
|
|
1760
|
-
console.log(JSON.stringify({
|
|
1761
|
-
results,
|
|
1762
|
-
summary: summarizeResults(results)
|
|
1763
|
-
}, null, 2));
|
|
1764
|
-
return;
|
|
1765
|
-
}
|
|
1766
|
-
const hasViolations = results.some((r) => r.violations.length > 0);
|
|
1767
|
-
if (!hasViolations) {
|
|
1768
|
-
return;
|
|
1769
|
-
}
|
|
1770
|
-
console.log("\n" +
|
|
1771
|
-
console.log(
|
|
1772
|
-
for (const result of results) {
|
|
1773
|
-
if (result.violations.length > 0) {
|
|
1774
|
-
formatViolations(result.violations);
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
// src/commands/hooks.ts
|
|
1780
|
-
init_esm_shims();
|
|
1781
|
-
import { Command as Command8 } from "commander";
|
|
1782
|
-
import chalk12 from "chalk";
|
|
1783
|
-
import fs13 from "fs/promises";
|
|
1784
|
-
import path14 from "path";
|
|
1785
|
-
var PRE_COMMIT_SCRIPT = `#!/bin/sh
|
|
1786
|
-
# Rigstate Guardian Pre-commit Hook
|
|
1787
|
-
# Installed by: rigstate hooks install
|
|
1788
|
-
|
|
1789
|
-
# 1. Silent Sentinel Check (Phase 5)
|
|
1790
|
-
if [ -f .rigstate/guardian.lock ]; then
|
|
1791
|
-
echo "\u{1F6D1} INTERVENTION ACTIVE: Commit blocked by Silent Sentinel."
|
|
1792
|
-
echo " A critical violation ('HARD_LOCK') was detected by the Guardian Daemon."
|
|
1793
|
-
echo " Please fix the violation to unlock the repo."
|
|
1794
|
-
echo ""
|
|
1795
|
-
if grep -q "HARD_LOCK_ACTIVE" .rigstate/guardian.lock; then
|
|
1796
|
-
cat .rigstate/guardian.lock
|
|
1797
|
-
fi
|
|
1798
|
-
exit 1
|
|
1799
|
-
fi
|
|
1800
|
-
|
|
1801
|
-
echo "\u{1F6E1}\uFE0F Running Guardian checks..."
|
|
1802
|
-
|
|
1803
|
-
# Run check with strict mode for critical violations
|
|
1804
|
-
rigstate check --staged --strict=critical
|
|
1805
|
-
|
|
1806
|
-
# Exit with the same code as rigstate check
|
|
1807
|
-
exit $?
|
|
1808
|
-
`;
|
|
1809
|
-
function createHooksCommand() {
|
|
1810
|
-
const hooks = new Command8("hooks").description("Manage git hooks for Guardian integration");
|
|
1811
|
-
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) => {
|
|
1812
|
-
try {
|
|
1813
|
-
const gitDir = path14.join(process.cwd(), ".git");
|
|
1814
|
-
try {
|
|
1815
|
-
await fs13.access(gitDir);
|
|
1816
|
-
} catch {
|
|
1817
|
-
console.log(chalk12.red("\u274C Not a git repository."));
|
|
1818
|
-
console.log(chalk12.dim(' Initialize with "git init" first.'));
|
|
1819
|
-
process.exit(1);
|
|
1820
|
-
}
|
|
1821
|
-
const hooksDir = path14.join(gitDir, "hooks");
|
|
1822
|
-
await fs13.mkdir(hooksDir, { recursive: true });
|
|
1823
|
-
const preCommitPath = path14.join(hooksDir, "pre-commit");
|
|
1824
|
-
let existingContent = "";
|
|
1825
|
-
try {
|
|
1826
|
-
existingContent = await fs13.readFile(preCommitPath, "utf-8");
|
|
1827
|
-
if (existingContent.includes("rigstate")) {
|
|
1828
|
-
console.log(chalk12.yellow("\u26A0 Rigstate pre-commit hook already installed."));
|
|
1829
|
-
console.log(chalk12.dim(' Use "rigstate hooks uninstall" to remove first.'));
|
|
1830
|
-
return;
|
|
1831
|
-
}
|
|
1832
|
-
} catch {
|
|
1833
|
-
}
|
|
1834
|
-
let script = PRE_COMMIT_SCRIPT;
|
|
1835
|
-
if (options.strict === "all") {
|
|
1836
|
-
script = script.replace("--strict=critical", "--strict");
|
|
1837
|
-
}
|
|
1838
|
-
if (existingContent && !existingContent.includes("rigstate")) {
|
|
1839
|
-
const combinedScript = existingContent + "\n\n" + script.replace("#!/bin/sh\n", "");
|
|
1840
|
-
await fs13.writeFile(preCommitPath, combinedScript, { mode: 493 });
|
|
1841
|
-
console.log(chalk12.green("\u2705 Rigstate hook appended to existing pre-commit."));
|
|
1842
|
-
} else {
|
|
1843
|
-
await fs13.writeFile(preCommitPath, script, { mode: 493 });
|
|
1844
|
-
console.log(chalk12.green("\u2705 Pre-commit hook installed!"));
|
|
1845
|
-
}
|
|
1846
|
-
console.log(chalk12.dim(` Path: ${preCommitPath}`));
|
|
1847
|
-
console.log(chalk12.dim(` Strict level: ${options.strict}`));
|
|
1848
|
-
console.log("");
|
|
1849
|
-
console.log(chalk12.cyan("Guardian will now check your code before each commit."));
|
|
1850
|
-
console.log(chalk12.dim('Use "rigstate hooks uninstall" to remove the hook.'));
|
|
1851
|
-
} catch (error) {
|
|
1852
|
-
console.error(chalk12.red("Failed to install hook:"), error.message);
|
|
1853
|
-
process.exit(1);
|
|
1854
|
-
}
|
|
1855
|
-
});
|
|
1856
|
-
hooks.command("uninstall").description("Remove Rigstate pre-commit hook").action(async () => {
|
|
1857
|
-
try {
|
|
1858
|
-
const preCommitPath = path14.join(process.cwd(), ".git", "hooks", "pre-commit");
|
|
1859
|
-
try {
|
|
1860
|
-
const content = await fs13.readFile(preCommitPath, "utf-8");
|
|
1861
|
-
if (!content.includes("rigstate")) {
|
|
1862
|
-
console.log(chalk12.yellow("\u26A0 No Rigstate hook found in pre-commit."));
|
|
1863
|
-
return;
|
|
1864
|
-
}
|
|
1865
|
-
if (content.includes("# Rigstate Guardian Pre-commit Hook") && content.trim().split("\n").filter((l) => l && !l.startsWith("#")).length <= 4) {
|
|
1866
|
-
await fs13.unlink(preCommitPath);
|
|
1867
|
-
console.log(chalk12.green("\u2705 Pre-commit hook removed."));
|
|
1868
|
-
} else {
|
|
1869
|
-
const lines = content.split("\n");
|
|
1870
|
-
const filteredLines = [];
|
|
1871
|
-
let inRigstateSection = false;
|
|
1872
|
-
for (const line of lines) {
|
|
1873
|
-
if (line.includes("Rigstate Guardian Pre-commit Hook")) {
|
|
1874
|
-
inRigstateSection = true;
|
|
1875
|
-
continue;
|
|
1876
|
-
}
|
|
1877
|
-
if (inRigstateSection && line.includes("exit $?")) {
|
|
1878
|
-
inRigstateSection = false;
|
|
1879
|
-
continue;
|
|
1880
|
-
}
|
|
1881
|
-
if (!inRigstateSection && !line.includes("rigstate check")) {
|
|
1882
|
-
filteredLines.push(line);
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
1885
|
-
await fs13.writeFile(preCommitPath, filteredLines.join("\n"), { mode: 493 });
|
|
1886
|
-
console.log(chalk12.green("\u2705 Rigstate section removed from pre-commit hook."));
|
|
1887
|
-
}
|
|
1888
|
-
} catch {
|
|
1889
|
-
console.log(chalk12.yellow("\u26A0 No pre-commit hook found."));
|
|
1890
|
-
}
|
|
1891
|
-
} catch (error) {
|
|
1892
|
-
console.error(chalk12.red("Failed to uninstall hook:"), error.message);
|
|
1893
|
-
process.exit(1);
|
|
2199
|
+
}
|
|
2200
|
+
function outputResults(results, json) {
|
|
2201
|
+
if (json) {
|
|
2202
|
+
console.log(JSON.stringify({
|
|
2203
|
+
results,
|
|
2204
|
+
summary: summarizeResults(results)
|
|
2205
|
+
}, null, 2));
|
|
2206
|
+
return;
|
|
2207
|
+
}
|
|
2208
|
+
const hasViolations = results.some((r) => r.violations.length > 0);
|
|
2209
|
+
if (!hasViolations) {
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
console.log("\n" + chalk14.bold("\u{1F50D} Violations Found"));
|
|
2213
|
+
console.log(chalk14.dim("\u2500".repeat(50)));
|
|
2214
|
+
for (const result of results) {
|
|
2215
|
+
if (result.violations.length > 0) {
|
|
2216
|
+
formatViolations(result.violations);
|
|
1894
2217
|
}
|
|
1895
|
-
}
|
|
1896
|
-
return hooks;
|
|
2218
|
+
}
|
|
1897
2219
|
}
|
|
1898
2220
|
|
|
2221
|
+
// src/index.ts
|
|
2222
|
+
init_hooks();
|
|
2223
|
+
|
|
1899
2224
|
// src/commands/daemon.ts
|
|
1900
2225
|
init_esm_shims();
|
|
1901
|
-
import { Command as
|
|
1902
|
-
import
|
|
1903
|
-
import
|
|
1904
|
-
import
|
|
1905
|
-
import
|
|
2226
|
+
import { Command as Command11 } from "commander";
|
|
2227
|
+
import chalk17 from "chalk";
|
|
2228
|
+
import ora8 from "ora";
|
|
2229
|
+
import fs18 from "fs/promises";
|
|
2230
|
+
import path20 from "path";
|
|
1906
2231
|
|
|
1907
2232
|
// src/daemon/factory.ts
|
|
1908
2233
|
init_esm_shims();
|
|
1909
2234
|
|
|
1910
2235
|
// src/daemon/core.ts
|
|
1911
2236
|
init_esm_shims();
|
|
1912
|
-
import
|
|
1913
|
-
import * as
|
|
2237
|
+
import chalk16 from "chalk";
|
|
2238
|
+
import * as fs17 from "fs/promises";
|
|
1914
2239
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
1915
2240
|
|
|
1916
2241
|
// src/daemon/file-watcher.ts
|
|
1917
2242
|
init_esm_shims();
|
|
1918
2243
|
import * as chokidar from "chokidar";
|
|
1919
|
-
import
|
|
2244
|
+
import path16 from "path";
|
|
1920
2245
|
import { EventEmitter } from "events";
|
|
1921
2246
|
var CODE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1922
2247
|
function isCodeFile3(filePath) {
|
|
1923
|
-
const ext =
|
|
2248
|
+
const ext = path16.extname(filePath).toLowerCase();
|
|
1924
2249
|
return CODE_EXTENSIONS.includes(ext);
|
|
1925
2250
|
}
|
|
1926
2251
|
function createFileWatcher(watchPath) {
|
|
1927
2252
|
const emitter = new EventEmitter();
|
|
1928
2253
|
let watcher = null;
|
|
1929
2254
|
emitter.start = () => {
|
|
1930
|
-
const absolutePath =
|
|
2255
|
+
const absolutePath = path16.resolve(process.cwd(), watchPath);
|
|
1931
2256
|
watcher = chokidar.watch(absolutePath, {
|
|
1932
|
-
ignored: (
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
2257
|
+
ignored: (absolutePath2) => {
|
|
2258
|
+
const relPath = path16.relative(process.cwd(), absolutePath2);
|
|
2259
|
+
const ignoredDirs = /* @__PURE__ */ new Set([
|
|
2260
|
+
"node_modules",
|
|
2261
|
+
".git",
|
|
2262
|
+
".next",
|
|
2263
|
+
".turbo",
|
|
2264
|
+
"dist",
|
|
2265
|
+
"build",
|
|
2266
|
+
".rigstate",
|
|
2267
|
+
"coverage",
|
|
2268
|
+
".DS_Store",
|
|
2269
|
+
"tmp",
|
|
2270
|
+
"temp",
|
|
2271
|
+
"vendor",
|
|
2272
|
+
".cache",
|
|
2273
|
+
"public"
|
|
2274
|
+
// Usually static assets, not code
|
|
2275
|
+
]);
|
|
2276
|
+
const segments = relPath.split(path16.sep);
|
|
2277
|
+
if (segments.some((s) => ignoredDirs.has(s))) {
|
|
2278
|
+
return true;
|
|
2279
|
+
}
|
|
1940
2280
|
return false;
|
|
1941
2281
|
},
|
|
1942
2282
|
persistent: true,
|
|
1943
2283
|
ignoreInitial: true,
|
|
1944
|
-
|
|
2284
|
+
ignorePermissionErrors: true,
|
|
2285
|
+
// Don't crash on EPERM
|
|
2286
|
+
depth: 20,
|
|
1945
2287
|
awaitWriteFinish: {
|
|
1946
|
-
stabilityThreshold:
|
|
2288
|
+
stabilityThreshold: 300,
|
|
1947
2289
|
pollInterval: 100
|
|
1948
2290
|
},
|
|
1949
2291
|
usePolling: false,
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
binaryInterval: 1e3
|
|
2292
|
+
atomic: true
|
|
2293
|
+
// Handle atomic writes (like vim/saving) better
|
|
1953
2294
|
});
|
|
1954
2295
|
watcher.on("change", (filePath) => {
|
|
1955
2296
|
if (isCodeFile3(filePath)) {
|
|
1956
|
-
emitter.emit("change",
|
|
2297
|
+
emitter.emit("change", path16.relative(process.cwd(), filePath));
|
|
1957
2298
|
}
|
|
1958
2299
|
});
|
|
1959
2300
|
watcher.on("add", (filePath) => {
|
|
1960
2301
|
if (isCodeFile3(filePath)) {
|
|
1961
|
-
emitter.emit("add",
|
|
2302
|
+
emitter.emit("add", path16.relative(process.cwd(), filePath));
|
|
1962
2303
|
}
|
|
1963
2304
|
});
|
|
1964
2305
|
watcher.on("unlink", (filePath) => {
|
|
1965
2306
|
if (isCodeFile3(filePath)) {
|
|
1966
|
-
emitter.emit("unlink",
|
|
2307
|
+
emitter.emit("unlink", path16.relative(process.cwd(), filePath));
|
|
1967
2308
|
}
|
|
1968
2309
|
});
|
|
1969
2310
|
watcher.on("error", (error) => {
|
|
@@ -1986,8 +2327,8 @@ function createFileWatcher(watchPath) {
|
|
|
1986
2327
|
init_esm_shims();
|
|
1987
2328
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
1988
2329
|
import { dirname } from "path";
|
|
1989
|
-
import
|
|
1990
|
-
import
|
|
2330
|
+
import path17 from "path";
|
|
2331
|
+
import axios10 from "axios";
|
|
1991
2332
|
var GLOBAL_HEURISTICS = [
|
|
1992
2333
|
{
|
|
1993
2334
|
skillId: "payment-expert",
|
|
@@ -2018,7 +2359,7 @@ var HeuristicEngine = class {
|
|
|
2018
2359
|
rules = [];
|
|
2019
2360
|
cachePath;
|
|
2020
2361
|
constructor() {
|
|
2021
|
-
this.cachePath =
|
|
2362
|
+
this.cachePath = path17.join(process.cwd(), ".rigstate", "cache", "heuristics.json");
|
|
2022
2363
|
this.loadRules();
|
|
2023
2364
|
}
|
|
2024
2365
|
async loadRules() {
|
|
@@ -2037,7 +2378,7 @@ var HeuristicEngine = class {
|
|
|
2037
2378
|
try {
|
|
2038
2379
|
await mkdir(dirname(this.cachePath), { recursive: true });
|
|
2039
2380
|
const endpoint = `${apiUrl}/api/v1/skills/triggers`;
|
|
2040
|
-
const response = await
|
|
2381
|
+
const response = await axios10.get(endpoint, {
|
|
2041
2382
|
headers: {
|
|
2042
2383
|
"x-api-key": apiKey,
|
|
2043
2384
|
"Content-Type": "application/json"
|
|
@@ -2133,9 +2474,9 @@ function createHeuristicEngine() {
|
|
|
2133
2474
|
|
|
2134
2475
|
// src/daemon/intervention-protocol.ts
|
|
2135
2476
|
init_esm_shims();
|
|
2136
|
-
import
|
|
2137
|
-
import * as
|
|
2138
|
-
import * as
|
|
2477
|
+
import chalk15 from "chalk";
|
|
2478
|
+
import * as fs15 from "fs";
|
|
2479
|
+
import * as path18 from "path";
|
|
2139
2480
|
var InterventionProtocol = class {
|
|
2140
2481
|
activeViolators = /* @__PURE__ */ new Set();
|
|
2141
2482
|
/**
|
|
@@ -2158,18 +2499,18 @@ var InterventionProtocol = class {
|
|
|
2158
2499
|
}
|
|
2159
2500
|
syncLockFile() {
|
|
2160
2501
|
try {
|
|
2161
|
-
const lockDir =
|
|
2162
|
-
if (!
|
|
2163
|
-
const lockPath =
|
|
2502
|
+
const lockDir = path18.join(process.cwd(), ".rigstate");
|
|
2503
|
+
if (!fs15.existsSync(lockDir)) fs15.mkdirSync(lockDir, { recursive: true });
|
|
2504
|
+
const lockPath = path18.join(lockDir, "guardian.lock");
|
|
2164
2505
|
if (this.activeViolators.size > 0) {
|
|
2165
2506
|
const content = `HARD_LOCK_ACTIVE
|
|
2166
2507
|
Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
2167
2508
|
|
|
2168
2509
|
Blocking Files:
|
|
2169
2510
|
${Array.from(this.activeViolators).join("\n")}`;
|
|
2170
|
-
|
|
2511
|
+
fs15.writeFileSync(lockPath, content, "utf-8");
|
|
2171
2512
|
} else {
|
|
2172
|
-
if (
|
|
2513
|
+
if (fs15.existsSync(lockPath)) fs15.unlinkSync(lockPath);
|
|
2173
2514
|
}
|
|
2174
2515
|
} catch (e) {
|
|
2175
2516
|
console.error("Failed to sync guardian lock file:", e);
|
|
@@ -2223,11 +2564,11 @@ ${Array.from(this.activeViolators).join("\n")}`;
|
|
|
2223
2564
|
enforce(decision) {
|
|
2224
2565
|
if (decision.mode === "OPEN") return;
|
|
2225
2566
|
const icon = decision.mode === "HARD_LOCK" ? "\u{1F6AB}" : "\u26A0\uFE0F";
|
|
2226
|
-
const color = decision.mode === "HARD_LOCK" ?
|
|
2567
|
+
const color = decision.mode === "HARD_LOCK" ? chalk15.bgRed.white.bold : chalk15.yellow.bold;
|
|
2227
2568
|
console.log("\n" + color(` ${icon} [${decision.mode}] INTERVENTION `));
|
|
2228
|
-
console.log(
|
|
2569
|
+
console.log(chalk15.redBright(` ${decision.message}`));
|
|
2229
2570
|
if (decision.blockCommit) {
|
|
2230
|
-
console.log(
|
|
2571
|
+
console.log(chalk15.dim(" \u{1F512} Commit functionality is logically suspended until fixed."));
|
|
2231
2572
|
}
|
|
2232
2573
|
}
|
|
2233
2574
|
};
|
|
@@ -2237,9 +2578,9 @@ function createInterventionProtocol() {
|
|
|
2237
2578
|
|
|
2238
2579
|
// src/daemon/guardian-monitor.ts
|
|
2239
2580
|
init_esm_shims();
|
|
2240
|
-
import
|
|
2241
|
-
import
|
|
2242
|
-
import
|
|
2581
|
+
import axios11 from "axios";
|
|
2582
|
+
import fs16 from "fs/promises";
|
|
2583
|
+
import path19 from "path";
|
|
2243
2584
|
var CACHE_FILE3 = ".rigstate/rules-cache.json";
|
|
2244
2585
|
var CACHE_TTL_MS2 = 5 * 60 * 1e3;
|
|
2245
2586
|
function createGuardianMonitor(projectId, apiUrl, apiKey) {
|
|
@@ -2250,7 +2591,7 @@ function createGuardianMonitor(projectId, apiUrl, apiKey) {
|
|
|
2250
2591
|
return;
|
|
2251
2592
|
}
|
|
2252
2593
|
try {
|
|
2253
|
-
const response = await
|
|
2594
|
+
const response = await axios11.get(`${apiUrl}/api/v1/guardian/rules`, {
|
|
2254
2595
|
params: { project_id: projectId },
|
|
2255
2596
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
2256
2597
|
timeout: 1e4
|
|
@@ -2280,7 +2621,7 @@ function createGuardianMonitor(projectId, apiUrl, apiKey) {
|
|
|
2280
2621
|
passed: true
|
|
2281
2622
|
};
|
|
2282
2623
|
}
|
|
2283
|
-
const absolutePath =
|
|
2624
|
+
const absolutePath = path19.resolve(process.cwd(), filePath);
|
|
2284
2625
|
return checkFile(absolutePath, rules, process.cwd());
|
|
2285
2626
|
};
|
|
2286
2627
|
const getRuleCount = () => rules.length;
|
|
@@ -2294,8 +2635,8 @@ function createGuardianMonitor(projectId, apiUrl, apiKey) {
|
|
|
2294
2635
|
}
|
|
2295
2636
|
async function loadCachedRules2(projectId) {
|
|
2296
2637
|
try {
|
|
2297
|
-
const cachePath =
|
|
2298
|
-
const content = await
|
|
2638
|
+
const cachePath = path19.join(process.cwd(), CACHE_FILE3);
|
|
2639
|
+
const content = await fs16.readFile(cachePath, "utf-8");
|
|
2299
2640
|
const cached = JSON.parse(content);
|
|
2300
2641
|
if (cached.projectId !== projectId) {
|
|
2301
2642
|
return null;
|
|
@@ -2307,16 +2648,16 @@ async function loadCachedRules2(projectId) {
|
|
|
2307
2648
|
}
|
|
2308
2649
|
async function saveCachedRules2(projectId, rules) {
|
|
2309
2650
|
try {
|
|
2310
|
-
const cacheDir =
|
|
2311
|
-
await
|
|
2651
|
+
const cacheDir = path19.join(process.cwd(), ".rigstate");
|
|
2652
|
+
await fs16.mkdir(cacheDir, { recursive: true });
|
|
2312
2653
|
const cached = {
|
|
2313
2654
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2314
2655
|
projectId,
|
|
2315
2656
|
rules,
|
|
2316
2657
|
settings: { lmax: 400, lmax_warning: 350 }
|
|
2317
2658
|
};
|
|
2318
|
-
await
|
|
2319
|
-
|
|
2659
|
+
await fs16.writeFile(
|
|
2660
|
+
path19.join(cacheDir, "rules-cache.json"),
|
|
2320
2661
|
JSON.stringify(cached, null, 2)
|
|
2321
2662
|
);
|
|
2322
2663
|
} catch {
|
|
@@ -2325,7 +2666,7 @@ async function saveCachedRules2(projectId, rules) {
|
|
|
2325
2666
|
|
|
2326
2667
|
// src/daemon/bridge-listener.ts
|
|
2327
2668
|
init_esm_shims();
|
|
2328
|
-
import
|
|
2669
|
+
import axios12 from "axios";
|
|
2329
2670
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
2330
2671
|
var POLL_INTERVAL_MS = 5e3;
|
|
2331
2672
|
function createBridgeListener(projectId, apiUrl, apiKey) {
|
|
@@ -2335,7 +2676,7 @@ function createBridgeListener(projectId, apiUrl, apiKey) {
|
|
|
2335
2676
|
let lastCheckedId = null;
|
|
2336
2677
|
const checkBridge = async () => {
|
|
2337
2678
|
try {
|
|
2338
|
-
const response = await
|
|
2679
|
+
const response = await axios12.get(`${apiUrl}/api/v1/agent/bridge`, {
|
|
2339
2680
|
params: {
|
|
2340
2681
|
project_id: projectId,
|
|
2341
2682
|
action: "check"
|
|
@@ -2363,7 +2704,7 @@ function createBridgeListener(projectId, apiUrl, apiKey) {
|
|
|
2363
2704
|
};
|
|
2364
2705
|
const acknowledgePing = async (taskId) => {
|
|
2365
2706
|
try {
|
|
2366
|
-
await
|
|
2707
|
+
await axios12.post(`${apiUrl}/api/v1/agent/bridge`, {
|
|
2367
2708
|
project_id: projectId,
|
|
2368
2709
|
action: "update",
|
|
2369
2710
|
bridge_id: taskId,
|
|
@@ -2396,10 +2737,10 @@ function createBridgeListener(projectId, apiUrl, apiKey) {
|
|
|
2396
2737
|
|
|
2397
2738
|
// src/daemon/telemetry.ts
|
|
2398
2739
|
init_esm_shims();
|
|
2399
|
-
import
|
|
2740
|
+
import axios13 from "axios";
|
|
2400
2741
|
async function trackSkillUsage(apiUrl, apiKey, projectId, skillId) {
|
|
2401
2742
|
try {
|
|
2402
|
-
await
|
|
2743
|
+
await axios13.post(`${apiUrl}/api/v1/skills/usage`, {
|
|
2403
2744
|
projectId,
|
|
2404
2745
|
skillName: skillId,
|
|
2405
2746
|
status: "ACTIVATED"
|
|
@@ -2434,7 +2775,7 @@ var GuardianDaemon = class extends EventEmitter3 {
|
|
|
2434
2775
|
}
|
|
2435
2776
|
async start() {
|
|
2436
2777
|
if (this.state.isRunning) {
|
|
2437
|
-
console.log(
|
|
2778
|
+
console.log(chalk16.yellow("Daemon is already running."));
|
|
2438
2779
|
return;
|
|
2439
2780
|
}
|
|
2440
2781
|
this.printWelcome();
|
|
@@ -2444,7 +2785,7 @@ var GuardianDaemon = class extends EventEmitter3 {
|
|
|
2444
2785
|
this.interventionProtocol = createInterventionProtocol();
|
|
2445
2786
|
this.guardianMonitor = createGuardianMonitor(this.config.projectId, this.config.apiUrl, this.config.apiKey);
|
|
2446
2787
|
await this.guardianMonitor.loadRules();
|
|
2447
|
-
console.log(
|
|
2788
|
+
console.log(chalk16.green(` \u2713 Loaded ${this.guardianMonitor.getRuleCount()} rules`));
|
|
2448
2789
|
await this.syncHeuristics();
|
|
2449
2790
|
if (this.config.checkOnChange) {
|
|
2450
2791
|
this.setupFileWatcher();
|
|
@@ -2456,34 +2797,34 @@ var GuardianDaemon = class extends EventEmitter3 {
|
|
|
2456
2797
|
this.emit("started", this.state);
|
|
2457
2798
|
}
|
|
2458
2799
|
printWelcome() {
|
|
2459
|
-
console.log(
|
|
2460
|
-
console.log(
|
|
2461
|
-
console.log(
|
|
2462
|
-
console.log(
|
|
2800
|
+
console.log(chalk16.bold.blue("\n\u{1F6E1}\uFE0F Guardian Daemon Starting..."));
|
|
2801
|
+
console.log(chalk16.dim(`Project: ${this.config.projectId}`));
|
|
2802
|
+
console.log(chalk16.dim(`Watch Path: ${this.config.watchPath}`));
|
|
2803
|
+
console.log(chalk16.dim("\u2500".repeat(50)));
|
|
2463
2804
|
}
|
|
2464
2805
|
printActive() {
|
|
2465
|
-
console.log(
|
|
2466
|
-
console.log(
|
|
2467
|
-
console.log(
|
|
2806
|
+
console.log(chalk16.dim("\u2500".repeat(50)));
|
|
2807
|
+
console.log(chalk16.green.bold("\u2705 Guardian Daemon is now active"));
|
|
2808
|
+
console.log(chalk16.dim("Press Ctrl+C to stop\n"));
|
|
2468
2809
|
}
|
|
2469
2810
|
async syncHeuristics() {
|
|
2470
2811
|
if (!this.heuristicEngine) return;
|
|
2471
2812
|
const synced = await this.heuristicEngine.refreshRules(this.config.projectId, this.config.apiUrl, this.config.apiKey);
|
|
2472
|
-
if (synced) console.log(
|
|
2813
|
+
if (synced) console.log(chalk16.green(" \u2713 Synced heuristic rules"));
|
|
2473
2814
|
}
|
|
2474
2815
|
setupFileWatcher() {
|
|
2475
|
-
console.log(
|
|
2816
|
+
console.log(chalk16.dim("\u{1F4C2} Starting file watcher..."));
|
|
2476
2817
|
this.fileWatcher = createFileWatcher(this.config.watchPath);
|
|
2477
2818
|
this.fileWatcher.on("change", (path24) => this.handleFileChange(path24));
|
|
2478
2819
|
this.fileWatcher.start();
|
|
2479
|
-
console.log(
|
|
2820
|
+
console.log(chalk16.green(" \u2713 File watcher active"));
|
|
2480
2821
|
}
|
|
2481
2822
|
async handleFileChange(filePath) {
|
|
2482
2823
|
this.state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
2483
|
-
if (this.config.verbose) console.log(
|
|
2824
|
+
if (this.config.verbose) console.log(chalk16.dim(` \u{1F4DD} File changed: ${filePath}`));
|
|
2484
2825
|
let lineCount = 0;
|
|
2485
2826
|
try {
|
|
2486
|
-
const content = await
|
|
2827
|
+
const content = await fs17.readFile(filePath, "utf-8");
|
|
2487
2828
|
lineCount = content.split("\n").length;
|
|
2488
2829
|
} catch (e) {
|
|
2489
2830
|
}
|
|
@@ -2493,8 +2834,8 @@ var GuardianDaemon = class extends EventEmitter3 {
|
|
|
2493
2834
|
rules: this.guardianMonitor.getRules()
|
|
2494
2835
|
});
|
|
2495
2836
|
for (const match of matches) {
|
|
2496
|
-
console.log(
|
|
2497
|
-
console.log(
|
|
2837
|
+
console.log(chalk16.magenta(` \u{1F4A1} PREDICTIVE ACTIVATION: ${match.skillId}`));
|
|
2838
|
+
console.log(chalk16.dim(` Reason: ${match.reason}`));
|
|
2498
2839
|
const decision = this.interventionProtocol.evaluateTrigger(match.skillId, match.confidence);
|
|
2499
2840
|
this.interventionProtocol.enforce(decision);
|
|
2500
2841
|
await jitProvisionSkill(match.skillId, this.config.apiUrl, this.config.apiKey, this.config.projectId, process.cwd());
|
|
@@ -2510,7 +2851,7 @@ var GuardianDaemon = class extends EventEmitter3 {
|
|
|
2510
2851
|
this.state.violationsFound += result.violations.length;
|
|
2511
2852
|
this.emit("violation", { file: filePath, violations: result.violations });
|
|
2512
2853
|
for (const v of result.violations) {
|
|
2513
|
-
const color = v.severity === "critical" ?
|
|
2854
|
+
const color = v.severity === "critical" ? chalk16.red : v.severity === "warning" ? chalk16.yellow : chalk16.blue;
|
|
2514
2855
|
console.log(color(` [${v.severity.toUpperCase()}] ${filePath}: ${v.message}`));
|
|
2515
2856
|
if (this.interventionProtocol) {
|
|
2516
2857
|
const decision = this.interventionProtocol.evaluateViolation(v.message, v.severity);
|
|
@@ -2522,25 +2863,25 @@ var GuardianDaemon = class extends EventEmitter3 {
|
|
|
2522
2863
|
}
|
|
2523
2864
|
}
|
|
2524
2865
|
async setupBridge() {
|
|
2525
|
-
console.log(
|
|
2866
|
+
console.log(chalk16.dim("\u{1F309} Connecting to Agent Bridge..."));
|
|
2526
2867
|
this.bridgeListener = createBridgeListener(this.config.projectId, this.config.apiUrl, this.config.apiKey);
|
|
2527
2868
|
this.bridgeListener.on("task", (task) => {
|
|
2528
2869
|
this.state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
2529
2870
|
this.state.tasksProcessed++;
|
|
2530
|
-
console.log(
|
|
2871
|
+
console.log(chalk16.cyan(`
|
|
2531
2872
|
\u{1F4E5} New task received: ${task.id}`));
|
|
2532
2873
|
this.emit("task", task);
|
|
2533
2874
|
});
|
|
2534
2875
|
await this.bridgeListener.connect();
|
|
2535
|
-
console.log(
|
|
2876
|
+
console.log(chalk16.green(" \u2713 Agent Bridge connected"));
|
|
2536
2877
|
}
|
|
2537
2878
|
async stop() {
|
|
2538
2879
|
if (!this.state.isRunning) return;
|
|
2539
|
-
console.log(
|
|
2880
|
+
console.log(chalk16.dim("\n\u{1F6D1} Stopping Guardian Daemon..."));
|
|
2540
2881
|
if (this.fileWatcher) await this.fileWatcher.stop();
|
|
2541
2882
|
if (this.bridgeListener) await this.bridgeListener.disconnect();
|
|
2542
2883
|
this.state.isRunning = false;
|
|
2543
|
-
console.log(
|
|
2884
|
+
console.log(chalk16.green("\u2713 Daemon stopped."));
|
|
2544
2885
|
this.emit("stopped", this.state);
|
|
2545
2886
|
}
|
|
2546
2887
|
getState() {
|
|
@@ -2581,18 +2922,26 @@ async function createDaemon(options) {
|
|
|
2581
2922
|
var PID_FILE = ".rigstate/daemon.pid";
|
|
2582
2923
|
var STATE_FILE = ".rigstate/daemon.state.json";
|
|
2583
2924
|
function createDaemonCommand() {
|
|
2584
|
-
const daemon = new
|
|
2925
|
+
const daemon = new Command11("daemon").description("Start the Guardian daemon for continuous monitoring");
|
|
2585
2926
|
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) => {
|
|
2586
2927
|
if (action === "status") {
|
|
2587
2928
|
await showStatus();
|
|
2588
2929
|
return;
|
|
2589
2930
|
}
|
|
2590
|
-
|
|
2931
|
+
if (action === "enable") {
|
|
2932
|
+
await enableDaemon();
|
|
2933
|
+
return;
|
|
2934
|
+
}
|
|
2935
|
+
if (action === "disable") {
|
|
2936
|
+
await disableDaemon();
|
|
2937
|
+
return;
|
|
2938
|
+
}
|
|
2939
|
+
const spinner = ora8();
|
|
2591
2940
|
try {
|
|
2592
2941
|
if (await isRunning()) {
|
|
2593
|
-
console.log(
|
|
2594
|
-
console.log(
|
|
2595
|
-
console.log(
|
|
2942
|
+
console.log(chalk17.yellow("\u26A0 Another daemon instance may be running."));
|
|
2943
|
+
console.log(chalk17.dim(` Check ${PID_FILE} or run "rigstate daemon status"`));
|
|
2944
|
+
console.log(chalk17.dim(" Use Ctrl+C to stop the running daemon first.\n"));
|
|
2596
2945
|
}
|
|
2597
2946
|
spinner.start("Initializing Guardian Daemon...");
|
|
2598
2947
|
const daemonInstance = await createDaemon({
|
|
@@ -2604,7 +2953,7 @@ function createDaemonCommand() {
|
|
|
2604
2953
|
spinner.stop();
|
|
2605
2954
|
await writePidFile();
|
|
2606
2955
|
process.on("SIGINT", async () => {
|
|
2607
|
-
console.log(
|
|
2956
|
+
console.log(chalk17.dim("\n\nShutting down..."));
|
|
2608
2957
|
await daemonInstance.stop();
|
|
2609
2958
|
await cleanupPidFile();
|
|
2610
2959
|
process.exit(0);
|
|
@@ -2624,8 +2973,8 @@ function createDaemonCommand() {
|
|
|
2624
2973
|
await new Promise(() => {
|
|
2625
2974
|
});
|
|
2626
2975
|
} catch (error) {
|
|
2627
|
-
spinner.fail(
|
|
2628
|
-
console.error(
|
|
2976
|
+
spinner.fail(chalk17.red("Failed to start daemon"));
|
|
2977
|
+
console.error(chalk17.red("Error:"), error.message);
|
|
2629
2978
|
process.exit(1);
|
|
2630
2979
|
}
|
|
2631
2980
|
});
|
|
@@ -2633,14 +2982,14 @@ function createDaemonCommand() {
|
|
|
2633
2982
|
}
|
|
2634
2983
|
async function isRunning() {
|
|
2635
2984
|
try {
|
|
2636
|
-
const pidPath =
|
|
2637
|
-
const content = await
|
|
2985
|
+
const pidPath = path20.join(process.cwd(), PID_FILE);
|
|
2986
|
+
const content = await fs18.readFile(pidPath, "utf-8");
|
|
2638
2987
|
const pid = parseInt(content.trim(), 10);
|
|
2639
2988
|
try {
|
|
2640
2989
|
process.kill(pid, 0);
|
|
2641
2990
|
return true;
|
|
2642
2991
|
} catch {
|
|
2643
|
-
await
|
|
2992
|
+
await fs18.unlink(pidPath);
|
|
2644
2993
|
return false;
|
|
2645
2994
|
}
|
|
2646
2995
|
} catch {
|
|
@@ -2649,86 +2998,176 @@ async function isRunning() {
|
|
|
2649
2998
|
}
|
|
2650
2999
|
async function writePidFile() {
|
|
2651
3000
|
try {
|
|
2652
|
-
const dir =
|
|
2653
|
-
await
|
|
2654
|
-
await
|
|
3001
|
+
const dir = path20.join(process.cwd(), ".rigstate");
|
|
3002
|
+
await fs18.mkdir(dir, { recursive: true });
|
|
3003
|
+
await fs18.writeFile(path20.join(dir, "daemon.pid"), process.pid.toString());
|
|
2655
3004
|
} catch {
|
|
2656
3005
|
}
|
|
2657
3006
|
}
|
|
2658
3007
|
async function cleanupPidFile() {
|
|
2659
3008
|
try {
|
|
2660
|
-
await
|
|
2661
|
-
await
|
|
3009
|
+
await fs18.unlink(path20.join(process.cwd(), PID_FILE));
|
|
3010
|
+
await fs18.unlink(path20.join(process.cwd(), STATE_FILE));
|
|
2662
3011
|
} catch {
|
|
2663
3012
|
}
|
|
2664
3013
|
}
|
|
2665
3014
|
async function writeStateFile(state) {
|
|
2666
3015
|
try {
|
|
2667
|
-
const dir =
|
|
2668
|
-
await
|
|
2669
|
-
await
|
|
2670
|
-
|
|
3016
|
+
const dir = path20.join(process.cwd(), ".rigstate");
|
|
3017
|
+
await fs18.mkdir(dir, { recursive: true });
|
|
3018
|
+
await fs18.writeFile(
|
|
3019
|
+
path20.join(dir, "daemon.state.json"),
|
|
2671
3020
|
JSON.stringify(state, null, 2)
|
|
2672
3021
|
);
|
|
2673
3022
|
} catch {
|
|
2674
3023
|
}
|
|
2675
3024
|
}
|
|
2676
3025
|
async function showStatus() {
|
|
2677
|
-
console.log(
|
|
3026
|
+
console.log(chalk17.bold("\n\u{1F6E1}\uFE0F Guardian Daemon Status\n"));
|
|
2678
3027
|
const running = await isRunning();
|
|
2679
3028
|
if (!running) {
|
|
2680
|
-
console.log(
|
|
2681
|
-
console.log(
|
|
3029
|
+
console.log(chalk17.yellow("Status: Not running"));
|
|
3030
|
+
console.log(chalk17.dim('Use "rigstate daemon" to start.\n'));
|
|
2682
3031
|
return;
|
|
2683
3032
|
}
|
|
2684
|
-
console.log(
|
|
3033
|
+
console.log(chalk17.green("Status: Running"));
|
|
2685
3034
|
try {
|
|
2686
|
-
const statePath =
|
|
2687
|
-
const content = await
|
|
3035
|
+
const statePath = path20.join(process.cwd(), STATE_FILE);
|
|
3036
|
+
const content = await fs18.readFile(statePath, "utf-8");
|
|
2688
3037
|
const state = JSON.parse(content);
|
|
2689
|
-
console.log(
|
|
3038
|
+
console.log(chalk17.dim("\u2500".repeat(40)));
|
|
2690
3039
|
console.log(`Started at: ${state.startedAt || "Unknown"}`);
|
|
2691
3040
|
console.log(`Files checked: ${state.filesChecked || 0}`);
|
|
2692
3041
|
console.log(`Violations: ${state.violationsFound || 0}`);
|
|
2693
3042
|
console.log(`Tasks processed: ${state.tasksProcessed || 0}`);
|
|
2694
3043
|
console.log(`Last activity: ${state.lastActivity || "None"}`);
|
|
2695
|
-
console.log(
|
|
3044
|
+
console.log(chalk17.dim("\u2500".repeat(40)));
|
|
2696
3045
|
} catch {
|
|
2697
|
-
console.log(
|
|
3046
|
+
console.log(chalk17.dim("(State file not found)"));
|
|
2698
3047
|
}
|
|
2699
3048
|
try {
|
|
2700
|
-
const pidPath =
|
|
2701
|
-
const pid = await
|
|
2702
|
-
console.log(
|
|
3049
|
+
const pidPath = path20.join(process.cwd(), PID_FILE);
|
|
3050
|
+
const pid = await fs18.readFile(pidPath, "utf-8");
|
|
3051
|
+
console.log(chalk17.dim(`PID: ${pid.trim()}`));
|
|
2703
3052
|
} catch {
|
|
2704
3053
|
}
|
|
2705
3054
|
console.log("");
|
|
2706
3055
|
}
|
|
3056
|
+
async function enableDaemon() {
|
|
3057
|
+
console.log(chalk17.bold("\n\u2699\uFE0F Enabling Rigstate Background Service (macOS)\n"));
|
|
3058
|
+
if (process.platform !== "darwin") {
|
|
3059
|
+
console.error(chalk17.red("\u274C Currently only macOS is supported for auto-start."));
|
|
3060
|
+
console.error(chalk17.yellow("PRs welcome for Linux/Windows support!"));
|
|
3061
|
+
return;
|
|
3062
|
+
}
|
|
3063
|
+
const homeDir = process.env.HOME || "";
|
|
3064
|
+
if (!homeDir) {
|
|
3065
|
+
console.error(chalk17.red("\u274C Could not determine HOME directory."));
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
const agentsDir = path20.join(homeDir, "Library/LaunchAgents");
|
|
3069
|
+
const logDir = path20.join(homeDir, ".rigstate/logs");
|
|
3070
|
+
const plistPath = path20.join(agentsDir, "com.rigstate.daemon.plist");
|
|
3071
|
+
await fs18.mkdir(agentsDir, { recursive: true });
|
|
3072
|
+
await fs18.mkdir(logDir, { recursive: true });
|
|
3073
|
+
const scriptPath = path20.resolve(__dirname, "../index.js");
|
|
3074
|
+
const nodePath = process.execPath;
|
|
3075
|
+
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3076
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3077
|
+
<plist version="1.0">
|
|
3078
|
+
<dict>
|
|
3079
|
+
<key>Label</key>
|
|
3080
|
+
<string>com.rigstate.daemon</string>
|
|
3081
|
+
<key>ProgramArguments</key>
|
|
3082
|
+
<array>
|
|
3083
|
+
<string>${nodePath}</string>
|
|
3084
|
+
<string>${scriptPath}</string>
|
|
3085
|
+
<string>daemon</string>
|
|
3086
|
+
<string>--no-bridge</string>
|
|
3087
|
+
</array>
|
|
3088
|
+
<key>WorkingDirectory</key>
|
|
3089
|
+
<string>${process.cwd()}</string>
|
|
3090
|
+
<key>StandardOutPath</key>
|
|
3091
|
+
<string>${path20.join(logDir, "daemon.out.log")}</string>
|
|
3092
|
+
<key>StandardErrorPath</key>
|
|
3093
|
+
<string>${path20.join(logDir, "daemon.err.log")}</string>
|
|
3094
|
+
<key>RunAtLoad</key>
|
|
3095
|
+
<true/>
|
|
3096
|
+
<key>KeepAlive</key>
|
|
3097
|
+
<true/>
|
|
3098
|
+
<key>EnvironmentVariables</key>
|
|
3099
|
+
<dict>
|
|
3100
|
+
<key>PATH</key>
|
|
3101
|
+
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:${process.env.PATH}</string>
|
|
3102
|
+
</dict>
|
|
3103
|
+
</dict>
|
|
3104
|
+
</plist>`;
|
|
3105
|
+
try {
|
|
3106
|
+
await fs18.writeFile(plistPath, plistContent);
|
|
3107
|
+
console.log(chalk17.dim(`Created plist at: ${plistPath}`));
|
|
3108
|
+
try {
|
|
3109
|
+
await execShellCommand(`launchctl unload ${plistPath}`);
|
|
3110
|
+
} catch (e) {
|
|
3111
|
+
}
|
|
3112
|
+
await execShellCommand(`launchctl load ${plistPath}`);
|
|
3113
|
+
console.log(chalk17.green("\u2705 Successfully enabled background daemon!"));
|
|
3114
|
+
console.log(chalk17.dim(`Logs: ${logDir}`));
|
|
3115
|
+
console.log(chalk17.dim("The daemon will now restart automatically if it crashes or on reboot."));
|
|
3116
|
+
} catch (error) {
|
|
3117
|
+
console.error(chalk17.red("\u274C Failed to enable daemon:"), error.message);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
async function disableDaemon() {
|
|
3121
|
+
console.log(chalk17.bold("\n\u2699\uFE0F Disabling Rigstate Background Service\n"));
|
|
3122
|
+
const homeDir = process.env.HOME || "";
|
|
3123
|
+
const plistPath = path20.join(homeDir, "Library/LaunchAgents/com.rigstate.daemon.plist");
|
|
3124
|
+
try {
|
|
3125
|
+
await execShellCommand(`launchctl unload ${plistPath}`);
|
|
3126
|
+
await fs18.unlink(plistPath);
|
|
3127
|
+
console.log(chalk17.green("\u2705 Successfully disabled background daemon."));
|
|
3128
|
+
} catch (error) {
|
|
3129
|
+
if (error.code === "ENOENT") {
|
|
3130
|
+
console.log(chalk17.green("\u2705 Daemon was not enabled."));
|
|
3131
|
+
} else {
|
|
3132
|
+
console.error(chalk17.red("\u274C Failed to disable daemon:"), error.message);
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
function execShellCommand(cmd) {
|
|
3137
|
+
const exec = __require("child_process").exec;
|
|
3138
|
+
return new Promise((resolve, reject) => {
|
|
3139
|
+
exec(cmd, (error, stdout, stderr) => {
|
|
3140
|
+
if (error) {
|
|
3141
|
+
}
|
|
3142
|
+
resolve(stdout ? stdout : stderr);
|
|
3143
|
+
});
|
|
3144
|
+
});
|
|
3145
|
+
}
|
|
2707
3146
|
|
|
2708
3147
|
// src/commands/work.ts
|
|
2709
3148
|
init_esm_shims();
|
|
2710
3149
|
init_config();
|
|
2711
|
-
import { Command as
|
|
2712
|
-
import
|
|
2713
|
-
import
|
|
2714
|
-
import
|
|
3150
|
+
import { Command as Command12 } from "commander";
|
|
3151
|
+
import chalk18 from "chalk";
|
|
3152
|
+
import ora9 from "ora";
|
|
3153
|
+
import axios14 from "axios";
|
|
2715
3154
|
import inquirer2 from "inquirer";
|
|
2716
|
-
import
|
|
3155
|
+
import fs19 from "fs/promises";
|
|
2717
3156
|
function createWorkCommand() {
|
|
2718
|
-
return new
|
|
2719
|
-
const spinner =
|
|
3157
|
+
return new Command12("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) => {
|
|
3158
|
+
const spinner = ora9();
|
|
2720
3159
|
try {
|
|
2721
3160
|
const apiKey = getApiKey();
|
|
2722
3161
|
const apiUrl = getApiUrl();
|
|
2723
3162
|
const projectId = options.project || getProjectId();
|
|
2724
3163
|
if (!projectId) {
|
|
2725
|
-
console.log(
|
|
3164
|
+
console.log(chalk18.red("\u274C Project ID is required. Run `rigstate link` or pass --project <id>"));
|
|
2726
3165
|
process.exit(1);
|
|
2727
3166
|
}
|
|
2728
3167
|
if (!taskId) {
|
|
2729
3168
|
spinner.start("Fetching active roadmap tasks...");
|
|
2730
3169
|
}
|
|
2731
|
-
const response = await
|
|
3170
|
+
const response = await axios14.get(
|
|
2732
3171
|
`${apiUrl}/api/v1/roadmap?project_id=${projectId}`,
|
|
2733
3172
|
{ headers: { "Authorization": `Bearer ${apiKey}` }, timeout: 1e4 }
|
|
2734
3173
|
);
|
|
@@ -2748,20 +3187,20 @@ function createWorkCommand() {
|
|
|
2748
3187
|
(t) => t.id === taskId || `T-${t.step_number}` === taskId || t.step_number.toString() === taskId
|
|
2749
3188
|
);
|
|
2750
3189
|
if (!selectedTask) {
|
|
2751
|
-
console.log(
|
|
3190
|
+
console.log(chalk18.red(`\u274C Task '${taskId}' not found in roadmap.`));
|
|
2752
3191
|
return;
|
|
2753
3192
|
}
|
|
2754
3193
|
} else {
|
|
2755
3194
|
if (actionableTasks.length === 0) {
|
|
2756
|
-
console.log(
|
|
3195
|
+
console.log(chalk18.yellow("No active or locked tasks found. The Roadmap is clear! \u{1F389}"));
|
|
2757
3196
|
return;
|
|
2758
3197
|
}
|
|
2759
3198
|
const choices = actionableTasks.map((t) => {
|
|
2760
3199
|
const id = `T-${t.step_number}`;
|
|
2761
3200
|
const statusIcon = t.status === "ACTIVE" ? "\u25B6\uFE0F" : "\u{1F512}";
|
|
2762
|
-
const priority = t.priority === "MVP" ?
|
|
3201
|
+
const priority = t.priority === "MVP" ? chalk18.magenta("[MVP]") : chalk18.blue(`[${t.priority}]`);
|
|
2763
3202
|
return {
|
|
2764
|
-
name: `${statusIcon} ${
|
|
3203
|
+
name: `${statusIcon} ${chalk18.bold(id)}: ${t.title} ${priority}`,
|
|
2765
3204
|
value: t,
|
|
2766
3205
|
short: `${id}: ${t.title}`
|
|
2767
3206
|
};
|
|
@@ -2775,13 +3214,13 @@ function createWorkCommand() {
|
|
|
2775
3214
|
}]);
|
|
2776
3215
|
selectedTask = answer.task;
|
|
2777
3216
|
}
|
|
2778
|
-
console.log("\n" +
|
|
2779
|
-
console.log(
|
|
3217
|
+
console.log("\n" + chalk18.bold.underline(`\u{1F680} WORK MODE: ${selectedTask.title}`));
|
|
3218
|
+
console.log(chalk18.dim(`ID: T-${selectedTask.step_number} | Status: ${selectedTask.status}`));
|
|
2780
3219
|
if (selectedTask.prompt_content) {
|
|
2781
|
-
console.log(
|
|
2782
|
-
console.log(
|
|
3220
|
+
console.log(chalk18.yellow.bold("\n\u{1F4CB} IDE EXECUTION SIGNAL (Prompt):"));
|
|
3221
|
+
console.log(chalk18.gray("--------------------------------------------------"));
|
|
2783
3222
|
console.log(selectedTask.prompt_content);
|
|
2784
|
-
console.log(
|
|
3223
|
+
console.log(chalk18.gray("--------------------------------------------------"));
|
|
2785
3224
|
const { action } = await inquirer2.prompt([{
|
|
2786
3225
|
type: "list",
|
|
2787
3226
|
name: "action",
|
|
@@ -2795,41 +3234,41 @@ function createWorkCommand() {
|
|
|
2795
3234
|
]
|
|
2796
3235
|
}]);
|
|
2797
3236
|
if (action === "cursorrules") {
|
|
2798
|
-
await
|
|
2799
|
-
console.log(
|
|
2800
|
-
console.log(
|
|
3237
|
+
await fs19.writeFile(".rigstate-prompt.md", selectedTask.prompt_content);
|
|
3238
|
+
console.log(chalk18.green(`\u2705 Prompt saved to ${chalk18.bold(".rigstate-prompt.md")}`));
|
|
3239
|
+
console.log(chalk18.dim("You can now reference this file in your IDE chat (@.rigstate-prompt.md)"));
|
|
2801
3240
|
} else if (action === "print") {
|
|
2802
3241
|
console.log("\n" + selectedTask.prompt_content + "\n");
|
|
2803
3242
|
} else if (action === "activate" && selectedTask.status !== "ACTIVE") {
|
|
2804
3243
|
try {
|
|
2805
|
-
await
|
|
3244
|
+
await axios14.post(
|
|
2806
3245
|
`${apiUrl}/api/v1/roadmap/update-status`,
|
|
2807
3246
|
{ step_id: selectedTask.id, status: "ACTIVE", project_id: projectId },
|
|
2808
3247
|
{ headers: { "Authorization": `Bearer ${apiKey}` } }
|
|
2809
3248
|
);
|
|
2810
|
-
console.log(
|
|
3249
|
+
console.log(chalk18.green(`\u2705 Task marked as ACTIVE.`));
|
|
2811
3250
|
} catch (e) {
|
|
2812
|
-
console.error(
|
|
3251
|
+
console.error(chalk18.red(`Failed to update status: ${e.message}`));
|
|
2813
3252
|
}
|
|
2814
3253
|
} else if (action === "complete") {
|
|
2815
3254
|
try {
|
|
2816
|
-
await
|
|
3255
|
+
await axios14.post(
|
|
2817
3256
|
`${apiUrl}/api/v1/roadmap/update-status`,
|
|
2818
3257
|
{ step_id: selectedTask.id, status: "COMPLETED", project_id: projectId },
|
|
2819
3258
|
{ headers: { "Authorization": `Bearer ${apiKey}` } }
|
|
2820
3259
|
);
|
|
2821
|
-
console.log(
|
|
3260
|
+
console.log(chalk18.green(`\u2705 Task marked as COMPLETED. Great job!`));
|
|
2822
3261
|
} catch (e) {
|
|
2823
|
-
console.error(
|
|
3262
|
+
console.error(chalk18.red(`Failed to update status: ${e.message}`));
|
|
2824
3263
|
}
|
|
2825
3264
|
}
|
|
2826
3265
|
} else {
|
|
2827
|
-
console.log(
|
|
2828
|
-
console.log(
|
|
3266
|
+
console.log(chalk18.yellow("\n\u26A0\uFE0F No specific IDE Prompt found for this task (Legacy Task?)."));
|
|
3267
|
+
console.log(chalk18.dim("Objective: " + (selectedTask.summary || selectedTask.description || "Check web UI for details.")));
|
|
2829
3268
|
}
|
|
2830
3269
|
} catch (error) {
|
|
2831
3270
|
spinner.stop();
|
|
2832
|
-
console.error(
|
|
3271
|
+
console.error(chalk18.red(`
|
|
2833
3272
|
Command failed: ${error.message}`));
|
|
2834
3273
|
}
|
|
2835
3274
|
});
|
|
@@ -2838,40 +3277,40 @@ Command failed: ${error.message}`));
|
|
|
2838
3277
|
// src/commands/watch.ts
|
|
2839
3278
|
init_esm_shims();
|
|
2840
3279
|
init_config();
|
|
2841
|
-
import { Command as
|
|
2842
|
-
import
|
|
2843
|
-
import
|
|
3280
|
+
import { Command as Command13 } from "commander";
|
|
3281
|
+
import chalk19 from "chalk";
|
|
3282
|
+
import ora10 from "ora";
|
|
2844
3283
|
import chokidar2 from "chokidar";
|
|
2845
|
-
import
|
|
2846
|
-
import
|
|
3284
|
+
import fs20 from "fs/promises";
|
|
3285
|
+
import path21 from "path";
|
|
2847
3286
|
import { execSync as execSync3 } from "child_process";
|
|
2848
|
-
import
|
|
3287
|
+
import axios15 from "axios";
|
|
2849
3288
|
function createWatchCommand() {
|
|
2850
|
-
const watch2 = new
|
|
3289
|
+
const watch2 = new Command13("watch");
|
|
2851
3290
|
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) => {
|
|
2852
|
-
console.log(
|
|
2853
|
-
console.log(
|
|
3291
|
+
console.log(chalk19.bold.blue("\u{1F52D} Rigstate Watch Mode"));
|
|
3292
|
+
console.log(chalk19.dim("Monitoring for task completion..."));
|
|
2854
3293
|
console.log("");
|
|
2855
3294
|
let apiKey;
|
|
2856
3295
|
let projectId;
|
|
2857
3296
|
try {
|
|
2858
3297
|
apiKey = getApiKey();
|
|
2859
3298
|
} catch (e) {
|
|
2860
|
-
console.log(
|
|
3299
|
+
console.log(chalk19.red('Not authenticated. Run "rigstate login" first.'));
|
|
2861
3300
|
return;
|
|
2862
3301
|
}
|
|
2863
3302
|
projectId = getProjectId();
|
|
2864
3303
|
if (!projectId) {
|
|
2865
3304
|
try {
|
|
2866
|
-
const manifestPath =
|
|
2867
|
-
const content = await
|
|
3305
|
+
const manifestPath = path21.join(process.cwd(), ".rigstate");
|
|
3306
|
+
const content = await fs20.readFile(manifestPath, "utf-8");
|
|
2868
3307
|
const manifest = JSON.parse(content);
|
|
2869
3308
|
projectId = manifest.project_id;
|
|
2870
3309
|
} catch (e) {
|
|
2871
3310
|
}
|
|
2872
3311
|
}
|
|
2873
3312
|
if (!projectId) {
|
|
2874
|
-
console.log(
|
|
3313
|
+
console.log(chalk19.red('No project context. Run "rigstate link" or "rigstate sync --project <id>" first.'));
|
|
2875
3314
|
return;
|
|
2876
3315
|
}
|
|
2877
3316
|
const apiUrl = getApiUrl();
|
|
@@ -2881,12 +3320,12 @@ function createWatchCommand() {
|
|
|
2881
3320
|
runTests: options.runTests || false,
|
|
2882
3321
|
testCommand: options.testCommand || "npm test"
|
|
2883
3322
|
};
|
|
2884
|
-
console.log(
|
|
2885
|
-
console.log(
|
|
3323
|
+
console.log(chalk19.dim(`Auto-commit: ${config2.autoCommit ? "ON" : "OFF"}`));
|
|
3324
|
+
console.log(chalk19.dim(`Auto-push: ${config2.autoPush ? "ON" : "OFF"}`));
|
|
2886
3325
|
console.log("");
|
|
2887
3326
|
const fetchActiveTask = async () => {
|
|
2888
3327
|
try {
|
|
2889
|
-
const response = await
|
|
3328
|
+
const response = await axios15.get(`${apiUrl}/api/v1/roadmap`, {
|
|
2890
3329
|
params: { project_id: projectId },
|
|
2891
3330
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
2892
3331
|
});
|
|
@@ -2910,17 +3349,17 @@ function createWatchCommand() {
|
|
|
2910
3349
|
};
|
|
2911
3350
|
const checkCriteria = async (criteria) => {
|
|
2912
3351
|
try {
|
|
2913
|
-
const fullPath =
|
|
3352
|
+
const fullPath = path21.resolve(process.cwd(), criteria.path);
|
|
2914
3353
|
switch (criteria.type) {
|
|
2915
3354
|
case "file_exists":
|
|
2916
|
-
await
|
|
3355
|
+
await fs20.access(fullPath);
|
|
2917
3356
|
return true;
|
|
2918
3357
|
case "file_content":
|
|
2919
|
-
const content = await
|
|
3358
|
+
const content = await fs20.readFile(fullPath, "utf-8");
|
|
2920
3359
|
return content.length > 0;
|
|
2921
3360
|
case "content_match":
|
|
2922
3361
|
if (!criteria.match) return false;
|
|
2923
|
-
const fileContent = await
|
|
3362
|
+
const fileContent = await fs20.readFile(fullPath, "utf-8");
|
|
2924
3363
|
return fileContent.includes(criteria.match);
|
|
2925
3364
|
default:
|
|
2926
3365
|
return false;
|
|
@@ -2930,7 +3369,7 @@ function createWatchCommand() {
|
|
|
2930
3369
|
}
|
|
2931
3370
|
};
|
|
2932
3371
|
const completeTask = async (taskId, task) => {
|
|
2933
|
-
const spinner =
|
|
3372
|
+
const spinner = ora10("Completing task...").start();
|
|
2934
3373
|
try {
|
|
2935
3374
|
if (config2.runTests) {
|
|
2936
3375
|
spinner.text = "Running tests...";
|
|
@@ -2942,14 +3381,14 @@ function createWatchCommand() {
|
|
|
2942
3381
|
return;
|
|
2943
3382
|
}
|
|
2944
3383
|
}
|
|
2945
|
-
await
|
|
3384
|
+
await axios15.post(`${apiUrl}/api/v1/roadmap/update-status`, {
|
|
2946
3385
|
project_id: projectId,
|
|
2947
3386
|
chunk_id: taskId,
|
|
2948
3387
|
status: "COMPLETED"
|
|
2949
3388
|
}, {
|
|
2950
3389
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
2951
3390
|
});
|
|
2952
|
-
spinner.succeed(
|
|
3391
|
+
spinner.succeed(chalk19.green(`\u2705 Task #${task.step_number} completed: ${task.title}`));
|
|
2953
3392
|
if (config2.autoCommit) {
|
|
2954
3393
|
spinner.start("Committing changes...");
|
|
2955
3394
|
try {
|
|
@@ -2971,7 +3410,7 @@ function createWatchCommand() {
|
|
|
2971
3410
|
}
|
|
2972
3411
|
}
|
|
2973
3412
|
console.log("");
|
|
2974
|
-
console.log(
|
|
3413
|
+
console.log(chalk19.blue("Watching for next task..."));
|
|
2975
3414
|
} catch (e) {
|
|
2976
3415
|
spinner.fail(`Failed to complete task: ${e.message}`);
|
|
2977
3416
|
}
|
|
@@ -2984,7 +3423,7 @@ function createWatchCommand() {
|
|
|
2984
3423
|
const task = await fetchActiveTask();
|
|
2985
3424
|
if (!task) {
|
|
2986
3425
|
if (currentTask) {
|
|
2987
|
-
console.log(
|
|
3426
|
+
console.log(chalk19.green("\u{1F389} All tasks completed! Watching for new tasks..."));
|
|
2988
3427
|
currentTask = null;
|
|
2989
3428
|
}
|
|
2990
3429
|
isProcessing = false;
|
|
@@ -2993,10 +3432,10 @@ function createWatchCommand() {
|
|
|
2993
3432
|
if (!currentTask || currentTask.id !== task.id) {
|
|
2994
3433
|
currentTask = task;
|
|
2995
3434
|
console.log("");
|
|
2996
|
-
console.log(
|
|
2997
|
-
console.log(
|
|
3435
|
+
console.log(chalk19.bold.yellow(`\u{1F4CC} Active Task #${task.step_number}: ${task.title}`));
|
|
3436
|
+
console.log(chalk19.dim(`Status: ${task.status}`));
|
|
2998
3437
|
if (task.verification_criteria) {
|
|
2999
|
-
console.log(
|
|
3438
|
+
console.log(chalk19.dim("Verification: Auto-checking criteria..."));
|
|
3000
3439
|
}
|
|
3001
3440
|
}
|
|
3002
3441
|
if (task.verification_criteria && Array.isArray(task.verification_criteria)) {
|
|
@@ -3009,7 +3448,7 @@ function createWatchCommand() {
|
|
|
3009
3448
|
}
|
|
3010
3449
|
}
|
|
3011
3450
|
if (allPassed) {
|
|
3012
|
-
console.log(
|
|
3451
|
+
console.log(chalk19.green("\u2713 All verification criteria passed!"));
|
|
3013
3452
|
await completeTask(task.id, task);
|
|
3014
3453
|
currentTask = null;
|
|
3015
3454
|
}
|
|
@@ -3034,11 +3473,11 @@ function createWatchCommand() {
|
|
|
3034
3473
|
setTimeout(() => processActiveTask(), 500);
|
|
3035
3474
|
}
|
|
3036
3475
|
});
|
|
3037
|
-
console.log(
|
|
3476
|
+
console.log(chalk19.dim("Watching for file changes... (Ctrl+C to exit)"));
|
|
3038
3477
|
setInterval(() => processActiveTask(), 3e4);
|
|
3039
3478
|
process.on("SIGINT", () => {
|
|
3040
3479
|
console.log("");
|
|
3041
|
-
console.log(
|
|
3480
|
+
console.log(chalk19.dim("Watch mode stopped."));
|
|
3042
3481
|
watcher.close();
|
|
3043
3482
|
process.exit(0);
|
|
3044
3483
|
});
|
|
@@ -3049,42 +3488,42 @@ function createWatchCommand() {
|
|
|
3049
3488
|
// src/commands/focus.ts
|
|
3050
3489
|
init_esm_shims();
|
|
3051
3490
|
init_config();
|
|
3052
|
-
import { Command as
|
|
3053
|
-
import
|
|
3054
|
-
import
|
|
3055
|
-
import
|
|
3491
|
+
import { Command as Command14 } from "commander";
|
|
3492
|
+
import chalk20 from "chalk";
|
|
3493
|
+
import ora11 from "ora";
|
|
3494
|
+
import axios16 from "axios";
|
|
3056
3495
|
import { execSync as execSync4 } from "child_process";
|
|
3057
|
-
import
|
|
3058
|
-
import
|
|
3496
|
+
import fs21 from "fs/promises";
|
|
3497
|
+
import path22 from "path";
|
|
3059
3498
|
function createFocusCommand() {
|
|
3060
|
-
const focus = new
|
|
3499
|
+
const focus = new Command14("focus");
|
|
3061
3500
|
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) => {
|
|
3062
|
-
const spinner =
|
|
3501
|
+
const spinner = ora11("Fetching next objective...").start();
|
|
3063
3502
|
let apiKey;
|
|
3064
3503
|
let projectId;
|
|
3065
3504
|
try {
|
|
3066
3505
|
apiKey = getApiKey();
|
|
3067
3506
|
} catch (e) {
|
|
3068
|
-
spinner.fail(
|
|
3507
|
+
spinner.fail(chalk20.red('Not authenticated. Run "rigstate login" first.'));
|
|
3069
3508
|
return;
|
|
3070
3509
|
}
|
|
3071
3510
|
projectId = getProjectId();
|
|
3072
3511
|
if (!projectId) {
|
|
3073
3512
|
try {
|
|
3074
|
-
const manifestPath =
|
|
3075
|
-
const content = await
|
|
3513
|
+
const manifestPath = path22.join(process.cwd(), ".rigstate");
|
|
3514
|
+
const content = await fs21.readFile(manifestPath, "utf-8");
|
|
3076
3515
|
const manifest = JSON.parse(content);
|
|
3077
3516
|
projectId = manifest.project_id;
|
|
3078
3517
|
} catch (e) {
|
|
3079
3518
|
}
|
|
3080
3519
|
}
|
|
3081
3520
|
if (!projectId) {
|
|
3082
|
-
spinner.fail(
|
|
3521
|
+
spinner.fail(chalk20.red('No project context. Run "rigstate link" first.'));
|
|
3083
3522
|
return;
|
|
3084
3523
|
}
|
|
3085
3524
|
const apiUrl = getApiUrl();
|
|
3086
3525
|
try {
|
|
3087
|
-
const response = await
|
|
3526
|
+
const response = await axios16.get(`${apiUrl}/api/v1/roadmap`, {
|
|
3088
3527
|
params: { project_id: projectId },
|
|
3089
3528
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3090
3529
|
});
|
|
@@ -3110,188 +3549,72 @@ function createFocusCommand() {
|
|
|
3110
3549
|
const nextTask = activeTasks[0];
|
|
3111
3550
|
spinner.stop();
|
|
3112
3551
|
console.log("");
|
|
3113
|
-
console.log(
|
|
3114
|
-
const statusColor = nextTask.status === "IN_PROGRESS" ?
|
|
3115
|
-
console.log(
|
|
3116
|
-
console.log(
|
|
3552
|
+
console.log(chalk20.bold.blue(`\u{1F4CC} Task #${nextTask.step_number || "?"}: ${nextTask.title}`));
|
|
3553
|
+
const statusColor = nextTask.status === "IN_PROGRESS" ? chalk20.yellow : nextTask.status === "ACTIVE" ? chalk20.green : chalk20.dim;
|
|
3554
|
+
console.log(chalk20.dim("Status: ") + statusColor(nextTask.status));
|
|
3555
|
+
console.log(chalk20.dim("\u2500".repeat(60)));
|
|
3117
3556
|
if (nextTask.prompt_content) {
|
|
3118
|
-
console.log(
|
|
3119
|
-
console.log(
|
|
3557
|
+
console.log(chalk20.white(nextTask.prompt_content));
|
|
3558
|
+
console.log(chalk20.dim("\u2500".repeat(60)));
|
|
3120
3559
|
if (options.copy !== false) {
|
|
3121
3560
|
try {
|
|
3122
3561
|
if (process.platform === "darwin") {
|
|
3123
3562
|
execSync4("pbcopy", { input: nextTask.prompt_content });
|
|
3124
|
-
console.log(
|
|
3563
|
+
console.log(chalk20.green("\u2705 Prompt copied to clipboard! Ready to paste (Cmd+V)."));
|
|
3125
3564
|
} else if (process.platform === "linux") {
|
|
3126
3565
|
try {
|
|
3127
3566
|
execSync4("xclip -selection clipboard", { input: nextTask.prompt_content });
|
|
3128
|
-
console.log(
|
|
3567
|
+
console.log(chalk20.green("\u2705 Prompt copied to clipboard!"));
|
|
3129
3568
|
} catch (e) {
|
|
3130
|
-
console.log(
|
|
3569
|
+
console.log(chalk20.yellow("\u2139\uFE0F Copy prompt manually (xclip not available)"));
|
|
3131
3570
|
}
|
|
3132
3571
|
} else {
|
|
3133
|
-
console.log(
|
|
3572
|
+
console.log(chalk20.yellow("\u2139\uFE0F Copy prompt manually (Auto-copy not supported on this OS)"));
|
|
3134
3573
|
}
|
|
3135
3574
|
} catch (e) {
|
|
3136
3575
|
}
|
|
3137
3576
|
}
|
|
3138
3577
|
} else {
|
|
3139
|
-
console.log(
|
|
3578
|
+
console.log(chalk20.yellow("No prompt instructions available."));
|
|
3140
3579
|
if (nextTask.architectural_brief) {
|
|
3141
|
-
console.log(
|
|
3580
|
+
console.log(chalk20.bold("Brief:"));
|
|
3142
3581
|
console.log(nextTask.architectural_brief);
|
|
3143
3582
|
}
|
|
3144
3583
|
}
|
|
3145
3584
|
console.log("");
|
|
3146
3585
|
} catch (e) {
|
|
3147
|
-
spinner.fail(
|
|
3586
|
+
spinner.fail(chalk20.red(`Failed to fetch task: ${e.message}`));
|
|
3148
3587
|
}
|
|
3149
3588
|
});
|
|
3150
3589
|
return focus;
|
|
3151
3590
|
}
|
|
3152
3591
|
|
|
3153
|
-
// src/
|
|
3154
|
-
|
|
3155
|
-
init_config();
|
|
3156
|
-
import { Command as Command13 } from "commander";
|
|
3157
|
-
import chalk19 from "chalk";
|
|
3158
|
-
import ora10 from "ora";
|
|
3159
|
-
import fs21 from "fs/promises";
|
|
3160
|
-
import path22 from "path";
|
|
3161
|
-
import axios15 from "axios";
|
|
3162
|
-
function createEnvPullCommand() {
|
|
3163
|
-
const envPull = new Command13("env");
|
|
3164
|
-
envPull.command("pull").description("Pull environment variables from project vault").action(async () => {
|
|
3165
|
-
console.log("");
|
|
3166
|
-
console.log(chalk19.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"));
|
|
3167
|
-
console.log(chalk19.bold.yellow("\u2551") + chalk19.bold.white(" \u{1F6E1}\uFE0F RIGSTATE SOVEREIGN VAULT SYNC \u{1F6E1}\uFE0F ") + chalk19.bold.yellow("\u2551"));
|
|
3168
|
-
console.log(chalk19.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"));
|
|
3169
|
-
console.log("");
|
|
3170
|
-
const spinner = ora10("Authenticating with Vault...").start();
|
|
3171
|
-
let apiKey;
|
|
3172
|
-
let projectId;
|
|
3173
|
-
try {
|
|
3174
|
-
apiKey = getApiKey();
|
|
3175
|
-
} catch (e) {
|
|
3176
|
-
spinner.fail(chalk19.red('Not authenticated. Run "rigstate login" first.'));
|
|
3177
|
-
return;
|
|
3178
|
-
}
|
|
3179
|
-
spinner.succeed("Authenticated");
|
|
3180
|
-
spinner.start("Reading project configuration...");
|
|
3181
|
-
projectId = getProjectId();
|
|
3182
|
-
if (!projectId) {
|
|
3183
|
-
try {
|
|
3184
|
-
const manifestPath = path22.join(process.cwd(), ".rigstate");
|
|
3185
|
-
const content = await fs21.readFile(manifestPath, "utf-8");
|
|
3186
|
-
const manifest = JSON.parse(content);
|
|
3187
|
-
projectId = manifest.project_id;
|
|
3188
|
-
} catch (e) {
|
|
3189
|
-
}
|
|
3190
|
-
}
|
|
3191
|
-
if (!projectId) {
|
|
3192
|
-
spinner.fail(chalk19.red('No project context. Run "rigstate link" first.'));
|
|
3193
|
-
return;
|
|
3194
|
-
}
|
|
3195
|
-
spinner.succeed(`Project: ${chalk19.cyan(projectId.substring(0, 8))}...`);
|
|
3196
|
-
const apiUrl = getApiUrl();
|
|
3197
|
-
spinner.start("Fetching secrets from Vault...");
|
|
3198
|
-
try {
|
|
3199
|
-
const response = await axios15.post(`${apiUrl}/api/v1/vault/sync`, {
|
|
3200
|
-
project_id: projectId
|
|
3201
|
-
}, {
|
|
3202
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3203
|
-
});
|
|
3204
|
-
if (!response.data.success) {
|
|
3205
|
-
throw new Error(response.data.error || "Failed to fetch secrets");
|
|
3206
|
-
}
|
|
3207
|
-
const vaultContent = response.data.data.content || "";
|
|
3208
|
-
const secretCount = response.data.data.count || 0;
|
|
3209
|
-
if (secretCount === 0) {
|
|
3210
|
-
spinner.info("No secrets found in Vault for this project.");
|
|
3211
|
-
console.log(chalk19.dim(" Add secrets via the Rigstate web interface."));
|
|
3212
|
-
return;
|
|
3213
|
-
}
|
|
3214
|
-
spinner.succeed(`Retrieved ${chalk19.bold(secretCount)} secret(s)`);
|
|
3215
|
-
const envFile = path22.resolve(process.cwd(), ".env.local");
|
|
3216
|
-
let existingContent = "";
|
|
3217
|
-
let existingKeys = /* @__PURE__ */ new Set();
|
|
3218
|
-
try {
|
|
3219
|
-
existingContent = await fs21.readFile(envFile, "utf-8");
|
|
3220
|
-
existingContent.split("\n").forEach((line) => {
|
|
3221
|
-
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
3222
|
-
if (match) existingKeys.add(match[1]);
|
|
3223
|
-
});
|
|
3224
|
-
} catch (e) {
|
|
3225
|
-
}
|
|
3226
|
-
const vaultKeys = /* @__PURE__ */ new Set();
|
|
3227
|
-
vaultContent.split("\n").forEach((line) => {
|
|
3228
|
-
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
3229
|
-
if (match) vaultKeys.add(match[1]);
|
|
3230
|
-
});
|
|
3231
|
-
let newCount = 0;
|
|
3232
|
-
let updatedCount = 0;
|
|
3233
|
-
vaultKeys.forEach((key) => {
|
|
3234
|
-
if (!existingKeys.has(key)) {
|
|
3235
|
-
newCount++;
|
|
3236
|
-
} else {
|
|
3237
|
-
updatedCount++;
|
|
3238
|
-
}
|
|
3239
|
-
});
|
|
3240
|
-
const unchangedCount = existingKeys.size - updatedCount;
|
|
3241
|
-
spinner.start("Writing .env.local...");
|
|
3242
|
-
const header = [
|
|
3243
|
-
"# ==========================================",
|
|
3244
|
-
"# RIGSTATE SOVEREIGN FOUNDATION",
|
|
3245
|
-
"# Authenticated Environment Configuration",
|
|
3246
|
-
`# Synced at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
3247
|
-
`# Project: ${projectId}`,
|
|
3248
|
-
"# ==========================================",
|
|
3249
|
-
""
|
|
3250
|
-
].join("\n");
|
|
3251
|
-
await fs21.writeFile(envFile, header + vaultContent + "\n");
|
|
3252
|
-
spinner.succeed("Written to .env.local");
|
|
3253
|
-
console.log("");
|
|
3254
|
-
console.log(chalk19.bold.green("\u2705 Environment synchronized successfully"));
|
|
3255
|
-
console.log("");
|
|
3256
|
-
console.log(chalk19.dim(" Summary:"));
|
|
3257
|
-
console.log(chalk19.green(` + ${newCount} new`));
|
|
3258
|
-
console.log(chalk19.yellow(` ~ ${updatedCount} updated`));
|
|
3259
|
-
console.log(chalk19.dim(` = ${unchangedCount} unchanged`));
|
|
3260
|
-
console.log("");
|
|
3261
|
-
console.log(chalk19.bold.yellow("\u26A0\uFE0F Security Reminder:"));
|
|
3262
|
-
console.log(chalk19.dim(" - Never commit .env.local to version control."));
|
|
3263
|
-
console.log(chalk19.dim(" - Ensure .gitignore includes .env.local"));
|
|
3264
|
-
console.log("");
|
|
3265
|
-
} catch (e) {
|
|
3266
|
-
spinner.fail(chalk19.red(`Failed to fetch secrets: ${e.message}`));
|
|
3267
|
-
}
|
|
3268
|
-
});
|
|
3269
|
-
return envPull;
|
|
3270
|
-
}
|
|
3592
|
+
// src/index.ts
|
|
3593
|
+
init_env();
|
|
3271
3594
|
|
|
3272
3595
|
// src/commands/config.ts
|
|
3273
3596
|
init_esm_shims();
|
|
3274
3597
|
init_config();
|
|
3275
|
-
import { Command as
|
|
3276
|
-
import
|
|
3598
|
+
import { Command as Command15 } from "commander";
|
|
3599
|
+
import chalk21 from "chalk";
|
|
3277
3600
|
function createConfigCommand() {
|
|
3278
|
-
const config2 = new
|
|
3601
|
+
const config2 = new Command15("config");
|
|
3279
3602
|
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) => {
|
|
3280
3603
|
if (!key) {
|
|
3281
|
-
console.log(
|
|
3282
|
-
console.log(
|
|
3604
|
+
console.log(chalk21.bold("Rigstate Configuration"));
|
|
3605
|
+
console.log(chalk21.dim("\u2500".repeat(40)));
|
|
3283
3606
|
try {
|
|
3284
3607
|
const apiKey = getApiKey();
|
|
3285
|
-
console.log(`${
|
|
3608
|
+
console.log(`${chalk21.cyan("api_key")}: ${apiKey.substring(0, 20)}...`);
|
|
3286
3609
|
} catch (e) {
|
|
3287
|
-
console.log(`${
|
|
3610
|
+
console.log(`${chalk21.cyan("api_key")}: ${chalk21.dim("(not set)")}`);
|
|
3288
3611
|
}
|
|
3289
3612
|
const projectId = getProjectId();
|
|
3290
|
-
console.log(`${
|
|
3613
|
+
console.log(`${chalk21.cyan("project_id")}: ${projectId || chalk21.dim("(not set)")}`);
|
|
3291
3614
|
const apiUrl = getApiUrl();
|
|
3292
|
-
console.log(`${
|
|
3615
|
+
console.log(`${chalk21.cyan("api_url")}: ${apiUrl}`);
|
|
3293
3616
|
console.log("");
|
|
3294
|
-
console.log(
|
|
3617
|
+
console.log(chalk21.dim('Use "rigstate config <key> <value>" to set a value.'));
|
|
3295
3618
|
return;
|
|
3296
3619
|
}
|
|
3297
3620
|
if (!value) {
|
|
@@ -3301,36 +3624,36 @@ function createConfigCommand() {
|
|
|
3301
3624
|
const apiKey = getApiKey();
|
|
3302
3625
|
console.log(apiKey);
|
|
3303
3626
|
} catch (e) {
|
|
3304
|
-
console.log(
|
|
3627
|
+
console.log(chalk21.dim("(not set)"));
|
|
3305
3628
|
}
|
|
3306
3629
|
break;
|
|
3307
3630
|
case "project_id":
|
|
3308
|
-
console.log(getProjectId() ||
|
|
3631
|
+
console.log(getProjectId() || chalk21.dim("(not set)"));
|
|
3309
3632
|
break;
|
|
3310
3633
|
case "api_url":
|
|
3311
3634
|
console.log(getApiUrl());
|
|
3312
3635
|
break;
|
|
3313
3636
|
default:
|
|
3314
|
-
console.log(
|
|
3315
|
-
console.log(
|
|
3637
|
+
console.log(chalk21.red(`Unknown config key: ${key}`));
|
|
3638
|
+
console.log(chalk21.dim("Valid keys: api_key, project_id, api_url"));
|
|
3316
3639
|
}
|
|
3317
3640
|
return;
|
|
3318
3641
|
}
|
|
3319
3642
|
switch (key) {
|
|
3320
3643
|
case "api_key":
|
|
3321
3644
|
setApiKey(value);
|
|
3322
|
-
console.log(
|
|
3645
|
+
console.log(chalk21.green(`\u2705 api_key updated`));
|
|
3323
3646
|
break;
|
|
3324
3647
|
case "project_id":
|
|
3325
3648
|
setProjectId(value);
|
|
3326
|
-
console.log(
|
|
3649
|
+
console.log(chalk21.green(`\u2705 project_id updated`));
|
|
3327
3650
|
break;
|
|
3328
3651
|
case "api_url":
|
|
3329
|
-
console.log(
|
|
3652
|
+
console.log(chalk21.yellow("api_url is set via RIGSTATE_API_URL environment variable"));
|
|
3330
3653
|
break;
|
|
3331
3654
|
default:
|
|
3332
|
-
console.log(
|
|
3333
|
-
console.log(
|
|
3655
|
+
console.log(chalk21.red(`Unknown config key: ${key}`));
|
|
3656
|
+
console.log(chalk21.dim("Valid keys: api_key, project_id"));
|
|
3334
3657
|
}
|
|
3335
3658
|
});
|
|
3336
3659
|
return config2;
|
|
@@ -3338,8 +3661,8 @@ function createConfigCommand() {
|
|
|
3338
3661
|
|
|
3339
3662
|
// src/commands/mcp.ts
|
|
3340
3663
|
init_esm_shims();
|
|
3341
|
-
import { Command as
|
|
3342
|
-
import
|
|
3664
|
+
import { Command as Command16 } from "commander";
|
|
3665
|
+
import chalk22 from "chalk";
|
|
3343
3666
|
import { spawn } from "child_process";
|
|
3344
3667
|
import path23 from "path";
|
|
3345
3668
|
import fs22 from "fs";
|
|
@@ -3347,7 +3670,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
3347
3670
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
3348
3671
|
var __dirname2 = path23.dirname(__filename2);
|
|
3349
3672
|
function createMcpCommand() {
|
|
3350
|
-
const mcp = new
|
|
3673
|
+
const mcp = new Command16("mcp");
|
|
3351
3674
|
mcp.description("Run the Rigstate MCP server for AI editors").action(async () => {
|
|
3352
3675
|
const possiblePaths = [
|
|
3353
3676
|
// From packages/cli -> packages/mcp (sibling package)
|
|
@@ -3365,15 +3688,15 @@ function createMcpCommand() {
|
|
|
3365
3688
|
}
|
|
3366
3689
|
}
|
|
3367
3690
|
if (!serverPath) {
|
|
3368
|
-
console.error(
|
|
3369
|
-
console.error(
|
|
3370
|
-
console.error(
|
|
3691
|
+
console.error(chalk22.red("\u274C Error: Rigstate MCP Server binary not found."));
|
|
3692
|
+
console.error(chalk22.yellow("Please ensure that the mcp package is built:"));
|
|
3693
|
+
console.error(chalk22.white(" cd packages/mcp && npm run build"));
|
|
3371
3694
|
console.error("");
|
|
3372
|
-
console.error(
|
|
3373
|
-
console.error(
|
|
3695
|
+
console.error(chalk22.dim("Or run directly with:"));
|
|
3696
|
+
console.error(chalk22.white(" npx @rigstate/mcp"));
|
|
3374
3697
|
process.exit(1);
|
|
3375
3698
|
}
|
|
3376
|
-
console.log(
|
|
3699
|
+
console.log(chalk22.dim(`Starting MCP server from: ${serverPath}`));
|
|
3377
3700
|
if (process.env.VIBE_API_KEY && !process.env.RIGSTATE_API_KEY) {
|
|
3378
3701
|
process.env.RIGSTATE_API_KEY = process.env.VIBE_API_KEY;
|
|
3379
3702
|
}
|
|
@@ -3382,7 +3705,7 @@ function createMcpCommand() {
|
|
|
3382
3705
|
stdio: ["inherit", "inherit", "inherit"]
|
|
3383
3706
|
});
|
|
3384
3707
|
worker.on("error", (err) => {
|
|
3385
|
-
console.error(
|
|
3708
|
+
console.error(chalk22.red(`\u274C Failed to start MCP server: ${err.message}`));
|
|
3386
3709
|
process.exit(1);
|
|
3387
3710
|
});
|
|
3388
3711
|
worker.on("exit", (code) => {
|
|
@@ -3396,8 +3719,8 @@ function createMcpCommand() {
|
|
|
3396
3719
|
|
|
3397
3720
|
// src/commands/nexus.ts
|
|
3398
3721
|
init_esm_shims();
|
|
3399
|
-
import { Command as
|
|
3400
|
-
import
|
|
3722
|
+
import { Command as Command17 } from "commander";
|
|
3723
|
+
import chalk25 from "chalk";
|
|
3401
3724
|
|
|
3402
3725
|
// src/nexus/dispatcher.ts
|
|
3403
3726
|
init_esm_shims();
|
|
@@ -3406,7 +3729,7 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
3406
3729
|
|
|
3407
3730
|
// src/hive/gateway.ts
|
|
3408
3731
|
init_esm_shims();
|
|
3409
|
-
import
|
|
3732
|
+
import axios17 from "axios";
|
|
3410
3733
|
|
|
3411
3734
|
// src/hive/scrubber.ts
|
|
3412
3735
|
init_esm_shims();
|
|
@@ -3465,7 +3788,7 @@ var HiveScrubber = class {
|
|
|
3465
3788
|
};
|
|
3466
3789
|
|
|
3467
3790
|
// src/hive/gateway.ts
|
|
3468
|
-
import
|
|
3791
|
+
import chalk23 from "chalk";
|
|
3469
3792
|
var HiveGateway = class {
|
|
3470
3793
|
client;
|
|
3471
3794
|
enabled;
|
|
@@ -3475,9 +3798,9 @@ var HiveGateway = class {
|
|
|
3475
3798
|
constructor(baseUrl, token) {
|
|
3476
3799
|
this.enabled = !!token;
|
|
3477
3800
|
if (!this.enabled) {
|
|
3478
|
-
console.log(
|
|
3801
|
+
console.log(chalk23.dim("\u26A0\uFE0F Hive Gateway disabled (No Token provided). Running in localized mode."));
|
|
3479
3802
|
}
|
|
3480
|
-
this.client =
|
|
3803
|
+
this.client = axios17.create({
|
|
3481
3804
|
baseURL: baseUrl,
|
|
3482
3805
|
headers: {
|
|
3483
3806
|
"Authorization": `Bearer ${token}`,
|
|
@@ -3495,23 +3818,23 @@ var HiveGateway = class {
|
|
|
3495
3818
|
if (!this.enabled) return false;
|
|
3496
3819
|
const now = Date.now();
|
|
3497
3820
|
if (now - this.lastSignalTime < this.MIN_INTERVAL_MS) {
|
|
3498
|
-
console.warn(
|
|
3821
|
+
console.warn(chalk23.yellow("\u23F3 Hive Gateway Throttled. Signal dropped to preventing spam."));
|
|
3499
3822
|
return false;
|
|
3500
3823
|
}
|
|
3501
3824
|
const scrubResult = HiveScrubber.scrub(signal.ruleContent);
|
|
3502
3825
|
if (scrubResult.riskScore > 20) {
|
|
3503
|
-
console.error(
|
|
3826
|
+
console.error(chalk23.red(`\u{1F6D1} HIVE BLOCKED: Signal contains sensitive data (Risk: ${scrubResult.riskScore})`));
|
|
3504
3827
|
return false;
|
|
3505
3828
|
}
|
|
3506
3829
|
try {
|
|
3507
|
-
console.log(
|
|
3830
|
+
console.log(chalk23.blue(`\u{1F4E1} Uplinking to Hive... [${signal.vector}]`));
|
|
3508
3831
|
const payload = { ...signal, ruleContent: scrubResult.sanitizedContent };
|
|
3509
3832
|
await this.client.post("/signal", payload);
|
|
3510
3833
|
this.lastSignalTime = now;
|
|
3511
|
-
console.log(
|
|
3834
|
+
console.log(chalk23.green("\u2705 Signal Received by Hive Core. Knowledge Shared."));
|
|
3512
3835
|
return true;
|
|
3513
3836
|
} catch (error) {
|
|
3514
|
-
console.error(
|
|
3837
|
+
console.error(chalk23.red(`\u274C Hive Transmission Failed: ${error.message}`));
|
|
3515
3838
|
return false;
|
|
3516
3839
|
}
|
|
3517
3840
|
}
|
|
@@ -3519,37 +3842,37 @@ var HiveGateway = class {
|
|
|
3519
3842
|
|
|
3520
3843
|
// src/utils/logger.ts
|
|
3521
3844
|
init_esm_shims();
|
|
3522
|
-
import
|
|
3845
|
+
import chalk24 from "chalk";
|
|
3523
3846
|
var Logger = class {
|
|
3524
3847
|
static formatMessage(level, message, context) {
|
|
3525
3848
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3526
3849
|
let prefix = "";
|
|
3527
3850
|
switch (level) {
|
|
3528
3851
|
case "INFO" /* INFO */:
|
|
3529
|
-
prefix =
|
|
3852
|
+
prefix = chalk24.blue(`[${"INFO" /* INFO */}]`);
|
|
3530
3853
|
break;
|
|
3531
3854
|
case "WARN" /* WARN */:
|
|
3532
|
-
prefix =
|
|
3855
|
+
prefix = chalk24.yellow(`[${"WARN" /* WARN */}]`);
|
|
3533
3856
|
break;
|
|
3534
3857
|
case "ERROR" /* ERROR */:
|
|
3535
|
-
prefix =
|
|
3858
|
+
prefix = chalk24.red(`[${"ERROR" /* ERROR */}]`);
|
|
3536
3859
|
break;
|
|
3537
3860
|
case "DEBUG" /* DEBUG */:
|
|
3538
|
-
prefix =
|
|
3861
|
+
prefix = chalk24.gray(`[${"DEBUG" /* DEBUG */}]`);
|
|
3539
3862
|
break;
|
|
3540
3863
|
}
|
|
3541
|
-
let output = `${
|
|
3864
|
+
let output = `${chalk24.gray(timestamp)} ${prefix} ${message}`;
|
|
3542
3865
|
if (context) {
|
|
3543
3866
|
if (context instanceof Error) {
|
|
3544
3867
|
output += `
|
|
3545
|
-
${
|
|
3868
|
+
${chalk24.red(context.stack || context.message)}`;
|
|
3546
3869
|
} else if (typeof context === "object") {
|
|
3547
3870
|
try {
|
|
3548
3871
|
output += `
|
|
3549
|
-
${
|
|
3872
|
+
${chalk24.gray(JSON.stringify(context, null, 2))}`;
|
|
3550
3873
|
} catch (e) {
|
|
3551
3874
|
output += `
|
|
3552
|
-
${
|
|
3875
|
+
${chalk24.gray("[Circular or invalid object]")}`;
|
|
3553
3876
|
}
|
|
3554
3877
|
} else {
|
|
3555
3878
|
output += ` ${String(context)}`;
|
|
@@ -3667,12 +3990,12 @@ var NexusDispatcher = class extends EventEmitter4 {
|
|
|
3667
3990
|
// src/commands/nexus.ts
|
|
3668
3991
|
import inquirer3 from "inquirer";
|
|
3669
3992
|
function createNexusCommand() {
|
|
3670
|
-
const command = new
|
|
3993
|
+
const command = new Command17("nexus");
|
|
3671
3994
|
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) => {
|
|
3672
|
-
console.log(
|
|
3995
|
+
console.log(chalk25.bold.magenta("\n\u{1F981} Welcome to The Nexus (Phase 8)\n"));
|
|
3673
3996
|
const dryRun = !options.force;
|
|
3674
3997
|
if (!dryRun) {
|
|
3675
|
-
console.log(
|
|
3998
|
+
console.log(chalk25.black.bgYellow(" WARNING ") + chalk25.yellow(" Dry-Run disabled! Eitri is authorized to write code."));
|
|
3676
3999
|
const { confirm } = await inquirer3.prompt([{
|
|
3677
4000
|
type: "confirm",
|
|
3678
4001
|
name: "confirm",
|
|
@@ -3693,169 +4016,33 @@ function createNexusCommand() {
|
|
|
3693
4016
|
};
|
|
3694
4017
|
const dispatcher = new NexusDispatcher(context);
|
|
3695
4018
|
dispatcher.on("order:created", (o) => {
|
|
3696
|
-
console.log(
|
|
4019
|
+
console.log(chalk25.blue(`\u{1F195} [${o.id.slice(0, 6)}] Order Created: `) + o.intent);
|
|
3697
4020
|
});
|
|
3698
4021
|
dispatcher.on("order:started", (o) => {
|
|
3699
|
-
console.log(
|
|
4022
|
+
console.log(chalk25.yellow(`\u23F3 [${o.id.slice(0, 6)}] Processing...`));
|
|
3700
4023
|
});
|
|
3701
4024
|
dispatcher.on("order:blocked", (o) => {
|
|
3702
|
-
console.log(
|
|
3703
|
-
console.log(
|
|
3704
|
-
console.log(
|
|
4025
|
+
console.log(chalk25.red(`\u{1F6D1} [${o.id.slice(0, 6)}] BLOCKED by Kill-Switch`));
|
|
4026
|
+
console.log(chalk25.dim(` Target: ${o.targetAgent} | Action: ${o.action}`));
|
|
4027
|
+
console.log(chalk25.dim(" Run with --force to execute automatically (NOT RECOMMENDED)."));
|
|
3705
4028
|
});
|
|
3706
|
-
dispatcher.on("agent:SINDRE", (o) => console.log(
|
|
3707
|
-
dispatcher.on("agent:EITRI", (o) => console.log(
|
|
3708
|
-
console.log(
|
|
4029
|
+
dispatcher.on("agent:SINDRE", (o) => console.log(chalk25.cyan(`\u{1F916} Sindre (Vault): I'm on it! (${o.action})`)));
|
|
4030
|
+
dispatcher.on("agent:EITRI", (o) => console.log(chalk25.green(`\u{1F477} Eitri (Smith): Ready to build! (${o.action})`)));
|
|
4031
|
+
console.log(chalk25.dim("\u{1F9E0} Frank is analyzing your intent..."));
|
|
3709
4032
|
await new Promise((r) => setTimeout(r, 800));
|
|
3710
4033
|
if (intent.toLowerCase().includes("db") || intent.toLowerCase().includes("database")) {
|
|
3711
4034
|
await dispatcher.dispatch("FRANK", "SINDRE", intent, "db.analyze", { raw: intent });
|
|
3712
4035
|
} else if (intent.toLowerCase().includes("create") || intent.toLowerCase().includes("code")) {
|
|
3713
4036
|
await dispatcher.dispatch("FRANK", "EITRI", intent, "fs.write", { path: "src/demo.ts", content: "// demo" });
|
|
3714
4037
|
} else {
|
|
3715
|
-
console.log(
|
|
4038
|
+
console.log(chalk25.gray("Frank didn't understand. Try 'create file' or 'check database'."));
|
|
3716
4039
|
}
|
|
3717
4040
|
});
|
|
3718
4041
|
return command;
|
|
3719
4042
|
}
|
|
3720
4043
|
|
|
3721
|
-
// src/
|
|
3722
|
-
|
|
3723
|
-
init_config();
|
|
3724
|
-
import { Command as Command17 } from "commander";
|
|
3725
|
-
import chalk25 from "chalk";
|
|
3726
|
-
import ora11 from "ora";
|
|
3727
|
-
import axios17 from "axios";
|
|
3728
|
-
function createSyncRulesCommand() {
|
|
3729
|
-
const syncRules = new Command17("sync-rules");
|
|
3730
|
-
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) => {
|
|
3731
|
-
const spinner = ora11("\u{1F6E1}\uFE0F Frank Protocol: Initializing retroactive sync...").start();
|
|
3732
|
-
const results = [];
|
|
3733
|
-
let apiKey;
|
|
3734
|
-
try {
|
|
3735
|
-
apiKey = getApiKey();
|
|
3736
|
-
} catch (e) {
|
|
3737
|
-
spinner.fail(chalk25.red('Not authenticated. Run "rigstate login" first.'));
|
|
3738
|
-
return;
|
|
3739
|
-
}
|
|
3740
|
-
const apiUrl = getApiUrl();
|
|
3741
|
-
try {
|
|
3742
|
-
spinner.text = "Fetching projects...";
|
|
3743
|
-
const projectsResponse = await axios17.get(`${apiUrl}/api/v1/projects`, {
|
|
3744
|
-
params: options.project ? { project_id: options.project } : {},
|
|
3745
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3746
|
-
});
|
|
3747
|
-
if (!projectsResponse.data.success) {
|
|
3748
|
-
throw new Error(projectsResponse.data.error || "Failed to fetch projects");
|
|
3749
|
-
}
|
|
3750
|
-
let projects = projectsResponse.data.data.projects || [];
|
|
3751
|
-
if (projects.length === 0) {
|
|
3752
|
-
spinner.fail(chalk25.red("No projects found."));
|
|
3753
|
-
return;
|
|
3754
|
-
}
|
|
3755
|
-
if (projects.length > 1 && !options.project) {
|
|
3756
|
-
spinner.stop();
|
|
3757
|
-
const inquirer4 = (await import("inquirer")).default;
|
|
3758
|
-
const { selectedProjectId } = await inquirer4.prompt([{
|
|
3759
|
-
type: "list",
|
|
3760
|
-
name: "selectedProjectId",
|
|
3761
|
-
message: "Multiple projects found. Which one do you want to sync?",
|
|
3762
|
-
choices: projects.map((p) => ({
|
|
3763
|
-
name: `${p.name} [${p.id}]`,
|
|
3764
|
-
value: p.id
|
|
3765
|
-
}))
|
|
3766
|
-
}]);
|
|
3767
|
-
projects = projects.filter((p) => p.id === selectedProjectId);
|
|
3768
|
-
options.project = selectedProjectId;
|
|
3769
|
-
try {
|
|
3770
|
-
const fs23 = await import("fs/promises");
|
|
3771
|
-
const path24 = await import("path");
|
|
3772
|
-
const envPath = path24.join(process.cwd(), ".env");
|
|
3773
|
-
const envLocalPath = path24.join(process.cwd(), ".env.local");
|
|
3774
|
-
let targetEnv = envLocalPath;
|
|
3775
|
-
try {
|
|
3776
|
-
await fs23.access(envLocalPath);
|
|
3777
|
-
} catch {
|
|
3778
|
-
try {
|
|
3779
|
-
await fs23.access(envPath);
|
|
3780
|
-
targetEnv = envPath;
|
|
3781
|
-
} catch {
|
|
3782
|
-
targetEnv = envPath;
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3785
|
-
let content = "";
|
|
3786
|
-
try {
|
|
3787
|
-
content = await fs23.readFile(targetEnv, "utf-8");
|
|
3788
|
-
} catch {
|
|
3789
|
-
}
|
|
3790
|
-
if (!content.includes("RIGSTATE_PROJECT_ID")) {
|
|
3791
|
-
const newContent = content.endsWith("\n") || content === "" ? `${content}RIGSTATE_PROJECT_ID=${selectedProjectId}
|
|
3792
|
-
` : `${content}
|
|
3793
|
-
RIGSTATE_PROJECT_ID=${selectedProjectId}
|
|
3794
|
-
`;
|
|
3795
|
-
await fs23.writeFile(targetEnv, newContent, "utf-8");
|
|
3796
|
-
console.log(chalk25.dim(` \u{1F4BE} Saved default project to ${path24.basename(targetEnv)}`));
|
|
3797
|
-
}
|
|
3798
|
-
} catch (e) {
|
|
3799
|
-
}
|
|
3800
|
-
}
|
|
3801
|
-
spinner.succeed(`Syncing project: ${projects[0].name}`);
|
|
3802
|
-
for (const project of projects) {
|
|
3803
|
-
const projectSpinner = ora11(` Syncing: ${project.name}...`).start();
|
|
3804
|
-
try {
|
|
3805
|
-
if (options.dryRun) {
|
|
3806
|
-
projectSpinner.succeed(chalk25.yellow(` [DRY-RUN] Would sync: ${project.name}`));
|
|
3807
|
-
results.push({ projectId: project.id, projectName: project.name, status: "success" });
|
|
3808
|
-
continue;
|
|
3809
|
-
}
|
|
3810
|
-
const syncResponse = await axios17.post(`${apiUrl}/api/v1/rules/sync`, {
|
|
3811
|
-
project_id: project.id
|
|
3812
|
-
}, {
|
|
3813
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
3814
|
-
});
|
|
3815
|
-
if (syncResponse.data.success) {
|
|
3816
|
-
if (syncResponse.data.data.github_synced) {
|
|
3817
|
-
projectSpinner.succeed(chalk25.green(` \u2705 ${project.name} [${project.id}] \u2192 GitHub synced`));
|
|
3818
|
-
} else {
|
|
3819
|
-
projectSpinner.info(chalk25.blue(` \u2139\uFE0F ${project.name} [${project.id}] \u2192 Rules generated (no GitHub)`));
|
|
3820
|
-
}
|
|
3821
|
-
const files = syncResponse.data.data.files;
|
|
3822
|
-
if (files && Array.isArray(files) && (projects.length === 1 || options.project)) {
|
|
3823
|
-
const fs23 = await import("fs/promises");
|
|
3824
|
-
const path24 = await import("path");
|
|
3825
|
-
for (const file of files) {
|
|
3826
|
-
const filePath = path24.join(process.cwd(), file.path);
|
|
3827
|
-
await fs23.mkdir(path24.dirname(filePath), { recursive: true });
|
|
3828
|
-
await fs23.writeFile(filePath, file.content, "utf-8");
|
|
3829
|
-
}
|
|
3830
|
-
console.log(chalk25.dim(` \u{1F4BE} Wrote ${files.length} rule files to local .cursor/rules/`));
|
|
3831
|
-
}
|
|
3832
|
-
results.push({ projectId: project.id, projectName: project.name, status: "success" });
|
|
3833
|
-
} else {
|
|
3834
|
-
projectSpinner.warn(chalk25.yellow(` \u26A0\uFE0F ${project.name} \u2192 ${syncResponse.data.error || "Unknown error"}`));
|
|
3835
|
-
results.push({ projectId: project.id, projectName: project.name, status: "failed", error: syncResponse.data.error });
|
|
3836
|
-
}
|
|
3837
|
-
} catch (e) {
|
|
3838
|
-
projectSpinner.fail(chalk25.red(` \u274C ${project.name}: ${e.message}`));
|
|
3839
|
-
results.push({ projectId: project.id, projectName: project.name, status: "failed", error: e.message });
|
|
3840
|
-
}
|
|
3841
|
-
}
|
|
3842
|
-
console.log("");
|
|
3843
|
-
console.log(chalk25.bold("\u{1F4CA} Sync Summary:"));
|
|
3844
|
-
const successful = results.filter((r) => r.status === "success").length;
|
|
3845
|
-
const failed = results.filter((r) => r.status === "failed").length;
|
|
3846
|
-
console.log(chalk25.green(` \u2705 Successful: ${successful}`));
|
|
3847
|
-
if (failed > 0) {
|
|
3848
|
-
console.log(chalk25.red(` \u274C Failed: ${failed}`));
|
|
3849
|
-
}
|
|
3850
|
-
console.log("");
|
|
3851
|
-
console.log(chalk25.cyan("\u{1F6E1}\uFE0F Frank Protocol v1.0 has been injected into the rules engine."));
|
|
3852
|
-
console.log(chalk25.dim(" All new chats will now boot with mandatory governance checks."));
|
|
3853
|
-
} catch (e) {
|
|
3854
|
-
spinner.fail(chalk25.red("Sync failed: " + e.message));
|
|
3855
|
-
}
|
|
3856
|
-
});
|
|
3857
|
-
return syncRules;
|
|
3858
|
-
}
|
|
4044
|
+
// src/index.ts
|
|
4045
|
+
init_sync_rules();
|
|
3859
4046
|
|
|
3860
4047
|
// src/commands/override.ts
|
|
3861
4048
|
init_esm_shims();
|