@flydocs/cli 0.6.0-alpha.24 → 0.6.0-alpha.27
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/cli.js +1389 -207
- package/package.json +1 -1
- package/template/.claude/hooks/session-start.py +117 -1
- package/template/.claude/skills/flydocs-workflow/reference/service-descriptor-schema.md +13 -4
- package/template/.claude/skills/flydocs-workflow/scripts/graph_build.py +2 -2
- package/template/.flydocs/config.json +1 -1
- package/template/.flydocs/version +1 -1
- package/template/manifest.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ var CLI_VERSION, CLI_NAME, PACKAGE_NAME, POSTHOG_API_KEY;
|
|
|
15
15
|
var init_constants = __esm({
|
|
16
16
|
"src/lib/constants.ts"() {
|
|
17
17
|
"use strict";
|
|
18
|
-
CLI_VERSION = "0.6.0-alpha.
|
|
18
|
+
CLI_VERSION = "0.6.0-alpha.27";
|
|
19
19
|
CLI_NAME = "flydocs";
|
|
20
20
|
PACKAGE_NAME = "@flydocs/cli";
|
|
21
21
|
POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
|
|
@@ -136,8 +136,8 @@ var init_template = __esm({
|
|
|
136
136
|
|
|
137
137
|
// src/lib/ui.ts
|
|
138
138
|
import pc2 from "picocolors";
|
|
139
|
-
function shadow(
|
|
140
|
-
return `\x1B[38;2;55;45;70m${
|
|
139
|
+
function shadow(text6) {
|
|
140
|
+
return `\x1B[38;2;55;45;70m${text6}\x1B[0m`;
|
|
141
141
|
}
|
|
142
142
|
function renderBannerBlock() {
|
|
143
143
|
const height = BANNER_ROWS.length;
|
|
@@ -268,12 +268,70 @@ var init_ui = __esm({
|
|
|
268
268
|
}
|
|
269
269
|
});
|
|
270
270
|
|
|
271
|
+
// src/lib/types.ts
|
|
272
|
+
function isConfigV2(config) {
|
|
273
|
+
return config.configFormat === 2;
|
|
274
|
+
}
|
|
275
|
+
var init_types = __esm({
|
|
276
|
+
"src/lib/types.ts"() {
|
|
277
|
+
"use strict";
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// src/lib/config-integrity.ts
|
|
282
|
+
import { createHash } from "crypto";
|
|
283
|
+
function computeConfigHash(config) {
|
|
284
|
+
const { configHash: _, ...rest } = config;
|
|
285
|
+
const serialized = JSON.stringify(rest, Object.keys(rest).sort());
|
|
286
|
+
return `sha256:${createHash("sha256").update(serialized, "utf-8").digest("hex")}`;
|
|
287
|
+
}
|
|
288
|
+
function validateConfigIntegrity(config) {
|
|
289
|
+
if (!config.configHash) return false;
|
|
290
|
+
const computed = computeConfigHash(config);
|
|
291
|
+
return computed === config.configHash;
|
|
292
|
+
}
|
|
293
|
+
function checkConfigIntegrity(config) {
|
|
294
|
+
if (!config.configHash) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
const valid = validateConfigIntegrity(config);
|
|
298
|
+
if (!valid) {
|
|
299
|
+
printWarning(
|
|
300
|
+
"Config modified locally \u2014 will restore from server on next sync."
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
return valid;
|
|
304
|
+
}
|
|
305
|
+
function applyConfigHash(config) {
|
|
306
|
+
const hash = computeConfigHash(config);
|
|
307
|
+
return { ...config, configHash: hash };
|
|
308
|
+
}
|
|
309
|
+
var init_config_integrity = __esm({
|
|
310
|
+
"src/lib/config-integrity.ts"() {
|
|
311
|
+
"use strict";
|
|
312
|
+
init_ui();
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
271
316
|
// src/lib/config.ts
|
|
272
317
|
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
273
318
|
import { join as join3 } from "path";
|
|
274
|
-
async function
|
|
319
|
+
async function readAnyConfig(dir) {
|
|
275
320
|
const content = await readFile2(join3(dir, ".flydocs", "config.json"), "utf-8");
|
|
276
|
-
|
|
321
|
+
const config = JSON.parse(content);
|
|
322
|
+
if (isConfigV2(config)) {
|
|
323
|
+
checkConfigIntegrity(config);
|
|
324
|
+
}
|
|
325
|
+
return config;
|
|
326
|
+
}
|
|
327
|
+
async function readConfig(dir) {
|
|
328
|
+
const config = await readAnyConfig(dir);
|
|
329
|
+
if (isConfigV2(config)) {
|
|
330
|
+
throw new Error(
|
|
331
|
+
"This command requires v1 config format. Run `flydocs sync` to use v2 commands."
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return config;
|
|
277
335
|
}
|
|
278
336
|
async function writeConfig(dir, config) {
|
|
279
337
|
const content = JSON.stringify(config, null, 2) + "\n";
|
|
@@ -343,6 +401,8 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
|
|
|
343
401
|
var init_config = __esm({
|
|
344
402
|
"src/lib/config.ts"() {
|
|
345
403
|
"use strict";
|
|
404
|
+
init_types();
|
|
405
|
+
init_config_integrity();
|
|
346
406
|
}
|
|
347
407
|
});
|
|
348
408
|
|
|
@@ -363,7 +423,7 @@ async function installOwnedSkills(templateDir, targetDir, _tier) {
|
|
|
363
423
|
async function replaceOwnedSkills(templateDir, targetDir, _tier) {
|
|
364
424
|
const skillsDir = join4(targetDir, ".claude", "skills");
|
|
365
425
|
const templateSkillsDir = join4(templateDir, ".claude", "skills");
|
|
366
|
-
const { rm:
|
|
426
|
+
const { rm: rm7 } = await import("fs/promises");
|
|
367
427
|
await replaceDirectory(
|
|
368
428
|
join4(templateSkillsDir, OWNED_SKILL),
|
|
369
429
|
join4(skillsDir, OWNED_SKILL)
|
|
@@ -371,7 +431,7 @@ async function replaceOwnedSkills(templateDir, targetDir, _tier) {
|
|
|
371
431
|
for (const legacy of LEGACY_SKILLS) {
|
|
372
432
|
const legacyPath = join4(skillsDir, legacy);
|
|
373
433
|
if (await pathExists(legacyPath)) {
|
|
374
|
-
await
|
|
434
|
+
await rm7(legacyPath, { recursive: true, force: true });
|
|
375
435
|
}
|
|
376
436
|
}
|
|
377
437
|
const readmeSrc = join4(templateSkillsDir, "README.md");
|
|
@@ -380,9 +440,9 @@ async function replaceOwnedSkills(templateDir, targetDir, _tier) {
|
|
|
380
440
|
}
|
|
381
441
|
}
|
|
382
442
|
async function copyCursorRules(targetDir) {
|
|
383
|
-
const { mkdir:
|
|
443
|
+
const { mkdir: mkdir12 } = await import("fs/promises");
|
|
384
444
|
const rulesDir = join4(targetDir, ".cursor", "rules");
|
|
385
|
-
await
|
|
445
|
+
await mkdir12(rulesDir, { recursive: true });
|
|
386
446
|
const workflowRule = join4(
|
|
387
447
|
targetDir,
|
|
388
448
|
".claude",
|
|
@@ -393,11 +453,11 @@ async function copyCursorRules(targetDir) {
|
|
|
393
453
|
if (await pathExists(workflowRule)) {
|
|
394
454
|
await copyFile(workflowRule, join4(rulesDir, "flydocs-workflow.mdc"));
|
|
395
455
|
}
|
|
396
|
-
const { rm:
|
|
456
|
+
const { rm: rm7 } = await import("fs/promises");
|
|
397
457
|
for (const legacy of ["flydocs-mechanism.mdc", "flydocs-context7.mdc"]) {
|
|
398
458
|
const legacyRule = join4(rulesDir, legacy);
|
|
399
459
|
if (await pathExists(legacyRule)) {
|
|
400
|
-
await
|
|
460
|
+
await rm7(legacyRule, { force: true });
|
|
401
461
|
}
|
|
402
462
|
}
|
|
403
463
|
}
|
|
@@ -715,8 +775,8 @@ function flushFrontmatterValue(mode, lines) {
|
|
|
715
775
|
return lines.join("\n").trim();
|
|
716
776
|
}
|
|
717
777
|
}
|
|
718
|
-
function parseFrontmatter(
|
|
719
|
-
const match =
|
|
778
|
+
function parseFrontmatter(text6) {
|
|
779
|
+
const match = text6.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
720
780
|
if (!match) return null;
|
|
721
781
|
const block = match[1];
|
|
722
782
|
const result = {};
|
|
@@ -1481,7 +1541,7 @@ async function ensurePlatformIgnores(targetDir) {
|
|
|
1481
1541
|
if (!await pathExists(filePath)) continue;
|
|
1482
1542
|
const content = await readFile6(filePath, "utf-8");
|
|
1483
1543
|
if (content.includes("# FlyDocs")) continue;
|
|
1484
|
-
const section = "\n# FlyDocs \u2014
|
|
1544
|
+
const section = "\n# FlyDocs \u2014 managed by flydocs-cli, do not edit this section\n" + FLYDOCS_DEPLOY_EXCLUSIONS.join("\n") + "\n";
|
|
1485
1545
|
await appendFile(filePath, section, "utf-8");
|
|
1486
1546
|
printStatus(`Added FlyDocs exclusions to ${filename}`);
|
|
1487
1547
|
}
|
|
@@ -1496,14 +1556,19 @@ var init_gitignore = __esm({
|
|
|
1496
1556
|
".gcloudignore",
|
|
1497
1557
|
".dockerignore",
|
|
1498
1558
|
".vercelignore",
|
|
1499
|
-
".npmignore"
|
|
1559
|
+
".npmignore",
|
|
1560
|
+
".cfignore",
|
|
1561
|
+
".ebignore",
|
|
1562
|
+
".funcignore"
|
|
1500
1563
|
];
|
|
1501
1564
|
FLYDOCS_DEPLOY_EXCLUSIONS = [
|
|
1502
1565
|
".flydocs/",
|
|
1503
1566
|
".claude/",
|
|
1504
1567
|
".cursor/",
|
|
1505
1568
|
"flydocs/",
|
|
1506
|
-
"
|
|
1569
|
+
".flydocs-workspace.json",
|
|
1570
|
+
"AGENTS.md",
|
|
1571
|
+
".cursorrules"
|
|
1507
1572
|
];
|
|
1508
1573
|
FLYDOCS_GITIGNORE_ENTRIES = [
|
|
1509
1574
|
".env",
|
|
@@ -2759,16 +2824,666 @@ var init_install = __esm({
|
|
|
2759
2824
|
}
|
|
2760
2825
|
});
|
|
2761
2826
|
|
|
2827
|
+
// src/lib/global-config.ts
|
|
2828
|
+
import { readFile as readFile12, writeFile as writeFile10, mkdir as mkdir8, stat, chmod } from "fs/promises";
|
|
2829
|
+
import { join as join17 } from "path";
|
|
2830
|
+
import { homedir as homedir3 } from "os";
|
|
2831
|
+
function globalDir() {
|
|
2832
|
+
return join17(homedir3(), ".flydocs");
|
|
2833
|
+
}
|
|
2834
|
+
function credentialsPath() {
|
|
2835
|
+
return join17(globalDir(), "credentials");
|
|
2836
|
+
}
|
|
2837
|
+
function globalMePath() {
|
|
2838
|
+
return join17(globalDir(), "me.json");
|
|
2839
|
+
}
|
|
2840
|
+
async function ensureGlobalDir() {
|
|
2841
|
+
const dir = globalDir();
|
|
2842
|
+
if (!await pathExists(dir)) {
|
|
2843
|
+
await mkdir8(dir, { recursive: true, mode: 448 });
|
|
2844
|
+
}
|
|
2845
|
+
return dir;
|
|
2846
|
+
}
|
|
2847
|
+
async function readGlobalCredential() {
|
|
2848
|
+
const path = credentialsPath();
|
|
2849
|
+
if (!await pathExists(path)) return null;
|
|
2850
|
+
try {
|
|
2851
|
+
const content = await readFile12(path, "utf-8");
|
|
2852
|
+
return JSON.parse(content);
|
|
2853
|
+
} catch {
|
|
2854
|
+
return null;
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
async function writeGlobalCredential(credential) {
|
|
2858
|
+
await ensureGlobalDir();
|
|
2859
|
+
const path = credentialsPath();
|
|
2860
|
+
await writeFile10(path, JSON.stringify(credential, null, 2) + "\n", {
|
|
2861
|
+
encoding: "utf-8",
|
|
2862
|
+
mode: 384
|
|
2863
|
+
});
|
|
2864
|
+
await chmod(path, 384);
|
|
2865
|
+
}
|
|
2866
|
+
async function checkCredentialPermissions() {
|
|
2867
|
+
const path = credentialsPath();
|
|
2868
|
+
if (!await pathExists(path)) return;
|
|
2869
|
+
try {
|
|
2870
|
+
const stats = await stat(path);
|
|
2871
|
+
const mode = stats.mode & 511;
|
|
2872
|
+
if (mode !== 384) {
|
|
2873
|
+
printWarning(
|
|
2874
|
+
`Credential file permissions are too open (${mode.toString(8)}). Expected 0600. Run: chmod 600 ${path}`
|
|
2875
|
+
);
|
|
2876
|
+
}
|
|
2877
|
+
} catch {
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
async function writeGlobalIdentity(identity) {
|
|
2881
|
+
await ensureGlobalDir();
|
|
2882
|
+
const path = globalMePath();
|
|
2883
|
+
await writeFile10(path, JSON.stringify(identity, null, 2) + "\n", "utf-8");
|
|
2884
|
+
}
|
|
2885
|
+
async function resolveApiKey(explicitKey, projectDir) {
|
|
2886
|
+
if (explicitKey) {
|
|
2887
|
+
return { key: explicitKey, source: "--key flag" };
|
|
2888
|
+
}
|
|
2889
|
+
const envKey = process.env.FLYDOCS_API_KEY;
|
|
2890
|
+
if (envKey) {
|
|
2891
|
+
return { key: envKey, source: "FLYDOCS_API_KEY env var" };
|
|
2892
|
+
}
|
|
2893
|
+
const globalCred = await readGlobalCredential();
|
|
2894
|
+
if (globalCred?.apiKey) {
|
|
2895
|
+
return { key: globalCred.apiKey, source: "~/.flydocs/credentials" };
|
|
2896
|
+
}
|
|
2897
|
+
if (projectDir) {
|
|
2898
|
+
const envLocalKey = await readEnvKey(projectDir, "FLYDOCS_API_KEY");
|
|
2899
|
+
if (envLocalKey) {
|
|
2900
|
+
return { key: envLocalKey, source: ".env.local (legacy)" };
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
return null;
|
|
2904
|
+
}
|
|
2905
|
+
async function readEnvKey(dir, varName) {
|
|
2906
|
+
for (const filename of [".env.local", ".env"]) {
|
|
2907
|
+
const filePath = join17(dir, filename);
|
|
2908
|
+
if (!await pathExists(filePath)) continue;
|
|
2909
|
+
try {
|
|
2910
|
+
const content = await readFile12(filePath, "utf-8");
|
|
2911
|
+
const match = content.match(new RegExp(`^${varName}=(.+)$`, "m"));
|
|
2912
|
+
if (match) return match[1].trim();
|
|
2913
|
+
} catch {
|
|
2914
|
+
continue;
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
return null;
|
|
2918
|
+
}
|
|
2919
|
+
var init_global_config = __esm({
|
|
2920
|
+
"src/lib/global-config.ts"() {
|
|
2921
|
+
"use strict";
|
|
2922
|
+
init_fs_ops();
|
|
2923
|
+
init_ui();
|
|
2924
|
+
}
|
|
2925
|
+
});
|
|
2926
|
+
|
|
2927
|
+
// src/lib/relay-client.ts
|
|
2928
|
+
function resolveRelayUrl() {
|
|
2929
|
+
return process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? DEFAULT_RELAY_URL;
|
|
2930
|
+
}
|
|
2931
|
+
function headers(apiKey, workspaceId) {
|
|
2932
|
+
const h = {
|
|
2933
|
+
Authorization: `Bearer ${apiKey}`,
|
|
2934
|
+
"Content-Type": "application/json",
|
|
2935
|
+
Accept: "application/json"
|
|
2936
|
+
};
|
|
2937
|
+
if (workspaceId) h["X-Workspace"] = workspaceId;
|
|
2938
|
+
return h;
|
|
2939
|
+
}
|
|
2940
|
+
async function fetchConfigV2(apiKey, options = {}) {
|
|
2941
|
+
const baseUrl = resolveRelayUrl();
|
|
2942
|
+
const params = new URLSearchParams({ format: "2" });
|
|
2943
|
+
if (options.includeContext) params.set("includeContext", "true");
|
|
2944
|
+
const response = await fetch(`${baseUrl}/config/generate?${params}`, {
|
|
2945
|
+
method: "GET",
|
|
2946
|
+
headers: headers(apiKey, options.workspaceId),
|
|
2947
|
+
signal: AbortSignal.timeout(3e4)
|
|
2948
|
+
});
|
|
2949
|
+
if (!response.ok) {
|
|
2950
|
+
const body = await response.text().catch(() => "");
|
|
2951
|
+
throw new RelayError(
|
|
2952
|
+
`config/generate failed (${response.status})`,
|
|
2953
|
+
response.status,
|
|
2954
|
+
body
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2957
|
+
return await response.json();
|
|
2958
|
+
}
|
|
2959
|
+
async function fetchTemplates(apiKey, sinceVersion, workspaceId) {
|
|
2960
|
+
const baseUrl = resolveRelayUrl();
|
|
2961
|
+
const response = await fetch(`${baseUrl}/templates?since=${sinceVersion}`, {
|
|
2962
|
+
method: "GET",
|
|
2963
|
+
headers: headers(apiKey, workspaceId),
|
|
2964
|
+
signal: AbortSignal.timeout(15e3)
|
|
2965
|
+
});
|
|
2966
|
+
if (!response.ok) {
|
|
2967
|
+
const body = await response.text().catch(() => "");
|
|
2968
|
+
throw new RelayError(
|
|
2969
|
+
`templates fetch failed (${response.status})`,
|
|
2970
|
+
response.status,
|
|
2971
|
+
body
|
|
2972
|
+
);
|
|
2973
|
+
}
|
|
2974
|
+
return await response.json();
|
|
2975
|
+
}
|
|
2976
|
+
var DEFAULT_RELAY_URL, RelayError;
|
|
2977
|
+
var init_relay_client = __esm({
|
|
2978
|
+
"src/lib/relay-client.ts"() {
|
|
2979
|
+
"use strict";
|
|
2980
|
+
DEFAULT_RELAY_URL = "https://app.flydocs.ai/api/relay";
|
|
2981
|
+
RelayError = class extends Error {
|
|
2982
|
+
constructor(message, status2, body) {
|
|
2983
|
+
super(message);
|
|
2984
|
+
this.status = status2;
|
|
2985
|
+
this.body = body;
|
|
2986
|
+
this.name = "RelayError";
|
|
2987
|
+
}
|
|
2988
|
+
};
|
|
2989
|
+
}
|
|
2990
|
+
});
|
|
2991
|
+
|
|
2992
|
+
// src/lib/workspace.ts
|
|
2993
|
+
import { readFile as readFile13, writeFile as writeFile11, readdir as readdir4, stat as stat2 } from "fs/promises";
|
|
2994
|
+
import { join as join18, dirname as dirname3, relative, resolve as resolve3 } from "path";
|
|
2995
|
+
async function readWorkspaceFile(dir) {
|
|
2996
|
+
const filePath = join18(dir, WORKSPACE_FILENAME);
|
|
2997
|
+
if (!await pathExists(filePath)) return null;
|
|
2998
|
+
const content = await readFile13(filePath, "utf-8");
|
|
2999
|
+
const parsed = JSON.parse(content);
|
|
3000
|
+
validateWorkspaceFile(parsed);
|
|
3001
|
+
return parsed;
|
|
3002
|
+
}
|
|
3003
|
+
async function writeWorkspaceFile(dir, workspace) {
|
|
3004
|
+
const filePath = join18(dir, WORKSPACE_FILENAME);
|
|
3005
|
+
const content = JSON.stringify(workspace, null, 2) + "\n";
|
|
3006
|
+
await writeFile11(filePath, content, "utf-8");
|
|
3007
|
+
}
|
|
3008
|
+
async function detectSiblingRepos(parentDir) {
|
|
3009
|
+
const repos = {};
|
|
3010
|
+
const entries = await readdir4(parentDir);
|
|
3011
|
+
for (const entry of entries) {
|
|
3012
|
+
if (entry.startsWith(".")) continue;
|
|
3013
|
+
const entryPath = join18(parentDir, entry);
|
|
3014
|
+
const entryStat = await stat2(entryPath).catch(() => null);
|
|
3015
|
+
if (!entryStat?.isDirectory()) continue;
|
|
3016
|
+
const hasGit = await pathExists(join18(entryPath, ".git"));
|
|
3017
|
+
const hasFlydocs = await pathExists(
|
|
3018
|
+
join18(entryPath, ".flydocs", "config.json")
|
|
3019
|
+
);
|
|
3020
|
+
if (hasGit || hasFlydocs) {
|
|
3021
|
+
repos[entry] = { path: `./${entry}` };
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
return repos;
|
|
3025
|
+
}
|
|
3026
|
+
function buildWorkspaceFile(workspaceId, repos) {
|
|
3027
|
+
return { workspaceId, repos };
|
|
3028
|
+
}
|
|
3029
|
+
async function readSiblingDescriptors(workspaceDir, repos) {
|
|
3030
|
+
const descriptors = [];
|
|
3031
|
+
for (const [name, entry] of Object.entries(repos)) {
|
|
3032
|
+
const repoDir = resolve3(workspaceDir, entry.path);
|
|
3033
|
+
const serviceJsonPath = join18(repoDir, "flydocs", "context", "service.json");
|
|
3034
|
+
if (!await pathExists(serviceJsonPath)) continue;
|
|
3035
|
+
try {
|
|
3036
|
+
const content = await readFile13(serviceJsonPath, "utf-8");
|
|
3037
|
+
const descriptor = JSON.parse(content);
|
|
3038
|
+
descriptors.push({ name, path: entry.path, descriptor });
|
|
3039
|
+
} catch {
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
return descriptors;
|
|
3043
|
+
}
|
|
3044
|
+
async function generateWorkspaceMd(workspaceDir, workspace, productName) {
|
|
3045
|
+
const descriptors = await readSiblingDescriptors(
|
|
3046
|
+
workspaceDir,
|
|
3047
|
+
workspace.repos
|
|
3048
|
+
);
|
|
3049
|
+
const repoCount = Object.keys(workspace.repos).length;
|
|
3050
|
+
const lines = [];
|
|
3051
|
+
const title = productName ?? "Workspace";
|
|
3052
|
+
lines.push(`# Product: ${title}`);
|
|
3053
|
+
lines.push("");
|
|
3054
|
+
lines.push("## Overview");
|
|
3055
|
+
lines.push("");
|
|
3056
|
+
if (descriptors.length > 0) {
|
|
3057
|
+
const purposes = descriptors.map((d) => d.descriptor.purpose).filter(Boolean);
|
|
3058
|
+
if (purposes.length > 0) {
|
|
3059
|
+
lines.push(purposes.join(". ") + ".");
|
|
3060
|
+
} else {
|
|
3061
|
+
lines.push(`Multi-repo workspace with ${repoCount} repositories.`);
|
|
3062
|
+
}
|
|
3063
|
+
} else {
|
|
3064
|
+
lines.push(`Multi-repo workspace with ${repoCount} repositories.`);
|
|
3065
|
+
}
|
|
3066
|
+
lines.push("");
|
|
3067
|
+
lines.push("## Architecture");
|
|
3068
|
+
lines.push("");
|
|
3069
|
+
lines.push(`${repoCount}-repo sibling topology.`);
|
|
3070
|
+
lines.push("");
|
|
3071
|
+
lines.push("## Repos");
|
|
3072
|
+
lines.push("");
|
|
3073
|
+
for (const [name, entry] of Object.entries(workspace.repos)) {
|
|
3074
|
+
const desc = descriptors.find((d) => d.name === name);
|
|
3075
|
+
const purpose = desc?.descriptor.purpose ?? "";
|
|
3076
|
+
const stack = desc?.descriptor.stack?.join(", ") ?? "";
|
|
3077
|
+
const detail = [purpose, stack].filter(Boolean).join(". ");
|
|
3078
|
+
const projectMdLink = `${entry.path}/flydocs/context/project.md`;
|
|
3079
|
+
lines.push(
|
|
3080
|
+
`- **${name}** \u2014 ${detail || "No description"}. [Details](${projectMdLink})`
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
lines.push("");
|
|
3084
|
+
const edges = [];
|
|
3085
|
+
for (const desc of descriptors) {
|
|
3086
|
+
for (const dep of desc.descriptor.dependencies ?? []) {
|
|
3087
|
+
const target = dep.service;
|
|
3088
|
+
const iface = dep.interface;
|
|
3089
|
+
edges.push(`- ${desc.name} \u2192 ${target} (${iface})`);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
lines.push("## Cross-Repo Dependencies");
|
|
3093
|
+
lines.push("");
|
|
3094
|
+
if (edges.length > 0) {
|
|
3095
|
+
lines.push(...edges);
|
|
3096
|
+
} else {
|
|
3097
|
+
lines.push("No cross-repo dependencies detected.");
|
|
3098
|
+
}
|
|
3099
|
+
lines.push("");
|
|
3100
|
+
return lines.join("\n");
|
|
3101
|
+
}
|
|
3102
|
+
function validateWorkspaceFile(value) {
|
|
3103
|
+
if (typeof value !== "object" || value === null) {
|
|
3104
|
+
throw new Error("Invalid workspace file: expected an object");
|
|
3105
|
+
}
|
|
3106
|
+
const obj = value;
|
|
3107
|
+
if (typeof obj.workspaceId !== "string" || obj.workspaceId.length === 0) {
|
|
3108
|
+
throw new Error(
|
|
3109
|
+
"Invalid workspace file: workspaceId must be a non-empty string"
|
|
3110
|
+
);
|
|
3111
|
+
}
|
|
3112
|
+
if (typeof obj.repos !== "object" || obj.repos === null) {
|
|
3113
|
+
throw new Error("Invalid workspace file: repos must be an object");
|
|
3114
|
+
}
|
|
3115
|
+
const repos = obj.repos;
|
|
3116
|
+
for (const [name, entry] of Object.entries(repos)) {
|
|
3117
|
+
if (typeof entry !== "object" || entry === null) {
|
|
3118
|
+
throw new Error(
|
|
3119
|
+
`Invalid workspace file: repos.${name} must be an object`
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
const repoEntry = entry;
|
|
3123
|
+
if (typeof repoEntry.path !== "string" || repoEntry.path.length === 0) {
|
|
3124
|
+
throw new Error(
|
|
3125
|
+
`Invalid workspace file: repos.${name}.path must be a non-empty string`
|
|
3126
|
+
);
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
var WORKSPACE_FILENAME;
|
|
3131
|
+
var init_workspace = __esm({
|
|
3132
|
+
"src/lib/workspace.ts"() {
|
|
3133
|
+
"use strict";
|
|
3134
|
+
init_fs_ops();
|
|
3135
|
+
WORKSPACE_FILENAME = ".flydocs-workspace.json";
|
|
3136
|
+
}
|
|
3137
|
+
});
|
|
3138
|
+
|
|
3139
|
+
// src/commands/init.ts
|
|
3140
|
+
var init_exports = {};
|
|
3141
|
+
__export(init_exports, {
|
|
3142
|
+
default: () => init_default
|
|
3143
|
+
});
|
|
3144
|
+
import { defineCommand as defineCommand2 } from "citty";
|
|
3145
|
+
import { text as text2, confirm as confirm3, select as select2, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
|
|
3146
|
+
import pc7 from "picocolors";
|
|
3147
|
+
import { join as join19 } from "path";
|
|
3148
|
+
import { mkdir as mkdir9, writeFile as writeFile12 } from "fs/promises";
|
|
3149
|
+
async function resolveAndValidateKey(keyArg, targetDir) {
|
|
3150
|
+
let resolved = await resolveApiKey(keyArg, targetDir);
|
|
3151
|
+
if (!resolved) {
|
|
3152
|
+
console.log(
|
|
3153
|
+
` ${pc7.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
3154
|
+
);
|
|
3155
|
+
console.log();
|
|
3156
|
+
const keyInput = await text2({
|
|
3157
|
+
message: "Enter your API key",
|
|
3158
|
+
placeholder: "fdk_...",
|
|
3159
|
+
validate(value) {
|
|
3160
|
+
if (!value.trim()) return "API key is required";
|
|
3161
|
+
if (detectKeyType(value.trim()) !== "relay") {
|
|
3162
|
+
return "Key must start with fdk_ (FlyDocs API key)";
|
|
3163
|
+
}
|
|
3164
|
+
return void 0;
|
|
3165
|
+
}
|
|
3166
|
+
});
|
|
3167
|
+
if (isCancel4(keyInput)) {
|
|
3168
|
+
cancel3("Init cancelled.");
|
|
3169
|
+
process.exit(0);
|
|
3170
|
+
}
|
|
3171
|
+
resolved = { key: keyInput.trim(), source: "prompt" };
|
|
3172
|
+
} else {
|
|
3173
|
+
printInfo(`Using key from ${resolved.source}`);
|
|
3174
|
+
}
|
|
3175
|
+
const apiKey = resolved.key;
|
|
3176
|
+
printInfo("Validating API key...");
|
|
3177
|
+
try {
|
|
3178
|
+
const result = await validateRelayKey(apiKey);
|
|
3179
|
+
if (!result.valid) {
|
|
3180
|
+
printError("Invalid API key. Check your key and try again.");
|
|
3181
|
+
process.exit(1);
|
|
3182
|
+
}
|
|
3183
|
+
printStatus(`Authenticated with ${pc7.bold(result.org)}`);
|
|
3184
|
+
} catch {
|
|
3185
|
+
printError(
|
|
3186
|
+
"Could not reach FlyDocs API. Check your network and try again."
|
|
3187
|
+
);
|
|
3188
|
+
console.log(
|
|
3189
|
+
` ${pc7.dim("flydocs init requires server access. For offline use, run flydocs install instead.")}`
|
|
3190
|
+
);
|
|
3191
|
+
process.exit(1);
|
|
3192
|
+
}
|
|
3193
|
+
const workspaces = await fetchWorkspaces(apiKey);
|
|
3194
|
+
let workspaceId;
|
|
3195
|
+
if (workspaces.length === 0) {
|
|
3196
|
+
printError("No workspaces found. Create one at app.flydocs.ai first.");
|
|
3197
|
+
process.exit(1);
|
|
3198
|
+
} else if (workspaces.length === 1) {
|
|
3199
|
+
workspaceId = workspaces[0].id;
|
|
3200
|
+
printStatus(`Workspace: ${pc7.bold(workspaces[0].name)}`);
|
|
3201
|
+
} else {
|
|
3202
|
+
const choice = await select2({
|
|
3203
|
+
message: "Select workspace",
|
|
3204
|
+
options: workspaces.map((ws) => ({
|
|
3205
|
+
value: ws.id,
|
|
3206
|
+
label: ws.name
|
|
3207
|
+
}))
|
|
3208
|
+
});
|
|
3209
|
+
if (isCancel4(choice)) {
|
|
3210
|
+
cancel3("Init cancelled.");
|
|
3211
|
+
process.exit(0);
|
|
3212
|
+
}
|
|
3213
|
+
workspaceId = choice;
|
|
3214
|
+
}
|
|
3215
|
+
await writeGlobalCredential({
|
|
3216
|
+
apiKey,
|
|
3217
|
+
workspaceId,
|
|
3218
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3219
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
3220
|
+
});
|
|
3221
|
+
await checkCredentialPermissions();
|
|
3222
|
+
return { apiKey, workspaceId };
|
|
3223
|
+
}
|
|
3224
|
+
async function pullServerConfig(apiKey, targetDir, workspaceId) {
|
|
3225
|
+
printInfo("Pulling config from server...");
|
|
3226
|
+
const isFirstInit = !await pathExists(
|
|
3227
|
+
join19(targetDir, ".flydocs", "config.json")
|
|
3228
|
+
);
|
|
3229
|
+
try {
|
|
3230
|
+
const response = await fetchConfigV2(apiKey, {
|
|
3231
|
+
includeContext: isFirstInit,
|
|
3232
|
+
workspaceId
|
|
3233
|
+
});
|
|
3234
|
+
if (!response.valid) {
|
|
3235
|
+
printError("Server returned invalid config. Contact support.");
|
|
3236
|
+
process.exit(1);
|
|
3237
|
+
}
|
|
3238
|
+
return response;
|
|
3239
|
+
} catch (err) {
|
|
3240
|
+
if (err instanceof RelayError) {
|
|
3241
|
+
printError(`Server error: ${err.message}`);
|
|
3242
|
+
if (err.status === 401) {
|
|
3243
|
+
console.log(
|
|
3244
|
+
` ${pc7.dim("Your API key may be revoked. Run flydocs auth with a new key.")}`
|
|
3245
|
+
);
|
|
3246
|
+
}
|
|
3247
|
+
} else {
|
|
3248
|
+
printError("Could not reach server. Check your network.");
|
|
3249
|
+
}
|
|
3250
|
+
process.exit(1);
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
async function initSingleRepo(targetDir, apiKey, serverResponse) {
|
|
3254
|
+
const actions = [];
|
|
3255
|
+
const skipped = [];
|
|
3256
|
+
await mkdir9(join19(targetDir, ".flydocs"), { recursive: true });
|
|
3257
|
+
const configWithHash = applyConfigHash(serverResponse.config);
|
|
3258
|
+
await writeConfig(targetDir, configWithHash);
|
|
3259
|
+
actions.push("Wrote .flydocs/config.json (from server)");
|
|
3260
|
+
if (serverResponse.identity) {
|
|
3261
|
+
await writeGlobalIdentity({
|
|
3262
|
+
name: serverResponse.identity.name,
|
|
3263
|
+
email: serverResponse.identity.email,
|
|
3264
|
+
providerIdentities: serverResponse.identity.providerIdentities
|
|
3265
|
+
});
|
|
3266
|
+
actions.push("Wrote ~/.flydocs/me.json");
|
|
3267
|
+
}
|
|
3268
|
+
if (serverResponse.context) {
|
|
3269
|
+
const contextDir = join19(targetDir, "flydocs", "context");
|
|
3270
|
+
await mkdir9(contextDir, { recursive: true });
|
|
3271
|
+
const projectMdPath = join19(contextDir, "project.md");
|
|
3272
|
+
if (!await pathExists(projectMdPath)) {
|
|
3273
|
+
await writeFile12(projectMdPath, serverResponse.context.projectMd, "utf-8");
|
|
3274
|
+
actions.push("Wrote flydocs/context/project.md");
|
|
3275
|
+
} else {
|
|
3276
|
+
skipped.push("flydocs/context/project.md (already exists)");
|
|
3277
|
+
}
|
|
3278
|
+
const serviceJsonPath = join19(contextDir, "service.json");
|
|
3279
|
+
if (serverResponse.context.serviceJson && !await pathExists(serviceJsonPath)) {
|
|
3280
|
+
await writeFile12(
|
|
3281
|
+
serviceJsonPath,
|
|
3282
|
+
JSON.stringify(serverResponse.context.serviceJson, null, 2) + "\n",
|
|
3283
|
+
"utf-8"
|
|
3284
|
+
);
|
|
3285
|
+
actions.push("Wrote flydocs/context/service.json");
|
|
3286
|
+
} else if (await pathExists(serviceJsonPath)) {
|
|
3287
|
+
skipped.push("flydocs/context/service.json (already exists)");
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
await ensureGitignore(targetDir);
|
|
3291
|
+
await ensurePlatformIgnores(targetDir);
|
|
3292
|
+
actions.push("Updated ignore files");
|
|
3293
|
+
return { actions, skipped };
|
|
3294
|
+
}
|
|
3295
|
+
async function isParentDirectory(dir) {
|
|
3296
|
+
const hasGit = await pathExists(join19(dir, ".git"));
|
|
3297
|
+
if (hasGit) return false;
|
|
3298
|
+
const repos = await detectSiblingRepos(dir);
|
|
3299
|
+
return Object.keys(repos).length >= 2;
|
|
3300
|
+
}
|
|
3301
|
+
async function runMultiRepoInit(parentDir, keyArg) {
|
|
3302
|
+
const repos = await detectSiblingRepos(parentDir);
|
|
3303
|
+
const repoNames = Object.keys(repos).sort();
|
|
3304
|
+
printInfo(`Detected ${repoNames.length} repos:`);
|
|
3305
|
+
for (const name of repoNames) {
|
|
3306
|
+
console.log(` ${pc7.cyan(name)}`);
|
|
3307
|
+
}
|
|
3308
|
+
console.log();
|
|
3309
|
+
const shouldContinue = await confirm3({
|
|
3310
|
+
message: `Initialize all ${repoNames.length} repos?`
|
|
3311
|
+
});
|
|
3312
|
+
if (isCancel4(shouldContinue) || !shouldContinue) {
|
|
3313
|
+
cancel3("Init cancelled.");
|
|
3314
|
+
process.exit(0);
|
|
3315
|
+
}
|
|
3316
|
+
const firstRepoDir = join19(parentDir, repoNames[0]);
|
|
3317
|
+
const { apiKey, workspaceId } = await resolveAndValidateKey(
|
|
3318
|
+
keyArg,
|
|
3319
|
+
firstRepoDir
|
|
3320
|
+
);
|
|
3321
|
+
const allActions = [
|
|
3322
|
+
"Stored credential globally (~/.flydocs/credentials)"
|
|
3323
|
+
];
|
|
3324
|
+
const allSkipped = [];
|
|
3325
|
+
let allWarnings = [];
|
|
3326
|
+
let firstResponse;
|
|
3327
|
+
for (const repoName of repoNames) {
|
|
3328
|
+
const repoDir = join19(parentDir, repoName);
|
|
3329
|
+
console.log();
|
|
3330
|
+
printInfo(`Initializing ${pc7.bold(repoName)}...`);
|
|
3331
|
+
const serverResponse = await pullServerConfig(apiKey, repoDir, workspaceId);
|
|
3332
|
+
if (!firstResponse) firstResponse = serverResponse;
|
|
3333
|
+
const { actions, skipped } = await initSingleRepo(
|
|
3334
|
+
repoDir,
|
|
3335
|
+
apiKey,
|
|
3336
|
+
serverResponse
|
|
3337
|
+
);
|
|
3338
|
+
for (const action of actions) {
|
|
3339
|
+
allActions.push(`[${repoName}] ${action}`);
|
|
3340
|
+
}
|
|
3341
|
+
for (const skip of skipped) {
|
|
3342
|
+
allSkipped.push(`[${repoName}] ${skip}`);
|
|
3343
|
+
}
|
|
3344
|
+
allWarnings = [...allWarnings, ...serverResponse.warnings];
|
|
3345
|
+
printStatus(`${repoName} initialized`);
|
|
3346
|
+
}
|
|
3347
|
+
if (firstResponse) {
|
|
3348
|
+
const existing = await readWorkspaceFile(parentDir);
|
|
3349
|
+
if (existing) {
|
|
3350
|
+
allSkipped.push(".flydocs-workspace.json (already exists)");
|
|
3351
|
+
} else {
|
|
3352
|
+
const workspaceFile = buildWorkspaceFile(
|
|
3353
|
+
firstResponse.workspaceId,
|
|
3354
|
+
repos
|
|
3355
|
+
);
|
|
3356
|
+
await writeWorkspaceFile(parentDir, workspaceFile);
|
|
3357
|
+
allActions.push(`.flydocs-workspace.json (${repoNames.length} repos)`);
|
|
3358
|
+
}
|
|
3359
|
+
const contextDir = join19(parentDir, "flydocs", "context");
|
|
3360
|
+
const workspaceMdPath = join19(contextDir, "workspace.md");
|
|
3361
|
+
if (await pathExists(workspaceMdPath)) {
|
|
3362
|
+
allSkipped.push("flydocs/context/workspace.md (already exists)");
|
|
3363
|
+
} else {
|
|
3364
|
+
await mkdir9(contextDir, { recursive: true });
|
|
3365
|
+
const workspaceJson = await readWorkspaceFile(parentDir) ?? buildWorkspaceFile(firstResponse.workspaceId, repos);
|
|
3366
|
+
const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(parentDir, workspaceJson);
|
|
3367
|
+
await writeFile12(workspaceMdPath, content, "utf-8");
|
|
3368
|
+
const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
|
|
3369
|
+
allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
console.log();
|
|
3373
|
+
printInitReport(allActions, allSkipped, allWarnings);
|
|
3374
|
+
}
|
|
3375
|
+
function printInitReport(actions, skipped, warnings) {
|
|
3376
|
+
console.log();
|
|
3377
|
+
const lines = [];
|
|
3378
|
+
for (const action of actions) {
|
|
3379
|
+
lines.push(`${pc7.green("+")} ${action}`);
|
|
3380
|
+
}
|
|
3381
|
+
for (const skip of skipped) {
|
|
3382
|
+
lines.push(`${pc7.dim("-")} ${skip}`);
|
|
3383
|
+
}
|
|
3384
|
+
if (warnings.length > 0) {
|
|
3385
|
+
lines.push("");
|
|
3386
|
+
for (const warning of warnings) {
|
|
3387
|
+
lines.push(`${pc7.yellow("!")} ${warning}`);
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
printCompletionBox("FlyDocs Initialized", lines);
|
|
3391
|
+
console.log();
|
|
3392
|
+
console.log(` Next steps:`);
|
|
3393
|
+
console.log(
|
|
3394
|
+
` ${pc7.cyan("flydocs sync")} \u2014 pull latest config and templates`
|
|
3395
|
+
);
|
|
3396
|
+
console.log(` ${pc7.cyan("/start-session")} \u2014 begin working`);
|
|
3397
|
+
console.log();
|
|
3398
|
+
}
|
|
3399
|
+
var init_default;
|
|
3400
|
+
var init_init = __esm({
|
|
3401
|
+
"src/commands/init.ts"() {
|
|
3402
|
+
"use strict";
|
|
3403
|
+
init_ui();
|
|
3404
|
+
init_api_key();
|
|
3405
|
+
init_global_config();
|
|
3406
|
+
init_config();
|
|
3407
|
+
init_config_integrity();
|
|
3408
|
+
init_gitignore();
|
|
3409
|
+
init_fs_ops();
|
|
3410
|
+
init_relay_client();
|
|
3411
|
+
init_workspace();
|
|
3412
|
+
init_default = defineCommand2({
|
|
3413
|
+
meta: {
|
|
3414
|
+
name: "init",
|
|
3415
|
+
description: "Initialize FlyDocs in this project (unified setup)"
|
|
3416
|
+
},
|
|
3417
|
+
args: {
|
|
3418
|
+
key: {
|
|
3419
|
+
type: "string",
|
|
3420
|
+
description: "FlyDocs API key (fdk_...)"
|
|
3421
|
+
},
|
|
3422
|
+
path: {
|
|
3423
|
+
type: "string",
|
|
3424
|
+
description: "Path to project directory"
|
|
3425
|
+
}
|
|
3426
|
+
},
|
|
3427
|
+
async run({ args }) {
|
|
3428
|
+
const targetDir = args.path ?? process.cwd();
|
|
3429
|
+
console.log();
|
|
3430
|
+
console.log(` ${pc7.bold("FlyDocs Init")}`);
|
|
3431
|
+
console.log();
|
|
3432
|
+
if (await isParentDirectory(targetDir)) {
|
|
3433
|
+
await runMultiRepoInit(targetDir, args.key);
|
|
3434
|
+
return;
|
|
3435
|
+
}
|
|
3436
|
+
const { apiKey, workspaceId } = await resolveAndValidateKey(
|
|
3437
|
+
args.key,
|
|
3438
|
+
targetDir
|
|
3439
|
+
);
|
|
3440
|
+
const serverResponse = await pullServerConfig(
|
|
3441
|
+
apiKey,
|
|
3442
|
+
targetDir,
|
|
3443
|
+
workspaceId
|
|
3444
|
+
);
|
|
3445
|
+
const { actions, skipped } = await initSingleRepo(
|
|
3446
|
+
targetDir,
|
|
3447
|
+
apiKey,
|
|
3448
|
+
serverResponse
|
|
3449
|
+
);
|
|
3450
|
+
actions.unshift("Stored credential globally (~/.flydocs/credentials)");
|
|
3451
|
+
const topology = serverResponse.config.topology;
|
|
3452
|
+
if (topology?.type === 4 && topology.label === "sibling-repos") {
|
|
3453
|
+
const parentDir = join19(targetDir, "..");
|
|
3454
|
+
const existing = await readWorkspaceFile(parentDir);
|
|
3455
|
+
if (existing) {
|
|
3456
|
+
skipped.push(".flydocs-workspace.json (already exists)");
|
|
3457
|
+
} else {
|
|
3458
|
+
const repos = await detectSiblingRepos(parentDir);
|
|
3459
|
+
if (Object.keys(repos).length > 0) {
|
|
3460
|
+
const workspaceFile = buildWorkspaceFile(
|
|
3461
|
+
serverResponse.workspaceId,
|
|
3462
|
+
repos
|
|
3463
|
+
);
|
|
3464
|
+
await writeWorkspaceFile(parentDir, workspaceFile);
|
|
3465
|
+
actions.push(
|
|
3466
|
+
`.flydocs-workspace.json (${Object.keys(repos).length} repos detected)`
|
|
3467
|
+
);
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
printInitReport(actions, skipped, serverResponse.warnings);
|
|
3472
|
+
}
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
});
|
|
3476
|
+
|
|
2762
3477
|
// src/commands/update.ts
|
|
2763
3478
|
var update_exports = {};
|
|
2764
3479
|
__export(update_exports, {
|
|
2765
3480
|
default: () => update_default
|
|
2766
3481
|
});
|
|
2767
|
-
import { defineCommand as
|
|
2768
|
-
import { resolve as
|
|
2769
|
-
import { mkdir as
|
|
2770
|
-
import { select as
|
|
2771
|
-
import
|
|
3482
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
3483
|
+
import { resolve as resolve4, join as join20 } from "path";
|
|
3484
|
+
import { mkdir as mkdir10, cp as cp2, readFile as readFile14, readdir as readdir5, rm as rm4 } from "fs/promises";
|
|
3485
|
+
import { select as select3, text as text3, confirm as confirm4, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
|
|
3486
|
+
import pc8 from "picocolors";
|
|
2772
3487
|
var update_default;
|
|
2773
3488
|
var init_update = __esm({
|
|
2774
3489
|
"src/commands/update.ts"() {
|
|
@@ -2787,7 +3502,7 @@ var init_update = __esm({
|
|
|
2787
3502
|
init_update_check();
|
|
2788
3503
|
init_telemetry();
|
|
2789
3504
|
init_integrity();
|
|
2790
|
-
update_default =
|
|
3505
|
+
update_default = defineCommand3({
|
|
2791
3506
|
meta: {
|
|
2792
3507
|
name: "update",
|
|
2793
3508
|
description: "Update an existing FlyDocs installation"
|
|
@@ -2832,11 +3547,11 @@ var init_update = __esm({
|
|
|
2832
3547
|
await capture("update_started", { template_version: version });
|
|
2833
3548
|
let targetDir;
|
|
2834
3549
|
if (args.path) {
|
|
2835
|
-
targetDir =
|
|
3550
|
+
targetDir = resolve4(args.path.replace(/^~/, process.env.HOME ?? "~"));
|
|
2836
3551
|
} else if (args.here) {
|
|
2837
3552
|
targetDir = process.cwd();
|
|
2838
3553
|
} else {
|
|
2839
|
-
const choice = await
|
|
3554
|
+
const choice = await select3({
|
|
2840
3555
|
message: "Which project would you like to update?",
|
|
2841
3556
|
options: [
|
|
2842
3557
|
{
|
|
@@ -2849,21 +3564,21 @@ var init_update = __esm({
|
|
|
2849
3564
|
}
|
|
2850
3565
|
]
|
|
2851
3566
|
});
|
|
2852
|
-
if (
|
|
2853
|
-
|
|
3567
|
+
if (isCancel5(choice)) {
|
|
3568
|
+
cancel4("Update cancelled.");
|
|
2854
3569
|
process.exit(0);
|
|
2855
3570
|
}
|
|
2856
3571
|
if (choice === "cwd") {
|
|
2857
3572
|
targetDir = process.cwd();
|
|
2858
3573
|
} else {
|
|
2859
|
-
const enteredPath = await
|
|
3574
|
+
const enteredPath = await text3({
|
|
2860
3575
|
message: "Enter project path:"
|
|
2861
3576
|
});
|
|
2862
|
-
if (
|
|
2863
|
-
|
|
3577
|
+
if (isCancel5(enteredPath)) {
|
|
3578
|
+
cancel4("Update cancelled.");
|
|
2864
3579
|
process.exit(0);
|
|
2865
3580
|
}
|
|
2866
|
-
targetDir =
|
|
3581
|
+
targetDir = resolve4(
|
|
2867
3582
|
enteredPath.replace(/^~/, process.env.HOME ?? "~")
|
|
2868
3583
|
);
|
|
2869
3584
|
}
|
|
@@ -2872,11 +3587,11 @@ var init_update = __esm({
|
|
|
2872
3587
|
printError(`Directory does not exist: ${targetDir}`);
|
|
2873
3588
|
process.exit(1);
|
|
2874
3589
|
}
|
|
2875
|
-
targetDir =
|
|
3590
|
+
targetDir = resolve4(targetDir);
|
|
2876
3591
|
process.chdir(targetDir);
|
|
2877
|
-
const hasVersion = await pathExists(
|
|
3592
|
+
const hasVersion = await pathExists(join20(targetDir, ".flydocs", "version"));
|
|
2878
3593
|
const hasConfig = await pathExists(
|
|
2879
|
-
|
|
3594
|
+
join20(targetDir, ".flydocs", "config.json")
|
|
2880
3595
|
);
|
|
2881
3596
|
if (!hasVersion && !hasConfig) {
|
|
2882
3597
|
printError(`Not a FlyDocs project: ${targetDir}`);
|
|
@@ -2887,8 +3602,8 @@ var init_update = __esm({
|
|
|
2887
3602
|
console.log();
|
|
2888
3603
|
let currentVersion = "0.1.0";
|
|
2889
3604
|
if (hasVersion) {
|
|
2890
|
-
const vContent = await
|
|
2891
|
-
|
|
3605
|
+
const vContent = await readFile14(
|
|
3606
|
+
join20(targetDir, ".flydocs", "version"),
|
|
2892
3607
|
"utf-8"
|
|
2893
3608
|
);
|
|
2894
3609
|
currentVersion = vContent.trim();
|
|
@@ -2907,10 +3622,10 @@ var init_update = __esm({
|
|
|
2907
3622
|
printWarning(
|
|
2908
3623
|
`Project version (${currentVersion}) is newer than installer (${version})`
|
|
2909
3624
|
);
|
|
2910
|
-
const shouldContinue = await
|
|
3625
|
+
const shouldContinue = await confirm4({
|
|
2911
3626
|
message: "Continue anyway?"
|
|
2912
3627
|
});
|
|
2913
|
-
if (
|
|
3628
|
+
if (isCancel5(shouldContinue) || !shouldContinue) {
|
|
2914
3629
|
process.exit(0);
|
|
2915
3630
|
}
|
|
2916
3631
|
}
|
|
@@ -2922,10 +3637,10 @@ var init_update = __esm({
|
|
|
2922
3637
|
});
|
|
2923
3638
|
console.log(`Updating: v${currentVersion} \u2192 v${version}`);
|
|
2924
3639
|
console.log();
|
|
2925
|
-
const changelogPath =
|
|
3640
|
+
const changelogPath = join20(templateDir, "CHANGELOG.md");
|
|
2926
3641
|
const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
|
|
2927
3642
|
if (whatsNew.length > 0) {
|
|
2928
|
-
console.log(
|
|
3643
|
+
console.log(pc8.cyan("What's new:"));
|
|
2929
3644
|
console.log();
|
|
2930
3645
|
for (const entry of whatsNew) {
|
|
2931
3646
|
console.log(` ${entry}`);
|
|
@@ -2934,23 +3649,23 @@ var init_update = __esm({
|
|
|
2934
3649
|
}
|
|
2935
3650
|
const now = /* @__PURE__ */ new Date();
|
|
2936
3651
|
const ts = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}${String(now.getSeconds()).padStart(2, "0")}`;
|
|
2937
|
-
const backupDir =
|
|
2938
|
-
await
|
|
3652
|
+
const backupDir = join20(targetDir, ".flydocs", `backup-${ts}`);
|
|
3653
|
+
await mkdir10(backupDir, { recursive: true });
|
|
2939
3654
|
if (hasConfig) {
|
|
2940
3655
|
await cp2(
|
|
2941
|
-
|
|
2942
|
-
|
|
3656
|
+
join20(targetDir, ".flydocs", "config.json"),
|
|
3657
|
+
join20(backupDir, "config.json")
|
|
2943
3658
|
);
|
|
2944
3659
|
printStatus(`Config backed up to .flydocs/backup-${ts}/`);
|
|
2945
3660
|
}
|
|
2946
3661
|
try {
|
|
2947
|
-
const flydocsDir =
|
|
2948
|
-
const entries = await
|
|
3662
|
+
const flydocsDir = join20(targetDir, ".flydocs");
|
|
3663
|
+
const entries = await readdir5(flydocsDir);
|
|
2949
3664
|
const backups = entries.filter((e) => e.startsWith("backup-")).sort();
|
|
2950
3665
|
if (backups.length > 3) {
|
|
2951
3666
|
const toRemove = backups.slice(0, backups.length - 3);
|
|
2952
3667
|
for (const old of toRemove) {
|
|
2953
|
-
await rm4(
|
|
3668
|
+
await rm4(join20(flydocsDir, old), { recursive: true, force: true });
|
|
2954
3669
|
}
|
|
2955
3670
|
}
|
|
2956
3671
|
} catch {
|
|
@@ -2989,17 +3704,17 @@ var init_update = __esm({
|
|
|
2989
3704
|
await ensureDirectories(targetDir, effectiveTier);
|
|
2990
3705
|
console.log("Replacing framework directories...");
|
|
2991
3706
|
await replaceDirectory(
|
|
2992
|
-
|
|
2993
|
-
|
|
3707
|
+
join20(templateDir, ".flydocs", "templates"),
|
|
3708
|
+
join20(targetDir, ".flydocs", "templates")
|
|
2994
3709
|
);
|
|
2995
3710
|
printStatus(".flydocs/templates");
|
|
2996
3711
|
await replaceDirectory(
|
|
2997
|
-
|
|
2998
|
-
|
|
3712
|
+
join20(templateDir, ".claude", "hooks"),
|
|
3713
|
+
join20(targetDir, ".claude", "hooks")
|
|
2999
3714
|
);
|
|
3000
3715
|
printStatus(".claude/hooks");
|
|
3001
3716
|
const hasExistingAgents = await pathExists(
|
|
3002
|
-
|
|
3717
|
+
join20(targetDir, ".claude", "agents")
|
|
3003
3718
|
);
|
|
3004
3719
|
let installAgents;
|
|
3005
3720
|
if (args.yes) {
|
|
@@ -3008,7 +3723,7 @@ var init_update = __esm({
|
|
|
3008
3723
|
installAgents = true;
|
|
3009
3724
|
} else {
|
|
3010
3725
|
console.log();
|
|
3011
|
-
console.log(` ${
|
|
3726
|
+
console.log(` ${pc8.bold(pc8.yellow("Sub-Agents (Recommended)"))}`);
|
|
3012
3727
|
console.log();
|
|
3013
3728
|
console.log(
|
|
3014
3729
|
" Sub-agents are specialized roles (PM, implementation, review,"
|
|
@@ -3020,31 +3735,31 @@ var init_update = __esm({
|
|
|
3020
3735
|
" structure work but are not required \u2014 everything works without them."
|
|
3021
3736
|
);
|
|
3022
3737
|
console.log();
|
|
3023
|
-
const agentConfirm = await
|
|
3738
|
+
const agentConfirm = await confirm4({
|
|
3024
3739
|
message: "Install sub-agents?",
|
|
3025
3740
|
initialValue: true
|
|
3026
3741
|
});
|
|
3027
|
-
if (
|
|
3742
|
+
if (isCancel5(agentConfirm)) {
|
|
3028
3743
|
installAgents = false;
|
|
3029
3744
|
} else {
|
|
3030
3745
|
installAgents = agentConfirm;
|
|
3031
3746
|
}
|
|
3032
3747
|
}
|
|
3033
3748
|
if (installAgents) {
|
|
3034
|
-
const claudeAgentsSrc =
|
|
3749
|
+
const claudeAgentsSrc = join20(templateDir, ".claude", "agents");
|
|
3035
3750
|
if (await pathExists(claudeAgentsSrc)) {
|
|
3036
|
-
await
|
|
3751
|
+
await mkdir10(join20(targetDir, ".claude", "agents"), { recursive: true });
|
|
3037
3752
|
await copyDirectoryContents(
|
|
3038
3753
|
claudeAgentsSrc,
|
|
3039
|
-
|
|
3754
|
+
join20(targetDir, ".claude", "agents")
|
|
3040
3755
|
);
|
|
3041
3756
|
}
|
|
3042
|
-
const cursorAgentsSrc =
|
|
3757
|
+
const cursorAgentsSrc = join20(templateDir, ".cursor", "agents");
|
|
3043
3758
|
if (await pathExists(cursorAgentsSrc)) {
|
|
3044
|
-
await
|
|
3759
|
+
await mkdir10(join20(targetDir, ".cursor", "agents"), { recursive: true });
|
|
3045
3760
|
await copyDirectoryContents(
|
|
3046
3761
|
cursorAgentsSrc,
|
|
3047
|
-
|
|
3762
|
+
join20(targetDir, ".cursor", "agents")
|
|
3048
3763
|
);
|
|
3049
3764
|
}
|
|
3050
3765
|
printStatus(
|
|
@@ -3058,58 +3773,58 @@ var init_update = __esm({
|
|
|
3058
3773
|
console.log();
|
|
3059
3774
|
console.log("Replacing framework files...");
|
|
3060
3775
|
await copyFile(
|
|
3061
|
-
|
|
3062
|
-
|
|
3776
|
+
join20(templateDir, ".claude", "CLAUDE.md"),
|
|
3777
|
+
join20(targetDir, ".claude", "CLAUDE.md")
|
|
3063
3778
|
);
|
|
3064
3779
|
await copyFile(
|
|
3065
|
-
|
|
3066
|
-
|
|
3780
|
+
join20(templateDir, ".claude", "settings.json"),
|
|
3781
|
+
join20(targetDir, ".claude", "settings.json")
|
|
3067
3782
|
);
|
|
3068
3783
|
printStatus(".claude/CLAUDE.md, settings.json");
|
|
3069
3784
|
await copyDirectoryContents(
|
|
3070
|
-
|
|
3071
|
-
|
|
3785
|
+
join20(templateDir, ".claude", "commands"),
|
|
3786
|
+
join20(targetDir, ".claude", "commands")
|
|
3072
3787
|
);
|
|
3073
3788
|
await copyDirectoryContents(
|
|
3074
|
-
|
|
3075
|
-
|
|
3789
|
+
join20(templateDir, ".claude", "commands"),
|
|
3790
|
+
join20(targetDir, ".cursor", "commands")
|
|
3076
3791
|
);
|
|
3077
3792
|
printStatus(".claude/commands, .cursor/commands");
|
|
3078
|
-
const skillsReadmeSrc =
|
|
3793
|
+
const skillsReadmeSrc = join20(templateDir, ".claude", "skills", "README.md");
|
|
3079
3794
|
if (await pathExists(skillsReadmeSrc)) {
|
|
3080
3795
|
await copyFile(
|
|
3081
3796
|
skillsReadmeSrc,
|
|
3082
|
-
|
|
3797
|
+
join20(targetDir, ".claude", "skills", "README.md")
|
|
3083
3798
|
);
|
|
3084
3799
|
}
|
|
3085
3800
|
printStatus(".claude/skills/README.md");
|
|
3086
3801
|
await copyFile(
|
|
3087
|
-
|
|
3088
|
-
|
|
3802
|
+
join20(templateDir, ".cursor", "hooks.json"),
|
|
3803
|
+
join20(targetDir, ".cursor", "hooks.json")
|
|
3089
3804
|
);
|
|
3090
3805
|
printStatus(".cursor/hooks.json");
|
|
3091
3806
|
await copyFile(
|
|
3092
|
-
|
|
3093
|
-
|
|
3807
|
+
join20(templateDir, "AGENTS.md"),
|
|
3808
|
+
join20(targetDir, "AGENTS.md")
|
|
3094
3809
|
);
|
|
3095
3810
|
printStatus("AGENTS.md");
|
|
3096
|
-
const envExampleSrc =
|
|
3811
|
+
const envExampleSrc = join20(templateDir, ".env.example");
|
|
3097
3812
|
if (await pathExists(envExampleSrc)) {
|
|
3098
|
-
await copyFile(envExampleSrc,
|
|
3813
|
+
await copyFile(envExampleSrc, join20(targetDir, ".env.example"));
|
|
3099
3814
|
printStatus(".env.example");
|
|
3100
3815
|
}
|
|
3101
|
-
const knowledgeTemplatesDir =
|
|
3816
|
+
const knowledgeTemplatesDir = join20(
|
|
3102
3817
|
targetDir,
|
|
3103
3818
|
"flydocs",
|
|
3104
3819
|
"knowledge",
|
|
3105
3820
|
"templates"
|
|
3106
3821
|
);
|
|
3107
3822
|
if (!await pathExists(knowledgeTemplatesDir)) {
|
|
3108
|
-
await
|
|
3823
|
+
await mkdir10(knowledgeTemplatesDir, { recursive: true });
|
|
3109
3824
|
}
|
|
3110
3825
|
for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
|
|
3111
|
-
const src =
|
|
3112
|
-
const dest =
|
|
3826
|
+
const src = join20(templateDir, "flydocs", "knowledge", "templates", tmpl);
|
|
3827
|
+
const dest = join20(knowledgeTemplatesDir, tmpl);
|
|
3113
3828
|
if (await pathExists(src) && !await pathExists(dest)) {
|
|
3114
3829
|
await copyFile(src, dest);
|
|
3115
3830
|
}
|
|
@@ -3136,18 +3851,18 @@ var init_update = __esm({
|
|
|
3136
3851
|
printWarning("Config merge failed \u2014 config.json preserved as-is");
|
|
3137
3852
|
}
|
|
3138
3853
|
await copyFile(
|
|
3139
|
-
|
|
3140
|
-
|
|
3854
|
+
join20(templateDir, ".flydocs", "version"),
|
|
3855
|
+
join20(targetDir, ".flydocs", "version")
|
|
3141
3856
|
);
|
|
3142
3857
|
printStatus(`.flydocs/version \u2192 ${version}`);
|
|
3143
|
-
const clSrc =
|
|
3858
|
+
const clSrc = join20(templateDir, "CHANGELOG.md");
|
|
3144
3859
|
if (await pathExists(clSrc)) {
|
|
3145
|
-
await copyFile(clSrc,
|
|
3860
|
+
await copyFile(clSrc, join20(targetDir, ".flydocs", "CHANGELOG.md"));
|
|
3146
3861
|
printStatus(".flydocs/CHANGELOG.md");
|
|
3147
3862
|
}
|
|
3148
|
-
const mfSrc =
|
|
3863
|
+
const mfSrc = join20(templateDir, "manifest.json");
|
|
3149
3864
|
if (await pathExists(mfSrc)) {
|
|
3150
|
-
await copyFile(mfSrc,
|
|
3865
|
+
await copyFile(mfSrc, join20(targetDir, ".flydocs", "manifest.json"));
|
|
3151
3866
|
printStatus(".flydocs/manifest.json");
|
|
3152
3867
|
}
|
|
3153
3868
|
await generateIntegrity(targetDir, version);
|
|
@@ -3209,22 +3924,22 @@ var uninstall_exports = {};
|
|
|
3209
3924
|
__export(uninstall_exports, {
|
|
3210
3925
|
default: () => uninstall_default
|
|
3211
3926
|
});
|
|
3212
|
-
import { defineCommand as
|
|
3213
|
-
import { resolve as
|
|
3214
|
-
import { readdir as
|
|
3215
|
-
import { confirm as
|
|
3216
|
-
import
|
|
3927
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
3928
|
+
import { resolve as resolve5, join as join21 } from "path";
|
|
3929
|
+
import { readdir as readdir6, rm as rm5, rename as rename2 } from "fs/promises";
|
|
3930
|
+
import { confirm as confirm5, select as select4, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
|
|
3931
|
+
import pc9 from "picocolors";
|
|
3217
3932
|
async function removeOwnedSkills(targetDir) {
|
|
3218
|
-
const skillsDir =
|
|
3933
|
+
const skillsDir = join21(targetDir, ".claude", "skills");
|
|
3219
3934
|
const removed = [];
|
|
3220
3935
|
if (!await pathExists(skillsDir)) {
|
|
3221
3936
|
return removed;
|
|
3222
3937
|
}
|
|
3223
3938
|
try {
|
|
3224
|
-
const entries = await
|
|
3939
|
+
const entries = await readdir6(skillsDir);
|
|
3225
3940
|
for (const entry of entries) {
|
|
3226
3941
|
if (entry.startsWith(OWNED_SKILL_PREFIX)) {
|
|
3227
|
-
await rm5(
|
|
3942
|
+
await rm5(join21(skillsDir, entry), { recursive: true, force: true });
|
|
3228
3943
|
removed.push(`.claude/skills/${entry}`);
|
|
3229
3944
|
}
|
|
3230
3945
|
}
|
|
@@ -3233,16 +3948,16 @@ async function removeOwnedSkills(targetDir) {
|
|
|
3233
3948
|
return removed;
|
|
3234
3949
|
}
|
|
3235
3950
|
async function removeOwnedCursorRules(targetDir) {
|
|
3236
|
-
const rulesDir =
|
|
3951
|
+
const rulesDir = join21(targetDir, ".cursor", "rules");
|
|
3237
3952
|
const removed = [];
|
|
3238
3953
|
if (!await pathExists(rulesDir)) {
|
|
3239
3954
|
return removed;
|
|
3240
3955
|
}
|
|
3241
3956
|
try {
|
|
3242
|
-
const entries = await
|
|
3957
|
+
const entries = await readdir6(rulesDir);
|
|
3243
3958
|
for (const entry of entries) {
|
|
3244
3959
|
if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
|
|
3245
|
-
await rm5(
|
|
3960
|
+
await rm5(join21(rulesDir, entry), { force: true });
|
|
3246
3961
|
removed.push(`.cursor/rules/${entry}`);
|
|
3247
3962
|
}
|
|
3248
3963
|
}
|
|
@@ -3252,7 +3967,7 @@ async function removeOwnedCursorRules(targetDir) {
|
|
|
3252
3967
|
}
|
|
3253
3968
|
async function isEmptyDir(dirPath) {
|
|
3254
3969
|
try {
|
|
3255
|
-
const entries = await
|
|
3970
|
+
const entries = await readdir6(dirPath);
|
|
3256
3971
|
return entries.length === 0;
|
|
3257
3972
|
} catch {
|
|
3258
3973
|
return false;
|
|
@@ -3261,7 +3976,7 @@ async function isEmptyDir(dirPath) {
|
|
|
3261
3976
|
async function cleanupEmptyParents(targetDir, dirs) {
|
|
3262
3977
|
const cleaned = [];
|
|
3263
3978
|
for (const dir of dirs) {
|
|
3264
|
-
const fullPath =
|
|
3979
|
+
const fullPath = join21(targetDir, dir);
|
|
3265
3980
|
if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
|
|
3266
3981
|
await rm5(fullPath, { recursive: true, force: true });
|
|
3267
3982
|
cleaned.push(dir);
|
|
@@ -3293,7 +4008,7 @@ var init_uninstall = __esm({
|
|
|
3293
4008
|
];
|
|
3294
4009
|
OWNED_SKILL_PREFIX = "flydocs-";
|
|
3295
4010
|
OWNED_RULE_PREFIX = "flydocs-";
|
|
3296
|
-
uninstall_default =
|
|
4011
|
+
uninstall_default = defineCommand4({
|
|
3297
4012
|
meta: {
|
|
3298
4013
|
name: "uninstall",
|
|
3299
4014
|
description: "Remove FlyDocs from a project directory"
|
|
@@ -3329,7 +4044,7 @@ var init_uninstall = __esm({
|
|
|
3329
4044
|
printBanner(CLI_VERSION);
|
|
3330
4045
|
let targetDir;
|
|
3331
4046
|
if (args.path) {
|
|
3332
|
-
targetDir =
|
|
4047
|
+
targetDir = resolve5(args.path.replace(/^~/, process.env.HOME ?? "~"));
|
|
3333
4048
|
} else if (args.here) {
|
|
3334
4049
|
targetDir = process.cwd();
|
|
3335
4050
|
} else {
|
|
@@ -3339,9 +4054,9 @@ var init_uninstall = __esm({
|
|
|
3339
4054
|
printError(`Directory does not exist: ${targetDir}`);
|
|
3340
4055
|
process.exit(1);
|
|
3341
4056
|
}
|
|
3342
|
-
targetDir =
|
|
3343
|
-
const hasFlydocs = await pathExists(
|
|
3344
|
-
const hasAgentsMd = await pathExists(
|
|
4057
|
+
targetDir = resolve5(targetDir);
|
|
4058
|
+
const hasFlydocs = await pathExists(join21(targetDir, ".flydocs"));
|
|
4059
|
+
const hasAgentsMd = await pathExists(join21(targetDir, "AGENTS.md"));
|
|
3345
4060
|
if (!hasFlydocs && !hasAgentsMd) {
|
|
3346
4061
|
printError(`Not a FlyDocs project: ${targetDir}`);
|
|
3347
4062
|
printInfo("No .flydocs/ directory or AGENTS.md found.");
|
|
@@ -3353,12 +4068,12 @@ var init_uninstall = __esm({
|
|
|
3353
4068
|
const removeAll = forceAll || args.all;
|
|
3354
4069
|
const skipPrompts = forceAll || args.yes;
|
|
3355
4070
|
let contentAction = "preserve";
|
|
3356
|
-
const hasUserContent = await pathExists(
|
|
4071
|
+
const hasUserContent = await pathExists(join21(targetDir, "flydocs"));
|
|
3357
4072
|
if (hasUserContent) {
|
|
3358
4073
|
if (removeAll) {
|
|
3359
4074
|
contentAction = "remove";
|
|
3360
4075
|
} else if (!skipPrompts) {
|
|
3361
|
-
const choice = await
|
|
4076
|
+
const choice = await select4({
|
|
3362
4077
|
message: "What should happen to your flydocs/ content (project docs, knowledge base)?",
|
|
3363
4078
|
options: [
|
|
3364
4079
|
{
|
|
@@ -3378,8 +4093,8 @@ var init_uninstall = __esm({
|
|
|
3378
4093
|
}
|
|
3379
4094
|
]
|
|
3380
4095
|
});
|
|
3381
|
-
if (
|
|
3382
|
-
|
|
4096
|
+
if (isCancel6(choice)) {
|
|
4097
|
+
cancel5("Uninstall cancelled.");
|
|
3383
4098
|
process.exit(0);
|
|
3384
4099
|
}
|
|
3385
4100
|
contentAction = choice;
|
|
@@ -3387,40 +4102,40 @@ var init_uninstall = __esm({
|
|
|
3387
4102
|
}
|
|
3388
4103
|
if (!skipPrompts) {
|
|
3389
4104
|
console.log();
|
|
3390
|
-
console.log(
|
|
4105
|
+
console.log(pc9.bold("The following will be removed:"));
|
|
3391
4106
|
console.log();
|
|
3392
4107
|
console.log(" Framework files:");
|
|
3393
4108
|
for (const [path] of ALWAYS_REMOVED) {
|
|
3394
|
-
console.log(` ${
|
|
4109
|
+
console.log(` ${pc9.dim(path)}`);
|
|
3395
4110
|
}
|
|
3396
|
-
console.log(` ${
|
|
3397
|
-
console.log(` ${
|
|
4111
|
+
console.log(` ${pc9.dim(".claude/skills/flydocs-*")}`);
|
|
4112
|
+
console.log(` ${pc9.dim(".cursor/rules/flydocs-*.mdc")}`);
|
|
3398
4113
|
if (hasUserContent) {
|
|
3399
4114
|
if (contentAction === "archive") {
|
|
3400
4115
|
console.log();
|
|
3401
4116
|
console.log(
|
|
3402
|
-
` User content: ${
|
|
4117
|
+
` User content: ${pc9.yellow("flydocs/ -> flydocs-archive/")}`
|
|
3403
4118
|
);
|
|
3404
4119
|
} else if (contentAction === "remove") {
|
|
3405
4120
|
console.log();
|
|
3406
|
-
console.log(` User content: ${
|
|
4121
|
+
console.log(` User content: ${pc9.red("flydocs/ (deleted)")}`);
|
|
3407
4122
|
} else {
|
|
3408
4123
|
console.log();
|
|
3409
|
-
console.log(` User content: ${
|
|
4124
|
+
console.log(` User content: ${pc9.green("flydocs/ (preserved)")}`);
|
|
3410
4125
|
}
|
|
3411
4126
|
}
|
|
3412
4127
|
console.log();
|
|
3413
|
-
console.log(
|
|
4128
|
+
console.log(pc9.bold("Preserved:"));
|
|
3414
4129
|
console.log(
|
|
3415
|
-
` ${
|
|
4130
|
+
` ${pc9.dim(".claude/skills/ (non-flydocs community skills)")}`
|
|
3416
4131
|
);
|
|
3417
|
-
console.log(` ${
|
|
4132
|
+
console.log(` ${pc9.dim(".env, .env.local")}`);
|
|
3418
4133
|
console.log();
|
|
3419
|
-
const shouldContinue = await
|
|
4134
|
+
const shouldContinue = await confirm5({
|
|
3420
4135
|
message: "Proceed with uninstall?"
|
|
3421
4136
|
});
|
|
3422
|
-
if (
|
|
3423
|
-
|
|
4137
|
+
if (isCancel6(shouldContinue) || !shouldContinue) {
|
|
4138
|
+
cancel5("Uninstall cancelled.");
|
|
3424
4139
|
process.exit(0);
|
|
3425
4140
|
}
|
|
3426
4141
|
}
|
|
@@ -3436,7 +4151,7 @@ var init_uninstall = __esm({
|
|
|
3436
4151
|
const removedRules = await removeOwnedCursorRules(targetDir);
|
|
3437
4152
|
result.removed.push(...removedRules);
|
|
3438
4153
|
for (const [relativePath, type] of ALWAYS_REMOVED) {
|
|
3439
|
-
const fullPath =
|
|
4154
|
+
const fullPath = join21(targetDir, relativePath);
|
|
3440
4155
|
if (!await pathExists(fullPath)) {
|
|
3441
4156
|
result.skipped.push(relativePath);
|
|
3442
4157
|
continue;
|
|
@@ -3454,9 +4169,9 @@ var init_uninstall = __esm({
|
|
|
3454
4169
|
}
|
|
3455
4170
|
}
|
|
3456
4171
|
if (hasUserContent) {
|
|
3457
|
-
const flydocsPath =
|
|
4172
|
+
const flydocsPath = join21(targetDir, "flydocs");
|
|
3458
4173
|
if (contentAction === "archive") {
|
|
3459
|
-
const archivePath =
|
|
4174
|
+
const archivePath = join21(targetDir, "flydocs-archive");
|
|
3460
4175
|
if (await pathExists(archivePath)) {
|
|
3461
4176
|
await rm5(archivePath, { recursive: true, force: true });
|
|
3462
4177
|
}
|
|
@@ -3482,17 +4197,17 @@ var init_uninstall = __esm({
|
|
|
3482
4197
|
result.restored = originals.map((f) => f.relativePath);
|
|
3483
4198
|
}
|
|
3484
4199
|
console.log();
|
|
3485
|
-
console.log(
|
|
4200
|
+
console.log(pc9.bold("Uninstall Summary"));
|
|
3486
4201
|
console.log();
|
|
3487
4202
|
if (result.removed.length > 0) {
|
|
3488
|
-
console.log(` ${
|
|
4203
|
+
console.log(` ${pc9.green("Removed")} (${result.removed.length}):`);
|
|
3489
4204
|
for (const item of result.removed) {
|
|
3490
4205
|
printStatus(item);
|
|
3491
4206
|
}
|
|
3492
4207
|
}
|
|
3493
4208
|
if (result.archived.length > 0) {
|
|
3494
4209
|
console.log();
|
|
3495
|
-
console.log(` ${
|
|
4210
|
+
console.log(` ${pc9.yellow("Archived")} (${result.archived.length}):`);
|
|
3496
4211
|
for (const item of result.archived) {
|
|
3497
4212
|
printInfo(item);
|
|
3498
4213
|
}
|
|
@@ -3500,7 +4215,7 @@ var init_uninstall = __esm({
|
|
|
3500
4215
|
if (result.restored.length > 0) {
|
|
3501
4216
|
console.log();
|
|
3502
4217
|
console.log(
|
|
3503
|
-
` ${
|
|
4218
|
+
` ${pc9.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
|
|
3504
4219
|
);
|
|
3505
4220
|
for (const item of result.restored) {
|
|
3506
4221
|
printInfo(item);
|
|
@@ -3509,16 +4224,16 @@ var init_uninstall = __esm({
|
|
|
3509
4224
|
if (result.skipped.length > 0) {
|
|
3510
4225
|
console.log();
|
|
3511
4226
|
console.log(
|
|
3512
|
-
` ${
|
|
4227
|
+
` ${pc9.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
|
|
3513
4228
|
);
|
|
3514
4229
|
for (const item of result.skipped) {
|
|
3515
|
-
console.log(` ${
|
|
4230
|
+
console.log(` ${pc9.dim(item)}`);
|
|
3516
4231
|
}
|
|
3517
4232
|
}
|
|
3518
4233
|
console.log();
|
|
3519
4234
|
printStatus("FlyDocs has been removed from this project.");
|
|
3520
4235
|
console.log();
|
|
3521
|
-
printInfo(`To reinstall: ${
|
|
4236
|
+
printInfo(`To reinstall: ${pc9.cyan("npx @flydocs/cli install --here")}`);
|
|
3522
4237
|
console.log();
|
|
3523
4238
|
}
|
|
3524
4239
|
});
|
|
@@ -3530,28 +4245,28 @@ var setup_exports = {};
|
|
|
3530
4245
|
__export(setup_exports, {
|
|
3531
4246
|
default: () => setup_default
|
|
3532
4247
|
});
|
|
3533
|
-
import { defineCommand as
|
|
3534
|
-
import
|
|
4248
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
4249
|
+
import pc10 from "picocolors";
|
|
3535
4250
|
var setup_default;
|
|
3536
4251
|
var init_setup = __esm({
|
|
3537
4252
|
"src/commands/setup.ts"() {
|
|
3538
4253
|
"use strict";
|
|
3539
|
-
setup_default =
|
|
4254
|
+
setup_default = defineCommand5({
|
|
3540
4255
|
meta: {
|
|
3541
4256
|
name: "setup",
|
|
3542
4257
|
description: "Configure FlyDocs settings for this project"
|
|
3543
4258
|
},
|
|
3544
4259
|
run() {
|
|
3545
4260
|
console.log();
|
|
3546
|
-
console.log(` ${
|
|
4261
|
+
console.log(` ${pc10.bold("FlyDocs Setup")}`);
|
|
3547
4262
|
console.log();
|
|
3548
4263
|
console.log(` Setup runs inside your IDE as an interactive AI command.`);
|
|
3549
4264
|
console.log();
|
|
3550
4265
|
console.log(
|
|
3551
|
-
` ${
|
|
4266
|
+
` ${pc10.cyan("Claude Code:")} Type ${pc10.bold("/flydocs-setup")} in chat`
|
|
3552
4267
|
);
|
|
3553
4268
|
console.log(
|
|
3554
|
-
` ${
|
|
4269
|
+
` ${pc10.cyan("Cursor:")} Type ${pc10.bold("/flydocs-setup")} in chat`
|
|
3555
4270
|
);
|
|
3556
4271
|
console.log();
|
|
3557
4272
|
console.log(` This configures your project context, detects your stack,`);
|
|
@@ -3567,14 +4282,14 @@ var skills_exports = {};
|
|
|
3567
4282
|
__export(skills_exports, {
|
|
3568
4283
|
default: () => skills_default
|
|
3569
4284
|
});
|
|
3570
|
-
import { defineCommand as
|
|
3571
|
-
import
|
|
4285
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
4286
|
+
import pc11 from "picocolors";
|
|
3572
4287
|
var list, search, add, remove, skills_default;
|
|
3573
4288
|
var init_skills2 = __esm({
|
|
3574
4289
|
"src/commands/skills.ts"() {
|
|
3575
4290
|
"use strict";
|
|
3576
4291
|
init_skill_manager();
|
|
3577
|
-
list =
|
|
4292
|
+
list = defineCommand6({
|
|
3578
4293
|
meta: {
|
|
3579
4294
|
name: "list",
|
|
3580
4295
|
description: "List installed skills"
|
|
@@ -3590,26 +4305,26 @@ var init_skills2 = __esm({
|
|
|
3590
4305
|
console.log(`${total} skill(s) installed:`);
|
|
3591
4306
|
if (result.platform.length > 0) {
|
|
3592
4307
|
console.log();
|
|
3593
|
-
console.log(
|
|
4308
|
+
console.log(pc11.bold("Platform"));
|
|
3594
4309
|
for (const skill of result.platform) {
|
|
3595
4310
|
console.log(
|
|
3596
|
-
` ${skill.name} ${
|
|
4311
|
+
` ${skill.name} ${pc11.dim(`(${skill.triggers} triggers)`)}`
|
|
3597
4312
|
);
|
|
3598
4313
|
}
|
|
3599
4314
|
}
|
|
3600
4315
|
if (result.community.length > 0) {
|
|
3601
4316
|
console.log();
|
|
3602
|
-
console.log(
|
|
4317
|
+
console.log(pc11.bold("Community"));
|
|
3603
4318
|
for (const skill of result.community) {
|
|
3604
4319
|
console.log(
|
|
3605
|
-
` ${skill.name} ${
|
|
4320
|
+
` ${skill.name} ${pc11.dim(`(${skill.triggers} triggers)`)}`
|
|
3606
4321
|
);
|
|
3607
4322
|
}
|
|
3608
4323
|
}
|
|
3609
4324
|
console.log();
|
|
3610
4325
|
}
|
|
3611
4326
|
});
|
|
3612
|
-
search =
|
|
4327
|
+
search = defineCommand6({
|
|
3613
4328
|
meta: {
|
|
3614
4329
|
name: "search",
|
|
3615
4330
|
description: "Search community skills"
|
|
@@ -3625,24 +4340,24 @@ var init_skills2 = __esm({
|
|
|
3625
4340
|
const results = await searchCatalog(args.keyword);
|
|
3626
4341
|
if (results.length === 0) {
|
|
3627
4342
|
console.log(`No skills found for "${args.keyword}".`);
|
|
3628
|
-
console.log(` Browse the catalog at: ${
|
|
4343
|
+
console.log(` Browse the catalog at: ${pc11.cyan("https://skills.sh/")}`);
|
|
3629
4344
|
return;
|
|
3630
4345
|
}
|
|
3631
4346
|
console.log();
|
|
3632
4347
|
console.log(`${results.length} skill(s) matching "${args.keyword}":`);
|
|
3633
4348
|
console.log();
|
|
3634
4349
|
for (const skill of results) {
|
|
3635
|
-
console.log(` ${
|
|
4350
|
+
console.log(` ${pc11.bold(skill.name)}`);
|
|
3636
4351
|
console.log(` ${skill.description}`);
|
|
3637
|
-
console.log(` ${
|
|
4352
|
+
console.log(` ${pc11.dim(skill.repo)}`);
|
|
3638
4353
|
if (skill.tags.length > 0) {
|
|
3639
|
-
console.log(` ${
|
|
4354
|
+
console.log(` ${pc11.dim(skill.tags.join(", "))}`);
|
|
3640
4355
|
}
|
|
3641
4356
|
console.log();
|
|
3642
4357
|
}
|
|
3643
4358
|
}
|
|
3644
4359
|
});
|
|
3645
|
-
add =
|
|
4360
|
+
add = defineCommand6({
|
|
3646
4361
|
meta: {
|
|
3647
4362
|
name: "add",
|
|
3648
4363
|
description: "Install a community skill"
|
|
@@ -3658,7 +4373,7 @@ var init_skills2 = __esm({
|
|
|
3658
4373
|
await addSkill(process.cwd(), args.source);
|
|
3659
4374
|
}
|
|
3660
4375
|
});
|
|
3661
|
-
remove =
|
|
4376
|
+
remove = defineCommand6({
|
|
3662
4377
|
meta: {
|
|
3663
4378
|
name: "remove",
|
|
3664
4379
|
description: "Remove an installed community skill"
|
|
@@ -3674,7 +4389,7 @@ var init_skills2 = __esm({
|
|
|
3674
4389
|
await removeSkill(process.cwd(), args.name);
|
|
3675
4390
|
}
|
|
3676
4391
|
});
|
|
3677
|
-
skills_default =
|
|
4392
|
+
skills_default = defineCommand6({
|
|
3678
4393
|
meta: {
|
|
3679
4394
|
name: "skills",
|
|
3680
4395
|
description: "Manage FlyDocs skills (list, search, add, remove)"
|
|
@@ -3694,10 +4409,10 @@ var connect_exports = {};
|
|
|
3694
4409
|
__export(connect_exports, {
|
|
3695
4410
|
default: () => connect_default
|
|
3696
4411
|
});
|
|
3697
|
-
import { defineCommand as
|
|
3698
|
-
import { text as
|
|
3699
|
-
import
|
|
3700
|
-
import { join as
|
|
4412
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
4413
|
+
import { text as text4, confirm as confirm6, isCancel as isCancel7, cancel as cancel6 } from "@clack/prompts";
|
|
4414
|
+
import pc12 from "picocolors";
|
|
4415
|
+
import { join as join22 } from "path";
|
|
3701
4416
|
var connect_default;
|
|
3702
4417
|
var init_connect = __esm({
|
|
3703
4418
|
"src/commands/connect.ts"() {
|
|
@@ -3707,7 +4422,7 @@ var init_connect = __esm({
|
|
|
3707
4422
|
init_template();
|
|
3708
4423
|
init_ui();
|
|
3709
4424
|
init_api_key();
|
|
3710
|
-
connect_default =
|
|
4425
|
+
connect_default = defineCommand7({
|
|
3711
4426
|
meta: {
|
|
3712
4427
|
name: "connect",
|
|
3713
4428
|
description: "Connect FlyDocs to a cloud provider"
|
|
@@ -3732,11 +4447,11 @@ var init_connect = __esm({
|
|
|
3732
4447
|
},
|
|
3733
4448
|
async run({ args }) {
|
|
3734
4449
|
const targetDir = args.path ?? process.cwd();
|
|
3735
|
-
const configPath =
|
|
4450
|
+
const configPath = join22(targetDir, ".flydocs", "config.json");
|
|
3736
4451
|
if (!await pathExists(configPath)) {
|
|
3737
4452
|
printError("Not a FlyDocs project (.flydocs/config.json not found).");
|
|
3738
4453
|
console.log(
|
|
3739
|
-
` Run ${
|
|
4454
|
+
` Run ${pc12.cyan("flydocs")} first to install FlyDocs in this project.`
|
|
3740
4455
|
);
|
|
3741
4456
|
process.exit(1);
|
|
3742
4457
|
}
|
|
@@ -3744,24 +4459,24 @@ var init_connect = __esm({
|
|
|
3744
4459
|
if (config.tier === "cloud") {
|
|
3745
4460
|
printInfo("This project is already connected to the cloud tier.");
|
|
3746
4461
|
console.log();
|
|
3747
|
-
const reconnect = await
|
|
4462
|
+
const reconnect = await confirm6({
|
|
3748
4463
|
message: "Want to update your API key?"
|
|
3749
4464
|
});
|
|
3750
|
-
if (
|
|
4465
|
+
if (isCancel7(reconnect) || !reconnect) {
|
|
3751
4466
|
console.log(` No changes made.`);
|
|
3752
4467
|
return;
|
|
3753
4468
|
}
|
|
3754
4469
|
}
|
|
3755
4470
|
console.log();
|
|
3756
|
-
console.log(` ${
|
|
4471
|
+
console.log(` ${pc12.bold("Connect to FlyDocs Cloud")}`);
|
|
3757
4472
|
console.log();
|
|
3758
4473
|
console.log(
|
|
3759
|
-
` ${
|
|
4474
|
+
` ${pc12.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
3760
4475
|
);
|
|
3761
4476
|
console.log();
|
|
3762
4477
|
let apiKey = args.key ?? "";
|
|
3763
4478
|
if (!apiKey) {
|
|
3764
|
-
const keyInput = await
|
|
4479
|
+
const keyInput = await text4({
|
|
3765
4480
|
message: "Enter your API key",
|
|
3766
4481
|
placeholder: "fdk_...",
|
|
3767
4482
|
validate(value) {
|
|
@@ -3772,8 +4487,8 @@ var init_connect = __esm({
|
|
|
3772
4487
|
return void 0;
|
|
3773
4488
|
}
|
|
3774
4489
|
});
|
|
3775
|
-
if (
|
|
3776
|
-
|
|
4490
|
+
if (isCancel7(keyInput)) {
|
|
4491
|
+
cancel6("Connection cancelled.");
|
|
3777
4492
|
process.exit(0);
|
|
3778
4493
|
}
|
|
3779
4494
|
apiKey = keyInput.trim();
|
|
@@ -3794,7 +4509,7 @@ var init_connect = __esm({
|
|
|
3794
4509
|
console.log(` Check your key and try again.`);
|
|
3795
4510
|
process.exit(1);
|
|
3796
4511
|
}
|
|
3797
|
-
printStatus(`Connected to ${
|
|
4512
|
+
printStatus(`Connected to ${pc12.bold(result.org)}`);
|
|
3798
4513
|
} catch {
|
|
3799
4514
|
printError(
|
|
3800
4515
|
"Could not reach relay API. Check your network and try again."
|
|
@@ -3802,7 +4517,7 @@ var init_connect = __esm({
|
|
|
3802
4517
|
process.exit(1);
|
|
3803
4518
|
}
|
|
3804
4519
|
const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
|
|
3805
|
-
printStatus(`API key stored in ${
|
|
4520
|
+
printStatus(`API key stored in ${pc12.dim(envFile)}`);
|
|
3806
4521
|
} else {
|
|
3807
4522
|
try {
|
|
3808
4523
|
const result = await validateLinearKey(apiKey);
|
|
@@ -3812,7 +4527,7 @@ var init_connect = __esm({
|
|
|
3812
4527
|
process.exit(1);
|
|
3813
4528
|
}
|
|
3814
4529
|
printStatus(
|
|
3815
|
-
`Authenticated as ${
|
|
4530
|
+
`Authenticated as ${pc12.bold(result.name)} (${result.email})`
|
|
3816
4531
|
);
|
|
3817
4532
|
} catch {
|
|
3818
4533
|
printError("Invalid API key or network error.");
|
|
@@ -3820,7 +4535,7 @@ var init_connect = __esm({
|
|
|
3820
4535
|
process.exit(1);
|
|
3821
4536
|
}
|
|
3822
4537
|
const envFile = await storeEnvKey(targetDir, "LINEAR_API_KEY", apiKey);
|
|
3823
|
-
printStatus(`API key stored in ${
|
|
4538
|
+
printStatus(`API key stored in ${pc12.dim(envFile)}`);
|
|
3824
4539
|
}
|
|
3825
4540
|
const wasLocal = config.tier === "local";
|
|
3826
4541
|
config.tier = "cloud";
|
|
@@ -3836,14 +4551,473 @@ var init_connect = __esm({
|
|
|
3836
4551
|
}
|
|
3837
4552
|
console.log();
|
|
3838
4553
|
console.log(
|
|
3839
|
-
` ${
|
|
4554
|
+
` ${pc12.bold("Connected!")} Your project is now on the cloud tier.`
|
|
3840
4555
|
);
|
|
3841
4556
|
console.log();
|
|
3842
4557
|
console.log(` Next steps:`);
|
|
3843
4558
|
console.log(
|
|
3844
|
-
` 1. Run ${
|
|
4559
|
+
` 1. Run ${pc12.cyan("/flydocs-setup")} in your IDE to configure your project`
|
|
4560
|
+
);
|
|
4561
|
+
console.log(` 2. Run ${pc12.cyan("/start-session")} to begin working`);
|
|
4562
|
+
console.log();
|
|
4563
|
+
}
|
|
4564
|
+
});
|
|
4565
|
+
}
|
|
4566
|
+
});
|
|
4567
|
+
|
|
4568
|
+
// src/commands/auth.ts
|
|
4569
|
+
var auth_exports = {};
|
|
4570
|
+
__export(auth_exports, {
|
|
4571
|
+
default: () => auth_default
|
|
4572
|
+
});
|
|
4573
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
4574
|
+
import { text as text5, confirm as confirm7, isCancel as isCancel8, cancel as cancel7 } from "@clack/prompts";
|
|
4575
|
+
import pc13 from "picocolors";
|
|
4576
|
+
var auth_default;
|
|
4577
|
+
var init_auth = __esm({
|
|
4578
|
+
"src/commands/auth.ts"() {
|
|
4579
|
+
"use strict";
|
|
4580
|
+
init_ui();
|
|
4581
|
+
init_api_key();
|
|
4582
|
+
init_global_config();
|
|
4583
|
+
auth_default = defineCommand8({
|
|
4584
|
+
meta: {
|
|
4585
|
+
name: "auth",
|
|
4586
|
+
description: "Store API key globally (~/.flydocs/credentials)"
|
|
4587
|
+
},
|
|
4588
|
+
args: {
|
|
4589
|
+
key: {
|
|
4590
|
+
type: "positional",
|
|
4591
|
+
description: "FlyDocs API key (fdk_...)",
|
|
4592
|
+
required: false
|
|
4593
|
+
}
|
|
4594
|
+
},
|
|
4595
|
+
async run({ args }) {
|
|
4596
|
+
console.log();
|
|
4597
|
+
console.log(` ${pc13.bold("FlyDocs Authentication")}`);
|
|
4598
|
+
console.log();
|
|
4599
|
+
let apiKey = args.key ?? "";
|
|
4600
|
+
const existing = await readGlobalCredential();
|
|
4601
|
+
if (existing?.apiKey && !apiKey) {
|
|
4602
|
+
printInfo(
|
|
4603
|
+
`Existing key found: ${pc13.dim(existing.apiKey.slice(0, 8) + "...")}`
|
|
4604
|
+
);
|
|
4605
|
+
const replace = await confirm7({
|
|
4606
|
+
message: "Replace with a new key?"
|
|
4607
|
+
});
|
|
4608
|
+
if (isCancel8(replace) || !replace) {
|
|
4609
|
+
console.log(` No changes made.`);
|
|
4610
|
+
return;
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
if (!apiKey) {
|
|
4614
|
+
console.log(
|
|
4615
|
+
` ${pc13.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
4616
|
+
);
|
|
4617
|
+
console.log();
|
|
4618
|
+
const keyInput = await text5({
|
|
4619
|
+
message: "Enter your API key",
|
|
4620
|
+
placeholder: "fdk_...",
|
|
4621
|
+
validate(value) {
|
|
4622
|
+
if (!value.trim()) return "API key is required";
|
|
4623
|
+
if (detectKeyType(value.trim()) !== "relay") {
|
|
4624
|
+
return "Key must start with fdk_ (FlyDocs API key)";
|
|
4625
|
+
}
|
|
4626
|
+
return void 0;
|
|
4627
|
+
}
|
|
4628
|
+
});
|
|
4629
|
+
if (isCancel8(keyInput)) {
|
|
4630
|
+
cancel7("Authentication cancelled.");
|
|
4631
|
+
process.exit(0);
|
|
4632
|
+
}
|
|
4633
|
+
apiKey = keyInput.trim();
|
|
4634
|
+
}
|
|
4635
|
+
if (detectKeyType(apiKey) !== "relay") {
|
|
4636
|
+
printError("Invalid key format. Expected fdk_ prefix (FlyDocs API key).");
|
|
4637
|
+
process.exit(1);
|
|
4638
|
+
}
|
|
4639
|
+
if (existing?.apiKey === apiKey) {
|
|
4640
|
+
printInfo("This key is already stored.");
|
|
4641
|
+
await checkCredentialPermissions();
|
|
4642
|
+
return;
|
|
4643
|
+
}
|
|
4644
|
+
printInfo("Validating API key...");
|
|
4645
|
+
try {
|
|
4646
|
+
const result = await validateRelayKey(apiKey);
|
|
4647
|
+
if (!result.valid) {
|
|
4648
|
+
printError("Invalid API key. Check your key and try again.");
|
|
4649
|
+
process.exit(1);
|
|
4650
|
+
}
|
|
4651
|
+
printStatus(`Authenticated with ${pc13.bold(result.org)}`);
|
|
4652
|
+
} catch {
|
|
4653
|
+
printError(
|
|
4654
|
+
"Could not reach FlyDocs API. Check your network and try again."
|
|
4655
|
+
);
|
|
4656
|
+
process.exit(1);
|
|
4657
|
+
}
|
|
4658
|
+
if (existing?.apiKey && existing.apiKey !== apiKey) {
|
|
4659
|
+
printWarning("Replacing existing key.");
|
|
4660
|
+
}
|
|
4661
|
+
await writeGlobalCredential({
|
|
4662
|
+
apiKey,
|
|
4663
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4664
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
4665
|
+
});
|
|
4666
|
+
printStatus(`Key stored at ${pc13.dim(credentialsPath())}`);
|
|
4667
|
+
await checkCredentialPermissions();
|
|
4668
|
+
console.log();
|
|
4669
|
+
console.log(` ${pc13.bold("Authenticated!")} Key stored globally.`);
|
|
4670
|
+
console.log(` All FlyDocs projects on this machine will use this key.`);
|
|
4671
|
+
console.log();
|
|
4672
|
+
}
|
|
4673
|
+
});
|
|
4674
|
+
}
|
|
4675
|
+
});
|
|
4676
|
+
|
|
4677
|
+
// src/commands/sync.ts
|
|
4678
|
+
var sync_exports = {};
|
|
4679
|
+
__export(sync_exports, {
|
|
4680
|
+
default: () => sync_default
|
|
4681
|
+
});
|
|
4682
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
4683
|
+
import pc14 from "picocolors";
|
|
4684
|
+
import { join as join23 } from "path";
|
|
4685
|
+
import { mkdir as mkdir11, writeFile as writeFile13 } from "fs/promises";
|
|
4686
|
+
var sync_default;
|
|
4687
|
+
var init_sync = __esm({
|
|
4688
|
+
"src/commands/sync.ts"() {
|
|
4689
|
+
"use strict";
|
|
4690
|
+
init_ui();
|
|
4691
|
+
init_global_config();
|
|
4692
|
+
init_config();
|
|
4693
|
+
init_config_integrity();
|
|
4694
|
+
init_fs_ops();
|
|
4695
|
+
init_types();
|
|
4696
|
+
init_relay_client();
|
|
4697
|
+
sync_default = defineCommand9({
|
|
4698
|
+
meta: {
|
|
4699
|
+
name: "sync",
|
|
4700
|
+
description: "Pull latest config and templates from server"
|
|
4701
|
+
},
|
|
4702
|
+
args: {
|
|
4703
|
+
path: {
|
|
4704
|
+
type: "string",
|
|
4705
|
+
description: "Path to project directory"
|
|
4706
|
+
}
|
|
4707
|
+
},
|
|
4708
|
+
async run({ args }) {
|
|
4709
|
+
const targetDir = args.path ?? process.cwd();
|
|
4710
|
+
const changes = [];
|
|
4711
|
+
console.log();
|
|
4712
|
+
printInfo("Syncing with server...");
|
|
4713
|
+
const resolved = await resolveApiKey(void 0, targetDir);
|
|
4714
|
+
if (!resolved) {
|
|
4715
|
+
printError(
|
|
4716
|
+
"No API key found. Run `flydocs auth` or `flydocs init` first."
|
|
4717
|
+
);
|
|
4718
|
+
process.exit(1);
|
|
4719
|
+
}
|
|
4720
|
+
const apiKey = resolved.key;
|
|
4721
|
+
const cred = await readGlobalCredential();
|
|
4722
|
+
const workspaceId = cred?.workspaceId;
|
|
4723
|
+
if (!workspaceId) {
|
|
4724
|
+
printError(
|
|
4725
|
+
"No workspace ID found. Run `flydocs init` to set up your workspace."
|
|
4726
|
+
);
|
|
4727
|
+
process.exit(1);
|
|
4728
|
+
}
|
|
4729
|
+
let currentTemplateVersion = 0;
|
|
4730
|
+
try {
|
|
4731
|
+
const currentConfig = await readAnyConfig(targetDir);
|
|
4732
|
+
if (isConfigV2(currentConfig)) {
|
|
4733
|
+
currentTemplateVersion = currentConfig.configVersion ?? 0;
|
|
4734
|
+
}
|
|
4735
|
+
} catch {
|
|
4736
|
+
}
|
|
4737
|
+
let serverResponse;
|
|
4738
|
+
try {
|
|
4739
|
+
serverResponse = await fetchConfigV2(apiKey, { workspaceId });
|
|
4740
|
+
} catch (err) {
|
|
4741
|
+
if (err instanceof RelayError) {
|
|
4742
|
+
printWarning(
|
|
4743
|
+
`Server unavailable (${err.status}), using cached config.`
|
|
4744
|
+
);
|
|
4745
|
+
} else {
|
|
4746
|
+
printWarning("Server unreachable, using cached config.");
|
|
4747
|
+
}
|
|
4748
|
+
console.log(` ${pc14.dim("Config may be stale. Retry when connected.")}`);
|
|
4749
|
+
return;
|
|
4750
|
+
}
|
|
4751
|
+
if (!serverResponse.valid) {
|
|
4752
|
+
printWarning("Server returned invalid config. Keeping current config.");
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4755
|
+
await mkdir11(join23(targetDir, ".flydocs"), { recursive: true });
|
|
4756
|
+
const configWithHash = applyConfigHash(serverResponse.config);
|
|
4757
|
+
await writeConfig(targetDir, configWithHash);
|
|
4758
|
+
changes.push("Updated .flydocs/config.json");
|
|
4759
|
+
const serverTemplateVersion = serverResponse.templates.version;
|
|
4760
|
+
if (serverTemplateVersion > currentTemplateVersion) {
|
|
4761
|
+
try {
|
|
4762
|
+
const templatesResponse = await fetchTemplates(
|
|
4763
|
+
apiKey,
|
|
4764
|
+
currentTemplateVersion,
|
|
4765
|
+
workspaceId
|
|
4766
|
+
);
|
|
4767
|
+
if (templatesResponse.templates.length > 0) {
|
|
4768
|
+
const templatesDir = join23(
|
|
4769
|
+
targetDir,
|
|
4770
|
+
".claude",
|
|
4771
|
+
"skills",
|
|
4772
|
+
"flydocs-workflow",
|
|
4773
|
+
"templates"
|
|
4774
|
+
);
|
|
4775
|
+
await mkdir11(templatesDir, { recursive: true });
|
|
4776
|
+
for (const template of templatesResponse.templates) {
|
|
4777
|
+
const filename = `${template.type}.md`;
|
|
4778
|
+
const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
|
|
4779
|
+
const templateDir = join23(templatesDir, subdir);
|
|
4780
|
+
await mkdir11(templateDir, { recursive: true });
|
|
4781
|
+
await writeFile13(
|
|
4782
|
+
join23(templateDir, filename),
|
|
4783
|
+
template.content,
|
|
4784
|
+
"utf-8"
|
|
4785
|
+
);
|
|
4786
|
+
}
|
|
4787
|
+
changes.push(
|
|
4788
|
+
`Updated ${templatesResponse.templates.length} templates (v${currentTemplateVersion} \u2192 v${serverTemplateVersion})`
|
|
4789
|
+
);
|
|
4790
|
+
}
|
|
4791
|
+
} catch (err) {
|
|
4792
|
+
if (err instanceof RelayError) {
|
|
4793
|
+
printWarning("Could not fetch templates. Will retry on next sync.");
|
|
4794
|
+
} else {
|
|
4795
|
+
printWarning("Template sync failed. Will retry on next sync.");
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
if (changes.length === 0) {
|
|
4800
|
+
printStatus("Already up to date.");
|
|
4801
|
+
} else {
|
|
4802
|
+
for (const change of changes) {
|
|
4803
|
+
printStatus(change);
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
console.log();
|
|
4807
|
+
}
|
|
4808
|
+
});
|
|
4809
|
+
}
|
|
4810
|
+
});
|
|
4811
|
+
|
|
4812
|
+
// src/commands/cleanup.ts
|
|
4813
|
+
var cleanup_exports = {};
|
|
4814
|
+
__export(cleanup_exports, {
|
|
4815
|
+
default: () => cleanup_default
|
|
4816
|
+
});
|
|
4817
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
4818
|
+
import pc15 from "picocolors";
|
|
4819
|
+
import { join as join24 } from "path";
|
|
4820
|
+
import { readFile as readFile15, writeFile as writeFile14, rm as rm6 } from "fs/promises";
|
|
4821
|
+
async function scanArtifacts(targetDir) {
|
|
4822
|
+
const actions = [];
|
|
4823
|
+
const localMePath = join24(targetDir, ".flydocs", "me.json");
|
|
4824
|
+
if (await pathExists(localMePath) && await pathExists(globalMePath())) {
|
|
4825
|
+
actions.push({
|
|
4826
|
+
description: "Remove .flydocs/me.json (migrated to ~/.flydocs/me.json)",
|
|
4827
|
+
path: localMePath,
|
|
4828
|
+
type: "file"
|
|
4829
|
+
});
|
|
4830
|
+
}
|
|
4831
|
+
const validationCachePath = join24(
|
|
4832
|
+
targetDir,
|
|
4833
|
+
".flydocs",
|
|
4834
|
+
"validation-cache.json"
|
|
4835
|
+
);
|
|
4836
|
+
if (await pathExists(validationCachePath)) {
|
|
4837
|
+
actions.push({
|
|
4838
|
+
description: "Remove .flydocs/validation-cache.json (replaced by configVersion)",
|
|
4839
|
+
path: validationCachePath,
|
|
4840
|
+
type: "file"
|
|
4841
|
+
});
|
|
4842
|
+
}
|
|
4843
|
+
const hasGlobalCreds = await pathExists(credentialsPath());
|
|
4844
|
+
for (const envFile of [".env", ".env.local"]) {
|
|
4845
|
+
const envPath = join24(targetDir, envFile);
|
|
4846
|
+
if (!await pathExists(envPath)) continue;
|
|
4847
|
+
const content = await readFile15(envPath, "utf-8");
|
|
4848
|
+
const lines = content.split("\n");
|
|
4849
|
+
for (const line of lines) {
|
|
4850
|
+
const trimmed = line.trim();
|
|
4851
|
+
if (hasGlobalCreds && trimmed.startsWith("FLYDOCS_API_KEY=")) {
|
|
4852
|
+
actions.push({
|
|
4853
|
+
description: `Remove FLYDOCS_API_KEY from ${envFile} (migrated to ~/.flydocs/credentials)`,
|
|
4854
|
+
path: envPath,
|
|
4855
|
+
type: "line"
|
|
4856
|
+
});
|
|
4857
|
+
}
|
|
4858
|
+
if (trimmed.startsWith("FLYDOCS_RELAY_URL=")) {
|
|
4859
|
+
actions.push({
|
|
4860
|
+
description: `Remove FLYDOCS_RELAY_URL from ${envFile}`,
|
|
4861
|
+
path: envPath,
|
|
4862
|
+
type: "line"
|
|
4863
|
+
});
|
|
4864
|
+
}
|
|
4865
|
+
}
|
|
4866
|
+
}
|
|
4867
|
+
try {
|
|
4868
|
+
const config = await readAnyConfig(targetDir);
|
|
4869
|
+
const rawContent = await readFile15(
|
|
4870
|
+
join24(targetDir, ".flydocs", "config.json"),
|
|
4871
|
+
"utf-8"
|
|
4872
|
+
);
|
|
4873
|
+
const raw = JSON.parse(rawContent);
|
|
4874
|
+
const ghostFields = ["provider", "statusMapping"];
|
|
4875
|
+
for (const field of ghostFields) {
|
|
4876
|
+
if (field in raw) {
|
|
4877
|
+
actions.push({
|
|
4878
|
+
description: `Remove ghost field "${field}" from config.json`,
|
|
4879
|
+
path: join24(targetDir, ".flydocs", "config.json"),
|
|
4880
|
+
type: "field"
|
|
4881
|
+
});
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
if (isConfigV2(config)) {
|
|
4885
|
+
const v1OnlyFields = [
|
|
4886
|
+
"workspaceId",
|
|
4887
|
+
"issueLabels",
|
|
4888
|
+
"workspace",
|
|
4889
|
+
"onboardComplete",
|
|
4890
|
+
"sourceRepo",
|
|
4891
|
+
"designSystem",
|
|
4892
|
+
"aiLabor"
|
|
4893
|
+
];
|
|
4894
|
+
for (const field of v1OnlyFields) {
|
|
4895
|
+
if (field in raw) {
|
|
4896
|
+
actions.push({
|
|
4897
|
+
description: `Remove v1 field "${field}" from config.json (server-owned in v2)`,
|
|
4898
|
+
path: join24(targetDir, ".flydocs", "config.json"),
|
|
4899
|
+
type: "field"
|
|
4900
|
+
});
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
}
|
|
4904
|
+
} catch {
|
|
4905
|
+
}
|
|
4906
|
+
return actions;
|
|
4907
|
+
}
|
|
4908
|
+
async function executeCleanup(targetDir, actions) {
|
|
4909
|
+
const fileRemovals = actions.filter((a) => a.type === "file");
|
|
4910
|
+
const lineRemovals = actions.filter((a) => a.type === "line");
|
|
4911
|
+
const fieldRemovals = actions.filter((a) => a.type === "field");
|
|
4912
|
+
for (const action of fileRemovals) {
|
|
4913
|
+
await rm6(action.path, { force: true });
|
|
4914
|
+
}
|
|
4915
|
+
const envFiles = /* @__PURE__ */ new Map();
|
|
4916
|
+
for (const action of lineRemovals) {
|
|
4917
|
+
if (!envFiles.has(action.path)) {
|
|
4918
|
+
const content = await readFile15(action.path, "utf-8");
|
|
4919
|
+
envFiles.set(action.path, content.split("\n"));
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
for (const [envPath, lines] of envFiles) {
|
|
4923
|
+
const filtered = lines.filter((line) => {
|
|
4924
|
+
const trimmed = line.trim();
|
|
4925
|
+
return !trimmed.startsWith("FLYDOCS_API_KEY=") && !trimmed.startsWith("FLYDOCS_RELAY_URL=");
|
|
4926
|
+
});
|
|
4927
|
+
const hasContent = filtered.some(
|
|
4928
|
+
(l) => l.trim().length > 0 && !l.trim().startsWith("#")
|
|
4929
|
+
);
|
|
4930
|
+
if (!hasContent) {
|
|
4931
|
+
await rm6(envPath, { force: true });
|
|
4932
|
+
} else {
|
|
4933
|
+
await writeFile14(envPath, filtered.join("\n"), "utf-8");
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
if (fieldRemovals.length > 0) {
|
|
4937
|
+
const configPath = join24(targetDir, ".flydocs", "config.json");
|
|
4938
|
+
const content = await readFile15(configPath, "utf-8");
|
|
4939
|
+
const config = JSON.parse(content);
|
|
4940
|
+
for (const action of fieldRemovals) {
|
|
4941
|
+
const match = action.description.match(/"(\w+)"/);
|
|
4942
|
+
if (match) {
|
|
4943
|
+
delete config[match[1]];
|
|
4944
|
+
}
|
|
4945
|
+
}
|
|
4946
|
+
await writeFile14(
|
|
4947
|
+
configPath,
|
|
4948
|
+
JSON.stringify(config, null, 2) + "\n",
|
|
4949
|
+
"utf-8"
|
|
4950
|
+
);
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
var cleanup_default;
|
|
4954
|
+
var init_cleanup = __esm({
|
|
4955
|
+
"src/commands/cleanup.ts"() {
|
|
4956
|
+
"use strict";
|
|
4957
|
+
init_ui();
|
|
4958
|
+
init_fs_ops();
|
|
4959
|
+
init_config();
|
|
4960
|
+
init_types();
|
|
4961
|
+
init_global_config();
|
|
4962
|
+
cleanup_default = defineCommand10({
|
|
4963
|
+
meta: {
|
|
4964
|
+
name: "cleanup",
|
|
4965
|
+
description: "Remove legacy v1 artifacts after migration (dry-run by default)"
|
|
4966
|
+
},
|
|
4967
|
+
args: {
|
|
4968
|
+
confirm: {
|
|
4969
|
+
type: "boolean",
|
|
4970
|
+
description: "Actually remove artifacts (default: dry-run only)",
|
|
4971
|
+
default: false
|
|
4972
|
+
},
|
|
4973
|
+
path: {
|
|
4974
|
+
type: "string",
|
|
4975
|
+
description: "Path to project directory"
|
|
4976
|
+
}
|
|
4977
|
+
},
|
|
4978
|
+
async run({ args }) {
|
|
4979
|
+
const targetDir = args.path ?? process.cwd();
|
|
4980
|
+
console.log();
|
|
4981
|
+
console.log(` ${pc15.bold("FlyDocs Cleanup")}`);
|
|
4982
|
+
console.log();
|
|
4983
|
+
const hasConfig = await pathExists(
|
|
4984
|
+
join24(targetDir, ".flydocs", "config.json")
|
|
3845
4985
|
);
|
|
3846
|
-
|
|
4986
|
+
if (!hasConfig) {
|
|
4987
|
+
printError("No .flydocs/config.json found. Run flydocs init first.");
|
|
4988
|
+
process.exit(1);
|
|
4989
|
+
}
|
|
4990
|
+
const hasGlobalCreds = await pathExists(credentialsPath());
|
|
4991
|
+
if (!hasGlobalCreds) {
|
|
4992
|
+
printWarning(
|
|
4993
|
+
"No global credentials found. Run flydocs init to set up credentials before cleanup."
|
|
4994
|
+
);
|
|
4995
|
+
}
|
|
4996
|
+
const actions = await scanArtifacts(targetDir);
|
|
4997
|
+
if (actions.length === 0) {
|
|
4998
|
+
printStatus("No legacy artifacts found. Already clean.");
|
|
4999
|
+
console.log();
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
5002
|
+
const mode = args.confirm ? "Removing" : "Would remove";
|
|
5003
|
+
printInfo(`${mode} ${actions.length} legacy artifact(s):`);
|
|
5004
|
+
console.log();
|
|
5005
|
+
for (const action of actions) {
|
|
5006
|
+
const icon = args.confirm ? pc15.red("-") : pc15.yellow("?");
|
|
5007
|
+
console.log(` ${icon} ${action.description}`);
|
|
5008
|
+
}
|
|
5009
|
+
console.log();
|
|
5010
|
+
if (!args.confirm) {
|
|
5011
|
+
printInfo(
|
|
5012
|
+
`Dry-run complete. Run ${pc15.cyan("flydocs cleanup --confirm")} to remove.`
|
|
5013
|
+
);
|
|
5014
|
+
console.log();
|
|
5015
|
+
return;
|
|
5016
|
+
}
|
|
5017
|
+
await executeCleanup(targetDir, actions);
|
|
5018
|
+
printCompletionBox("Cleanup Complete", [
|
|
5019
|
+
`${pc15.green("+")} Removed ${actions.length} legacy artifact(s)`
|
|
5020
|
+
]);
|
|
3847
5021
|
console.log();
|
|
3848
5022
|
}
|
|
3849
5023
|
});
|
|
@@ -3855,15 +5029,15 @@ var upgrade_exports = {};
|
|
|
3855
5029
|
__export(upgrade_exports, {
|
|
3856
5030
|
default: () => upgrade_default
|
|
3857
5031
|
});
|
|
3858
|
-
import { defineCommand as
|
|
3859
|
-
import
|
|
5032
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
5033
|
+
import pc16 from "picocolors";
|
|
3860
5034
|
var upgrade_default;
|
|
3861
5035
|
var init_upgrade = __esm({
|
|
3862
5036
|
"src/commands/upgrade.ts"() {
|
|
3863
5037
|
"use strict";
|
|
3864
5038
|
init_config();
|
|
3865
5039
|
init_fs_ops();
|
|
3866
|
-
upgrade_default =
|
|
5040
|
+
upgrade_default = defineCommand11({
|
|
3867
5041
|
meta: {
|
|
3868
5042
|
name: "upgrade",
|
|
3869
5043
|
description: "Learn about FlyDocs Cloud tier and upgrade from local"
|
|
@@ -3892,34 +5066,34 @@ var init_upgrade = __esm({
|
|
|
3892
5066
|
console.log();
|
|
3893
5067
|
if (currentTier === "cloud") {
|
|
3894
5068
|
console.log(
|
|
3895
|
-
` ${
|
|
5069
|
+
` ${pc16.green("\u2713")} You're already on the ${pc16.bold("cloud")} tier.`
|
|
3896
5070
|
);
|
|
3897
5071
|
console.log();
|
|
3898
5072
|
console.log(` Your issues sync with your provider via the relay API.`);
|
|
3899
5073
|
console.log(
|
|
3900
|
-
` Run ${
|
|
5074
|
+
` Run ${pc16.cyan("flydocs connect")} to update your connection settings.`
|
|
3901
5075
|
);
|
|
3902
5076
|
console.log();
|
|
3903
5077
|
return;
|
|
3904
5078
|
}
|
|
3905
|
-
console.log(` ${
|
|
5079
|
+
console.log(` ${pc16.bold("FlyDocs Cloud Tier")}`);
|
|
3906
5080
|
console.log();
|
|
3907
|
-
console.log(` You're currently on the ${
|
|
5081
|
+
console.log(` You're currently on the ${pc16.yellow("local")} tier.`);
|
|
3908
5082
|
console.log(` Upgrade to cloud for:`);
|
|
3909
5083
|
console.log();
|
|
3910
|
-
console.log(` ${
|
|
3911
|
-
console.log(` ${
|
|
3912
|
-
console.log(` ${
|
|
3913
|
-
console.log(` ${
|
|
3914
|
-
console.log(` ${
|
|
5084
|
+
console.log(` ${pc16.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
|
|
5085
|
+
console.log(` ${pc16.cyan("\u2192")} Project milestones and cycle management`);
|
|
5086
|
+
console.log(` ${pc16.cyan("\u2192")} Team assignment and priority tracking`);
|
|
5087
|
+
console.log(` ${pc16.cyan("\u2192")} Project health updates and dashboards`);
|
|
5088
|
+
console.log(` ${pc16.cyan("\u2192")} Cross-project issue linking`);
|
|
3915
5089
|
console.log();
|
|
3916
|
-
console.log(` ${
|
|
5090
|
+
console.log(` ${pc16.bold("How to upgrade:")}`);
|
|
3917
5091
|
console.log();
|
|
3918
|
-
console.log(` Option 1: Run ${
|
|
5092
|
+
console.log(` Option 1: Run ${pc16.cyan("/flydocs-upgrade")} in your IDE`);
|
|
3919
5093
|
console.log(` Guided migration with issue transfer`);
|
|
3920
5094
|
console.log();
|
|
3921
5095
|
console.log(
|
|
3922
|
-
` Option 2: Run ${
|
|
5096
|
+
` Option 2: Run ${pc16.cyan("flydocs connect")} from terminal`
|
|
3923
5097
|
);
|
|
3924
5098
|
console.log(` Quick tier swap (no issue migration)`);
|
|
3925
5099
|
console.log();
|
|
@@ -3933,23 +5107,23 @@ var self_update_exports = {};
|
|
|
3933
5107
|
__export(self_update_exports, {
|
|
3934
5108
|
default: () => self_update_default
|
|
3935
5109
|
});
|
|
3936
|
-
import { defineCommand as
|
|
5110
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
3937
5111
|
import { execSync } from "child_process";
|
|
3938
|
-
import
|
|
5112
|
+
import pc17 from "picocolors";
|
|
3939
5113
|
var self_update_default;
|
|
3940
5114
|
var init_self_update = __esm({
|
|
3941
5115
|
"src/commands/self-update.ts"() {
|
|
3942
5116
|
"use strict";
|
|
3943
5117
|
init_constants();
|
|
3944
5118
|
init_ui();
|
|
3945
|
-
self_update_default =
|
|
5119
|
+
self_update_default = defineCommand12({
|
|
3946
5120
|
meta: {
|
|
3947
5121
|
name: "self-update",
|
|
3948
5122
|
description: "Update FlyDocs CLI to the latest version"
|
|
3949
5123
|
},
|
|
3950
5124
|
async run() {
|
|
3951
5125
|
console.log();
|
|
3952
|
-
console.log(` Updating ${
|
|
5126
|
+
console.log(` Updating ${pc17.cyan(PACKAGE_NAME)}...`);
|
|
3953
5127
|
console.log();
|
|
3954
5128
|
try {
|
|
3955
5129
|
execSync(`npm install -g ${PACKAGE_NAME}@beta`, {
|
|
@@ -3974,15 +5148,15 @@ var telemetry_exports = {};
|
|
|
3974
5148
|
__export(telemetry_exports, {
|
|
3975
5149
|
default: () => telemetry_default
|
|
3976
5150
|
});
|
|
3977
|
-
import { defineCommand as
|
|
3978
|
-
import
|
|
5151
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
5152
|
+
import pc18 from "picocolors";
|
|
3979
5153
|
var enable, disable, status, telemetry_default;
|
|
3980
5154
|
var init_telemetry2 = __esm({
|
|
3981
5155
|
"src/commands/telemetry.ts"() {
|
|
3982
5156
|
"use strict";
|
|
3983
5157
|
init_telemetry();
|
|
3984
5158
|
init_ui();
|
|
3985
|
-
enable =
|
|
5159
|
+
enable = defineCommand13({
|
|
3986
5160
|
meta: {
|
|
3987
5161
|
name: "enable",
|
|
3988
5162
|
description: "Enable anonymous usage analytics"
|
|
@@ -3997,7 +5171,7 @@ var init_telemetry2 = __esm({
|
|
|
3997
5171
|
}
|
|
3998
5172
|
}
|
|
3999
5173
|
});
|
|
4000
|
-
disable =
|
|
5174
|
+
disable = defineCommand13({
|
|
4001
5175
|
meta: {
|
|
4002
5176
|
name: "disable",
|
|
4003
5177
|
description: "Disable anonymous usage analytics"
|
|
@@ -4015,7 +5189,7 @@ var init_telemetry2 = __esm({
|
|
|
4015
5189
|
}
|
|
4016
5190
|
}
|
|
4017
5191
|
});
|
|
4018
|
-
status =
|
|
5192
|
+
status = defineCommand13({
|
|
4019
5193
|
meta: {
|
|
4020
5194
|
name: "status",
|
|
4021
5195
|
description: "Show current telemetry status"
|
|
@@ -4023,37 +5197,37 @@ var init_telemetry2 = __esm({
|
|
|
4023
5197
|
async run() {
|
|
4024
5198
|
const info = await getStatus();
|
|
4025
5199
|
console.log();
|
|
4026
|
-
console.log(
|
|
5200
|
+
console.log(pc18.bold("Telemetry Status"));
|
|
4027
5201
|
console.log();
|
|
4028
5202
|
const effectivelyEnabled = info.enabled && !info.envOverride;
|
|
4029
5203
|
console.log(
|
|
4030
|
-
` Enabled: ${effectivelyEnabled ?
|
|
5204
|
+
` Enabled: ${effectivelyEnabled ? pc18.green("yes") : pc18.yellow("no")}`
|
|
4031
5205
|
);
|
|
4032
5206
|
if (info.envOverride) {
|
|
4033
5207
|
console.log(
|
|
4034
|
-
` Env override: ${
|
|
5208
|
+
` Env override: ${pc18.yellow("FLYDOCS_TELEMETRY=0 (disabled via env)")}`
|
|
4035
5209
|
);
|
|
4036
5210
|
}
|
|
4037
5211
|
if (info.anonymousId) {
|
|
4038
|
-
console.log(` Anonymous ID: ${
|
|
5212
|
+
console.log(` Anonymous ID: ${pc18.dim(info.anonymousId)}`);
|
|
4039
5213
|
} else {
|
|
4040
5214
|
console.log(
|
|
4041
|
-
` Anonymous ID: ${
|
|
5215
|
+
` Anonymous ID: ${pc18.dim("(not yet created \u2014 generated on first run)")}`
|
|
4042
5216
|
);
|
|
4043
5217
|
}
|
|
4044
5218
|
console.log();
|
|
4045
5219
|
console.log(
|
|
4046
|
-
|
|
5220
|
+
pc18.dim(
|
|
4047
5221
|
" FlyDocs collects anonymous usage analytics to improve the CLI."
|
|
4048
5222
|
)
|
|
4049
5223
|
);
|
|
4050
5224
|
console.log(
|
|
4051
|
-
|
|
5225
|
+
pc18.dim(" No personal data, file contents, or code is ever collected.")
|
|
4052
5226
|
);
|
|
4053
5227
|
console.log();
|
|
4054
5228
|
}
|
|
4055
5229
|
});
|
|
4056
|
-
telemetry_default =
|
|
5230
|
+
telemetry_default = defineCommand13({
|
|
4057
5231
|
meta: {
|
|
4058
5232
|
name: "telemetry",
|
|
4059
5233
|
description: "Manage anonymous usage analytics (enable, disable, status)"
|
|
@@ -4069,14 +5243,18 @@ var init_telemetry2 = __esm({
|
|
|
4069
5243
|
|
|
4070
5244
|
// src/cli.ts
|
|
4071
5245
|
init_constants();
|
|
4072
|
-
import { defineCommand as
|
|
5246
|
+
import { defineCommand as defineCommand14, runMain } from "citty";
|
|
4073
5247
|
var SUB_COMMANDS = /* @__PURE__ */ new Set([
|
|
4074
5248
|
"install",
|
|
5249
|
+
"init",
|
|
4075
5250
|
"update",
|
|
4076
5251
|
"uninstall",
|
|
4077
5252
|
"setup",
|
|
4078
5253
|
"skills",
|
|
4079
5254
|
"connect",
|
|
5255
|
+
"auth",
|
|
5256
|
+
"sync",
|
|
5257
|
+
"cleanup",
|
|
4080
5258
|
"upgrade",
|
|
4081
5259
|
"self-update",
|
|
4082
5260
|
"telemetry"
|
|
@@ -4089,7 +5267,7 @@ var firstPositional = userArgs.find((a) => !a.startsWith("-"));
|
|
|
4089
5267
|
if (!hasMetaFlag && (!firstPositional || !SUB_COMMANDS.has(firstPositional))) {
|
|
4090
5268
|
process.argv.splice(2, 0, "install");
|
|
4091
5269
|
}
|
|
4092
|
-
var main =
|
|
5270
|
+
var main = defineCommand14({
|
|
4093
5271
|
meta: {
|
|
4094
5272
|
name: CLI_NAME,
|
|
4095
5273
|
version: CLI_VERSION,
|
|
@@ -4097,11 +5275,15 @@ var main = defineCommand10({
|
|
|
4097
5275
|
},
|
|
4098
5276
|
subCommands: {
|
|
4099
5277
|
install: () => Promise.resolve().then(() => (init_install(), install_exports)).then((m) => m.default),
|
|
5278
|
+
init: () => Promise.resolve().then(() => (init_init(), init_exports)).then((m) => m.default),
|
|
4100
5279
|
update: () => Promise.resolve().then(() => (init_update(), update_exports)).then((m) => m.default),
|
|
4101
5280
|
uninstall: () => Promise.resolve().then(() => (init_uninstall(), uninstall_exports)).then((m) => m.default),
|
|
4102
5281
|
setup: () => Promise.resolve().then(() => (init_setup(), setup_exports)).then((m) => m.default),
|
|
4103
5282
|
skills: () => Promise.resolve().then(() => (init_skills2(), skills_exports)).then((m) => m.default),
|
|
4104
5283
|
connect: () => Promise.resolve().then(() => (init_connect(), connect_exports)).then((m) => m.default),
|
|
5284
|
+
auth: () => Promise.resolve().then(() => (init_auth(), auth_exports)).then((m) => m.default),
|
|
5285
|
+
sync: () => Promise.resolve().then(() => (init_sync(), sync_exports)).then((m) => m.default),
|
|
5286
|
+
cleanup: () => Promise.resolve().then(() => (init_cleanup(), cleanup_exports)).then((m) => m.default),
|
|
4105
5287
|
upgrade: () => Promise.resolve().then(() => (init_upgrade(), upgrade_exports)).then((m) => m.default),
|
|
4106
5288
|
"self-update": () => Promise.resolve().then(() => (init_self_update(), self_update_exports)).then((m) => m.default),
|
|
4107
5289
|
telemetry: () => Promise.resolve().then(() => (init_telemetry2(), telemetry_exports)).then((m) => m.default)
|