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