@codespring-app/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1403 -0
- package/package.json +26 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1403 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
+
for (let key of __getOwnPropNames(mod))
|
|
12
|
+
if (!__hasOwnProp.call(to, key))
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: () => mod[key],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
29
|
+
var __require = import.meta.require;
|
|
30
|
+
|
|
31
|
+
// src/lib/credentials.ts
|
|
32
|
+
import { join } from "path";
|
|
33
|
+
import { homedir } from "os";
|
|
34
|
+
import { mkdir } from "fs/promises";
|
|
35
|
+
async function loadCredentials() {
|
|
36
|
+
try {
|
|
37
|
+
const file = Bun.file(CREDENTIALS_PATH);
|
|
38
|
+
if (!await file.exists())
|
|
39
|
+
return null;
|
|
40
|
+
return await file.json();
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function saveCredentials(creds) {
|
|
46
|
+
await mkdir(CODESPRING_DIR, { recursive: true });
|
|
47
|
+
await Bun.write(CREDENTIALS_PATH, JSON.stringify(creds, null, 2));
|
|
48
|
+
}
|
|
49
|
+
async function clearCredentials() {
|
|
50
|
+
try {
|
|
51
|
+
const file = Bun.file(CREDENTIALS_PATH);
|
|
52
|
+
if (await file.exists()) {
|
|
53
|
+
await Bun.write(CREDENTIALS_PATH, "");
|
|
54
|
+
const { unlink } = await import("fs/promises");
|
|
55
|
+
await unlink(CREDENTIALS_PATH);
|
|
56
|
+
}
|
|
57
|
+
} catch {}
|
|
58
|
+
}
|
|
59
|
+
var CODESPRING_DIR, CREDENTIALS_PATH;
|
|
60
|
+
var init_credentials = __esm(() => {
|
|
61
|
+
CODESPRING_DIR = join(homedir(), ".codespring");
|
|
62
|
+
CREDENTIALS_PATH = join(CODESPRING_DIR, "credentials.json");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// src/lib/prompts.ts
|
|
66
|
+
import * as readline from "readline";
|
|
67
|
+
function isTTY() {
|
|
68
|
+
return process.stdin.isTTY === true;
|
|
69
|
+
}
|
|
70
|
+
function createRL() {
|
|
71
|
+
return readline.createInterface({
|
|
72
|
+
input: process.stdin,
|
|
73
|
+
output: process.stderr
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async function selectOne(message, options) {
|
|
77
|
+
if (!isTTY()) {
|
|
78
|
+
throw new Error("Interactive prompts require a TTY. Use flags instead.");
|
|
79
|
+
}
|
|
80
|
+
const rl = createRL();
|
|
81
|
+
console.error(`
|
|
82
|
+
${message}`);
|
|
83
|
+
for (let i = 0;i < options.length; i++) {
|
|
84
|
+
console.error(` ${i + 1}) ${options[i].label}`);
|
|
85
|
+
}
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
rl.question(`
|
|
88
|
+
Select (number): `, (answer) => {
|
|
89
|
+
rl.close();
|
|
90
|
+
const idx = parseInt(answer.trim(), 10) - 1;
|
|
91
|
+
if (idx >= 0 && idx < options.length) {
|
|
92
|
+
resolve(options[idx].value);
|
|
93
|
+
} else {
|
|
94
|
+
reject(new Error("Invalid selection"));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async function confirm(message) {
|
|
100
|
+
if (!isTTY())
|
|
101
|
+
return true;
|
|
102
|
+
const rl = createRL();
|
|
103
|
+
return new Promise((resolve) => {
|
|
104
|
+
rl.question(`${message} [Y/n] `, (answer) => {
|
|
105
|
+
rl.close();
|
|
106
|
+
const trimmed = answer.trim().toLowerCase();
|
|
107
|
+
resolve(trimmed === "" || trimmed === "y" || trimmed === "yes");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async function input(message) {
|
|
112
|
+
if (!isTTY()) {
|
|
113
|
+
throw new Error("Interactive prompts require a TTY. Use flags instead.");
|
|
114
|
+
}
|
|
115
|
+
const rl = createRL();
|
|
116
|
+
return new Promise((resolve) => {
|
|
117
|
+
rl.question(`${message} `, (answer) => {
|
|
118
|
+
rl.close();
|
|
119
|
+
resolve(answer.trim());
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
var init_prompts = () => {};
|
|
124
|
+
|
|
125
|
+
// src/lib/output.ts
|
|
126
|
+
function getMode(flags) {
|
|
127
|
+
if (flags.json)
|
|
128
|
+
return "json";
|
|
129
|
+
if (flags.md)
|
|
130
|
+
return "md";
|
|
131
|
+
return process.stdout.isTTY ? "md" : "json";
|
|
132
|
+
}
|
|
133
|
+
function output(data, flags = {}) {
|
|
134
|
+
const mode = getMode(flags);
|
|
135
|
+
if (mode === "md") {
|
|
136
|
+
console.log(toMarkdown(data));
|
|
137
|
+
} else {
|
|
138
|
+
console.log(JSON.stringify(data, null, flags.pretty ? 2 : undefined));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function success(message) {
|
|
142
|
+
console.error(`\u2713 ${message}`);
|
|
143
|
+
}
|
|
144
|
+
function toMarkdown(data, depth = 0) {
|
|
145
|
+
if (data == null)
|
|
146
|
+
return "_null_";
|
|
147
|
+
if (typeof data === "string")
|
|
148
|
+
return data;
|
|
149
|
+
if (typeof data === "number" || typeof data === "boolean")
|
|
150
|
+
return String(data);
|
|
151
|
+
if (Array.isArray(data)) {
|
|
152
|
+
if (data.length === 0)
|
|
153
|
+
return "_empty_";
|
|
154
|
+
if (typeof data[0] === "object" && data[0] !== null && !Array.isArray(data[0])) {
|
|
155
|
+
return toTable(data);
|
|
156
|
+
}
|
|
157
|
+
return data.map((item) => `- ${toMarkdown(item, depth + 1)}`).join(`
|
|
158
|
+
`);
|
|
159
|
+
}
|
|
160
|
+
if (typeof data === "object") {
|
|
161
|
+
const obj = data;
|
|
162
|
+
if ("data" in obj && Object.keys(obj).length <= 3) {
|
|
163
|
+
const inner = obj.data;
|
|
164
|
+
const extra = [];
|
|
165
|
+
if ("count" in obj)
|
|
166
|
+
extra.push(`**${obj.count}** results`);
|
|
167
|
+
if ("hasMore" in obj && obj.hasMore)
|
|
168
|
+
extra.push("_(has more)_");
|
|
169
|
+
const footer = extra.length ? `
|
|
170
|
+
|
|
171
|
+
${extra.join(" | ")}` : "";
|
|
172
|
+
return toMarkdown(inner, depth) + footer;
|
|
173
|
+
}
|
|
174
|
+
if ("content" in obj && typeof obj.content === "string" && obj.content.length > 100) {
|
|
175
|
+
const meta = Object.entries(obj).filter(([k]) => k !== "content").map(([k, v]) => `**${formatKey(k)}:** ${truncate(String(v ?? ""), 80)}`).join(`
|
|
176
|
+
`);
|
|
177
|
+
return `${meta}
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
${obj.content}`;
|
|
182
|
+
}
|
|
183
|
+
return Object.entries(obj).filter(([, v]) => v != null).map(([k, v]) => {
|
|
184
|
+
if (typeof v === "object" && v !== null) {
|
|
185
|
+
return `### ${formatKey(k)}
|
|
186
|
+
|
|
187
|
+
${toMarkdown(v, depth + 1)}`;
|
|
188
|
+
}
|
|
189
|
+
return `**${formatKey(k)}:** ${truncate(String(v), 120)}`;
|
|
190
|
+
}).join(`
|
|
191
|
+
`);
|
|
192
|
+
}
|
|
193
|
+
return String(data);
|
|
194
|
+
}
|
|
195
|
+
function toTable(rows) {
|
|
196
|
+
const allKeys = Object.keys(rows[0]);
|
|
197
|
+
const columns = allKeys.filter((k) => {
|
|
198
|
+
if (SKIP_FIELDS.has(k))
|
|
199
|
+
return false;
|
|
200
|
+
if (rows.every((r) => r[k] == null))
|
|
201
|
+
return false;
|
|
202
|
+
const sample = rows[0][k];
|
|
203
|
+
if (typeof sample === "string" && sample.length > 300)
|
|
204
|
+
return false;
|
|
205
|
+
if (typeof sample === "object" && sample !== null && !Array.isArray(sample))
|
|
206
|
+
return false;
|
|
207
|
+
return true;
|
|
208
|
+
});
|
|
209
|
+
const cols = columns.slice(0, 7);
|
|
210
|
+
const header = cols.map(formatKey);
|
|
211
|
+
const separator = cols.map(() => "---");
|
|
212
|
+
const body = rows.map((row) => cols.map((col) => {
|
|
213
|
+
const val = row[col];
|
|
214
|
+
if (val == null)
|
|
215
|
+
return "-";
|
|
216
|
+
if (Array.isArray(val))
|
|
217
|
+
return val.length ? val.join(", ") : "-";
|
|
218
|
+
return truncate(String(val), 50);
|
|
219
|
+
}));
|
|
220
|
+
const lines = [
|
|
221
|
+
`| ${header.join(" | ")} |`,
|
|
222
|
+
`| ${separator.join(" | ")} |`,
|
|
223
|
+
...body.map((row) => `| ${row.join(" | ")} |`)
|
|
224
|
+
];
|
|
225
|
+
return lines.join(`
|
|
226
|
+
`);
|
|
227
|
+
}
|
|
228
|
+
function formatKey(key) {
|
|
229
|
+
return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).replace(/\bId\b/g, "ID").replace(/\bUrl\b/g, "URL").replace(/\bPrd\b/g, "PRD");
|
|
230
|
+
}
|
|
231
|
+
function truncate(str, max) {
|
|
232
|
+
if (str.length <= max)
|
|
233
|
+
return str;
|
|
234
|
+
return `${str.slice(0, max - 1)}\u2026`;
|
|
235
|
+
}
|
|
236
|
+
var SKIP_FIELDS;
|
|
237
|
+
var init_output = __esm(() => {
|
|
238
|
+
SKIP_FIELDS = new Set([
|
|
239
|
+
"flowJson",
|
|
240
|
+
"mindmapData",
|
|
241
|
+
"plan",
|
|
242
|
+
"toolPreferences",
|
|
243
|
+
"marketResearchId",
|
|
244
|
+
"currentCheckpointId",
|
|
245
|
+
"initialCheckpointId",
|
|
246
|
+
"selectedStarterKit",
|
|
247
|
+
"userId",
|
|
248
|
+
"mindmapId"
|
|
249
|
+
]);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// src/lib/oauth.ts
|
|
253
|
+
function generateCodeVerifier() {
|
|
254
|
+
const array = new Uint8Array(64);
|
|
255
|
+
crypto.getRandomValues(array);
|
|
256
|
+
return base64url(array);
|
|
257
|
+
}
|
|
258
|
+
async function computeCodeChallenge(verifier) {
|
|
259
|
+
const encoder = new TextEncoder;
|
|
260
|
+
const data = encoder.encode(verifier);
|
|
261
|
+
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
262
|
+
return base64url(new Uint8Array(hash));
|
|
263
|
+
}
|
|
264
|
+
function base64url(bytes) {
|
|
265
|
+
let binary = "";
|
|
266
|
+
for (const byte of bytes) {
|
|
267
|
+
binary += String.fromCharCode(byte);
|
|
268
|
+
}
|
|
269
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
270
|
+
}
|
|
271
|
+
function openBrowser(url) {
|
|
272
|
+
const platform = process.platform;
|
|
273
|
+
if (platform === "darwin") {
|
|
274
|
+
Bun.spawn(["open", url]);
|
|
275
|
+
} else if (platform === "linux") {
|
|
276
|
+
Bun.spawn(["xdg-open", url]);
|
|
277
|
+
} else if (platform === "win32") {
|
|
278
|
+
Bun.spawn(["cmd", "/c", "start", url]);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async function startCallbackServer(preferredPort, expectedState) {
|
|
282
|
+
const port = await findAvailablePort(preferredPort);
|
|
283
|
+
let resolveCallback;
|
|
284
|
+
let rejectCallback;
|
|
285
|
+
const promise = new Promise((resolve, reject) => {
|
|
286
|
+
resolveCallback = resolve;
|
|
287
|
+
rejectCallback = reject;
|
|
288
|
+
});
|
|
289
|
+
const server = Bun.serve({
|
|
290
|
+
port,
|
|
291
|
+
fetch(req) {
|
|
292
|
+
const url = new URL(req.url);
|
|
293
|
+
if (url.pathname === "/callback") {
|
|
294
|
+
const code = url.searchParams.get("code");
|
|
295
|
+
const state = url.searchParams.get("state");
|
|
296
|
+
const error = url.searchParams.get("error");
|
|
297
|
+
if (error) {
|
|
298
|
+
const desc = url.searchParams.get("error_description") || error;
|
|
299
|
+
rejectCallback(new Error(`OAuth error: ${desc}`));
|
|
300
|
+
return new Response("<html><body><h2>Login failed</h2><p>You can close this tab.</p></body></html>", { headers: { "Content-Type": "text/html" } });
|
|
301
|
+
}
|
|
302
|
+
if (!code || state !== expectedState) {
|
|
303
|
+
rejectCallback(new Error("Invalid callback: missing code or state mismatch"));
|
|
304
|
+
return new Response("Invalid callback", { status: 400 });
|
|
305
|
+
}
|
|
306
|
+
resolveCallback({ code, state });
|
|
307
|
+
return new Response("<html><body><h2>Login successful!</h2><p>You can close this tab and return to the terminal.</p></body></html>", { headers: { "Content-Type": "text/html" } });
|
|
308
|
+
}
|
|
309
|
+
return new Response("Not found", { status: 404 });
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
return {
|
|
313
|
+
promise,
|
|
314
|
+
stop: () => server.stop(),
|
|
315
|
+
port
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
async function findAvailablePort(start) {
|
|
319
|
+
for (let port = start;port < start + 10; port++) {
|
|
320
|
+
try {
|
|
321
|
+
const server = Bun.serve({ port, fetch: () => new Response("") });
|
|
322
|
+
server.stop();
|
|
323
|
+
return port;
|
|
324
|
+
} catch {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
throw new Error(`No available port found (tried ${start}-${start + 9})`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// src/commands/auth/login.ts
|
|
332
|
+
var exports_login = {};
|
|
333
|
+
__export(exports_login, {
|
|
334
|
+
authLogin: () => authLogin
|
|
335
|
+
});
|
|
336
|
+
function getApiUrl() {
|
|
337
|
+
return process.env.CODESPRING_API_URL || DEFAULT_API_URL;
|
|
338
|
+
}
|
|
339
|
+
async function authLogin(flags) {
|
|
340
|
+
const apiUrl = flags.url || getApiUrl();
|
|
341
|
+
if (flags["api-key"]) {
|
|
342
|
+
await apiKeyLogin(apiUrl);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
await oauthLogin(apiUrl);
|
|
346
|
+
}
|
|
347
|
+
async function apiKeyLogin(apiUrl) {
|
|
348
|
+
const key = await input("Paste your API key (csk_...):");
|
|
349
|
+
if (!key.startsWith("csk_")) {
|
|
350
|
+
throw new Error("Invalid API key format. Keys start with csk_");
|
|
351
|
+
}
|
|
352
|
+
const response = await fetch(`${apiUrl}/api/workspaces`, {
|
|
353
|
+
headers: { "x-api-key": key }
|
|
354
|
+
});
|
|
355
|
+
if (!response.ok) {
|
|
356
|
+
throw new Error("Invalid API key. Please check and try again.");
|
|
357
|
+
}
|
|
358
|
+
await saveCredentials({
|
|
359
|
+
type: "api-key",
|
|
360
|
+
apiKey: key,
|
|
361
|
+
apiUrl
|
|
362
|
+
});
|
|
363
|
+
success("Authenticated with API key.");
|
|
364
|
+
}
|
|
365
|
+
async function oauthLogin(apiUrl) {
|
|
366
|
+
const existing = await loadCredentials();
|
|
367
|
+
if (existing?.accessToken) {
|
|
368
|
+
console.error("Already logged in. Use 'codespring auth logout' first, or --api-key to switch.");
|
|
369
|
+
}
|
|
370
|
+
const codeVerifier = generateCodeVerifier();
|
|
371
|
+
const codeChallenge = await computeCodeChallenge(codeVerifier);
|
|
372
|
+
const state = crypto.randomUUID();
|
|
373
|
+
const { promise: callbackPromise, stop, port } = await startCallbackServer(CALLBACK_PORT, state);
|
|
374
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
375
|
+
const params = new URLSearchParams({
|
|
376
|
+
response_type: "code",
|
|
377
|
+
client_id: "codespring-cli",
|
|
378
|
+
redirect_uri: redirectUri,
|
|
379
|
+
scope: "openid profile email offline_access",
|
|
380
|
+
state,
|
|
381
|
+
code_challenge: codeChallenge,
|
|
382
|
+
code_challenge_method: "S256"
|
|
383
|
+
});
|
|
384
|
+
const authUrl = `${apiUrl}/api/auth/oauth2/authorize?${params}`;
|
|
385
|
+
console.error("Opening browser to log in...");
|
|
386
|
+
openBrowser(authUrl);
|
|
387
|
+
console.error("Waiting for authentication...");
|
|
388
|
+
try {
|
|
389
|
+
const { code } = await callbackPromise;
|
|
390
|
+
const tokenResponse = await fetch(`${apiUrl}/api/auth/oauth2/token`, {
|
|
391
|
+
method: "POST",
|
|
392
|
+
headers: {
|
|
393
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
394
|
+
},
|
|
395
|
+
body: new URLSearchParams({
|
|
396
|
+
grant_type: "authorization_code",
|
|
397
|
+
code,
|
|
398
|
+
redirect_uri: redirectUri,
|
|
399
|
+
client_id: "codespring-cli",
|
|
400
|
+
code_verifier: codeVerifier
|
|
401
|
+
})
|
|
402
|
+
});
|
|
403
|
+
if (!tokenResponse.ok) {
|
|
404
|
+
const errText = await tokenResponse.text();
|
|
405
|
+
throw new Error(`Token exchange failed: ${errText}`);
|
|
406
|
+
}
|
|
407
|
+
const tokens = await tokenResponse.json();
|
|
408
|
+
await saveCredentials({
|
|
409
|
+
type: "oauth",
|
|
410
|
+
accessToken: tokens.access_token,
|
|
411
|
+
refreshToken: tokens.refresh_token,
|
|
412
|
+
expiresAt: new Date(Date.now() + tokens.expires_in * 1000).toISOString(),
|
|
413
|
+
apiUrl
|
|
414
|
+
});
|
|
415
|
+
success("Logged in successfully.");
|
|
416
|
+
} finally {
|
|
417
|
+
stop();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
var DEFAULT_API_URL = "https://server.codespring.app", CALLBACK_PORT = 8923;
|
|
421
|
+
var init_login = __esm(() => {
|
|
422
|
+
init_credentials();
|
|
423
|
+
init_prompts();
|
|
424
|
+
init_output();
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// src/commands/auth/status.ts
|
|
428
|
+
var exports_status = {};
|
|
429
|
+
__export(exports_status, {
|
|
430
|
+
authStatus: () => authStatus
|
|
431
|
+
});
|
|
432
|
+
async function authStatus(flags) {
|
|
433
|
+
const creds = await loadCredentials();
|
|
434
|
+
if (!creds || !creds.accessToken && !creds.apiKey) {
|
|
435
|
+
output({ authenticated: false, message: "Not logged in" }, flags);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
output({
|
|
439
|
+
authenticated: true,
|
|
440
|
+
type: creds.type,
|
|
441
|
+
apiUrl: creds.apiUrl,
|
|
442
|
+
...creds.expiresAt && { expiresAt: creds.expiresAt }
|
|
443
|
+
}, flags);
|
|
444
|
+
}
|
|
445
|
+
var init_status = __esm(() => {
|
|
446
|
+
init_credentials();
|
|
447
|
+
init_output();
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// src/commands/auth/logout.ts
|
|
451
|
+
var exports_logout = {};
|
|
452
|
+
__export(exports_logout, {
|
|
453
|
+
authLogout: () => authLogout
|
|
454
|
+
});
|
|
455
|
+
async function authLogout() {
|
|
456
|
+
await clearCredentials();
|
|
457
|
+
success("Logged out.");
|
|
458
|
+
}
|
|
459
|
+
var init_logout = __esm(() => {
|
|
460
|
+
init_credentials();
|
|
461
|
+
init_output();
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// src/lib/config.ts
|
|
465
|
+
import { join as join2 } from "path";
|
|
466
|
+
import { mkdir as mkdir2 } from "fs/promises";
|
|
467
|
+
function getConfigDir(cwd) {
|
|
468
|
+
return join2(cwd || process.cwd(), ".codespring");
|
|
469
|
+
}
|
|
470
|
+
function getConfigPath(cwd) {
|
|
471
|
+
return join2(getConfigDir(cwd), "config.json");
|
|
472
|
+
}
|
|
473
|
+
async function loadConfig(cwd) {
|
|
474
|
+
try {
|
|
475
|
+
const file = Bun.file(getConfigPath(cwd));
|
|
476
|
+
if (!await file.exists())
|
|
477
|
+
return null;
|
|
478
|
+
return await file.json();
|
|
479
|
+
} catch {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async function saveConfig(config, cwd) {
|
|
484
|
+
const dir = getConfigDir(cwd);
|
|
485
|
+
await mkdir2(dir, { recursive: true });
|
|
486
|
+
await Bun.write(getConfigPath(cwd), JSON.stringify(config, null, 2) + `
|
|
487
|
+
`);
|
|
488
|
+
await ensureGitignore(cwd);
|
|
489
|
+
}
|
|
490
|
+
async function ensureGitignore(cwd) {
|
|
491
|
+
const gitignorePath = join2(cwd || process.cwd(), ".gitignore");
|
|
492
|
+
try {
|
|
493
|
+
const file = Bun.file(gitignorePath);
|
|
494
|
+
if (await file.exists()) {
|
|
495
|
+
const content = await file.text();
|
|
496
|
+
if (!content.includes(".codespring")) {
|
|
497
|
+
await Bun.write(gitignorePath, content.trimEnd() + `
|
|
498
|
+
|
|
499
|
+
# CodeSpring local config
|
|
500
|
+
.codespring/
|
|
501
|
+
`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
} catch {}
|
|
505
|
+
}
|
|
506
|
+
var init_config = () => {};
|
|
507
|
+
|
|
508
|
+
// src/lib/api.ts
|
|
509
|
+
async function refreshToken(creds) {
|
|
510
|
+
if (!creds.refreshToken) {
|
|
511
|
+
throw new Error("No refresh token available. Run: codespring auth login");
|
|
512
|
+
}
|
|
513
|
+
const response = await fetch(`${creds.apiUrl}/api/auth/oauth2/token`, {
|
|
514
|
+
method: "POST",
|
|
515
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
516
|
+
body: new URLSearchParams({
|
|
517
|
+
grant_type: "refresh_token",
|
|
518
|
+
refresh_token: creds.refreshToken,
|
|
519
|
+
client_id: "codespring-cli"
|
|
520
|
+
})
|
|
521
|
+
});
|
|
522
|
+
if (!response.ok) {
|
|
523
|
+
throw new Error("Token refresh failed. Run: codespring auth login");
|
|
524
|
+
}
|
|
525
|
+
const tokens = await response.json();
|
|
526
|
+
const updated = {
|
|
527
|
+
...creds,
|
|
528
|
+
accessToken: tokens.access_token,
|
|
529
|
+
refreshToken: tokens.refresh_token || creds.refreshToken,
|
|
530
|
+
expiresAt: new Date(Date.now() + tokens.expires_in * 1000).toISOString()
|
|
531
|
+
};
|
|
532
|
+
await saveCredentials(updated);
|
|
533
|
+
return updated;
|
|
534
|
+
}
|
|
535
|
+
async function apiCall(options) {
|
|
536
|
+
let creds = await loadCredentials();
|
|
537
|
+
if (!creds) {
|
|
538
|
+
throw new Error("Not authenticated. Run: codespring auth login");
|
|
539
|
+
}
|
|
540
|
+
if (creds.type === "oauth" && creds.expiresAt && new Date(creds.expiresAt) < new Date) {
|
|
541
|
+
creds = await refreshToken(creds);
|
|
542
|
+
}
|
|
543
|
+
const headers = {
|
|
544
|
+
"Content-Type": "application/json"
|
|
545
|
+
};
|
|
546
|
+
if (creds.type === "api-key" && creds.apiKey) {
|
|
547
|
+
headers["x-api-key"] = creds.apiKey;
|
|
548
|
+
} else if (creds.type === "oauth" && creds.accessToken) {
|
|
549
|
+
headers["Authorization"] = `Bearer ${creds.accessToken}`;
|
|
550
|
+
}
|
|
551
|
+
const apiUrl = creds.apiUrl || DEFAULT_API_URL2;
|
|
552
|
+
const baseUrl = apiUrl.replace(/\/api\/?$/, "");
|
|
553
|
+
const response = await fetch(`${baseUrl}/api${options.path}`, {
|
|
554
|
+
method: options.method,
|
|
555
|
+
headers,
|
|
556
|
+
body: options.body ? JSON.stringify(options.body) : undefined
|
|
557
|
+
});
|
|
558
|
+
const text = await response.text();
|
|
559
|
+
if (!response.ok) {
|
|
560
|
+
throw new Error(`API error (${response.status}): ${text.slice(0, 200)}`);
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
return { data: JSON.parse(text), status: response.status };
|
|
564
|
+
} catch {
|
|
565
|
+
return { data: text, status: response.status };
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
var DEFAULT_API_URL2 = "https://server.codespring.app";
|
|
569
|
+
var init_api = __esm(() => {
|
|
570
|
+
init_credentials();
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// src/commands/init.ts
|
|
574
|
+
var exports_init = {};
|
|
575
|
+
__export(exports_init, {
|
|
576
|
+
init: () => init
|
|
577
|
+
});
|
|
578
|
+
import { basename } from "path";
|
|
579
|
+
async function init(flags) {
|
|
580
|
+
const existing = await loadConfig();
|
|
581
|
+
if (existing && !flags.force) {
|
|
582
|
+
console.error(`Already linked to: ${existing.projectName}`);
|
|
583
|
+
const proceed2 = await confirm("Re-link to a different project?");
|
|
584
|
+
if (!proceed2)
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (flags.project) {
|
|
588
|
+
const { data } = await apiCall({
|
|
589
|
+
path: `/projects/${flags.project}`,
|
|
590
|
+
method: "GET"
|
|
591
|
+
});
|
|
592
|
+
const project2 = data;
|
|
593
|
+
await saveConfig({
|
|
594
|
+
version: "1.0",
|
|
595
|
+
projectId: project2.id,
|
|
596
|
+
projectName: project2.name,
|
|
597
|
+
organizationId: project2.organizationId,
|
|
598
|
+
linkedAt: new Date().toISOString()
|
|
599
|
+
});
|
|
600
|
+
output({ linked: true, project: project2.name, projectId: project2.id }, flags);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (!process.stdin.isTTY) {
|
|
604
|
+
throw new Error("Interactive init requires a TTY. Use: codespring init --project <id>");
|
|
605
|
+
}
|
|
606
|
+
const proceed = await confirm(`Set up CodeSpring in "${process.cwd()}"?`);
|
|
607
|
+
if (!proceed)
|
|
608
|
+
return;
|
|
609
|
+
const { data: wsData } = await apiCall({
|
|
610
|
+
path: "/workspaces",
|
|
611
|
+
method: "GET"
|
|
612
|
+
});
|
|
613
|
+
const workspaces = wsData.data;
|
|
614
|
+
const workspace = await selectOne("Select a workspace:", workspaces.map((w) => ({
|
|
615
|
+
label: `${w.name} (${w.projectCount} projects)${w.role ? ` - ${w.role}` : ""}`,
|
|
616
|
+
value: w
|
|
617
|
+
})));
|
|
618
|
+
const orgParam = workspace.id ? `?organizationId=${workspace.id}` : "";
|
|
619
|
+
const { data: projData } = await apiCall({
|
|
620
|
+
path: `/projects${orgParam}`,
|
|
621
|
+
method: "GET"
|
|
622
|
+
});
|
|
623
|
+
const projects = projData.data;
|
|
624
|
+
if (projects.length === 0) {
|
|
625
|
+
throw new Error("No projects found in this workspace.");
|
|
626
|
+
}
|
|
627
|
+
const dirName = basename(process.cwd()).toLowerCase();
|
|
628
|
+
const autoMatch = projects.find((p) => p.name.toLowerCase() === dirName || slugify(p.name) === dirName);
|
|
629
|
+
let project;
|
|
630
|
+
if (autoMatch) {
|
|
631
|
+
const useMatch = await confirm(`Found project "${autoMatch.name}". Link to it?`);
|
|
632
|
+
if (useMatch) {
|
|
633
|
+
project = autoMatch;
|
|
634
|
+
} else {
|
|
635
|
+
project = await selectOne("Select a project:", projects.map((p) => ({ label: p.name, value: p })));
|
|
636
|
+
}
|
|
637
|
+
} else {
|
|
638
|
+
project = await selectOne("Select a project:", projects.map((p) => ({ label: p.name, value: p })));
|
|
639
|
+
}
|
|
640
|
+
await saveConfig({
|
|
641
|
+
version: "1.0",
|
|
642
|
+
projectId: project.id,
|
|
643
|
+
projectName: project.name,
|
|
644
|
+
organizationId: project.organizationId,
|
|
645
|
+
linkedAt: new Date().toISOString()
|
|
646
|
+
});
|
|
647
|
+
success(`Linked to ${project.name}`);
|
|
648
|
+
output({ linked: true, project: project.name, projectId: project.id }, flags);
|
|
649
|
+
}
|
|
650
|
+
function slugify(str) {
|
|
651
|
+
return str.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
652
|
+
}
|
|
653
|
+
var init_init = __esm(() => {
|
|
654
|
+
init_config();
|
|
655
|
+
init_api();
|
|
656
|
+
init_output();
|
|
657
|
+
init_prompts();
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
// src/commands/status.ts
|
|
661
|
+
var exports_status2 = {};
|
|
662
|
+
__export(exports_status2, {
|
|
663
|
+
status: () => status
|
|
664
|
+
});
|
|
665
|
+
async function status(flags) {
|
|
666
|
+
const config = await loadConfig();
|
|
667
|
+
const creds = await loadCredentials();
|
|
668
|
+
output({
|
|
669
|
+
linked: !!config,
|
|
670
|
+
...config && {
|
|
671
|
+
projectId: config.projectId,
|
|
672
|
+
projectName: config.projectName,
|
|
673
|
+
organizationId: config.organizationId,
|
|
674
|
+
linkedAt: config.linkedAt
|
|
675
|
+
},
|
|
676
|
+
authenticated: !!(creds?.accessToken || creds?.apiKey),
|
|
677
|
+
authType: creds?.type || null
|
|
678
|
+
}, flags);
|
|
679
|
+
}
|
|
680
|
+
var init_status2 = __esm(() => {
|
|
681
|
+
init_config();
|
|
682
|
+
init_credentials();
|
|
683
|
+
init_output();
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
// src/commands/workspaces.ts
|
|
687
|
+
var exports_workspaces = {};
|
|
688
|
+
__export(exports_workspaces, {
|
|
689
|
+
workspaces: () => workspaces
|
|
690
|
+
});
|
|
691
|
+
async function workspaces(flags) {
|
|
692
|
+
const { data } = await apiCall({ path: "/workspaces", method: "GET" });
|
|
693
|
+
output(data, flags);
|
|
694
|
+
}
|
|
695
|
+
var init_workspaces = __esm(() => {
|
|
696
|
+
init_api();
|
|
697
|
+
init_output();
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// src/commands/projects.ts
|
|
701
|
+
var exports_projects = {};
|
|
702
|
+
__export(exports_projects, {
|
|
703
|
+
projects: () => projects
|
|
704
|
+
});
|
|
705
|
+
async function projects(flags) {
|
|
706
|
+
const params = new URLSearchParams;
|
|
707
|
+
if (flags.org)
|
|
708
|
+
params.set("organizationId", flags.org);
|
|
709
|
+
const query = params.toString();
|
|
710
|
+
const { data } = await apiCall({
|
|
711
|
+
path: `/projects${query ? `?${query}` : ""}`,
|
|
712
|
+
method: "GET"
|
|
713
|
+
});
|
|
714
|
+
output(data, flags);
|
|
715
|
+
}
|
|
716
|
+
var init_projects = __esm(() => {
|
|
717
|
+
init_api();
|
|
718
|
+
init_output();
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// src/lib/ensure-linked.ts
|
|
722
|
+
async function ensureLinked() {
|
|
723
|
+
const config = await loadConfig();
|
|
724
|
+
if (config)
|
|
725
|
+
return config;
|
|
726
|
+
if (!process.stdin.isTTY) {
|
|
727
|
+
throw new Error("Not linked to a CodeSpring project. Run: codespring init");
|
|
728
|
+
}
|
|
729
|
+
console.error("This directory is not linked to a CodeSpring project.");
|
|
730
|
+
await init({});
|
|
731
|
+
const newConfig = await loadConfig();
|
|
732
|
+
if (!newConfig) {
|
|
733
|
+
throw new Error("Failed to link project. Run: codespring init");
|
|
734
|
+
}
|
|
735
|
+
return newConfig;
|
|
736
|
+
}
|
|
737
|
+
var init_ensure_linked = __esm(() => {
|
|
738
|
+
init_config();
|
|
739
|
+
init_init();
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
// src/commands/features.ts
|
|
743
|
+
var exports_features = {};
|
|
744
|
+
__export(exports_features, {
|
|
745
|
+
features: () => features
|
|
746
|
+
});
|
|
747
|
+
async function features(flags) {
|
|
748
|
+
const config = await ensureLinked();
|
|
749
|
+
const { data } = await apiCall({
|
|
750
|
+
path: `/mindmap-features/project/${config.projectId}`,
|
|
751
|
+
method: "GET"
|
|
752
|
+
});
|
|
753
|
+
output(data, flags);
|
|
754
|
+
}
|
|
755
|
+
var init_features = __esm(() => {
|
|
756
|
+
init_api();
|
|
757
|
+
init_output();
|
|
758
|
+
init_ensure_linked();
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
// src/commands/tasks.ts
|
|
762
|
+
var exports_tasks = {};
|
|
763
|
+
__export(exports_tasks, {
|
|
764
|
+
tasks: () => tasks,
|
|
765
|
+
taskUpdate: () => taskUpdate,
|
|
766
|
+
taskStart: () => taskStart,
|
|
767
|
+
taskDone: () => taskDone
|
|
768
|
+
});
|
|
769
|
+
async function tasks(flags) {
|
|
770
|
+
const config = await ensureLinked();
|
|
771
|
+
const params = new URLSearchParams;
|
|
772
|
+
if (flags.status)
|
|
773
|
+
params.set("status", flags.status);
|
|
774
|
+
if (flags.feature)
|
|
775
|
+
params.set("featureId", flags.feature);
|
|
776
|
+
if (flags.priority)
|
|
777
|
+
params.set("priority", flags.priority);
|
|
778
|
+
const query = params.toString();
|
|
779
|
+
const { data } = await apiCall({
|
|
780
|
+
path: `/tasks/${config.projectId}${query ? `?${query}` : ""}`,
|
|
781
|
+
method: "GET"
|
|
782
|
+
});
|
|
783
|
+
output(data, flags);
|
|
784
|
+
}
|
|
785
|
+
async function taskStart(taskId, flags) {
|
|
786
|
+
const config = await ensureLinked();
|
|
787
|
+
const { data } = await apiCall({
|
|
788
|
+
path: `/tasks/${config.projectId}/${taskId}`,
|
|
789
|
+
method: "PATCH",
|
|
790
|
+
body: { status: "in_progress" }
|
|
791
|
+
});
|
|
792
|
+
output(data, flags);
|
|
793
|
+
}
|
|
794
|
+
async function taskDone(taskId, flags) {
|
|
795
|
+
const config = await ensureLinked();
|
|
796
|
+
const { data } = await apiCall({
|
|
797
|
+
path: `/tasks/${config.projectId}/${taskId}`,
|
|
798
|
+
method: "PATCH",
|
|
799
|
+
body: { status: "done" }
|
|
800
|
+
});
|
|
801
|
+
output(data, flags);
|
|
802
|
+
}
|
|
803
|
+
async function taskUpdate(taskId, flags) {
|
|
804
|
+
const config = await ensureLinked();
|
|
805
|
+
const body = {};
|
|
806
|
+
if (flags.status)
|
|
807
|
+
body.status = flags.status;
|
|
808
|
+
if (flags.title)
|
|
809
|
+
body.title = flags.title;
|
|
810
|
+
if (flags.description)
|
|
811
|
+
body.description = flags.description;
|
|
812
|
+
if (flags.priority)
|
|
813
|
+
body.priority = flags.priority;
|
|
814
|
+
if (flags.estimate)
|
|
815
|
+
body.estimate = flags.estimate;
|
|
816
|
+
if (Object.keys(body).length === 0) {
|
|
817
|
+
throw new Error("No fields to update. Use --status, --title, etc.");
|
|
818
|
+
}
|
|
819
|
+
const { data } = await apiCall({
|
|
820
|
+
path: `/tasks/${config.projectId}/${taskId}`,
|
|
821
|
+
method: "PATCH",
|
|
822
|
+
body
|
|
823
|
+
});
|
|
824
|
+
output(data, flags);
|
|
825
|
+
}
|
|
826
|
+
var init_tasks = __esm(() => {
|
|
827
|
+
init_api();
|
|
828
|
+
init_output();
|
|
829
|
+
init_ensure_linked();
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
// src/commands/prds.ts
|
|
833
|
+
var exports_prds = {};
|
|
834
|
+
__export(exports_prds, {
|
|
835
|
+
prds: () => prds,
|
|
836
|
+
prdSync: () => prdSync,
|
|
837
|
+
prdGet: () => prdGet
|
|
838
|
+
});
|
|
839
|
+
async function prds(flags) {
|
|
840
|
+
const config = await ensureLinked();
|
|
841
|
+
const { data } = await apiCall({
|
|
842
|
+
path: `/prds/project/${config.projectId}/structure?includeContent=false`,
|
|
843
|
+
method: "GET"
|
|
844
|
+
});
|
|
845
|
+
output(data, flags);
|
|
846
|
+
}
|
|
847
|
+
async function prdGet(prdId, flags) {
|
|
848
|
+
const { data } = await apiCall({
|
|
849
|
+
path: `/prds/${prdId}`,
|
|
850
|
+
method: "GET"
|
|
851
|
+
});
|
|
852
|
+
output(data, flags);
|
|
853
|
+
}
|
|
854
|
+
async function prdSync(prdId, flags) {
|
|
855
|
+
let content;
|
|
856
|
+
if (flags.file) {
|
|
857
|
+
const file = Bun.file(flags.file);
|
|
858
|
+
if (!await file.exists()) {
|
|
859
|
+
throw new Error(`File not found: ${flags.file}`);
|
|
860
|
+
}
|
|
861
|
+
content = await file.text();
|
|
862
|
+
} else if (flags.stdin) {
|
|
863
|
+
const chunks = [];
|
|
864
|
+
const reader = process.stdin;
|
|
865
|
+
for await (const chunk of reader) {
|
|
866
|
+
chunks.push(typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk));
|
|
867
|
+
}
|
|
868
|
+
content = chunks.join("");
|
|
869
|
+
} else {
|
|
870
|
+
throw new Error("Provide --file <path> or --stdin");
|
|
871
|
+
}
|
|
872
|
+
const body = { content };
|
|
873
|
+
if (flags.name)
|
|
874
|
+
body.name = flags.name;
|
|
875
|
+
const { data } = await apiCall({
|
|
876
|
+
path: `/prds/${prdId}`,
|
|
877
|
+
method: "PUT",
|
|
878
|
+
body
|
|
879
|
+
});
|
|
880
|
+
output(data, flags);
|
|
881
|
+
}
|
|
882
|
+
var init_prds = __esm(() => {
|
|
883
|
+
init_api();
|
|
884
|
+
init_output();
|
|
885
|
+
init_ensure_linked();
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
// src/commands/mindmap.ts
|
|
889
|
+
var exports_mindmap = {};
|
|
890
|
+
__export(exports_mindmap, {
|
|
891
|
+
mindmapTechStack: () => mindmapTechStack,
|
|
892
|
+
mindmapNote: () => mindmapNote,
|
|
893
|
+
mindmapGet: () => mindmapGet,
|
|
894
|
+
mindmapFeatures: () => mindmapFeatures
|
|
895
|
+
});
|
|
896
|
+
async function mindmapGet(flags) {
|
|
897
|
+
const config = await ensureLinked();
|
|
898
|
+
const { data } = await apiCall({
|
|
899
|
+
path: `/mindmaps/project/${config.projectId}`,
|
|
900
|
+
method: "GET"
|
|
901
|
+
});
|
|
902
|
+
output(data, flags);
|
|
903
|
+
}
|
|
904
|
+
async function mindmapTechStack(flags) {
|
|
905
|
+
const config = await ensureLinked();
|
|
906
|
+
if (!flags.add) {
|
|
907
|
+
throw new Error("Provide --add '<json array of items>'");
|
|
908
|
+
}
|
|
909
|
+
let items;
|
|
910
|
+
try {
|
|
911
|
+
items = JSON.parse(flags.add);
|
|
912
|
+
} catch {
|
|
913
|
+
throw new Error("Invalid JSON for --add. Expected array of {id, title, description}");
|
|
914
|
+
}
|
|
915
|
+
const { data } = await apiCall({
|
|
916
|
+
path: `/mindmaps/project/${config.projectId}/tech-stack`,
|
|
917
|
+
method: "PATCH",
|
|
918
|
+
body: {
|
|
919
|
+
items,
|
|
920
|
+
mode: flags.replace ? "replace" : "merge"
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
output(data, flags);
|
|
924
|
+
}
|
|
925
|
+
async function mindmapFeatures(flags) {
|
|
926
|
+
const config = await ensureLinked();
|
|
927
|
+
if (!flags.add) {
|
|
928
|
+
throw new Error("Provide --add '<json array of items>'");
|
|
929
|
+
}
|
|
930
|
+
let items;
|
|
931
|
+
try {
|
|
932
|
+
items = JSON.parse(flags.add);
|
|
933
|
+
} catch {
|
|
934
|
+
throw new Error("Invalid JSON for --add. Expected array of {title, description}");
|
|
935
|
+
}
|
|
936
|
+
const { data } = await apiCall({
|
|
937
|
+
path: `/mindmaps/project/${config.projectId}/features`,
|
|
938
|
+
method: "PATCH",
|
|
939
|
+
body: {
|
|
940
|
+
items,
|
|
941
|
+
mode: flags.replace ? "replace" : "append"
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
output(data, flags);
|
|
945
|
+
}
|
|
946
|
+
async function mindmapNote(featureId, flags) {
|
|
947
|
+
const config = await ensureLinked();
|
|
948
|
+
if (!flags.text) {
|
|
949
|
+
throw new Error("Provide --text '<note content>'");
|
|
950
|
+
}
|
|
951
|
+
const content = flags.text;
|
|
952
|
+
const title = flags.title || "Notes";
|
|
953
|
+
const { data: mindmapData } = await apiCall({
|
|
954
|
+
path: `/mindmaps/project/${config.projectId}`,
|
|
955
|
+
method: "GET"
|
|
956
|
+
});
|
|
957
|
+
const mindmap = mindmapData;
|
|
958
|
+
if (!mindmap.flowJson) {
|
|
959
|
+
throw new Error("Mindmap has no flowJson data");
|
|
960
|
+
}
|
|
961
|
+
const nodes = mindmap.flowJson.nodes;
|
|
962
|
+
const edges = mindmap.flowJson.edges;
|
|
963
|
+
const featuresNode = nodes.find((n) => n.id === "node-features");
|
|
964
|
+
if (!featuresNode?.data?.items) {
|
|
965
|
+
throw new Error("Features node not found or has no items");
|
|
966
|
+
}
|
|
967
|
+
const featureItems = featuresNode.data.items;
|
|
968
|
+
const feature = featureItems.find((item) => item.id === featureId);
|
|
969
|
+
if (!feature) {
|
|
970
|
+
const validIds = featureItems.map((i) => i.id).join(", ");
|
|
971
|
+
throw new Error(`Feature "${featureId}" not found. Valid IDs: ${validIds}`);
|
|
972
|
+
}
|
|
973
|
+
const bridgeNodeId = `bridge-feature-${featureId}`;
|
|
974
|
+
let bridgeNode = nodes.find((n) => n.id === bridgeNodeId);
|
|
975
|
+
const newNodes = [];
|
|
976
|
+
const newEdges = [];
|
|
977
|
+
if (!bridgeNode) {
|
|
978
|
+
const featuresX = featuresNode.position?.x || 1200;
|
|
979
|
+
const featuresY = featuresNode.position?.y || 200;
|
|
980
|
+
const featureIndex = featureItems.findIndex((i) => i.id === featureId);
|
|
981
|
+
const bridgeX = featuresX + 496;
|
|
982
|
+
const bridgeY = featuresY + 56 + featureIndex * 50;
|
|
983
|
+
bridgeNode = {
|
|
984
|
+
id: bridgeNodeId,
|
|
985
|
+
type: "bridge",
|
|
986
|
+
position: { x: bridgeX, y: bridgeY },
|
|
987
|
+
data: {
|
|
988
|
+
featureId,
|
|
989
|
+
count: 1,
|
|
990
|
+
isExpanded: true,
|
|
991
|
+
handlePosition: "left"
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
newNodes.push(bridgeNode);
|
|
995
|
+
newEdges.push({
|
|
996
|
+
id: `edge-features-bridge-${featureId}`,
|
|
997
|
+
source: "node-features",
|
|
998
|
+
target: bridgeNodeId,
|
|
999
|
+
sourceHandle: `node-features-source-${featureId}`,
|
|
1000
|
+
targetHandle: `${bridgeNodeId}-target-${featureId}`,
|
|
1001
|
+
type: "default",
|
|
1002
|
+
animated: true,
|
|
1003
|
+
className: "stroke-foreground",
|
|
1004
|
+
style: { strokeWidth: 2 }
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
const notesNodeId = `notes-${featureId}`;
|
|
1008
|
+
let notesNode = nodes.find((n) => n.id === notesNodeId);
|
|
1009
|
+
if (notesNode) {
|
|
1010
|
+
notesNode.data = { ...notesNode.data, title, content };
|
|
1011
|
+
} else {
|
|
1012
|
+
const bridgeX = bridgeNode.position?.x || 1700;
|
|
1013
|
+
const bridgeY = bridgeNode.position?.y || 256;
|
|
1014
|
+
notesNode = {
|
|
1015
|
+
id: notesNodeId,
|
|
1016
|
+
type: "tertiary",
|
|
1017
|
+
position: { x: bridgeX + 200, y: bridgeY },
|
|
1018
|
+
data: {
|
|
1019
|
+
type: "notes",
|
|
1020
|
+
title,
|
|
1021
|
+
content,
|
|
1022
|
+
handlePosition: "left"
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
newNodes.push(notesNode);
|
|
1026
|
+
newEdges.push({
|
|
1027
|
+
id: `edge-bridge-${featureId}-notes`,
|
|
1028
|
+
source: bridgeNodeId,
|
|
1029
|
+
target: notesNodeId,
|
|
1030
|
+
sourceHandle: `${bridgeNodeId}-source-notes`,
|
|
1031
|
+
targetHandle: `${notesNodeId}-target-notes`,
|
|
1032
|
+
type: "default",
|
|
1033
|
+
animated: true,
|
|
1034
|
+
className: "stroke-foreground",
|
|
1035
|
+
style: { strokeWidth: 2 }
|
|
1036
|
+
});
|
|
1037
|
+
if (bridgeNode.data) {
|
|
1038
|
+
const currentCount = typeof bridgeNode.data.count === "number" ? bridgeNode.data.count : 0;
|
|
1039
|
+
bridgeNode.data.count = currentCount + 1;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
const updatedNodes = [
|
|
1043
|
+
...nodes.filter((n) => n.id !== notesNodeId && !newNodes.some((nn) => nn.id === n.id)),
|
|
1044
|
+
...newNodes,
|
|
1045
|
+
...notesNode && !newNodes.includes(notesNode) ? [notesNode] : []
|
|
1046
|
+
];
|
|
1047
|
+
const updatedEdges = [...edges, ...newEdges];
|
|
1048
|
+
const { data } = await apiCall({
|
|
1049
|
+
path: `/mindmaps/project/${config.projectId}`,
|
|
1050
|
+
method: "PUT",
|
|
1051
|
+
body: { flowJson: { nodes: updatedNodes, edges: updatedEdges } }
|
|
1052
|
+
});
|
|
1053
|
+
output(data, flags);
|
|
1054
|
+
}
|
|
1055
|
+
var init_mindmap = __esm(() => {
|
|
1056
|
+
init_api();
|
|
1057
|
+
init_output();
|
|
1058
|
+
init_ensure_linked();
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
// src/commands/schema.ts
|
|
1062
|
+
var exports_schema = {};
|
|
1063
|
+
__export(exports_schema, {
|
|
1064
|
+
schema: () => schema
|
|
1065
|
+
});
|
|
1066
|
+
async function schema(flags) {
|
|
1067
|
+
output(SCHEMA, flags);
|
|
1068
|
+
}
|
|
1069
|
+
var SCHEMA;
|
|
1070
|
+
var init_schema = __esm(() => {
|
|
1071
|
+
init_output();
|
|
1072
|
+
SCHEMA = {
|
|
1073
|
+
project: {
|
|
1074
|
+
id: "string (UUID)",
|
|
1075
|
+
name: "string",
|
|
1076
|
+
description: "string | null",
|
|
1077
|
+
userId: "string",
|
|
1078
|
+
organizationId: "string | null",
|
|
1079
|
+
planningComplete: "boolean",
|
|
1080
|
+
createdAt: "Date",
|
|
1081
|
+
updatedAt: "Date"
|
|
1082
|
+
},
|
|
1083
|
+
mindmap: {
|
|
1084
|
+
id: "string",
|
|
1085
|
+
projectId: "string",
|
|
1086
|
+
flowJson: "{ nodes: MindmapNode[], edges: MindmapEdge[] }"
|
|
1087
|
+
},
|
|
1088
|
+
techStackItem: {
|
|
1089
|
+
id: 'string (e.g., "tech-react")',
|
|
1090
|
+
title: 'string (e.g., "React")',
|
|
1091
|
+
description: 'string (category, e.g., "Frontend")'
|
|
1092
|
+
},
|
|
1093
|
+
featureItem: {
|
|
1094
|
+
id: "string (optional, auto-generated)",
|
|
1095
|
+
title: 'string (e.g., "Authentication")',
|
|
1096
|
+
description: "string (brief description)"
|
|
1097
|
+
},
|
|
1098
|
+
prd: {
|
|
1099
|
+
id: "string (UUID)",
|
|
1100
|
+
name: "string",
|
|
1101
|
+
projectId: "string",
|
|
1102
|
+
prdType: "frontend | backend | database",
|
|
1103
|
+
content: "string (Markdown)"
|
|
1104
|
+
},
|
|
1105
|
+
techCategories: [
|
|
1106
|
+
"Frontend",
|
|
1107
|
+
"Backend",
|
|
1108
|
+
"Database",
|
|
1109
|
+
"State",
|
|
1110
|
+
"Styling",
|
|
1111
|
+
"Testing",
|
|
1112
|
+
"DevTools",
|
|
1113
|
+
"Infrastructure",
|
|
1114
|
+
"Utilities"
|
|
1115
|
+
]
|
|
1116
|
+
};
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
// src/commands/node-types.ts
|
|
1120
|
+
var exports_node_types = {};
|
|
1121
|
+
__export(exports_node_types, {
|
|
1122
|
+
nodeTypes: () => nodeTypes
|
|
1123
|
+
});
|
|
1124
|
+
async function nodeTypes(flags) {
|
|
1125
|
+
output(NODE_TYPES, flags);
|
|
1126
|
+
}
|
|
1127
|
+
var NODE_TYPES;
|
|
1128
|
+
var init_node_types = __esm(() => {
|
|
1129
|
+
init_output();
|
|
1130
|
+
NODE_TYPES = {
|
|
1131
|
+
primary: {
|
|
1132
|
+
description: "Central project node",
|
|
1133
|
+
exampleId: "node-primary",
|
|
1134
|
+
dataFields: ["title", "description", "githubUrl"]
|
|
1135
|
+
},
|
|
1136
|
+
secondary: {
|
|
1137
|
+
description: "Major category nodes (features, tech-stack, competitors, audience)",
|
|
1138
|
+
exampleIds: [
|
|
1139
|
+
"node-features",
|
|
1140
|
+
"node-tech-stack",
|
|
1141
|
+
"node-competitors",
|
|
1142
|
+
"node-audience"
|
|
1143
|
+
],
|
|
1144
|
+
dataFields: ["title", "subtitle", "handlePosition", "layout", "items"]
|
|
1145
|
+
},
|
|
1146
|
+
bridge: {
|
|
1147
|
+
description: "Connector nodes between features and detail nodes",
|
|
1148
|
+
idPattern: "bridge-feature-{featureId}",
|
|
1149
|
+
dataFields: ["featureId", "count", "isExpanded", "handlePosition"]
|
|
1150
|
+
},
|
|
1151
|
+
tertiary: {
|
|
1152
|
+
description: "Detail nodes (notes, PRDs)",
|
|
1153
|
+
idPattern: "notes-{featureId} or prd-{featureId}",
|
|
1154
|
+
dataFields: ["type", "title", "content", "handlePosition"]
|
|
1155
|
+
},
|
|
1156
|
+
specialNodeIds: {
|
|
1157
|
+
"node-primary": "Main project node (required)",
|
|
1158
|
+
"node-features": "Features list (required)",
|
|
1159
|
+
"node-tech-stack": "Tech stack (required)",
|
|
1160
|
+
"node-competitors": "Competitors list",
|
|
1161
|
+
"node-audience": "Target audience"
|
|
1162
|
+
},
|
|
1163
|
+
handlePatterns: {
|
|
1164
|
+
featureToBridge: {
|
|
1165
|
+
sourceHandle: "node-features-source-{featureId}",
|
|
1166
|
+
targetHandle: "bridge-feature-{featureId}-target-{featureId}"
|
|
1167
|
+
},
|
|
1168
|
+
bridgeToNotes: {
|
|
1169
|
+
sourceHandle: "bridge-feature-{featureId}-source-notes",
|
|
1170
|
+
targetHandle: "notes-{featureId}-target-notes"
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
// src/index.ts
|
|
1177
|
+
var VERSION = "0.1.0";
|
|
1178
|
+
function parseArgs(argv) {
|
|
1179
|
+
const positional = [];
|
|
1180
|
+
const flags = {};
|
|
1181
|
+
for (let i = 0;i < argv.length; i++) {
|
|
1182
|
+
const arg = argv[i];
|
|
1183
|
+
if (arg.startsWith("--")) {
|
|
1184
|
+
const key = arg.slice(2);
|
|
1185
|
+
const next = argv[i + 1];
|
|
1186
|
+
if (next && !next.startsWith("--")) {
|
|
1187
|
+
flags[key] = next;
|
|
1188
|
+
i++;
|
|
1189
|
+
} else {
|
|
1190
|
+
flags[key] = true;
|
|
1191
|
+
}
|
|
1192
|
+
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
1193
|
+
const key = arg.slice(1);
|
|
1194
|
+
const next = argv[i + 1];
|
|
1195
|
+
if (next && !next.startsWith("-")) {
|
|
1196
|
+
flags[key] = next;
|
|
1197
|
+
i++;
|
|
1198
|
+
} else {
|
|
1199
|
+
flags[key] = true;
|
|
1200
|
+
}
|
|
1201
|
+
} else {
|
|
1202
|
+
positional.push(arg);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
return { positional, flags };
|
|
1206
|
+
}
|
|
1207
|
+
function showHelp() {
|
|
1208
|
+
console.log(`codespring v${VERSION} \u2014 CodeSpring CLI
|
|
1209
|
+
|
|
1210
|
+
Usage: codespring <command> [options]
|
|
1211
|
+
|
|
1212
|
+
Auth:
|
|
1213
|
+
auth login [--api-key] [--url <api-url>] Log in via browser OAuth or API key
|
|
1214
|
+
auth status Show authentication status
|
|
1215
|
+
auth logout Clear stored credentials
|
|
1216
|
+
|
|
1217
|
+
Setup:
|
|
1218
|
+
init [--project <id>] [--force] Link directory to a CodeSpring project
|
|
1219
|
+
status Show linked project info
|
|
1220
|
+
|
|
1221
|
+
Data:
|
|
1222
|
+
workspaces List workspaces
|
|
1223
|
+
projects [--org <id>] List projects
|
|
1224
|
+
features List features for linked project
|
|
1225
|
+
|
|
1226
|
+
Tasks:
|
|
1227
|
+
tasks [--status <s>] [--feature <id>] [--priority <p>] List tasks
|
|
1228
|
+
task start <id> Mark task as in_progress
|
|
1229
|
+
task done <id> Mark task as done
|
|
1230
|
+
task update <id> --status <s> Update task fields
|
|
1231
|
+
|
|
1232
|
+
PRDs:
|
|
1233
|
+
prds List PRDs by feature
|
|
1234
|
+
prd <id> Get full PRD content
|
|
1235
|
+
prd sync <id> --file <path> Update PRD content from file
|
|
1236
|
+
|
|
1237
|
+
Mindmap:
|
|
1238
|
+
mindmap Get mindmap structure
|
|
1239
|
+
mindmap tech-stack --add '<json>' [--replace] Update tech stack
|
|
1240
|
+
mindmap features --add '<json>' [--replace] Update features
|
|
1241
|
+
mindmap note <featureId> --text '<text>' Add notes to a feature
|
|
1242
|
+
|
|
1243
|
+
Reference:
|
|
1244
|
+
schema Show data schema reference
|
|
1245
|
+
node-types Show mindmap node types
|
|
1246
|
+
|
|
1247
|
+
Options:
|
|
1248
|
+
--md Markdown output (default in terminal)
|
|
1249
|
+
--json JSON output (default when piped)
|
|
1250
|
+
--pretty Pretty-print JSON output
|
|
1251
|
+
--help, -h Show this help
|
|
1252
|
+
--version, -v Show version`);
|
|
1253
|
+
}
|
|
1254
|
+
async function main() {
|
|
1255
|
+
const { positional, flags } = parseArgs(process.argv.slice(2));
|
|
1256
|
+
const [command, subcommand, ...rest] = positional;
|
|
1257
|
+
if (flags.help || flags.h) {
|
|
1258
|
+
showHelp();
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
if (flags.version || flags.v) {
|
|
1262
|
+
console.log(VERSION);
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
if (!command) {
|
|
1266
|
+
showHelp();
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
try {
|
|
1270
|
+
switch (command) {
|
|
1271
|
+
case "auth": {
|
|
1272
|
+
switch (subcommand) {
|
|
1273
|
+
case "login": {
|
|
1274
|
+
const { authLogin: authLogin2 } = await Promise.resolve().then(() => (init_login(), exports_login));
|
|
1275
|
+
return authLogin2(flags);
|
|
1276
|
+
}
|
|
1277
|
+
case "status": {
|
|
1278
|
+
const { authStatus: authStatus2 } = await Promise.resolve().then(() => (init_status(), exports_status));
|
|
1279
|
+
return authStatus2(flags);
|
|
1280
|
+
}
|
|
1281
|
+
case "logout": {
|
|
1282
|
+
const { authLogout: authLogout2 } = await Promise.resolve().then(() => (init_logout(), exports_logout));
|
|
1283
|
+
return authLogout2();
|
|
1284
|
+
}
|
|
1285
|
+
default:
|
|
1286
|
+
console.error("Usage: codespring auth <login|status|logout>");
|
|
1287
|
+
process.exit(1);
|
|
1288
|
+
}
|
|
1289
|
+
break;
|
|
1290
|
+
}
|
|
1291
|
+
case "init": {
|
|
1292
|
+
const { init: init2 } = await Promise.resolve().then(() => (init_init(), exports_init));
|
|
1293
|
+
return init2(flags);
|
|
1294
|
+
}
|
|
1295
|
+
case "status": {
|
|
1296
|
+
const { status: status2 } = await Promise.resolve().then(() => (init_status2(), exports_status2));
|
|
1297
|
+
return status2(flags);
|
|
1298
|
+
}
|
|
1299
|
+
case "workspaces": {
|
|
1300
|
+
const { workspaces: workspaces2 } = await Promise.resolve().then(() => (init_workspaces(), exports_workspaces));
|
|
1301
|
+
return workspaces2(flags);
|
|
1302
|
+
}
|
|
1303
|
+
case "projects": {
|
|
1304
|
+
const { projects: projects2 } = await Promise.resolve().then(() => (init_projects(), exports_projects));
|
|
1305
|
+
return projects2(flags);
|
|
1306
|
+
}
|
|
1307
|
+
case "features": {
|
|
1308
|
+
const { features: features2 } = await Promise.resolve().then(() => (init_features(), exports_features));
|
|
1309
|
+
return features2(flags);
|
|
1310
|
+
}
|
|
1311
|
+
case "tasks": {
|
|
1312
|
+
const { tasks: tasks2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
1313
|
+
return tasks2(flags);
|
|
1314
|
+
}
|
|
1315
|
+
case "task": {
|
|
1316
|
+
switch (subcommand) {
|
|
1317
|
+
case "start": {
|
|
1318
|
+
const taskId = rest[0];
|
|
1319
|
+
if (!taskId)
|
|
1320
|
+
throw new Error("Usage: codespring task start <id>");
|
|
1321
|
+
const { taskStart: taskStart2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
1322
|
+
return taskStart2(taskId, flags);
|
|
1323
|
+
}
|
|
1324
|
+
case "done": {
|
|
1325
|
+
const taskId = rest[0];
|
|
1326
|
+
if (!taskId)
|
|
1327
|
+
throw new Error("Usage: codespring task done <id>");
|
|
1328
|
+
const { taskDone: taskDone2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
1329
|
+
return taskDone2(taskId, flags);
|
|
1330
|
+
}
|
|
1331
|
+
case "update": {
|
|
1332
|
+
const taskId = rest[0];
|
|
1333
|
+
if (!taskId)
|
|
1334
|
+
throw new Error("Usage: codespring task update <id> --status <s>");
|
|
1335
|
+
const { taskUpdate: taskUpdate2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
1336
|
+
return taskUpdate2(taskId, flags);
|
|
1337
|
+
}
|
|
1338
|
+
default:
|
|
1339
|
+
console.error("Usage: codespring task <start|done|update> <id>");
|
|
1340
|
+
process.exit(1);
|
|
1341
|
+
}
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
case "prds": {
|
|
1345
|
+
const { prds: prds2 } = await Promise.resolve().then(() => (init_prds(), exports_prds));
|
|
1346
|
+
return prds2(flags);
|
|
1347
|
+
}
|
|
1348
|
+
case "prd": {
|
|
1349
|
+
if (subcommand === "sync") {
|
|
1350
|
+
const prdId = rest[0];
|
|
1351
|
+
if (!prdId)
|
|
1352
|
+
throw new Error("Usage: codespring prd sync <id> --file <path>");
|
|
1353
|
+
const { prdSync: prdSync2 } = await Promise.resolve().then(() => (init_prds(), exports_prds));
|
|
1354
|
+
return prdSync2(prdId, flags);
|
|
1355
|
+
}
|
|
1356
|
+
if (!subcommand)
|
|
1357
|
+
throw new Error("Usage: codespring prd <id>");
|
|
1358
|
+
const { prdGet: prdGet2 } = await Promise.resolve().then(() => (init_prds(), exports_prds));
|
|
1359
|
+
return prdGet2(subcommand, flags);
|
|
1360
|
+
}
|
|
1361
|
+
case "mindmap": {
|
|
1362
|
+
switch (subcommand) {
|
|
1363
|
+
case "tech-stack": {
|
|
1364
|
+
const { mindmapTechStack: mindmapTechStack2 } = await Promise.resolve().then(() => (init_mindmap(), exports_mindmap));
|
|
1365
|
+
return mindmapTechStack2(flags);
|
|
1366
|
+
}
|
|
1367
|
+
case "features": {
|
|
1368
|
+
const { mindmapFeatures: mindmapFeatures2 } = await Promise.resolve().then(() => (init_mindmap(), exports_mindmap));
|
|
1369
|
+
return mindmapFeatures2(flags);
|
|
1370
|
+
}
|
|
1371
|
+
case "note": {
|
|
1372
|
+
const featureId = rest[0];
|
|
1373
|
+
if (!featureId)
|
|
1374
|
+
throw new Error("Usage: codespring mindmap note <featureId> --text '<text>'");
|
|
1375
|
+
const { mindmapNote: mindmapNote2 } = await Promise.resolve().then(() => (init_mindmap(), exports_mindmap));
|
|
1376
|
+
return mindmapNote2(featureId, flags);
|
|
1377
|
+
}
|
|
1378
|
+
default: {
|
|
1379
|
+
const { mindmapGet: mindmapGet2 } = await Promise.resolve().then(() => (init_mindmap(), exports_mindmap));
|
|
1380
|
+
return mindmapGet2(flags);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
case "schema": {
|
|
1385
|
+
const { schema: schema2 } = await Promise.resolve().then(() => (init_schema(), exports_schema));
|
|
1386
|
+
return schema2(flags);
|
|
1387
|
+
}
|
|
1388
|
+
case "node-types": {
|
|
1389
|
+
const { nodeTypes: nodeTypes2 } = await Promise.resolve().then(() => (init_node_types(), exports_node_types));
|
|
1390
|
+
return nodeTypes2(flags);
|
|
1391
|
+
}
|
|
1392
|
+
default:
|
|
1393
|
+
console.error(`Unknown command: ${command}`);
|
|
1394
|
+
showHelp();
|
|
1395
|
+
process.exit(1);
|
|
1396
|
+
}
|
|
1397
|
+
} catch (err) {
|
|
1398
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1399
|
+
console.error(JSON.stringify({ error: message }));
|
|
1400
|
+
process.exit(1);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codespring-app/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CodeSpring CLI — project planning and management from the terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"codespring": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "bun src/index.ts",
|
|
14
|
+
"build": "bun build src/index.ts --outdir=dist --target=bun",
|
|
15
|
+
"compile": "bun build src/index.ts --compile --outfile=dist/codespring"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"zod": "^3.23.8"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/bun": "latest"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"typescript": "^5"
|
|
25
|
+
}
|
|
26
|
+
}
|