runshift 0.0.3 → 0.0.5
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 +4 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +149 -16
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/remove.d.ts +2 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +49 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/context/collector.d.ts.map +1 -1
- package/dist/context/collector.js +33 -0
- package/dist/context/collector.js.map +1 -1
- package/dist/index.js +17 -5
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/display.d.ts +5 -0
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +56 -4
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/prompt.d.ts +2 -0
- package/dist/ui/prompt.d.ts.map +1 -1
- package/dist/ui/prompt.js +37 -0
- package/dist/ui/prompt.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/init.ts +180 -18
- package/src/commands/remove.ts +59 -0
- package/src/context/collector.ts +44 -0
- package/src/index.ts +17 -5
- package/src/types.ts +2 -0
- package/src/ui/display.ts +59 -4
- package/src/ui/prompt.ts +42 -0
package/dist/ui/display.js
CHANGED
|
@@ -10,8 +10,9 @@ export function showBanner() {
|
|
|
10
10
|
horizontalLayout: "default",
|
|
11
11
|
});
|
|
12
12
|
console.log(amber(banner));
|
|
13
|
-
console.log(muted(" v0.0.
|
|
14
|
-
console.log(muted(" the control plane for agents, wherever they run
|
|
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"));
|
|
15
16
|
console.log(divider + "\n");
|
|
16
17
|
}
|
|
17
18
|
export function showNotGitRepo() {
|
|
@@ -55,12 +56,16 @@ export function showScanResults(context) {
|
|
|
55
56
|
detections.push(`supabase/migrations/ — ${context.migrationCount} migration file${context.migrationCount === 1 ? "" : "s"} found`);
|
|
56
57
|
}
|
|
57
58
|
const existingRuleKeys = Object.keys(context.existingRules);
|
|
59
|
+
const protectedSet = new Set(context.protectedPaths);
|
|
58
60
|
const cursorRules = existingRuleKeys.filter((k) => k.startsWith(".cursor/rules/"));
|
|
59
61
|
if (cursorRules.length > 0) {
|
|
60
|
-
|
|
62
|
+
const protectedCount = cursorRules.filter((k) => protectedSet.has(k)).length;
|
|
63
|
+
const suffix = protectedCount > 0 ? ` (${protectedCount} protected)` : "";
|
|
64
|
+
detections.push(`.cursor/rules/ — ${cursorRules.length} existing file${cursorRules.length === 1 ? "" : "s"} detected${suffix}`);
|
|
61
65
|
}
|
|
62
66
|
if (existingRuleKeys.includes("CLAUDE.md")) {
|
|
63
|
-
|
|
67
|
+
const claudeProtected = protectedSet.has("CLAUDE.md") ? " (protected — human-written)" : "";
|
|
68
|
+
detections.push(`existing CLAUDE.md detected${claudeProtected}`);
|
|
64
69
|
}
|
|
65
70
|
if (context.tsconfig) {
|
|
66
71
|
detections.push("tsconfig.json detected");
|
|
@@ -105,12 +110,35 @@ export function showFileList(files) {
|
|
|
105
110
|
}
|
|
106
111
|
console.log(dim("\n + = create, ~ = update existing file\n"));
|
|
107
112
|
}
|
|
113
|
+
export function showSelectedFiles(allFiles, selectedPaths) {
|
|
114
|
+
const selected = new Set(selectedPaths);
|
|
115
|
+
console.log(amber(" relay understood:\n"));
|
|
116
|
+
for (const file of allFiles) {
|
|
117
|
+
if (selected.has(file.path)) {
|
|
118
|
+
console.log(dim(" ✓ ") + file.path + dim(" (included)"));
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
console.log(dim(" ✗ ") + muted(file.path) + dim(" (excluded)"));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
console.log();
|
|
125
|
+
}
|
|
108
126
|
export function showWriting(filePath) {
|
|
109
127
|
console.log(dim(" ✓ ") + filePath);
|
|
110
128
|
}
|
|
111
129
|
export function showCommit() {
|
|
112
130
|
console.log(dim(" ✓ ") + "committed");
|
|
113
131
|
}
|
|
132
|
+
export function showProtectedFiles(protectedPaths) {
|
|
133
|
+
if (protectedPaths.length === 0)
|
|
134
|
+
return;
|
|
135
|
+
console.log();
|
|
136
|
+
console.log(amber(" relay preserved your existing files:"));
|
|
137
|
+
for (const p of protectedPaths) {
|
|
138
|
+
console.log(dim(` ~ ${p} — human-written, not overwritten`));
|
|
139
|
+
}
|
|
140
|
+
console.log(dim(" run npx runshift update to review suggested changes.\n"));
|
|
141
|
+
}
|
|
114
142
|
export function showSummary(summary) {
|
|
115
143
|
console.log(muted(` ${summary.replace(/\n/g, "\n ")}\n`));
|
|
116
144
|
}
|
|
@@ -123,6 +151,30 @@ export function showSuccess() {
|
|
|
123
151
|
console.log(muted(" connect to the runshift control plane: ") + amber("runshift.ai"));
|
|
124
152
|
console.log("\n" + divider + "\n");
|
|
125
153
|
}
|
|
154
|
+
export function showDataPolicy() {
|
|
155
|
+
console.log(amber(" relay will send to runshift.ai:\n"));
|
|
156
|
+
console.log(dim(" ✓ package.json (dependencies and scripts only)"));
|
|
157
|
+
console.log(dim(" ✓ directory structure (top 2 levels, folder names only)"));
|
|
158
|
+
console.log(dim(" ✓ .env.example (key names only — values are never read)"));
|
|
159
|
+
console.log(dim(" ✓ existing CLAUDE.md (if present)"));
|
|
160
|
+
console.log(dim(" ✓ existing .cursor/rules/ (if present)"));
|
|
161
|
+
console.log(dim(" ✓ migration file names (no file contents)"));
|
|
162
|
+
console.log(dim("\n no source code is sent."));
|
|
163
|
+
console.log(dim(" no secret values are ever read.\n"));
|
|
164
|
+
}
|
|
165
|
+
export function showDryRunComplete() {
|
|
166
|
+
console.log("\n" + divider + "\n");
|
|
167
|
+
console.log(amber(" dry run complete — no files written."));
|
|
168
|
+
console.log(dim(" run npx runshift init to install.\n"));
|
|
169
|
+
console.log(divider + "\n");
|
|
170
|
+
}
|
|
171
|
+
export function showCancelled() {
|
|
172
|
+
console.log();
|
|
173
|
+
console.log(amber(" no changes made."));
|
|
174
|
+
console.log(dim(" run npx runshift init when you're ready."));
|
|
175
|
+
console.log();
|
|
176
|
+
console.log(divider + "\n");
|
|
177
|
+
}
|
|
126
178
|
export function showError(type, message) {
|
|
127
179
|
console.log();
|
|
128
180
|
switch (type) {
|
package/dist/ui/display.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/ui/display.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACnC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACnC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;AACtB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AAE7C,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE;QACzC,IAAI,EAAE,UAAU;QAChB,gBAAgB,EAAE,SAAS;KAC5B,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/ui/display.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/prompt.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export declare function confirm(question: string): Promise<boolean>;
|
|
2
2
|
export declare function promptChoice(question: string): Promise<"y" | "a" | "n">;
|
|
3
3
|
export declare function promptFilePath(question: string): Promise<string>;
|
|
4
|
+
export declare function promptFileSelection(question: string): Promise<"a" | "s" | "n">;
|
|
5
|
+
export declare function promptPreview(message: string): Promise<boolean>;
|
|
4
6
|
//# sourceMappingURL=prompt.d.ts.map
|
package/dist/ui/prompt.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
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;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAMpF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgC/D"}
|
package/dist/ui/prompt.js
CHANGED
|
@@ -28,4 +28,41 @@ export async function promptChoice(question) {
|
|
|
28
28
|
export async function promptFilePath(question) {
|
|
29
29
|
return ask(question);
|
|
30
30
|
}
|
|
31
|
+
export async function promptFileSelection(question) {
|
|
32
|
+
const answer = await ask(question);
|
|
33
|
+
const normalized = answer.toLowerCase();
|
|
34
|
+
if (normalized === "a" || normalized === "accept")
|
|
35
|
+
return "a";
|
|
36
|
+
if (normalized === "s" || normalized === "select")
|
|
37
|
+
return "s";
|
|
38
|
+
return "n";
|
|
39
|
+
}
|
|
40
|
+
export function promptPreview(message) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
process.stdout.write(message);
|
|
43
|
+
const wasRaw = process.stdin.isRaw;
|
|
44
|
+
process.stdin.setRawMode(true);
|
|
45
|
+
process.stdin.resume();
|
|
46
|
+
const onData = (key) => {
|
|
47
|
+
process.stdin.setRawMode(wasRaw ?? false);
|
|
48
|
+
process.stdin.pause();
|
|
49
|
+
process.stdin.removeListener("data", onData);
|
|
50
|
+
const ch = key.toString();
|
|
51
|
+
// ctrl-c
|
|
52
|
+
if (ch === "\x03") {
|
|
53
|
+
console.log();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
if (ch.toLowerCase() === "o") {
|
|
57
|
+
console.log("o\n");
|
|
58
|
+
resolve(true);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.log("\n");
|
|
62
|
+
resolve(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
process.stdin.on("data", onData);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
31
68
|
//# sourceMappingURL=prompt.js.map
|
package/dist/ui/prompt.js.map
CHANGED
|
@@ -1 +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"}
|
|
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;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACxD,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,QAAQ;QAAE,OAAO,GAAG,CAAC;IAC9D,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAC9D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAE9B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QACnC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAEvB,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,EAAE;YAC7B,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;YAC1C,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAE7C,MAAM,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;YAE1B,SAAS;YACT,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;gBAClB,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,EAAE,CAAC;gBAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACnB,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC;QAEF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ora from "ora";
|
|
2
|
+
import { exec, execSync } from "node:child_process";
|
|
2
3
|
import { collectRepoContext, addFileToContext } from "../context/collector.js";
|
|
3
4
|
import { getGitState } from "../context/git.js";
|
|
4
5
|
import {
|
|
@@ -7,24 +8,77 @@ import {
|
|
|
7
8
|
showDirtyWarning,
|
|
8
9
|
showBranchInfo,
|
|
9
10
|
showScanResults,
|
|
11
|
+
showDataPolicy,
|
|
10
12
|
showFindings,
|
|
11
13
|
showFileList,
|
|
14
|
+
showSelectedFiles,
|
|
15
|
+
showProtectedFiles,
|
|
12
16
|
showSummary,
|
|
13
17
|
showSuccess,
|
|
18
|
+
showDryRunComplete,
|
|
19
|
+
showCancelled,
|
|
14
20
|
showError,
|
|
15
21
|
} from "../ui/display.js";
|
|
16
|
-
import { confirm, promptChoice, promptFilePath } from "../ui/prompt.js";
|
|
22
|
+
import { confirm, promptChoice, promptFilePath, promptFileSelection, promptPreview } from "../ui/prompt.js";
|
|
17
23
|
import { writeFiles, commitFiles } from "../writer.js";
|
|
18
24
|
import type { InitResponse } from "../types.js";
|
|
19
25
|
|
|
20
|
-
const
|
|
21
|
-
process.env.RUNSHIFT_DEV === "true"
|
|
22
|
-
? "http://localhost:3000/api/cli/init"
|
|
23
|
-
: "https://runshift.ai/api/cli/init";
|
|
26
|
+
const IS_DEV = process.env.RUNSHIFT_DEV === "true";
|
|
24
27
|
|
|
25
|
-
const
|
|
28
|
+
const API_URL = IS_DEV
|
|
29
|
+
? "http://localhost:3000/api/cli/init"
|
|
30
|
+
: "https://runshift.ai/api/cli/init";
|
|
31
|
+
|
|
32
|
+
const BASE_URL = IS_DEV
|
|
33
|
+
? "http://localhost:3000"
|
|
34
|
+
: "https://runshift.ai";
|
|
35
|
+
|
|
36
|
+
const TIMEOUT_MS = 180_000;
|
|
37
|
+
|
|
38
|
+
interface InitFlags {
|
|
39
|
+
dryRun: boolean;
|
|
40
|
+
branch: string | null;
|
|
41
|
+
help: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseFlags(args: string[]): InitFlags {
|
|
45
|
+
const flags: InitFlags = { dryRun: false, branch: null, help: false };
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < args.length; i++) {
|
|
48
|
+
const arg = args[i];
|
|
49
|
+
if (arg === "--dry-run") {
|
|
50
|
+
flags.dryRun = true;
|
|
51
|
+
} else if (arg === "--branch") {
|
|
52
|
+
const next = args[i + 1];
|
|
53
|
+
flags.branch = next && !next.startsWith("--") ? next : "relay-init";
|
|
54
|
+
if (next && !next.startsWith("--")) i++;
|
|
55
|
+
} else if (arg === "--help") {
|
|
56
|
+
flags.help = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return flags;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function showInitHelp(): void {
|
|
64
|
+
console.log(`
|
|
65
|
+
npx runshift init [options]
|
|
66
|
+
|
|
67
|
+
options:
|
|
68
|
+
--dry-run preview changes without writing files
|
|
69
|
+
--branch <name> run on a new branch (default: relay-init)
|
|
70
|
+
--help show this help
|
|
71
|
+
`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function init(args: string[] = []): Promise<void> {
|
|
75
|
+
const flags = parseFlags(args);
|
|
76
|
+
|
|
77
|
+
if (flags.help) {
|
|
78
|
+
showInitHelp();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
26
81
|
|
|
27
|
-
export async function init(): Promise<void> {
|
|
28
82
|
showBanner();
|
|
29
83
|
|
|
30
84
|
// ── 1. Git safety ─────────────────────────────────────────────────
|
|
@@ -41,16 +95,33 @@ export async function init(): Promise<void> {
|
|
|
41
95
|
showDirtyWarning();
|
|
42
96
|
const proceed = await confirm(" continue with uncommitted changes? (y/n) ");
|
|
43
97
|
if (!proceed) {
|
|
98
|
+
showCancelled();
|
|
44
99
|
process.exit(0);
|
|
45
100
|
}
|
|
46
101
|
}
|
|
47
102
|
|
|
103
|
+
// ── 1b. Branch flag — create and switch ───────────────────────────
|
|
104
|
+
if (flags.branch) {
|
|
105
|
+
try {
|
|
106
|
+
execSync(`git rev-parse --verify ${flags.branch}`, { stdio: "pipe" });
|
|
107
|
+
console.log(` branch ${flags.branch} already exists.\n`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
} catch {
|
|
110
|
+
// branch doesn't exist — good
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
execSync(`git checkout -b ${flags.branch}`, { stdio: "pipe" });
|
|
114
|
+
console.log(` switched to new branch ${flags.branch}\n`);
|
|
115
|
+
}
|
|
116
|
+
|
|
48
117
|
// ── 2. Collect context ────────────────────────────────────────────
|
|
49
118
|
const root = process.cwd();
|
|
50
119
|
const context = collectRepoContext(root);
|
|
51
120
|
|
|
52
|
-
// ── 3. Show scan results + prompt
|
|
121
|
+
// ── 3. Show scan results + protected files + data policy + prompt ─
|
|
53
122
|
showScanResults(context);
|
|
123
|
+
showProtectedFiles(context.protectedPaths);
|
|
124
|
+
showDataPolicy();
|
|
54
125
|
|
|
55
126
|
let choice = await promptChoice(" proceed? [y] add more files? [a] cancel? [n] ");
|
|
56
127
|
|
|
@@ -63,10 +134,12 @@ export async function init(): Promise<void> {
|
|
|
63
134
|
}
|
|
64
135
|
}
|
|
65
136
|
showScanResults(context);
|
|
137
|
+
showDataPolicy();
|
|
66
138
|
choice = await promptChoice(" proceed? [y] add more files? [a] cancel? [n] ");
|
|
67
139
|
}
|
|
68
140
|
|
|
69
141
|
if (choice === "n") {
|
|
142
|
+
showCancelled();
|
|
70
143
|
process.exit(0);
|
|
71
144
|
}
|
|
72
145
|
|
|
@@ -76,7 +149,16 @@ export async function init(): Promise<void> {
|
|
|
76
149
|
color: "yellow",
|
|
77
150
|
}).start();
|
|
78
151
|
|
|
79
|
-
|
|
152
|
+
const spinnerMessages = [
|
|
153
|
+
{ delay: 8000, text: "relay is analyzing your stack..." },
|
|
154
|
+
{ delay: 20000, text: "generating governance rules..." },
|
|
155
|
+
{ delay: 45000, text: "reviewing with second model..." },
|
|
156
|
+
{ delay: 70000, text: "synthesizing final rules..." },
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const spinnerTimeouts = spinnerMessages.map(({ delay, text }) =>
|
|
160
|
+
setTimeout(() => { spinner.text = text; }, delay),
|
|
161
|
+
);
|
|
80
162
|
|
|
81
163
|
let response: Response;
|
|
82
164
|
try {
|
|
@@ -91,10 +173,12 @@ export async function init(): Promise<void> {
|
|
|
91
173
|
});
|
|
92
174
|
|
|
93
175
|
clearTimeout(timeout);
|
|
176
|
+
spinnerTimeouts.forEach((t) => clearTimeout(t));
|
|
94
177
|
} catch (err) {
|
|
178
|
+
spinnerTimeouts.forEach((t) => clearTimeout(t));
|
|
95
179
|
spinner.stop();
|
|
96
180
|
if (err instanceof Error && err.name === "AbortError") {
|
|
97
|
-
showError("network", "request timed out after
|
|
181
|
+
showError("network", "request timed out after 180s");
|
|
98
182
|
} else {
|
|
99
183
|
showError("network");
|
|
100
184
|
}
|
|
@@ -133,31 +217,109 @@ export async function init(): Promise<void> {
|
|
|
133
217
|
showFindings(data.findings);
|
|
134
218
|
showFileList(data.files);
|
|
135
219
|
|
|
136
|
-
// ── 6.
|
|
137
|
-
|
|
138
|
-
|
|
220
|
+
// ── 6. Preview link ───────────────────────────────────────────────
|
|
221
|
+
if (data.previewId) {
|
|
222
|
+
const previewUrl = `${BASE_URL}/preview/${data.previewId}`;
|
|
223
|
+
const open = await promptPreview(` preview ready — ${previewUrl}\n\n [o] open in browser [enter] continue `);
|
|
224
|
+
if (open) {
|
|
225
|
+
exec(`open ${previewUrl}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ── 7. Dry run exit ───────────────────────────────────────────────
|
|
230
|
+
if (flags.dryRun) {
|
|
231
|
+
showDryRunComplete();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── 8. File selection ────────────────────────────────────────────
|
|
236
|
+
const fileChoice = await promptFileSelection(" [a] accept all [s] select files [n] don't change anything ");
|
|
237
|
+
|
|
238
|
+
if (fileChoice === "n") {
|
|
239
|
+
showCancelled();
|
|
139
240
|
process.exit(0);
|
|
140
241
|
}
|
|
141
242
|
|
|
142
|
-
|
|
243
|
+
let filesToWrite = data.files;
|
|
244
|
+
|
|
245
|
+
if (fileChoice === "a") {
|
|
246
|
+
const preserved = context.protectedPaths.length;
|
|
247
|
+
if (preserved > 0) {
|
|
248
|
+
console.log(`\n relay will write ${data.files.length} files.`);
|
|
249
|
+
console.log(` ${preserved} file${preserved === 1 ? " was" : "s were"} preserved (human-written).\n`);
|
|
250
|
+
} else {
|
|
251
|
+
console.log(`\n relay will write all ${data.files.length} files.\n`);
|
|
252
|
+
}
|
|
253
|
+
const acceptConfirm = await confirm(" confirm? (y/n) ");
|
|
254
|
+
if (!acceptConfirm) {
|
|
255
|
+
showCancelled();
|
|
256
|
+
process.exit(0);
|
|
257
|
+
}
|
|
258
|
+
} else if (fileChoice === "s") {
|
|
259
|
+
console.log("\n which files do you want? you can say things like:");
|
|
260
|
+
console.log(" 'all except CLAUDE.md'");
|
|
261
|
+
console.log(" 'only the cursor rules'");
|
|
262
|
+
console.log(" 'just core.mdc and worktrees'\n");
|
|
263
|
+
|
|
264
|
+
const instruction = await promptFilePath(" → ");
|
|
265
|
+
|
|
266
|
+
const selectUrl = `${BASE_URL}/api/cli/select`;
|
|
267
|
+
let selectedPaths: string[];
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const selectRes = await fetch(selectUrl, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers: { "Content-Type": "application/json" },
|
|
273
|
+
body: JSON.stringify({
|
|
274
|
+
files: data.files.map((f) => f.path),
|
|
275
|
+
instruction,
|
|
276
|
+
}),
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
if (!selectRes.ok) {
|
|
280
|
+
showError("server", "file selection failed");
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const selectData = (await selectRes.json()) as { selectedPaths: string[] };
|
|
285
|
+
selectedPaths = selectData.selectedPaths;
|
|
286
|
+
} catch {
|
|
287
|
+
showError("network");
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
filesToWrite = data.files.filter((f) => selectedPaths.includes(f.path));
|
|
292
|
+
|
|
293
|
+
showSelectedFiles(data.files, selectedPaths);
|
|
294
|
+
console.log(` relay will write ${filesToWrite.length} of ${data.files.length} files.\n`);
|
|
295
|
+
|
|
296
|
+
const selectConfirm = await confirm(" confirm? (y/n) ");
|
|
297
|
+
if (!selectConfirm) {
|
|
298
|
+
showCancelled();
|
|
299
|
+
process.exit(0);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ── 9. Re-check git before writing ────────────────────────────────
|
|
143
304
|
const gitNow = getGitState();
|
|
144
305
|
if (gitNow.isDirty && !git.isDirty) {
|
|
145
306
|
const proceed = await confirm(" working tree changed since scan — continue? (y/n) ");
|
|
146
307
|
if (!proceed) {
|
|
308
|
+
showCancelled();
|
|
147
309
|
process.exit(0);
|
|
148
310
|
}
|
|
149
311
|
}
|
|
150
312
|
|
|
151
|
-
// ──
|
|
313
|
+
// ── 10. Write + commit ────────────────────────────────────────────
|
|
152
314
|
console.log();
|
|
153
|
-
writeFiles(root,
|
|
315
|
+
writeFiles(root, filesToWrite);
|
|
154
316
|
console.log();
|
|
155
317
|
|
|
156
|
-
const committed = commitFiles(root,
|
|
318
|
+
const committed = commitFiles(root, filesToWrite);
|
|
157
319
|
if (!committed) {
|
|
158
320
|
console.log(" ⚠ files written but git commit failed\n");
|
|
159
321
|
}
|
|
160
322
|
|
|
161
|
-
// ──
|
|
323
|
+
// ── 11. Success ───────────────────────────────────────────────────
|
|
162
324
|
showSuccess();
|
|
163
325
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { showBanner, showCancelled } from "../ui/display.js";
|
|
4
|
+
import { confirm } from "../ui/prompt.js";
|
|
5
|
+
|
|
6
|
+
const amber = chalk.hex("#f5a623");
|
|
7
|
+
const dim = chalk.dim;
|
|
8
|
+
|
|
9
|
+
const COMMIT_GREP = "install runshift agent governance rules";
|
|
10
|
+
|
|
11
|
+
export async function remove(): Promise<void> {
|
|
12
|
+
showBanner();
|
|
13
|
+
|
|
14
|
+
// ── Find the most recent runshift commit ──────────────────────────
|
|
15
|
+
let logLine: string;
|
|
16
|
+
try {
|
|
17
|
+
logLine = execSync(
|
|
18
|
+
`git log --oneline --grep="${COMMIT_GREP}" -1 --format="%H %s %ad" --date=short`,
|
|
19
|
+
{ stdio: "pipe" },
|
|
20
|
+
)
|
|
21
|
+
.toString()
|
|
22
|
+
.trim();
|
|
23
|
+
} catch {
|
|
24
|
+
console.log(amber(" no runshift commit found in this repository.\n"));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!logLine) {
|
|
29
|
+
console.log(amber(" no runshift commit found in this repository.\n"));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const spaceIdx = logLine.indexOf(" ");
|
|
34
|
+
const hash = logLine.slice(0, spaceIdx);
|
|
35
|
+
const rest = logLine.slice(spaceIdx + 1);
|
|
36
|
+
|
|
37
|
+
// Split rest into message and date (date is last 10 chars: YYYY-MM-DD)
|
|
38
|
+
const date = rest.slice(-10);
|
|
39
|
+
const message = rest.slice(0, -11).trim();
|
|
40
|
+
|
|
41
|
+
console.log(amber(" relay will revert commit:\n"));
|
|
42
|
+
console.log(dim(` ${hash.slice(0, 7)}`));
|
|
43
|
+
console.log(dim(` ${message}`));
|
|
44
|
+
console.log(dim(` ${date}\n`));
|
|
45
|
+
|
|
46
|
+
const proceed = await confirm(" revert this commit? (y/n) ");
|
|
47
|
+
if (!proceed) {
|
|
48
|
+
showCancelled();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
execSync(`git revert ${hash} --no-edit`, { stdio: "pipe" });
|
|
54
|
+
console.log(amber("\n ✓ reverted — runshift governance rules removed\n"));
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.log(amber("\n revert failed."));
|
|
57
|
+
console.log(dim(` ${err instanceof Error ? err.message : err}\n`));
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/context/collector.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
3
4
|
import type { RepoContext } from "../types.js";
|
|
4
5
|
import { getGitState } from "./git.js";
|
|
5
6
|
|
|
@@ -96,6 +97,45 @@ function getRootConfigs(root: string): string[] {
|
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
function getProtectedPaths(root: string, existingRules: Record<string, string>): string[] {
|
|
101
|
+
const protectedPaths: string[] = [];
|
|
102
|
+
|
|
103
|
+
for (const filePath of Object.keys(existingRules)) {
|
|
104
|
+
// CLAUDE.md is always protected regardless of git history
|
|
105
|
+
if (filePath === "CLAUDE.md") {
|
|
106
|
+
protectedPaths.push(filePath);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const lastCommitMsg = execSync(
|
|
112
|
+
`git log --follow -1 --pretty=format:"%s" -- "${filePath}"`,
|
|
113
|
+
{ cwd: root, stdio: "pipe" },
|
|
114
|
+
).toString().trim();
|
|
115
|
+
|
|
116
|
+
if (!lastCommitMsg) {
|
|
117
|
+
// Not in git → human created → protect
|
|
118
|
+
protectedPaths.push(filePath);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const isRelay =
|
|
123
|
+
lastCommitMsg.includes("install runshift agent governance rules") ||
|
|
124
|
+
lastCommitMsg.includes("runshift update");
|
|
125
|
+
|
|
126
|
+
if (!isRelay) {
|
|
127
|
+
// Last commit was by a human → protect
|
|
128
|
+
protectedPaths.push(filePath);
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
// git log failed → protect to be safe
|
|
132
|
+
protectedPaths.push(filePath);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return protectedPaths;
|
|
137
|
+
}
|
|
138
|
+
|
|
99
139
|
export function collectRepoContext(root: string): RepoContext {
|
|
100
140
|
// ── package.json ──
|
|
101
141
|
const pkgJson = readJsonSafe(path.join(root, "package.json"));
|
|
@@ -163,6 +203,9 @@ export function collectRepoContext(root: string): RepoContext {
|
|
|
163
203
|
existingRules["CLAUDE.md"] = claudeMd;
|
|
164
204
|
}
|
|
165
205
|
|
|
206
|
+
// ── Protected paths ──
|
|
207
|
+
const protectedPaths = getProtectedPaths(root, existingRules);
|
|
208
|
+
|
|
166
209
|
// ── Migrations ──
|
|
167
210
|
let migrationCount = 0;
|
|
168
211
|
let migrationNames: string[] = [];
|
|
@@ -192,6 +235,7 @@ export function collectRepoContext(root: string): RepoContext {
|
|
|
192
235
|
migrationCount,
|
|
193
236
|
migrationNames,
|
|
194
237
|
rootConfigs,
|
|
238
|
+
protectedPaths,
|
|
195
239
|
gitState,
|
|
196
240
|
};
|
|
197
241
|
}
|