runshift 0.0.2 → 0.0.4
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/.claude/settings.local.json +13 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +184 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/context/collector.d.ts +4 -0
- package/dist/context/collector.d.ts.map +1 -0
- package/dist/context/collector.js +191 -0
- package/dist/context/collector.js.map +1 -0
- package/dist/context/git.d.ts +7 -0
- package/dist/context/git.d.ts.map +1 -0
- package/dist/context/git.js +30 -0
- package/dist/context/git.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/display.d.ts +16 -0
- package/dist/ui/display.d.ts.map +1 -0
- package/dist/ui/display.js +166 -0
- package/dist/ui/display.js.map +1 -0
- package/dist/ui/prompt.d.ts +4 -0
- package/dist/ui/prompt.d.ts.map +1 -0
- package/dist/ui/prompt.js +31 -0
- package/dist/ui/prompt.js.map +1 -0
- package/dist/writer.d.ts +4 -0
- package/dist/writer.d.ts.map +1 -0
- package/dist/writer.js +29 -0
- package/dist/writer.js.map +1 -0
- package/package.json +22 -4
- package/src/commands/init.ts +231 -0
- package/src/context/collector.ts +205 -0
- package/src/context/git.ts +36 -0
- package/src/index.ts +39 -0
- package/src/types.ts +44 -0
- package/src/ui/display.ts +184 -0
- package/src/ui/prompt.ts +33 -0
- package/src/writer.ts +37 -0
- package/tsconfig.json +19 -0
- package/index.js +0 -2
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import figlet from "figlet";
|
|
3
|
+
const amber = chalk.hex("#f5a623");
|
|
4
|
+
const muted = chalk.hex("#6b6b7b");
|
|
5
|
+
const dim = chalk.dim;
|
|
6
|
+
const divider = muted(" " + "─".repeat(45));
|
|
7
|
+
export function showBanner() {
|
|
8
|
+
const banner = figlet.textSync("runshift", {
|
|
9
|
+
font: "Standard",
|
|
10
|
+
horizontalLayout: "default",
|
|
11
|
+
});
|
|
12
|
+
console.log(amber(banner));
|
|
13
|
+
console.log(muted(" v0.0.3"));
|
|
14
|
+
console.log(muted(" the control plane for agents, wherever they run."));
|
|
15
|
+
console.log(dim(" usage: npx runshift init [--dry-run] [--branch <name>]\n"));
|
|
16
|
+
console.log(divider + "\n");
|
|
17
|
+
}
|
|
18
|
+
export function showNotGitRepo() {
|
|
19
|
+
console.log(amber(" this directory is not a git repository."));
|
|
20
|
+
console.log(dim(" run runshift init from a project root with git initialized.\n"));
|
|
21
|
+
}
|
|
22
|
+
export function showDirtyWarning() {
|
|
23
|
+
console.log(muted(" ⚠ uncommitted changes detected\n"));
|
|
24
|
+
}
|
|
25
|
+
export function showBranchInfo(branch) {
|
|
26
|
+
console.log(dim(` on branch ${amber(branch)}\n`));
|
|
27
|
+
}
|
|
28
|
+
export function showScanResults(context) {
|
|
29
|
+
console.log(amber(" relay scanned your repository:\n"));
|
|
30
|
+
const deps = {
|
|
31
|
+
...context.packageJson.dependencies,
|
|
32
|
+
...context.packageJson.devDependencies,
|
|
33
|
+
};
|
|
34
|
+
const detections = [];
|
|
35
|
+
if (context.packageJson.name) {
|
|
36
|
+
const stack = [];
|
|
37
|
+
if (deps["next"])
|
|
38
|
+
stack.push("Next.js");
|
|
39
|
+
if (deps["@supabase/supabase-js"] || deps["@supabase/ssr"])
|
|
40
|
+
stack.push("Supabase");
|
|
41
|
+
if (deps["tailwindcss"])
|
|
42
|
+
stack.push("Tailwind");
|
|
43
|
+
if (deps["prisma"] || deps["@prisma/client"])
|
|
44
|
+
stack.push("Prisma");
|
|
45
|
+
if (deps["drizzle-orm"])
|
|
46
|
+
stack.push("Drizzle");
|
|
47
|
+
if (deps["stripe"])
|
|
48
|
+
stack.push("Stripe");
|
|
49
|
+
const label = stack.length > 0 ? stack.join(", ") + " detected" : "detected";
|
|
50
|
+
detections.push(`package.json — ${label}`);
|
|
51
|
+
}
|
|
52
|
+
if (context.envKeys.length > 0) {
|
|
53
|
+
detections.push(`.env.example — ${context.envKeys.length} environment variable${context.envKeys.length === 1 ? "" : "s"} found`);
|
|
54
|
+
}
|
|
55
|
+
if (context.migrationCount > 0) {
|
|
56
|
+
detections.push(`supabase/migrations/ — ${context.migrationCount} migration file${context.migrationCount === 1 ? "" : "s"} found`);
|
|
57
|
+
}
|
|
58
|
+
const existingRuleKeys = Object.keys(context.existingRules);
|
|
59
|
+
const cursorRules = existingRuleKeys.filter((k) => k.startsWith(".cursor/rules/"));
|
|
60
|
+
if (cursorRules.length > 0) {
|
|
61
|
+
detections.push(`.cursor/rules/ — ${cursorRules.length} existing file${cursorRules.length === 1 ? "" : "s"} detected`);
|
|
62
|
+
}
|
|
63
|
+
if (existingRuleKeys.includes("CLAUDE.md")) {
|
|
64
|
+
detections.push("existing CLAUDE.md detected");
|
|
65
|
+
}
|
|
66
|
+
if (context.tsconfig) {
|
|
67
|
+
detections.push("tsconfig.json detected");
|
|
68
|
+
}
|
|
69
|
+
const configCount = Object.keys(context.configFiles).length;
|
|
70
|
+
if (configCount > 0) {
|
|
71
|
+
detections.push(`${configCount} config file${configCount === 1 ? "" : "s"} found`);
|
|
72
|
+
}
|
|
73
|
+
for (const d of detections) {
|
|
74
|
+
console.log(dim(" ✓ ") + d);
|
|
75
|
+
}
|
|
76
|
+
console.log();
|
|
77
|
+
}
|
|
78
|
+
export function showFindings(findings) {
|
|
79
|
+
const sections = [
|
|
80
|
+
["blast radius", findings.blastRadius],
|
|
81
|
+
["security gaps", findings.securityGaps],
|
|
82
|
+
["agent failure patterns", findings.agentFailurePatterns],
|
|
83
|
+
["parallelization boundaries", findings.parallelizationBoundaries],
|
|
84
|
+
["deprecated patterns", findings.deprecatedPatterns],
|
|
85
|
+
];
|
|
86
|
+
const hasFindings = sections.some(([, items]) => items.length > 0);
|
|
87
|
+
if (!hasFindings)
|
|
88
|
+
return;
|
|
89
|
+
console.log(amber(" relay found issues in your codebase:\n"));
|
|
90
|
+
for (const [title, items] of sections) {
|
|
91
|
+
if (items.length === 0)
|
|
92
|
+
continue;
|
|
93
|
+
console.log(amber(` ${title}`));
|
|
94
|
+
for (const item of items) {
|
|
95
|
+
console.log(dim(` → ${item}`));
|
|
96
|
+
}
|
|
97
|
+
console.log();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function showFileList(files) {
|
|
101
|
+
console.log(amber(` relay will write ${files.length} file${files.length === 1 ? "" : "s"}:\n`));
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
const prefix = file.action === "create" ? amber("+") : amber("~");
|
|
104
|
+
const action = file.action === "create" ? dim("(create)") : dim("(update — existing file)");
|
|
105
|
+
console.log(` ${prefix} ${file.path} ${action}`);
|
|
106
|
+
}
|
|
107
|
+
console.log(dim("\n + = create, ~ = update existing file\n"));
|
|
108
|
+
}
|
|
109
|
+
export function showWriting(filePath) {
|
|
110
|
+
console.log(dim(" ✓ ") + filePath);
|
|
111
|
+
}
|
|
112
|
+
export function showCommit() {
|
|
113
|
+
console.log(dim(" ✓ ") + "committed");
|
|
114
|
+
}
|
|
115
|
+
export function showSummary(summary) {
|
|
116
|
+
console.log(muted(` ${summary.replace(/\n/g, "\n ")}\n`));
|
|
117
|
+
}
|
|
118
|
+
export function showSuccess() {
|
|
119
|
+
console.log("\n" + divider + "\n");
|
|
120
|
+
console.log(amber(" ✓ relay is installed in your development workflow\n"));
|
|
121
|
+
console.log(muted(" next steps:"));
|
|
122
|
+
console.log(dim(" → open Claude Code and type /validate to run your first check"));
|
|
123
|
+
console.log(dim(" → type /runshift-update to refresh rules as your stack evolves\n"));
|
|
124
|
+
console.log(muted(" connect to the runshift control plane: ") + amber("runshift.ai"));
|
|
125
|
+
console.log("\n" + divider + "\n");
|
|
126
|
+
}
|
|
127
|
+
export function showDataPolicy() {
|
|
128
|
+
console.log(amber(" relay will send to runshift.ai:\n"));
|
|
129
|
+
console.log(dim(" ✓ package.json (dependencies and scripts only)"));
|
|
130
|
+
console.log(dim(" ✓ directory structure (top 2 levels, folder names only)"));
|
|
131
|
+
console.log(dim(" ✓ .env.example (key names only — values are never read)"));
|
|
132
|
+
console.log(dim(" ✓ existing CLAUDE.md (if present)"));
|
|
133
|
+
console.log(dim(" ✓ existing .cursor/rules/ (if present)"));
|
|
134
|
+
console.log(dim(" ✓ migration file names (no file contents)"));
|
|
135
|
+
console.log(dim("\n no source code is sent."));
|
|
136
|
+
console.log(dim(" no secret values are ever read.\n"));
|
|
137
|
+
}
|
|
138
|
+
export function showDryRunComplete() {
|
|
139
|
+
console.log("\n" + divider + "\n");
|
|
140
|
+
console.log(amber(" dry run complete — no files written."));
|
|
141
|
+
console.log(dim(" run npx runshift init to install.\n"));
|
|
142
|
+
console.log(divider + "\n");
|
|
143
|
+
}
|
|
144
|
+
export function showError(type, message) {
|
|
145
|
+
console.log();
|
|
146
|
+
switch (type) {
|
|
147
|
+
case "network":
|
|
148
|
+
console.log(amber(" could not reach relay."));
|
|
149
|
+
console.log(dim(" check your connection and try again.\n"));
|
|
150
|
+
break;
|
|
151
|
+
case "rate-limit":
|
|
152
|
+
console.log(amber(" relay rate limit reached — try again in 1 hour.\n"));
|
|
153
|
+
break;
|
|
154
|
+
case "validation":
|
|
155
|
+
console.log(amber(" relay could not read this repository."));
|
|
156
|
+
if (message)
|
|
157
|
+
console.log(dim(` ${message}\n`));
|
|
158
|
+
break;
|
|
159
|
+
case "server":
|
|
160
|
+
console.log(amber(" relay encountered an error — try again or visit runshift.ai."));
|
|
161
|
+
if (message)
|
|
162
|
+
console.log(dim(` ${message}\n`));
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/ui/display.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/ui/prompt.ts"],"names":[],"mappings":"AAgBA,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAIhE;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAM7E;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as readline from "node:readline";
|
|
2
|
+
function ask(question) {
|
|
3
|
+
const rl = readline.createInterface({
|
|
4
|
+
input: process.stdin,
|
|
5
|
+
output: process.stdout,
|
|
6
|
+
});
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
rl.question(question, (answer) => {
|
|
9
|
+
rl.close();
|
|
10
|
+
resolve(answer.trim());
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export async function confirm(question) {
|
|
15
|
+
const answer = await ask(question);
|
|
16
|
+
const normalized = answer.toLowerCase();
|
|
17
|
+
return normalized === "y" || normalized === "yes";
|
|
18
|
+
}
|
|
19
|
+
export async function promptChoice(question) {
|
|
20
|
+
const answer = await ask(question);
|
|
21
|
+
const normalized = answer.toLowerCase();
|
|
22
|
+
if (normalized === "a" || normalized === "add")
|
|
23
|
+
return "a";
|
|
24
|
+
if (normalized === "y" || normalized === "yes")
|
|
25
|
+
return "y";
|
|
26
|
+
return "n";
|
|
27
|
+
}
|
|
28
|
+
export async function promptFilePath(question) {
|
|
29
|
+
return ask(question);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/ui/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C,SAAS,GAAG,CAAC,QAAgB;IAC3B,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,QAAgB;IAC5C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACxC,OAAO,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,KAAK,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC;IAC3D,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC;IAC3D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB;IACnD,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;AACvB,CAAC"}
|
package/dist/writer.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../src/writer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,IAAI,CASrE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,OAAO,CAmBzE"}
|
package/dist/writer.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { showWriting, showCommit } from "./ui/display.js";
|
|
5
|
+
export function writeFiles(root, files) {
|
|
6
|
+
for (const file of files) {
|
|
7
|
+
const fullPath = path.join(root, file.path);
|
|
8
|
+
const dir = path.dirname(fullPath);
|
|
9
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
10
|
+
fs.writeFileSync(fullPath, file.content, "utf-8");
|
|
11
|
+
showWriting(file.path);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function commitFiles(root, files) {
|
|
15
|
+
try {
|
|
16
|
+
const filePaths = files.map((f) => `"${f.path}"`).join(" ");
|
|
17
|
+
execSync(`git add ${filePaths}`, {
|
|
18
|
+
cwd: root,
|
|
19
|
+
stdio: "pipe",
|
|
20
|
+
});
|
|
21
|
+
execSync('git commit -m "chore: install runshift agent governance rules"', { cwd: root, stdio: "pipe" });
|
|
22
|
+
showCommit();
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../src/writer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE1D,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAsB;IAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAEnC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAClD,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAsB;IAC9D,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE5D,QAAQ,CAAC,WAAW,SAAS,EAAE,EAAE;YAC/B,GAAG,EAAE,IAAI;YACT,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;QAEH,QAAQ,CACN,gEAAgE,EAChE,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAC7B,CAAC;QAEF,UAAU,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runshift",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "The control plane for agents, wherever they run.",
|
|
5
|
-
"
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
6
7
|
"bin": {
|
|
7
|
-
"runshift": "index.js"
|
|
8
|
+
"runshift": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
8
13
|
},
|
|
9
14
|
"keywords": [
|
|
10
15
|
"ai",
|
|
@@ -14,5 +19,18 @@
|
|
|
14
19
|
"governance"
|
|
15
20
|
],
|
|
16
21
|
"author": "Devin Crane",
|
|
17
|
-
"license": "MIT"
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"chalk": "^5.4.1",
|
|
25
|
+
"figlet": "^1.8.0",
|
|
26
|
+
"ora": "^8.2.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/figlet": "^1.7.0",
|
|
30
|
+
"@types/node": "^22.15.0",
|
|
31
|
+
"typescript": "^5.8.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
}
|
|
18
36
|
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { collectRepoContext, addFileToContext } from "../context/collector.js";
|
|
4
|
+
import { getGitState } from "../context/git.js";
|
|
5
|
+
import {
|
|
6
|
+
showBanner,
|
|
7
|
+
showNotGitRepo,
|
|
8
|
+
showDirtyWarning,
|
|
9
|
+
showBranchInfo,
|
|
10
|
+
showScanResults,
|
|
11
|
+
showDataPolicy,
|
|
12
|
+
showFindings,
|
|
13
|
+
showFileList,
|
|
14
|
+
showSummary,
|
|
15
|
+
showSuccess,
|
|
16
|
+
showDryRunComplete,
|
|
17
|
+
showError,
|
|
18
|
+
} from "../ui/display.js";
|
|
19
|
+
import { confirm, promptChoice, promptFilePath } from "../ui/prompt.js";
|
|
20
|
+
import { writeFiles, commitFiles } from "../writer.js";
|
|
21
|
+
import type { InitResponse } from "../types.js";
|
|
22
|
+
|
|
23
|
+
const API_URL =
|
|
24
|
+
process.env.RUNSHIFT_DEV === "true"
|
|
25
|
+
? "http://localhost:3000/api/cli/init"
|
|
26
|
+
: "https://runshift.ai/api/cli/init";
|
|
27
|
+
|
|
28
|
+
const TIMEOUT_MS = 240_000;
|
|
29
|
+
|
|
30
|
+
interface InitFlags {
|
|
31
|
+
dryRun: boolean;
|
|
32
|
+
branch: string | null;
|
|
33
|
+
help: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseFlags(args: string[]): InitFlags {
|
|
37
|
+
const flags: InitFlags = { dryRun: false, branch: null, help: false };
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < args.length; i++) {
|
|
40
|
+
const arg = args[i];
|
|
41
|
+
if (arg === "--dry-run") {
|
|
42
|
+
flags.dryRun = true;
|
|
43
|
+
} else if (arg === "--branch") {
|
|
44
|
+
const next = args[i + 1];
|
|
45
|
+
flags.branch = next && !next.startsWith("--") ? next : "relay-init";
|
|
46
|
+
if (next && !next.startsWith("--")) i++;
|
|
47
|
+
} else if (arg === "--help") {
|
|
48
|
+
flags.help = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return flags;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function showInitHelp(): void {
|
|
56
|
+
console.log(`
|
|
57
|
+
npx runshift init [options]
|
|
58
|
+
|
|
59
|
+
options:
|
|
60
|
+
--dry-run preview changes without writing files
|
|
61
|
+
--branch <name> run on a new branch (default: relay-init)
|
|
62
|
+
--help show this help
|
|
63
|
+
`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function init(args: string[] = []): Promise<void> {
|
|
67
|
+
const flags = parseFlags(args);
|
|
68
|
+
|
|
69
|
+
if (flags.help) {
|
|
70
|
+
showInitHelp();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
showBanner();
|
|
75
|
+
|
|
76
|
+
// ── 1. Git safety ─────────────────────────────────────────────────
|
|
77
|
+
const git = getGitState();
|
|
78
|
+
|
|
79
|
+
if (!git.isGitRepo) {
|
|
80
|
+
showNotGitRepo();
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
showBranchInfo(git.branch);
|
|
85
|
+
|
|
86
|
+
if (git.isDirty) {
|
|
87
|
+
showDirtyWarning();
|
|
88
|
+
const proceed = await confirm(" continue with uncommitted changes? (y/n) ");
|
|
89
|
+
if (!proceed) {
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── 1b. Branch flag — create and switch ───────────────────────────
|
|
95
|
+
if (flags.branch) {
|
|
96
|
+
try {
|
|
97
|
+
execSync(`git rev-parse --verify ${flags.branch}`, { stdio: "pipe" });
|
|
98
|
+
console.log(` branch ${flags.branch} already exists.\n`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
} catch {
|
|
101
|
+
// branch doesn't exist — good
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
execSync(`git checkout -b ${flags.branch}`, { stdio: "pipe" });
|
|
105
|
+
console.log(` switched to new branch ${flags.branch}\n`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── 2. Collect context ────────────────────────────────────────────
|
|
109
|
+
const root = process.cwd();
|
|
110
|
+
const context = collectRepoContext(root);
|
|
111
|
+
|
|
112
|
+
// ── 3. Show scan results + data policy + prompt ───────────────────
|
|
113
|
+
showScanResults(context);
|
|
114
|
+
showDataPolicy();
|
|
115
|
+
|
|
116
|
+
let choice = await promptChoice(" proceed? [y] add more files? [a] cancel? [n] ");
|
|
117
|
+
|
|
118
|
+
while (choice === "a") {
|
|
119
|
+
const filePath = await promptFilePath(" file path: ");
|
|
120
|
+
if (filePath) {
|
|
121
|
+
const added = addFileToContext(root, filePath, context);
|
|
122
|
+
if (!added) {
|
|
123
|
+
console.log(` could not read ${filePath}\n`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
showScanResults(context);
|
|
127
|
+
showDataPolicy();
|
|
128
|
+
choice = await promptChoice(" proceed? [y] add more files? [a] cancel? [n] ");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (choice === "n") {
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── 4. Call API ───────────────────────────────────────────────────
|
|
136
|
+
const spinner = ora({
|
|
137
|
+
text: "relay is reading your repository...",
|
|
138
|
+
color: "yellow",
|
|
139
|
+
}).start();
|
|
140
|
+
|
|
141
|
+
console.log("calling:", API_URL);
|
|
142
|
+
|
|
143
|
+
let response: Response;
|
|
144
|
+
try {
|
|
145
|
+
const controller = new AbortController();
|
|
146
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
147
|
+
|
|
148
|
+
response = await fetch(API_URL, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: { "Content-Type": "application/json" },
|
|
151
|
+
body: JSON.stringify(context),
|
|
152
|
+
signal: controller.signal,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
clearTimeout(timeout);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
spinner.stop();
|
|
158
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
159
|
+
showError("network", "request timed out after 240s");
|
|
160
|
+
} else {
|
|
161
|
+
showError("network");
|
|
162
|
+
}
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
spinner.stop();
|
|
168
|
+
const body = (await response.json().catch(() => ({}))) as Record<string, unknown>;
|
|
169
|
+
const msg = (body.message ?? body.error) as string | undefined;
|
|
170
|
+
|
|
171
|
+
if (response.status === 429) {
|
|
172
|
+
showError("rate-limit");
|
|
173
|
+
} else if (response.status === 400) {
|
|
174
|
+
showError("validation", msg);
|
|
175
|
+
} else {
|
|
176
|
+
showError("server", msg);
|
|
177
|
+
}
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let data: InitResponse;
|
|
182
|
+
try {
|
|
183
|
+
data = (await response.json()) as InitResponse;
|
|
184
|
+
} catch {
|
|
185
|
+
spinner.stop();
|
|
186
|
+
showError("server", "invalid response from relay");
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
spinner.stop();
|
|
191
|
+
console.log();
|
|
192
|
+
|
|
193
|
+
// ── 5. Show findings + file list ──────────────────────────────────
|
|
194
|
+
showSummary(data.summary);
|
|
195
|
+
showFindings(data.findings);
|
|
196
|
+
showFileList(data.files);
|
|
197
|
+
|
|
198
|
+
// ── 6. Dry run exit ───────────────────────────────────────────────
|
|
199
|
+
if (flags.dryRun) {
|
|
200
|
+
showDryRunComplete();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ── 7. Confirm write ──────────────────────────────────────────────
|
|
205
|
+
const writeConfirm = await confirm(" write these files? (y/n) ");
|
|
206
|
+
if (!writeConfirm) {
|
|
207
|
+
process.exit(0);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ── 8. Re-check git before writing ────────────────────────────────
|
|
211
|
+
const gitNow = getGitState();
|
|
212
|
+
if (gitNow.isDirty && !git.isDirty) {
|
|
213
|
+
const proceed = await confirm(" working tree changed since scan — continue? (y/n) ");
|
|
214
|
+
if (!proceed) {
|
|
215
|
+
process.exit(0);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ── 9. Write + commit ─────────────────────────────────────────────
|
|
220
|
+
console.log();
|
|
221
|
+
writeFiles(root, data.files);
|
|
222
|
+
console.log();
|
|
223
|
+
|
|
224
|
+
const committed = commitFiles(root, data.files);
|
|
225
|
+
if (!committed) {
|
|
226
|
+
console.log(" ⚠ files written but git commit failed\n");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ── 10. Success ───────────────────────────────────────────────────
|
|
230
|
+
showSuccess();
|
|
231
|
+
}
|