@flydocs/cli 0.6.0-alpha.27 → 0.6.0-alpha.29

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 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.27";
18
+ CLI_VERSION = "0.6.0-alpha.29";
19
19
  CLI_NAME = "flydocs";
20
20
  PACKAGE_NAME = "@flydocs/cli";
21
21
  POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
@@ -2989,33 +2989,250 @@ var init_relay_client = __esm({
2989
2989
  }
2990
2990
  });
2991
2991
 
2992
+ // src/commands/cleanup.ts
2993
+ var cleanup_exports = {};
2994
+ __export(cleanup_exports, {
2995
+ default: () => cleanup_default,
2996
+ executeCleanup: () => executeCleanup,
2997
+ scanArtifacts: () => scanArtifacts
2998
+ });
2999
+ import { defineCommand as defineCommand2 } from "citty";
3000
+ import pc7 from "picocolors";
3001
+ import { join as join18 } from "path";
3002
+ import { readFile as readFile13, writeFile as writeFile11, rm as rm4 } from "fs/promises";
3003
+ async function scanArtifacts(targetDir) {
3004
+ const actions = [];
3005
+ const localMePath = join18(targetDir, ".flydocs", "me.json");
3006
+ if (await pathExists(localMePath) && await pathExists(globalMePath())) {
3007
+ actions.push({
3008
+ description: "Remove .flydocs/me.json (migrated to ~/.flydocs/me.json)",
3009
+ path: localMePath,
3010
+ type: "file"
3011
+ });
3012
+ }
3013
+ const validationCachePath = join18(
3014
+ targetDir,
3015
+ ".flydocs",
3016
+ "validation-cache.json"
3017
+ );
3018
+ if (await pathExists(validationCachePath)) {
3019
+ actions.push({
3020
+ description: "Remove .flydocs/validation-cache.json (replaced by configVersion)",
3021
+ path: validationCachePath,
3022
+ type: "file"
3023
+ });
3024
+ }
3025
+ const hasGlobalCreds = await pathExists(credentialsPath());
3026
+ for (const envFile of [".env", ".env.local"]) {
3027
+ const envPath = join18(targetDir, envFile);
3028
+ if (!await pathExists(envPath)) continue;
3029
+ const content = await readFile13(envPath, "utf-8");
3030
+ const lines = content.split("\n");
3031
+ for (const line of lines) {
3032
+ const trimmed = line.trim();
3033
+ if (hasGlobalCreds && trimmed.startsWith("FLYDOCS_API_KEY=")) {
3034
+ actions.push({
3035
+ description: `Remove FLYDOCS_API_KEY from ${envFile} (migrated to ~/.flydocs/credentials)`,
3036
+ path: envPath,
3037
+ type: "line"
3038
+ });
3039
+ }
3040
+ if (trimmed.startsWith("FLYDOCS_RELAY_URL=")) {
3041
+ actions.push({
3042
+ description: `Remove FLYDOCS_RELAY_URL from ${envFile}`,
3043
+ path: envPath,
3044
+ type: "line"
3045
+ });
3046
+ }
3047
+ }
3048
+ }
3049
+ try {
3050
+ const config = await readAnyConfig(targetDir);
3051
+ const rawContent = await readFile13(
3052
+ join18(targetDir, ".flydocs", "config.json"),
3053
+ "utf-8"
3054
+ );
3055
+ const raw = JSON.parse(rawContent);
3056
+ const ghostFields = ["provider", "statusMapping"];
3057
+ for (const field of ghostFields) {
3058
+ if (field in raw) {
3059
+ actions.push({
3060
+ description: `Remove ghost field "${field}" from config.json`,
3061
+ path: join18(targetDir, ".flydocs", "config.json"),
3062
+ type: "field"
3063
+ });
3064
+ }
3065
+ }
3066
+ if (isConfigV2(config)) {
3067
+ const v1OnlyFields = [
3068
+ "workspaceId",
3069
+ "issueLabels",
3070
+ "workspace",
3071
+ "onboardComplete",
3072
+ "sourceRepo",
3073
+ "designSystem",
3074
+ "aiLabor"
3075
+ ];
3076
+ for (const field of v1OnlyFields) {
3077
+ if (field in raw) {
3078
+ actions.push({
3079
+ description: `Remove v1 field "${field}" from config.json (server-owned in v2)`,
3080
+ path: join18(targetDir, ".flydocs", "config.json"),
3081
+ type: "field"
3082
+ });
3083
+ }
3084
+ }
3085
+ }
3086
+ } catch {
3087
+ }
3088
+ return actions;
3089
+ }
3090
+ async function executeCleanup(targetDir, actions) {
3091
+ const fileRemovals = actions.filter((a) => a.type === "file");
3092
+ const lineRemovals = actions.filter((a) => a.type === "line");
3093
+ const fieldRemovals = actions.filter((a) => a.type === "field");
3094
+ for (const action of fileRemovals) {
3095
+ await rm4(action.path, { force: true });
3096
+ }
3097
+ const envFiles = /* @__PURE__ */ new Map();
3098
+ for (const action of lineRemovals) {
3099
+ if (!envFiles.has(action.path)) {
3100
+ const content = await readFile13(action.path, "utf-8");
3101
+ envFiles.set(action.path, content.split("\n"));
3102
+ }
3103
+ }
3104
+ for (const [envPath, lines] of envFiles) {
3105
+ const filtered = lines.filter((line) => {
3106
+ const trimmed = line.trim();
3107
+ return !trimmed.startsWith("FLYDOCS_API_KEY=") && !trimmed.startsWith("FLYDOCS_RELAY_URL=");
3108
+ });
3109
+ const hasContent = filtered.some(
3110
+ (l) => l.trim().length > 0 && !l.trim().startsWith("#")
3111
+ );
3112
+ if (!hasContent) {
3113
+ await rm4(envPath, { force: true });
3114
+ } else {
3115
+ await writeFile11(envPath, filtered.join("\n"), "utf-8");
3116
+ }
3117
+ }
3118
+ if (fieldRemovals.length > 0) {
3119
+ const configPath = join18(targetDir, ".flydocs", "config.json");
3120
+ const content = await readFile13(configPath, "utf-8");
3121
+ const config = JSON.parse(content);
3122
+ for (const action of fieldRemovals) {
3123
+ const match = action.description.match(/"(\w+)"/);
3124
+ if (match) {
3125
+ delete config[match[1]];
3126
+ }
3127
+ }
3128
+ await writeFile11(
3129
+ configPath,
3130
+ JSON.stringify(config, null, 2) + "\n",
3131
+ "utf-8"
3132
+ );
3133
+ }
3134
+ }
3135
+ var cleanup_default;
3136
+ var init_cleanup = __esm({
3137
+ "src/commands/cleanup.ts"() {
3138
+ "use strict";
3139
+ init_ui();
3140
+ init_fs_ops();
3141
+ init_config();
3142
+ init_types();
3143
+ init_global_config();
3144
+ cleanup_default = defineCommand2({
3145
+ meta: {
3146
+ name: "cleanup",
3147
+ description: "Remove legacy v1 artifacts after migration (dry-run by default)"
3148
+ },
3149
+ args: {
3150
+ confirm: {
3151
+ type: "boolean",
3152
+ description: "Actually remove artifacts (default: dry-run only)",
3153
+ default: false
3154
+ },
3155
+ path: {
3156
+ type: "string",
3157
+ description: "Path to project directory"
3158
+ }
3159
+ },
3160
+ async run({ args }) {
3161
+ const targetDir = args.path ?? process.cwd();
3162
+ console.log();
3163
+ console.log(` ${pc7.bold("FlyDocs Cleanup")}`);
3164
+ console.log();
3165
+ const hasConfig = await pathExists(
3166
+ join18(targetDir, ".flydocs", "config.json")
3167
+ );
3168
+ if (!hasConfig) {
3169
+ printError("No .flydocs/config.json found. Run flydocs init first.");
3170
+ process.exit(1);
3171
+ }
3172
+ const hasGlobalCreds = await pathExists(credentialsPath());
3173
+ if (!hasGlobalCreds) {
3174
+ printWarning(
3175
+ "No global credentials found. Run flydocs init to set up credentials before cleanup."
3176
+ );
3177
+ }
3178
+ const actions = await scanArtifacts(targetDir);
3179
+ if (actions.length === 0) {
3180
+ printStatus("No legacy artifacts found. Already clean.");
3181
+ console.log();
3182
+ return;
3183
+ }
3184
+ const mode = args.confirm ? "Removing" : "Would remove";
3185
+ printInfo(`${mode} ${actions.length} legacy artifact(s):`);
3186
+ console.log();
3187
+ for (const action of actions) {
3188
+ const icon = args.confirm ? pc7.red("-") : pc7.yellow("?");
3189
+ console.log(` ${icon} ${action.description}`);
3190
+ }
3191
+ console.log();
3192
+ if (!args.confirm) {
3193
+ printInfo(
3194
+ `Dry-run complete. Run ${pc7.cyan("flydocs cleanup --confirm")} to remove.`
3195
+ );
3196
+ console.log();
3197
+ return;
3198
+ }
3199
+ await executeCleanup(targetDir, actions);
3200
+ printCompletionBox("Cleanup Complete", [
3201
+ `${pc7.green("+")} Removed ${actions.length} legacy artifact(s)`
3202
+ ]);
3203
+ console.log();
3204
+ }
3205
+ });
3206
+ }
3207
+ });
3208
+
2992
3209
  // 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";
3210
+ import { readFile as readFile14, writeFile as writeFile12, readdir as readdir4, stat as stat2 } from "fs/promises";
3211
+ import { join as join19, dirname as dirname3, relative, resolve as resolve3 } from "path";
2995
3212
  async function readWorkspaceFile(dir) {
2996
- const filePath = join18(dir, WORKSPACE_FILENAME);
3213
+ const filePath = join19(dir, WORKSPACE_FILENAME);
2997
3214
  if (!await pathExists(filePath)) return null;
2998
- const content = await readFile13(filePath, "utf-8");
3215
+ const content = await readFile14(filePath, "utf-8");
2999
3216
  const parsed = JSON.parse(content);
3000
3217
  validateWorkspaceFile(parsed);
3001
3218
  return parsed;
3002
3219
  }
3003
3220
  async function writeWorkspaceFile(dir, workspace) {
3004
- const filePath = join18(dir, WORKSPACE_FILENAME);
3221
+ const filePath = join19(dir, WORKSPACE_FILENAME);
3005
3222
  const content = JSON.stringify(workspace, null, 2) + "\n";
3006
- await writeFile11(filePath, content, "utf-8");
3223
+ await writeFile12(filePath, content, "utf-8");
3007
3224
  }
3008
3225
  async function detectSiblingRepos(parentDir) {
3009
3226
  const repos = {};
3010
3227
  const entries = await readdir4(parentDir);
3011
3228
  for (const entry of entries) {
3012
3229
  if (entry.startsWith(".")) continue;
3013
- const entryPath = join18(parentDir, entry);
3230
+ const entryPath = join19(parentDir, entry);
3014
3231
  const entryStat = await stat2(entryPath).catch(() => null);
3015
3232
  if (!entryStat?.isDirectory()) continue;
3016
- const hasGit = await pathExists(join18(entryPath, ".git"));
3233
+ const hasGit = await pathExists(join19(entryPath, ".git"));
3017
3234
  const hasFlydocs = await pathExists(
3018
- join18(entryPath, ".flydocs", "config.json")
3235
+ join19(entryPath, ".flydocs", "config.json")
3019
3236
  );
3020
3237
  if (hasGit || hasFlydocs) {
3021
3238
  repos[entry] = { path: `./${entry}` };
@@ -3030,10 +3247,10 @@ async function readSiblingDescriptors(workspaceDir, repos) {
3030
3247
  const descriptors = [];
3031
3248
  for (const [name, entry] of Object.entries(repos)) {
3032
3249
  const repoDir = resolve3(workspaceDir, entry.path);
3033
- const serviceJsonPath = join18(repoDir, "flydocs", "context", "service.json");
3250
+ const serviceJsonPath = join19(repoDir, "flydocs", "context", "service.json");
3034
3251
  if (!await pathExists(serviceJsonPath)) continue;
3035
3252
  try {
3036
- const content = await readFile13(serviceJsonPath, "utf-8");
3253
+ const content = await readFile14(serviceJsonPath, "utf-8");
3037
3254
  const descriptor = JSON.parse(content);
3038
3255
  descriptors.push({ name, path: entry.path, descriptor });
3039
3256
  } catch {
@@ -3141,16 +3358,18 @@ var init_exports = {};
3141
3358
  __export(init_exports, {
3142
3359
  default: () => init_default
3143
3360
  });
3144
- import { defineCommand as defineCommand2 } from "citty";
3361
+ import { defineCommand as defineCommand3 } from "citty";
3145
3362
  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";
3363
+ import pc8 from "picocolors";
3364
+ import { join as join20 } from "path";
3365
+ import { mkdir as mkdir9, writeFile as writeFile13 } from "fs/promises";
3366
+ import { execFile } from "child_process";
3367
+ import { promisify } from "util";
3149
3368
  async function resolveAndValidateKey(keyArg, targetDir) {
3150
3369
  let resolved = await resolveApiKey(keyArg, targetDir);
3151
3370
  if (!resolved) {
3152
3371
  console.log(
3153
- ` ${pc7.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
3372
+ ` ${pc8.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
3154
3373
  );
3155
3374
  console.log();
3156
3375
  const keyInput = await text2({
@@ -3180,13 +3399,13 @@ async function resolveAndValidateKey(keyArg, targetDir) {
3180
3399
  printError("Invalid API key. Check your key and try again.");
3181
3400
  process.exit(1);
3182
3401
  }
3183
- printStatus(`Authenticated with ${pc7.bold(result.org)}`);
3402
+ printStatus(`Authenticated with ${pc8.bold(result.org)}`);
3184
3403
  } catch {
3185
3404
  printError(
3186
3405
  "Could not reach FlyDocs API. Check your network and try again."
3187
3406
  );
3188
3407
  console.log(
3189
- ` ${pc7.dim("flydocs init requires server access. For offline use, run flydocs install instead.")}`
3408
+ ` ${pc8.dim("flydocs init requires server access. For offline use, run flydocs install instead.")}`
3190
3409
  );
3191
3410
  process.exit(1);
3192
3411
  }
@@ -3197,7 +3416,7 @@ async function resolveAndValidateKey(keyArg, targetDir) {
3197
3416
  process.exit(1);
3198
3417
  } else if (workspaces.length === 1) {
3199
3418
  workspaceId = workspaces[0].id;
3200
- printStatus(`Workspace: ${pc7.bold(workspaces[0].name)}`);
3419
+ printStatus(`Workspace: ${pc8.bold(workspaces[0].name)}`);
3201
3420
  } else {
3202
3421
  const choice = await select2({
3203
3422
  message: "Select workspace",
@@ -3224,7 +3443,7 @@ async function resolveAndValidateKey(keyArg, targetDir) {
3224
3443
  async function pullServerConfig(apiKey, targetDir, workspaceId) {
3225
3444
  printInfo("Pulling config from server...");
3226
3445
  const isFirstInit = !await pathExists(
3227
- join19(targetDir, ".flydocs", "config.json")
3446
+ join20(targetDir, ".flydocs", "config.json")
3228
3447
  );
3229
3448
  try {
3230
3449
  const response = await fetchConfigV2(apiKey, {
@@ -3241,7 +3460,7 @@ async function pullServerConfig(apiKey, targetDir, workspaceId) {
3241
3460
  printError(`Server error: ${err.message}`);
3242
3461
  if (err.status === 401) {
3243
3462
  console.log(
3244
- ` ${pc7.dim("Your API key may be revoked. Run flydocs auth with a new key.")}`
3463
+ ` ${pc8.dim("Your API key may be revoked. Run flydocs auth with a new key.")}`
3245
3464
  );
3246
3465
  }
3247
3466
  } else {
@@ -3253,7 +3472,7 @@ async function pullServerConfig(apiKey, targetDir, workspaceId) {
3253
3472
  async function initSingleRepo(targetDir, apiKey, serverResponse) {
3254
3473
  const actions = [];
3255
3474
  const skipped = [];
3256
- await mkdir9(join19(targetDir, ".flydocs"), { recursive: true });
3475
+ await mkdir9(join20(targetDir, ".flydocs"), { recursive: true });
3257
3476
  const configWithHash = applyConfigHash(serverResponse.config);
3258
3477
  await writeConfig(targetDir, configWithHash);
3259
3478
  actions.push("Wrote .flydocs/config.json (from server)");
@@ -3266,18 +3485,18 @@ async function initSingleRepo(targetDir, apiKey, serverResponse) {
3266
3485
  actions.push("Wrote ~/.flydocs/me.json");
3267
3486
  }
3268
3487
  if (serverResponse.context) {
3269
- const contextDir = join19(targetDir, "flydocs", "context");
3488
+ const contextDir = join20(targetDir, "flydocs", "context");
3270
3489
  await mkdir9(contextDir, { recursive: true });
3271
- const projectMdPath = join19(contextDir, "project.md");
3490
+ const projectMdPath = join20(contextDir, "project.md");
3272
3491
  if (!await pathExists(projectMdPath)) {
3273
- await writeFile12(projectMdPath, serverResponse.context.projectMd, "utf-8");
3492
+ await writeFile13(projectMdPath, serverResponse.context.projectMd, "utf-8");
3274
3493
  actions.push("Wrote flydocs/context/project.md");
3275
3494
  } else {
3276
3495
  skipped.push("flydocs/context/project.md (already exists)");
3277
3496
  }
3278
- const serviceJsonPath = join19(contextDir, "service.json");
3497
+ const serviceJsonPath = join20(contextDir, "service.json");
3279
3498
  if (serverResponse.context.serviceJson && !await pathExists(serviceJsonPath)) {
3280
- await writeFile12(
3499
+ await writeFile13(
3281
3500
  serviceJsonPath,
3282
3501
  JSON.stringify(serverResponse.context.serviceJson, null, 2) + "\n",
3283
3502
  "utf-8"
@@ -3290,10 +3509,175 @@ async function initSingleRepo(targetDir, apiKey, serverResponse) {
3290
3509
  await ensureGitignore(targetDir);
3291
3510
  await ensurePlatformIgnores(targetDir);
3292
3511
  actions.push("Updated ignore files");
3512
+ const artifacts = await scanArtifacts(targetDir);
3513
+ if (artifacts.length > 0) {
3514
+ await executeCleanup(targetDir, artifacts);
3515
+ actions.push(`Cleaned ${artifacts.length} legacy artifact(s)`);
3516
+ }
3293
3517
  return { actions, skipped };
3294
3518
  }
3519
+ async function checkGitFreshness(repoDir) {
3520
+ const hasGit = await pathExists(join20(repoDir, ".git"));
3521
+ if (!hasGit) return;
3522
+ try {
3523
+ await execFileAsync("git", ["-C", repoDir, "fetch", "--quiet"], {
3524
+ timeout: 15e3
3525
+ });
3526
+ const { stdout } = await execFileAsync(
3527
+ "git",
3528
+ ["-C", repoDir, "rev-list", "--count", "HEAD..@{upstream}"],
3529
+ { timeout: 5e3 }
3530
+ );
3531
+ const behind = parseInt(stdout.trim(), 10);
3532
+ if (behind <= 0) return;
3533
+ printWarning(`Repo is ${behind} commit(s) behind remote.`);
3534
+ const shouldPull = await confirm3({
3535
+ message: "Pull latest before init?"
3536
+ });
3537
+ if (isCancel4(shouldPull) || !shouldPull) {
3538
+ printInfo("Continuing without pulling.");
3539
+ return;
3540
+ }
3541
+ await execFileAsync("git", ["-C", repoDir, "pull", "--ff-only"], {
3542
+ timeout: 3e4
3543
+ });
3544
+ printStatus("Pulled latest from remote.");
3545
+ } catch {
3546
+ }
3547
+ }
3548
+ async function isEmptyOrNonRepo(dir) {
3549
+ const hasGit = await pathExists(join20(dir, ".git"));
3550
+ if (hasGit) return false;
3551
+ const siblings = await detectSiblingRepos(dir);
3552
+ if (Object.keys(siblings).length >= 2) return false;
3553
+ return true;
3554
+ }
3555
+ function repoShortName(fullName) {
3556
+ return fullName.includes("/") ? fullName.split("/").pop() : fullName;
3557
+ }
3558
+ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
3559
+ if (repos.length === 1) {
3560
+ const repo = repos[0];
3561
+ const shortName = repoShortName(repo.name);
3562
+ printInfo(`Workspace has 1 repo: ${pc8.bold(shortName)}`);
3563
+ const shouldClone = await confirm3({
3564
+ message: `Clone ${shortName} and initialize?`
3565
+ });
3566
+ if (isCancel4(shouldClone) || !shouldClone) {
3567
+ cancel3("Init cancelled.");
3568
+ process.exit(0);
3569
+ }
3570
+ const cloneDir = join20(targetDir, shortName);
3571
+ printInfo(`Cloning ${shortName}...`);
3572
+ await execFileAsync("git", ["clone", repo.cloneUrl, cloneDir], {
3573
+ timeout: 6e4
3574
+ });
3575
+ printStatus(`Cloned ${shortName}`);
3576
+ const serverResponse = await pullServerConfig(
3577
+ apiKey,
3578
+ cloneDir,
3579
+ workspaceId
3580
+ );
3581
+ const { actions, skipped } = await initSingleRepo(
3582
+ cloneDir,
3583
+ apiKey,
3584
+ serverResponse
3585
+ );
3586
+ actions.unshift("Stored credential globally (~/.flydocs/credentials)");
3587
+ actions.unshift(`Cloned ${shortName}`);
3588
+ printInitReport(
3589
+ actions,
3590
+ skipped,
3591
+ serverResponse.warnings,
3592
+ !!serverResponse.context
3593
+ );
3594
+ } else {
3595
+ const repoNames = repos.map((r) => repoShortName(r.name));
3596
+ printInfo(`Workspace has ${repos.length} repos:`);
3597
+ for (const name of repoNames) {
3598
+ console.log(` ${pc8.cyan(name)}`);
3599
+ }
3600
+ console.log();
3601
+ const shouldClone = await confirm3({
3602
+ message: `Clone and initialize all ${repos.length} repos?`
3603
+ });
3604
+ if (isCancel4(shouldClone) || !shouldClone) {
3605
+ cancel3("Init cancelled.");
3606
+ process.exit(0);
3607
+ }
3608
+ const allActions = [
3609
+ "Stored credential globally (~/.flydocs/credentials)"
3610
+ ];
3611
+ const allSkipped = [];
3612
+ let allWarnings = [];
3613
+ let firstResponse;
3614
+ for (let i = 0; i < repos.length; i++) {
3615
+ const repo = repos[i];
3616
+ const shortName = repoNames[i];
3617
+ const cloneDir = join20(targetDir, shortName);
3618
+ console.log();
3619
+ if (await pathExists(join20(cloneDir, ".git"))) {
3620
+ printInfo(`${pc8.bold(shortName)} already cloned, initializing...`);
3621
+ await checkGitFreshness(cloneDir);
3622
+ } else {
3623
+ printInfo(`Cloning ${pc8.bold(shortName)}...`);
3624
+ await execFileAsync("git", ["clone", repo.cloneUrl, cloneDir], {
3625
+ timeout: 6e4
3626
+ });
3627
+ allActions.push(`Cloned ${shortName}`);
3628
+ }
3629
+ const serverResponse = await pullServerConfig(
3630
+ apiKey,
3631
+ cloneDir,
3632
+ workspaceId
3633
+ );
3634
+ if (!firstResponse) firstResponse = serverResponse;
3635
+ const { actions, skipped } = await initSingleRepo(
3636
+ cloneDir,
3637
+ apiKey,
3638
+ serverResponse
3639
+ );
3640
+ for (const action of actions) {
3641
+ allActions.push(`[${shortName}] ${action}`);
3642
+ }
3643
+ for (const skip of skipped) {
3644
+ allSkipped.push(`[${shortName}] ${skip}`);
3645
+ }
3646
+ allWarnings = [...allWarnings, ...serverResponse.warnings];
3647
+ printStatus(`${shortName} initialized`);
3648
+ }
3649
+ if (firstResponse) {
3650
+ const repoEntries = {};
3651
+ for (const name of repoNames) {
3652
+ repoEntries[name] = { path: `./${name}` };
3653
+ }
3654
+ const workspaceFile = buildWorkspaceFile(
3655
+ firstResponse.workspaceId,
3656
+ repoEntries
3657
+ );
3658
+ await writeWorkspaceFile(targetDir, workspaceFile);
3659
+ allActions.push(`.flydocs-workspace.json (${repos.length} repos)`);
3660
+ const contextDir = join20(targetDir, "flydocs", "context");
3661
+ const workspaceMdPath = join20(contextDir, "workspace.md");
3662
+ if (!await pathExists(workspaceMdPath)) {
3663
+ await mkdir9(contextDir, { recursive: true });
3664
+ const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(targetDir, workspaceFile);
3665
+ await writeFile13(workspaceMdPath, content, "utf-8");
3666
+ const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
3667
+ allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
3668
+ }
3669
+ }
3670
+ console.log();
3671
+ printInitReport(
3672
+ allActions,
3673
+ allSkipped,
3674
+ allWarnings,
3675
+ !!firstResponse?.context
3676
+ );
3677
+ }
3678
+ }
3295
3679
  async function isParentDirectory(dir) {
3296
- const hasGit = await pathExists(join19(dir, ".git"));
3680
+ const hasGit = await pathExists(join20(dir, ".git"));
3297
3681
  if (hasGit) return false;
3298
3682
  const repos = await detectSiblingRepos(dir);
3299
3683
  return Object.keys(repos).length >= 2;
@@ -3303,7 +3687,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
3303
3687
  const repoNames = Object.keys(repos).sort();
3304
3688
  printInfo(`Detected ${repoNames.length} repos:`);
3305
3689
  for (const name of repoNames) {
3306
- console.log(` ${pc7.cyan(name)}`);
3690
+ console.log(` ${pc8.cyan(name)}`);
3307
3691
  }
3308
3692
  console.log();
3309
3693
  const shouldContinue = await confirm3({
@@ -3313,7 +3697,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
3313
3697
  cancel3("Init cancelled.");
3314
3698
  process.exit(0);
3315
3699
  }
3316
- const firstRepoDir = join19(parentDir, repoNames[0]);
3700
+ const firstRepoDir = join20(parentDir, repoNames[0]);
3317
3701
  const { apiKey, workspaceId } = await resolveAndValidateKey(
3318
3702
  keyArg,
3319
3703
  firstRepoDir
@@ -3325,9 +3709,10 @@ async function runMultiRepoInit(parentDir, keyArg) {
3325
3709
  let allWarnings = [];
3326
3710
  let firstResponse;
3327
3711
  for (const repoName of repoNames) {
3328
- const repoDir = join19(parentDir, repoName);
3712
+ const repoDir = join20(parentDir, repoName);
3329
3713
  console.log();
3330
- printInfo(`Initializing ${pc7.bold(repoName)}...`);
3714
+ printInfo(`Initializing ${pc8.bold(repoName)}...`);
3715
+ await checkGitFreshness(repoDir);
3331
3716
  const serverResponse = await pullServerConfig(apiKey, repoDir, workspaceId);
3332
3717
  if (!firstResponse) firstResponse = serverResponse;
3333
3718
  const { actions, skipped } = await initSingleRepo(
@@ -3356,47 +3741,60 @@ async function runMultiRepoInit(parentDir, keyArg) {
3356
3741
  await writeWorkspaceFile(parentDir, workspaceFile);
3357
3742
  allActions.push(`.flydocs-workspace.json (${repoNames.length} repos)`);
3358
3743
  }
3359
- const contextDir = join19(parentDir, "flydocs", "context");
3360
- const workspaceMdPath = join19(contextDir, "workspace.md");
3744
+ const contextDir = join20(parentDir, "flydocs", "context");
3745
+ const workspaceMdPath = join20(contextDir, "workspace.md");
3361
3746
  if (await pathExists(workspaceMdPath)) {
3362
3747
  allSkipped.push("flydocs/context/workspace.md (already exists)");
3363
3748
  } else {
3364
3749
  await mkdir9(contextDir, { recursive: true });
3365
3750
  const workspaceJson = await readWorkspaceFile(parentDir) ?? buildWorkspaceFile(firstResponse.workspaceId, repos);
3366
3751
  const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(parentDir, workspaceJson);
3367
- await writeFile12(workspaceMdPath, content, "utf-8");
3752
+ await writeFile13(workspaceMdPath, content, "utf-8");
3368
3753
  const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
3369
3754
  allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
3370
3755
  }
3371
3756
  }
3372
3757
  console.log();
3373
- printInitReport(allActions, allSkipped, allWarnings);
3758
+ printInitReport(
3759
+ allActions,
3760
+ allSkipped,
3761
+ allWarnings,
3762
+ !!firstResponse?.context
3763
+ );
3374
3764
  }
3375
- function printInitReport(actions, skipped, warnings) {
3765
+ function printInitReport(actions, skipped, warnings, hasContext) {
3376
3766
  console.log();
3377
3767
  const lines = [];
3378
3768
  for (const action of actions) {
3379
- lines.push(`${pc7.green("+")} ${action}`);
3769
+ lines.push(`${pc8.green("+")} ${action}`);
3380
3770
  }
3381
3771
  for (const skip of skipped) {
3382
- lines.push(`${pc7.dim("-")} ${skip}`);
3772
+ lines.push(`${pc8.dim("-")} ${skip}`);
3773
+ }
3774
+ if (!hasContext) {
3775
+ lines.push("");
3776
+ lines.push(
3777
+ `${pc8.yellow("!")} No generated context yet. An admin can generate it from the portal.`
3778
+ );
3383
3779
  }
3384
3780
  if (warnings.length > 0) {
3385
3781
  lines.push("");
3386
3782
  for (const warning of warnings) {
3387
- lines.push(`${pc7.yellow("!")} ${warning}`);
3783
+ lines.push(`${pc8.yellow("!")} ${warning}`);
3388
3784
  }
3389
3785
  }
3390
3786
  printCompletionBox("FlyDocs Initialized", lines);
3391
3787
  console.log();
3392
3788
  console.log(` Next steps:`);
3393
3789
  console.log(
3394
- ` ${pc7.cyan("flydocs sync")} \u2014 pull latest config and templates`
3790
+ ` ${pc8.cyan("flydocs sync")} Pull latest config and templates`
3791
+ );
3792
+ console.log(
3793
+ ` ${pc8.cyan("cursor .")} ${pc8.dim("or")} ${pc8.cyan("code .")} Open your IDE, then use ${pc8.bold("/start-session")}`
3395
3794
  );
3396
- console.log(` ${pc7.cyan("/start-session")} \u2014 begin working`);
3397
3795
  console.log();
3398
3796
  }
3399
- var init_default;
3797
+ var execFileAsync, init_default;
3400
3798
  var init_init = __esm({
3401
3799
  "src/commands/init.ts"() {
3402
3800
  "use strict";
@@ -3408,8 +3806,10 @@ var init_init = __esm({
3408
3806
  init_gitignore();
3409
3807
  init_fs_ops();
3410
3808
  init_relay_client();
3809
+ init_cleanup();
3411
3810
  init_workspace();
3412
- init_default = defineCommand2({
3811
+ execFileAsync = promisify(execFile);
3812
+ init_default = defineCommand3({
3413
3813
  meta: {
3414
3814
  name: "init",
3415
3815
  description: "Initialize FlyDocs in this project (unified setup)"
@@ -3427,12 +3827,34 @@ var init_init = __esm({
3427
3827
  async run({ args }) {
3428
3828
  const targetDir = args.path ?? process.cwd();
3429
3829
  console.log();
3430
- console.log(` ${pc7.bold("FlyDocs Init")}`);
3830
+ console.log(` ${pc8.bold("FlyDocs Init")}`);
3431
3831
  console.log();
3432
3832
  if (await isParentDirectory(targetDir)) {
3433
3833
  await runMultiRepoInit(targetDir, args.key);
3434
3834
  return;
3435
3835
  }
3836
+ if (await isEmptyOrNonRepo(targetDir)) {
3837
+ const { apiKey: apiKey2, workspaceId: workspaceId2 } = await resolveAndValidateKey(
3838
+ args.key,
3839
+ targetDir
3840
+ );
3841
+ const preflightResponse = await pullServerConfig(
3842
+ apiKey2,
3843
+ targetDir,
3844
+ workspaceId2
3845
+ );
3846
+ if (preflightResponse.repos && preflightResponse.repos.length > 0) {
3847
+ await runCloneAndInit(
3848
+ targetDir,
3849
+ args.key,
3850
+ preflightResponse.repos,
3851
+ apiKey2,
3852
+ workspaceId2
3853
+ );
3854
+ return;
3855
+ }
3856
+ }
3857
+ await checkGitFreshness(targetDir);
3436
3858
  const { apiKey, workspaceId } = await resolveAndValidateKey(
3437
3859
  args.key,
3438
3860
  targetDir
@@ -3450,7 +3872,7 @@ var init_init = __esm({
3450
3872
  actions.unshift("Stored credential globally (~/.flydocs/credentials)");
3451
3873
  const topology = serverResponse.config.topology;
3452
3874
  if (topology?.type === 4 && topology.label === "sibling-repos") {
3453
- const parentDir = join19(targetDir, "..");
3875
+ const parentDir = join20(targetDir, "..");
3454
3876
  const existing = await readWorkspaceFile(parentDir);
3455
3877
  if (existing) {
3456
3878
  skipped.push(".flydocs-workspace.json (already exists)");
@@ -3468,7 +3890,12 @@ var init_init = __esm({
3468
3890
  }
3469
3891
  }
3470
3892
  }
3471
- printInitReport(actions, skipped, serverResponse.warnings);
3893
+ printInitReport(
3894
+ actions,
3895
+ skipped,
3896
+ serverResponse.warnings,
3897
+ !!serverResponse.context
3898
+ );
3472
3899
  }
3473
3900
  });
3474
3901
  }
@@ -3479,11 +3906,11 @@ var update_exports = {};
3479
3906
  __export(update_exports, {
3480
3907
  default: () => update_default
3481
3908
  });
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";
3909
+ import { defineCommand as defineCommand4 } from "citty";
3910
+ import { resolve as resolve4, join as join21 } from "path";
3911
+ import { mkdir as mkdir10, cp as cp2, readFile as readFile15, readdir as readdir5, rm as rm5 } from "fs/promises";
3485
3912
  import { select as select3, text as text3, confirm as confirm4, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
3486
- import pc8 from "picocolors";
3913
+ import pc9 from "picocolors";
3487
3914
  var update_default;
3488
3915
  var init_update = __esm({
3489
3916
  "src/commands/update.ts"() {
@@ -3502,7 +3929,7 @@ var init_update = __esm({
3502
3929
  init_update_check();
3503
3930
  init_telemetry();
3504
3931
  init_integrity();
3505
- update_default = defineCommand3({
3932
+ update_default = defineCommand4({
3506
3933
  meta: {
3507
3934
  name: "update",
3508
3935
  description: "Update an existing FlyDocs installation"
@@ -3589,9 +4016,9 @@ var init_update = __esm({
3589
4016
  }
3590
4017
  targetDir = resolve4(targetDir);
3591
4018
  process.chdir(targetDir);
3592
- const hasVersion = await pathExists(join20(targetDir, ".flydocs", "version"));
4019
+ const hasVersion = await pathExists(join21(targetDir, ".flydocs", "version"));
3593
4020
  const hasConfig = await pathExists(
3594
- join20(targetDir, ".flydocs", "config.json")
4021
+ join21(targetDir, ".flydocs", "config.json")
3595
4022
  );
3596
4023
  if (!hasVersion && !hasConfig) {
3597
4024
  printError(`Not a FlyDocs project: ${targetDir}`);
@@ -3602,8 +4029,8 @@ var init_update = __esm({
3602
4029
  console.log();
3603
4030
  let currentVersion = "0.1.0";
3604
4031
  if (hasVersion) {
3605
- const vContent = await readFile14(
3606
- join20(targetDir, ".flydocs", "version"),
4032
+ const vContent = await readFile15(
4033
+ join21(targetDir, ".flydocs", "version"),
3607
4034
  "utf-8"
3608
4035
  );
3609
4036
  currentVersion = vContent.trim();
@@ -3637,10 +4064,10 @@ var init_update = __esm({
3637
4064
  });
3638
4065
  console.log(`Updating: v${currentVersion} \u2192 v${version}`);
3639
4066
  console.log();
3640
- const changelogPath = join20(templateDir, "CHANGELOG.md");
4067
+ const changelogPath = join21(templateDir, "CHANGELOG.md");
3641
4068
  const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
3642
4069
  if (whatsNew.length > 0) {
3643
- console.log(pc8.cyan("What's new:"));
4070
+ console.log(pc9.cyan("What's new:"));
3644
4071
  console.log();
3645
4072
  for (const entry of whatsNew) {
3646
4073
  console.log(` ${entry}`);
@@ -3649,23 +4076,23 @@ var init_update = __esm({
3649
4076
  }
3650
4077
  const now = /* @__PURE__ */ new Date();
3651
4078
  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")}`;
3652
- const backupDir = join20(targetDir, ".flydocs", `backup-${ts}`);
4079
+ const backupDir = join21(targetDir, ".flydocs", `backup-${ts}`);
3653
4080
  await mkdir10(backupDir, { recursive: true });
3654
4081
  if (hasConfig) {
3655
4082
  await cp2(
3656
- join20(targetDir, ".flydocs", "config.json"),
3657
- join20(backupDir, "config.json")
4083
+ join21(targetDir, ".flydocs", "config.json"),
4084
+ join21(backupDir, "config.json")
3658
4085
  );
3659
4086
  printStatus(`Config backed up to .flydocs/backup-${ts}/`);
3660
4087
  }
3661
4088
  try {
3662
- const flydocsDir = join20(targetDir, ".flydocs");
4089
+ const flydocsDir = join21(targetDir, ".flydocs");
3663
4090
  const entries = await readdir5(flydocsDir);
3664
4091
  const backups = entries.filter((e) => e.startsWith("backup-")).sort();
3665
4092
  if (backups.length > 3) {
3666
4093
  const toRemove = backups.slice(0, backups.length - 3);
3667
4094
  for (const old of toRemove) {
3668
- await rm4(join20(flydocsDir, old), { recursive: true, force: true });
4095
+ await rm5(join21(flydocsDir, old), { recursive: true, force: true });
3669
4096
  }
3670
4097
  }
3671
4098
  } catch {
@@ -3704,17 +4131,17 @@ var init_update = __esm({
3704
4131
  await ensureDirectories(targetDir, effectiveTier);
3705
4132
  console.log("Replacing framework directories...");
3706
4133
  await replaceDirectory(
3707
- join20(templateDir, ".flydocs", "templates"),
3708
- join20(targetDir, ".flydocs", "templates")
4134
+ join21(templateDir, ".flydocs", "templates"),
4135
+ join21(targetDir, ".flydocs", "templates")
3709
4136
  );
3710
4137
  printStatus(".flydocs/templates");
3711
4138
  await replaceDirectory(
3712
- join20(templateDir, ".claude", "hooks"),
3713
- join20(targetDir, ".claude", "hooks")
4139
+ join21(templateDir, ".claude", "hooks"),
4140
+ join21(targetDir, ".claude", "hooks")
3714
4141
  );
3715
4142
  printStatus(".claude/hooks");
3716
4143
  const hasExistingAgents = await pathExists(
3717
- join20(targetDir, ".claude", "agents")
4144
+ join21(targetDir, ".claude", "agents")
3718
4145
  );
3719
4146
  let installAgents;
3720
4147
  if (args.yes) {
@@ -3723,7 +4150,7 @@ var init_update = __esm({
3723
4150
  installAgents = true;
3724
4151
  } else {
3725
4152
  console.log();
3726
- console.log(` ${pc8.bold(pc8.yellow("Sub-Agents (Recommended)"))}`);
4153
+ console.log(` ${pc9.bold(pc9.yellow("Sub-Agents (Recommended)"))}`);
3727
4154
  console.log();
3728
4155
  console.log(
3729
4156
  " Sub-agents are specialized roles (PM, implementation, review,"
@@ -3746,20 +4173,20 @@ var init_update = __esm({
3746
4173
  }
3747
4174
  }
3748
4175
  if (installAgents) {
3749
- const claudeAgentsSrc = join20(templateDir, ".claude", "agents");
4176
+ const claudeAgentsSrc = join21(templateDir, ".claude", "agents");
3750
4177
  if (await pathExists(claudeAgentsSrc)) {
3751
- await mkdir10(join20(targetDir, ".claude", "agents"), { recursive: true });
4178
+ await mkdir10(join21(targetDir, ".claude", "agents"), { recursive: true });
3752
4179
  await copyDirectoryContents(
3753
4180
  claudeAgentsSrc,
3754
- join20(targetDir, ".claude", "agents")
4181
+ join21(targetDir, ".claude", "agents")
3755
4182
  );
3756
4183
  }
3757
- const cursorAgentsSrc = join20(templateDir, ".cursor", "agents");
4184
+ const cursorAgentsSrc = join21(templateDir, ".cursor", "agents");
3758
4185
  if (await pathExists(cursorAgentsSrc)) {
3759
- await mkdir10(join20(targetDir, ".cursor", "agents"), { recursive: true });
4186
+ await mkdir10(join21(targetDir, ".cursor", "agents"), { recursive: true });
3760
4187
  await copyDirectoryContents(
3761
4188
  cursorAgentsSrc,
3762
- join20(targetDir, ".cursor", "agents")
4189
+ join21(targetDir, ".cursor", "agents")
3763
4190
  );
3764
4191
  }
3765
4192
  printStatus(
@@ -3773,47 +4200,47 @@ var init_update = __esm({
3773
4200
  console.log();
3774
4201
  console.log("Replacing framework files...");
3775
4202
  await copyFile(
3776
- join20(templateDir, ".claude", "CLAUDE.md"),
3777
- join20(targetDir, ".claude", "CLAUDE.md")
4203
+ join21(templateDir, ".claude", "CLAUDE.md"),
4204
+ join21(targetDir, ".claude", "CLAUDE.md")
3778
4205
  );
3779
4206
  await copyFile(
3780
- join20(templateDir, ".claude", "settings.json"),
3781
- join20(targetDir, ".claude", "settings.json")
4207
+ join21(templateDir, ".claude", "settings.json"),
4208
+ join21(targetDir, ".claude", "settings.json")
3782
4209
  );
3783
4210
  printStatus(".claude/CLAUDE.md, settings.json");
3784
4211
  await copyDirectoryContents(
3785
- join20(templateDir, ".claude", "commands"),
3786
- join20(targetDir, ".claude", "commands")
4212
+ join21(templateDir, ".claude", "commands"),
4213
+ join21(targetDir, ".claude", "commands")
3787
4214
  );
3788
4215
  await copyDirectoryContents(
3789
- join20(templateDir, ".claude", "commands"),
3790
- join20(targetDir, ".cursor", "commands")
4216
+ join21(templateDir, ".claude", "commands"),
4217
+ join21(targetDir, ".cursor", "commands")
3791
4218
  );
3792
4219
  printStatus(".claude/commands, .cursor/commands");
3793
- const skillsReadmeSrc = join20(templateDir, ".claude", "skills", "README.md");
4220
+ const skillsReadmeSrc = join21(templateDir, ".claude", "skills", "README.md");
3794
4221
  if (await pathExists(skillsReadmeSrc)) {
3795
4222
  await copyFile(
3796
4223
  skillsReadmeSrc,
3797
- join20(targetDir, ".claude", "skills", "README.md")
4224
+ join21(targetDir, ".claude", "skills", "README.md")
3798
4225
  );
3799
4226
  }
3800
4227
  printStatus(".claude/skills/README.md");
3801
4228
  await copyFile(
3802
- join20(templateDir, ".cursor", "hooks.json"),
3803
- join20(targetDir, ".cursor", "hooks.json")
4229
+ join21(templateDir, ".cursor", "hooks.json"),
4230
+ join21(targetDir, ".cursor", "hooks.json")
3804
4231
  );
3805
4232
  printStatus(".cursor/hooks.json");
3806
4233
  await copyFile(
3807
- join20(templateDir, "AGENTS.md"),
3808
- join20(targetDir, "AGENTS.md")
4234
+ join21(templateDir, "AGENTS.md"),
4235
+ join21(targetDir, "AGENTS.md")
3809
4236
  );
3810
4237
  printStatus("AGENTS.md");
3811
- const envExampleSrc = join20(templateDir, ".env.example");
4238
+ const envExampleSrc = join21(templateDir, ".env.example");
3812
4239
  if (await pathExists(envExampleSrc)) {
3813
- await copyFile(envExampleSrc, join20(targetDir, ".env.example"));
4240
+ await copyFile(envExampleSrc, join21(targetDir, ".env.example"));
3814
4241
  printStatus(".env.example");
3815
4242
  }
3816
- const knowledgeTemplatesDir = join20(
4243
+ const knowledgeTemplatesDir = join21(
3817
4244
  targetDir,
3818
4245
  "flydocs",
3819
4246
  "knowledge",
@@ -3823,8 +4250,8 @@ var init_update = __esm({
3823
4250
  await mkdir10(knowledgeTemplatesDir, { recursive: true });
3824
4251
  }
3825
4252
  for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
3826
- const src = join20(templateDir, "flydocs", "knowledge", "templates", tmpl);
3827
- const dest = join20(knowledgeTemplatesDir, tmpl);
4253
+ const src = join21(templateDir, "flydocs", "knowledge", "templates", tmpl);
4254
+ const dest = join21(knowledgeTemplatesDir, tmpl);
3828
4255
  if (await pathExists(src) && !await pathExists(dest)) {
3829
4256
  await copyFile(src, dest);
3830
4257
  }
@@ -3851,18 +4278,18 @@ var init_update = __esm({
3851
4278
  printWarning("Config merge failed \u2014 config.json preserved as-is");
3852
4279
  }
3853
4280
  await copyFile(
3854
- join20(templateDir, ".flydocs", "version"),
3855
- join20(targetDir, ".flydocs", "version")
4281
+ join21(templateDir, ".flydocs", "version"),
4282
+ join21(targetDir, ".flydocs", "version")
3856
4283
  );
3857
4284
  printStatus(`.flydocs/version \u2192 ${version}`);
3858
- const clSrc = join20(templateDir, "CHANGELOG.md");
4285
+ const clSrc = join21(templateDir, "CHANGELOG.md");
3859
4286
  if (await pathExists(clSrc)) {
3860
- await copyFile(clSrc, join20(targetDir, ".flydocs", "CHANGELOG.md"));
4287
+ await copyFile(clSrc, join21(targetDir, ".flydocs", "CHANGELOG.md"));
3861
4288
  printStatus(".flydocs/CHANGELOG.md");
3862
4289
  }
3863
- const mfSrc = join20(templateDir, "manifest.json");
4290
+ const mfSrc = join21(templateDir, "manifest.json");
3864
4291
  if (await pathExists(mfSrc)) {
3865
- await copyFile(mfSrc, join20(targetDir, ".flydocs", "manifest.json"));
4292
+ await copyFile(mfSrc, join21(targetDir, ".flydocs", "manifest.json"));
3866
4293
  printStatus(".flydocs/manifest.json");
3867
4294
  }
3868
4295
  await generateIntegrity(targetDir, version);
@@ -3924,13 +4351,13 @@ var uninstall_exports = {};
3924
4351
  __export(uninstall_exports, {
3925
4352
  default: () => uninstall_default
3926
4353
  });
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";
4354
+ import { defineCommand as defineCommand5 } from "citty";
4355
+ import { resolve as resolve5, join as join22 } from "path";
4356
+ import { readdir as readdir6, rm as rm6, rename as rename2 } from "fs/promises";
3930
4357
  import { confirm as confirm5, select as select4, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
3931
- import pc9 from "picocolors";
4358
+ import pc10 from "picocolors";
3932
4359
  async function removeOwnedSkills(targetDir) {
3933
- const skillsDir = join21(targetDir, ".claude", "skills");
4360
+ const skillsDir = join22(targetDir, ".claude", "skills");
3934
4361
  const removed = [];
3935
4362
  if (!await pathExists(skillsDir)) {
3936
4363
  return removed;
@@ -3939,7 +4366,7 @@ async function removeOwnedSkills(targetDir) {
3939
4366
  const entries = await readdir6(skillsDir);
3940
4367
  for (const entry of entries) {
3941
4368
  if (entry.startsWith(OWNED_SKILL_PREFIX)) {
3942
- await rm5(join21(skillsDir, entry), { recursive: true, force: true });
4369
+ await rm6(join22(skillsDir, entry), { recursive: true, force: true });
3943
4370
  removed.push(`.claude/skills/${entry}`);
3944
4371
  }
3945
4372
  }
@@ -3948,7 +4375,7 @@ async function removeOwnedSkills(targetDir) {
3948
4375
  return removed;
3949
4376
  }
3950
4377
  async function removeOwnedCursorRules(targetDir) {
3951
- const rulesDir = join21(targetDir, ".cursor", "rules");
4378
+ const rulesDir = join22(targetDir, ".cursor", "rules");
3952
4379
  const removed = [];
3953
4380
  if (!await pathExists(rulesDir)) {
3954
4381
  return removed;
@@ -3957,7 +4384,7 @@ async function removeOwnedCursorRules(targetDir) {
3957
4384
  const entries = await readdir6(rulesDir);
3958
4385
  for (const entry of entries) {
3959
4386
  if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
3960
- await rm5(join21(rulesDir, entry), { force: true });
4387
+ await rm6(join22(rulesDir, entry), { force: true });
3961
4388
  removed.push(`.cursor/rules/${entry}`);
3962
4389
  }
3963
4390
  }
@@ -3976,9 +4403,9 @@ async function isEmptyDir(dirPath) {
3976
4403
  async function cleanupEmptyParents(targetDir, dirs) {
3977
4404
  const cleaned = [];
3978
4405
  for (const dir of dirs) {
3979
- const fullPath = join21(targetDir, dir);
4406
+ const fullPath = join22(targetDir, dir);
3980
4407
  if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
3981
- await rm5(fullPath, { recursive: true, force: true });
4408
+ await rm6(fullPath, { recursive: true, force: true });
3982
4409
  cleaned.push(dir);
3983
4410
  }
3984
4411
  }
@@ -4008,7 +4435,7 @@ var init_uninstall = __esm({
4008
4435
  ];
4009
4436
  OWNED_SKILL_PREFIX = "flydocs-";
4010
4437
  OWNED_RULE_PREFIX = "flydocs-";
4011
- uninstall_default = defineCommand4({
4438
+ uninstall_default = defineCommand5({
4012
4439
  meta: {
4013
4440
  name: "uninstall",
4014
4441
  description: "Remove FlyDocs from a project directory"
@@ -4055,8 +4482,8 @@ var init_uninstall = __esm({
4055
4482
  process.exit(1);
4056
4483
  }
4057
4484
  targetDir = resolve5(targetDir);
4058
- const hasFlydocs = await pathExists(join21(targetDir, ".flydocs"));
4059
- const hasAgentsMd = await pathExists(join21(targetDir, "AGENTS.md"));
4485
+ const hasFlydocs = await pathExists(join22(targetDir, ".flydocs"));
4486
+ const hasAgentsMd = await pathExists(join22(targetDir, "AGENTS.md"));
4060
4487
  if (!hasFlydocs && !hasAgentsMd) {
4061
4488
  printError(`Not a FlyDocs project: ${targetDir}`);
4062
4489
  printInfo("No .flydocs/ directory or AGENTS.md found.");
@@ -4068,7 +4495,7 @@ var init_uninstall = __esm({
4068
4495
  const removeAll = forceAll || args.all;
4069
4496
  const skipPrompts = forceAll || args.yes;
4070
4497
  let contentAction = "preserve";
4071
- const hasUserContent = await pathExists(join21(targetDir, "flydocs"));
4498
+ const hasUserContent = await pathExists(join22(targetDir, "flydocs"));
4072
4499
  if (hasUserContent) {
4073
4500
  if (removeAll) {
4074
4501
  contentAction = "remove";
@@ -4102,34 +4529,34 @@ var init_uninstall = __esm({
4102
4529
  }
4103
4530
  if (!skipPrompts) {
4104
4531
  console.log();
4105
- console.log(pc9.bold("The following will be removed:"));
4532
+ console.log(pc10.bold("The following will be removed:"));
4106
4533
  console.log();
4107
4534
  console.log(" Framework files:");
4108
4535
  for (const [path] of ALWAYS_REMOVED) {
4109
- console.log(` ${pc9.dim(path)}`);
4536
+ console.log(` ${pc10.dim(path)}`);
4110
4537
  }
4111
- console.log(` ${pc9.dim(".claude/skills/flydocs-*")}`);
4112
- console.log(` ${pc9.dim(".cursor/rules/flydocs-*.mdc")}`);
4538
+ console.log(` ${pc10.dim(".claude/skills/flydocs-*")}`);
4539
+ console.log(` ${pc10.dim(".cursor/rules/flydocs-*.mdc")}`);
4113
4540
  if (hasUserContent) {
4114
4541
  if (contentAction === "archive") {
4115
4542
  console.log();
4116
4543
  console.log(
4117
- ` User content: ${pc9.yellow("flydocs/ -> flydocs-archive/")}`
4544
+ ` User content: ${pc10.yellow("flydocs/ -> flydocs-archive/")}`
4118
4545
  );
4119
4546
  } else if (contentAction === "remove") {
4120
4547
  console.log();
4121
- console.log(` User content: ${pc9.red("flydocs/ (deleted)")}`);
4548
+ console.log(` User content: ${pc10.red("flydocs/ (deleted)")}`);
4122
4549
  } else {
4123
4550
  console.log();
4124
- console.log(` User content: ${pc9.green("flydocs/ (preserved)")}`);
4551
+ console.log(` User content: ${pc10.green("flydocs/ (preserved)")}`);
4125
4552
  }
4126
4553
  }
4127
4554
  console.log();
4128
- console.log(pc9.bold("Preserved:"));
4555
+ console.log(pc10.bold("Preserved:"));
4129
4556
  console.log(
4130
- ` ${pc9.dim(".claude/skills/ (non-flydocs community skills)")}`
4557
+ ` ${pc10.dim(".claude/skills/ (non-flydocs community skills)")}`
4131
4558
  );
4132
- console.log(` ${pc9.dim(".env, .env.local")}`);
4559
+ console.log(` ${pc10.dim(".env, .env.local")}`);
4133
4560
  console.log();
4134
4561
  const shouldContinue = await confirm5({
4135
4562
  message: "Proceed with uninstall?"
@@ -4151,16 +4578,16 @@ var init_uninstall = __esm({
4151
4578
  const removedRules = await removeOwnedCursorRules(targetDir);
4152
4579
  result.removed.push(...removedRules);
4153
4580
  for (const [relativePath, type] of ALWAYS_REMOVED) {
4154
- const fullPath = join21(targetDir, relativePath);
4581
+ const fullPath = join22(targetDir, relativePath);
4155
4582
  if (!await pathExists(fullPath)) {
4156
4583
  result.skipped.push(relativePath);
4157
4584
  continue;
4158
4585
  }
4159
4586
  try {
4160
4587
  if (type === "dir") {
4161
- await rm5(fullPath, { recursive: true, force: true });
4588
+ await rm6(fullPath, { recursive: true, force: true });
4162
4589
  } else {
4163
- await rm5(fullPath, { force: true });
4590
+ await rm6(fullPath, { force: true });
4164
4591
  }
4165
4592
  result.removed.push(relativePath);
4166
4593
  } catch {
@@ -4169,16 +4596,16 @@ var init_uninstall = __esm({
4169
4596
  }
4170
4597
  }
4171
4598
  if (hasUserContent) {
4172
- const flydocsPath = join21(targetDir, "flydocs");
4599
+ const flydocsPath = join22(targetDir, "flydocs");
4173
4600
  if (contentAction === "archive") {
4174
- const archivePath = join21(targetDir, "flydocs-archive");
4601
+ const archivePath = join22(targetDir, "flydocs-archive");
4175
4602
  if (await pathExists(archivePath)) {
4176
- await rm5(archivePath, { recursive: true, force: true });
4603
+ await rm6(archivePath, { recursive: true, force: true });
4177
4604
  }
4178
4605
  await rename2(flydocsPath, archivePath);
4179
4606
  result.archived.push("flydocs/ -> flydocs-archive/");
4180
4607
  } else if (contentAction === "remove") {
4181
- await rm5(flydocsPath, { recursive: true, force: true });
4608
+ await rm6(flydocsPath, { recursive: true, force: true });
4182
4609
  result.removed.push("flydocs/");
4183
4610
  }
4184
4611
  }
@@ -4197,17 +4624,17 @@ var init_uninstall = __esm({
4197
4624
  result.restored = originals.map((f) => f.relativePath);
4198
4625
  }
4199
4626
  console.log();
4200
- console.log(pc9.bold("Uninstall Summary"));
4627
+ console.log(pc10.bold("Uninstall Summary"));
4201
4628
  console.log();
4202
4629
  if (result.removed.length > 0) {
4203
- console.log(` ${pc9.green("Removed")} (${result.removed.length}):`);
4630
+ console.log(` ${pc10.green("Removed")} (${result.removed.length}):`);
4204
4631
  for (const item of result.removed) {
4205
4632
  printStatus(item);
4206
4633
  }
4207
4634
  }
4208
4635
  if (result.archived.length > 0) {
4209
4636
  console.log();
4210
- console.log(` ${pc9.yellow("Archived")} (${result.archived.length}):`);
4637
+ console.log(` ${pc10.yellow("Archived")} (${result.archived.length}):`);
4211
4638
  for (const item of result.archived) {
4212
4639
  printInfo(item);
4213
4640
  }
@@ -4215,7 +4642,7 @@ var init_uninstall = __esm({
4215
4642
  if (result.restored.length > 0) {
4216
4643
  console.log();
4217
4644
  console.log(
4218
- ` ${pc9.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
4645
+ ` ${pc10.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
4219
4646
  );
4220
4647
  for (const item of result.restored) {
4221
4648
  printInfo(item);
@@ -4224,16 +4651,16 @@ var init_uninstall = __esm({
4224
4651
  if (result.skipped.length > 0) {
4225
4652
  console.log();
4226
4653
  console.log(
4227
- ` ${pc9.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
4654
+ ` ${pc10.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
4228
4655
  );
4229
4656
  for (const item of result.skipped) {
4230
- console.log(` ${pc9.dim(item)}`);
4657
+ console.log(` ${pc10.dim(item)}`);
4231
4658
  }
4232
4659
  }
4233
4660
  console.log();
4234
4661
  printStatus("FlyDocs has been removed from this project.");
4235
4662
  console.log();
4236
- printInfo(`To reinstall: ${pc9.cyan("npx @flydocs/cli install --here")}`);
4663
+ printInfo(`To reinstall: ${pc10.cyan("npx @flydocs/cli install --here")}`);
4237
4664
  console.log();
4238
4665
  }
4239
4666
  });
@@ -4245,28 +4672,28 @@ var setup_exports = {};
4245
4672
  __export(setup_exports, {
4246
4673
  default: () => setup_default
4247
4674
  });
4248
- import { defineCommand as defineCommand5 } from "citty";
4249
- import pc10 from "picocolors";
4675
+ import { defineCommand as defineCommand6 } from "citty";
4676
+ import pc11 from "picocolors";
4250
4677
  var setup_default;
4251
4678
  var init_setup = __esm({
4252
4679
  "src/commands/setup.ts"() {
4253
4680
  "use strict";
4254
- setup_default = defineCommand5({
4681
+ setup_default = defineCommand6({
4255
4682
  meta: {
4256
4683
  name: "setup",
4257
4684
  description: "Configure FlyDocs settings for this project"
4258
4685
  },
4259
4686
  run() {
4260
4687
  console.log();
4261
- console.log(` ${pc10.bold("FlyDocs Setup")}`);
4688
+ console.log(` ${pc11.bold("FlyDocs Setup")}`);
4262
4689
  console.log();
4263
4690
  console.log(` Setup runs inside your IDE as an interactive AI command.`);
4264
4691
  console.log();
4265
4692
  console.log(
4266
- ` ${pc10.cyan("Claude Code:")} Type ${pc10.bold("/flydocs-setup")} in chat`
4693
+ ` ${pc11.cyan("Claude Code:")} Type ${pc11.bold("/flydocs-setup")} in chat`
4267
4694
  );
4268
4695
  console.log(
4269
- ` ${pc10.cyan("Cursor:")} Type ${pc10.bold("/flydocs-setup")} in chat`
4696
+ ` ${pc11.cyan("Cursor:")} Type ${pc11.bold("/flydocs-setup")} in chat`
4270
4697
  );
4271
4698
  console.log();
4272
4699
  console.log(` This configures your project context, detects your stack,`);
@@ -4282,14 +4709,14 @@ var skills_exports = {};
4282
4709
  __export(skills_exports, {
4283
4710
  default: () => skills_default
4284
4711
  });
4285
- import { defineCommand as defineCommand6 } from "citty";
4286
- import pc11 from "picocolors";
4712
+ import { defineCommand as defineCommand7 } from "citty";
4713
+ import pc12 from "picocolors";
4287
4714
  var list, search, add, remove, skills_default;
4288
4715
  var init_skills2 = __esm({
4289
4716
  "src/commands/skills.ts"() {
4290
4717
  "use strict";
4291
4718
  init_skill_manager();
4292
- list = defineCommand6({
4719
+ list = defineCommand7({
4293
4720
  meta: {
4294
4721
  name: "list",
4295
4722
  description: "List installed skills"
@@ -4305,26 +4732,26 @@ var init_skills2 = __esm({
4305
4732
  console.log(`${total} skill(s) installed:`);
4306
4733
  if (result.platform.length > 0) {
4307
4734
  console.log();
4308
- console.log(pc11.bold("Platform"));
4735
+ console.log(pc12.bold("Platform"));
4309
4736
  for (const skill of result.platform) {
4310
4737
  console.log(
4311
- ` ${skill.name} ${pc11.dim(`(${skill.triggers} triggers)`)}`
4738
+ ` ${skill.name} ${pc12.dim(`(${skill.triggers} triggers)`)}`
4312
4739
  );
4313
4740
  }
4314
4741
  }
4315
4742
  if (result.community.length > 0) {
4316
4743
  console.log();
4317
- console.log(pc11.bold("Community"));
4744
+ console.log(pc12.bold("Community"));
4318
4745
  for (const skill of result.community) {
4319
4746
  console.log(
4320
- ` ${skill.name} ${pc11.dim(`(${skill.triggers} triggers)`)}`
4747
+ ` ${skill.name} ${pc12.dim(`(${skill.triggers} triggers)`)}`
4321
4748
  );
4322
4749
  }
4323
4750
  }
4324
4751
  console.log();
4325
4752
  }
4326
4753
  });
4327
- search = defineCommand6({
4754
+ search = defineCommand7({
4328
4755
  meta: {
4329
4756
  name: "search",
4330
4757
  description: "Search community skills"
@@ -4340,24 +4767,24 @@ var init_skills2 = __esm({
4340
4767
  const results = await searchCatalog(args.keyword);
4341
4768
  if (results.length === 0) {
4342
4769
  console.log(`No skills found for "${args.keyword}".`);
4343
- console.log(` Browse the catalog at: ${pc11.cyan("https://skills.sh/")}`);
4770
+ console.log(` Browse the catalog at: ${pc12.cyan("https://skills.sh/")}`);
4344
4771
  return;
4345
4772
  }
4346
4773
  console.log();
4347
4774
  console.log(`${results.length} skill(s) matching "${args.keyword}":`);
4348
4775
  console.log();
4349
4776
  for (const skill of results) {
4350
- console.log(` ${pc11.bold(skill.name)}`);
4777
+ console.log(` ${pc12.bold(skill.name)}`);
4351
4778
  console.log(` ${skill.description}`);
4352
- console.log(` ${pc11.dim(skill.repo)}`);
4779
+ console.log(` ${pc12.dim(skill.repo)}`);
4353
4780
  if (skill.tags.length > 0) {
4354
- console.log(` ${pc11.dim(skill.tags.join(", "))}`);
4781
+ console.log(` ${pc12.dim(skill.tags.join(", "))}`);
4355
4782
  }
4356
4783
  console.log();
4357
4784
  }
4358
4785
  }
4359
4786
  });
4360
- add = defineCommand6({
4787
+ add = defineCommand7({
4361
4788
  meta: {
4362
4789
  name: "add",
4363
4790
  description: "Install a community skill"
@@ -4373,7 +4800,7 @@ var init_skills2 = __esm({
4373
4800
  await addSkill(process.cwd(), args.source);
4374
4801
  }
4375
4802
  });
4376
- remove = defineCommand6({
4803
+ remove = defineCommand7({
4377
4804
  meta: {
4378
4805
  name: "remove",
4379
4806
  description: "Remove an installed community skill"
@@ -4389,7 +4816,7 @@ var init_skills2 = __esm({
4389
4816
  await removeSkill(process.cwd(), args.name);
4390
4817
  }
4391
4818
  });
4392
- skills_default = defineCommand6({
4819
+ skills_default = defineCommand7({
4393
4820
  meta: {
4394
4821
  name: "skills",
4395
4822
  description: "Manage FlyDocs skills (list, search, add, remove)"
@@ -4409,10 +4836,10 @@ var connect_exports = {};
4409
4836
  __export(connect_exports, {
4410
4837
  default: () => connect_default
4411
4838
  });
4412
- import { defineCommand as defineCommand7 } from "citty";
4839
+ import { defineCommand as defineCommand8 } from "citty";
4413
4840
  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";
4841
+ import pc13 from "picocolors";
4842
+ import { join as join23 } from "path";
4416
4843
  var connect_default;
4417
4844
  var init_connect = __esm({
4418
4845
  "src/commands/connect.ts"() {
@@ -4422,7 +4849,7 @@ var init_connect = __esm({
4422
4849
  init_template();
4423
4850
  init_ui();
4424
4851
  init_api_key();
4425
- connect_default = defineCommand7({
4852
+ connect_default = defineCommand8({
4426
4853
  meta: {
4427
4854
  name: "connect",
4428
4855
  description: "Connect FlyDocs to a cloud provider"
@@ -4447,11 +4874,11 @@ var init_connect = __esm({
4447
4874
  },
4448
4875
  async run({ args }) {
4449
4876
  const targetDir = args.path ?? process.cwd();
4450
- const configPath = join22(targetDir, ".flydocs", "config.json");
4877
+ const configPath = join23(targetDir, ".flydocs", "config.json");
4451
4878
  if (!await pathExists(configPath)) {
4452
4879
  printError("Not a FlyDocs project (.flydocs/config.json not found).");
4453
4880
  console.log(
4454
- ` Run ${pc12.cyan("flydocs")} first to install FlyDocs in this project.`
4881
+ ` Run ${pc13.cyan("flydocs")} first to install FlyDocs in this project.`
4455
4882
  );
4456
4883
  process.exit(1);
4457
4884
  }
@@ -4468,10 +4895,10 @@ var init_connect = __esm({
4468
4895
  }
4469
4896
  }
4470
4897
  console.log();
4471
- console.log(` ${pc12.bold("Connect to FlyDocs Cloud")}`);
4898
+ console.log(` ${pc13.bold("Connect to FlyDocs Cloud")}`);
4472
4899
  console.log();
4473
4900
  console.log(
4474
- ` ${pc12.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
4901
+ ` ${pc13.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
4475
4902
  );
4476
4903
  console.log();
4477
4904
  let apiKey = args.key ?? "";
@@ -4509,7 +4936,7 @@ var init_connect = __esm({
4509
4936
  console.log(` Check your key and try again.`);
4510
4937
  process.exit(1);
4511
4938
  }
4512
- printStatus(`Connected to ${pc12.bold(result.org)}`);
4939
+ printStatus(`Connected to ${pc13.bold(result.org)}`);
4513
4940
  } catch {
4514
4941
  printError(
4515
4942
  "Could not reach relay API. Check your network and try again."
@@ -4517,7 +4944,7 @@ var init_connect = __esm({
4517
4944
  process.exit(1);
4518
4945
  }
4519
4946
  const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
4520
- printStatus(`API key stored in ${pc12.dim(envFile)}`);
4947
+ printStatus(`API key stored in ${pc13.dim(envFile)}`);
4521
4948
  } else {
4522
4949
  try {
4523
4950
  const result = await validateLinearKey(apiKey);
@@ -4527,7 +4954,7 @@ var init_connect = __esm({
4527
4954
  process.exit(1);
4528
4955
  }
4529
4956
  printStatus(
4530
- `Authenticated as ${pc12.bold(result.name)} (${result.email})`
4957
+ `Authenticated as ${pc13.bold(result.name)} (${result.email})`
4531
4958
  );
4532
4959
  } catch {
4533
4960
  printError("Invalid API key or network error.");
@@ -4535,7 +4962,7 @@ var init_connect = __esm({
4535
4962
  process.exit(1);
4536
4963
  }
4537
4964
  const envFile = await storeEnvKey(targetDir, "LINEAR_API_KEY", apiKey);
4538
- printStatus(`API key stored in ${pc12.dim(envFile)}`);
4965
+ printStatus(`API key stored in ${pc13.dim(envFile)}`);
4539
4966
  }
4540
4967
  const wasLocal = config.tier === "local";
4541
4968
  config.tier = "cloud";
@@ -4551,14 +4978,14 @@ var init_connect = __esm({
4551
4978
  }
4552
4979
  console.log();
4553
4980
  console.log(
4554
- ` ${pc12.bold("Connected!")} Your project is now on the cloud tier.`
4981
+ ` ${pc13.bold("Connected!")} Your project is now on the cloud tier.`
4555
4982
  );
4556
4983
  console.log();
4557
4984
  console.log(` Next steps:`);
4558
4985
  console.log(
4559
- ` 1. Run ${pc12.cyan("/flydocs-setup")} in your IDE to configure your project`
4986
+ ` 1. Run ${pc13.cyan("/flydocs-setup")} in your IDE to configure your project`
4560
4987
  );
4561
- console.log(` 2. Run ${pc12.cyan("/start-session")} to begin working`);
4988
+ console.log(` 2. Run ${pc13.cyan("/start-session")} to begin working`);
4562
4989
  console.log();
4563
4990
  }
4564
4991
  });
@@ -4570,9 +4997,9 @@ var auth_exports = {};
4570
4997
  __export(auth_exports, {
4571
4998
  default: () => auth_default
4572
4999
  });
4573
- import { defineCommand as defineCommand8 } from "citty";
5000
+ import { defineCommand as defineCommand9 } from "citty";
4574
5001
  import { text as text5, confirm as confirm7, isCancel as isCancel8, cancel as cancel7 } from "@clack/prompts";
4575
- import pc13 from "picocolors";
5002
+ import pc14 from "picocolors";
4576
5003
  var auth_default;
4577
5004
  var init_auth = __esm({
4578
5005
  "src/commands/auth.ts"() {
@@ -4580,7 +5007,7 @@ var init_auth = __esm({
4580
5007
  init_ui();
4581
5008
  init_api_key();
4582
5009
  init_global_config();
4583
- auth_default = defineCommand8({
5010
+ auth_default = defineCommand9({
4584
5011
  meta: {
4585
5012
  name: "auth",
4586
5013
  description: "Store API key globally (~/.flydocs/credentials)"
@@ -4594,13 +5021,13 @@ var init_auth = __esm({
4594
5021
  },
4595
5022
  async run({ args }) {
4596
5023
  console.log();
4597
- console.log(` ${pc13.bold("FlyDocs Authentication")}`);
5024
+ console.log(` ${pc14.bold("FlyDocs Authentication")}`);
4598
5025
  console.log();
4599
5026
  let apiKey = args.key ?? "";
4600
5027
  const existing = await readGlobalCredential();
4601
5028
  if (existing?.apiKey && !apiKey) {
4602
5029
  printInfo(
4603
- `Existing key found: ${pc13.dim(existing.apiKey.slice(0, 8) + "...")}`
5030
+ `Existing key found: ${pc14.dim(existing.apiKey.slice(0, 8) + "...")}`
4604
5031
  );
4605
5032
  const replace = await confirm7({
4606
5033
  message: "Replace with a new key?"
@@ -4612,7 +5039,7 @@ var init_auth = __esm({
4612
5039
  }
4613
5040
  if (!apiKey) {
4614
5041
  console.log(
4615
- ` ${pc13.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
5042
+ ` ${pc14.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
4616
5043
  );
4617
5044
  console.log();
4618
5045
  const keyInput = await text5({
@@ -4648,7 +5075,7 @@ var init_auth = __esm({
4648
5075
  printError("Invalid API key. Check your key and try again.");
4649
5076
  process.exit(1);
4650
5077
  }
4651
- printStatus(`Authenticated with ${pc13.bold(result.org)}`);
5078
+ printStatus(`Authenticated with ${pc14.bold(result.org)}`);
4652
5079
  } catch {
4653
5080
  printError(
4654
5081
  "Could not reach FlyDocs API. Check your network and try again."
@@ -4663,10 +5090,10 @@ var init_auth = __esm({
4663
5090
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4664
5091
  lastValidated: (/* @__PURE__ */ new Date()).toISOString()
4665
5092
  });
4666
- printStatus(`Key stored at ${pc13.dim(credentialsPath())}`);
5093
+ printStatus(`Key stored at ${pc14.dim(credentialsPath())}`);
4667
5094
  await checkCredentialPermissions();
4668
5095
  console.log();
4669
- console.log(` ${pc13.bold("Authenticated!")} Key stored globally.`);
5096
+ console.log(` ${pc14.bold("Authenticated!")} Key stored globally.`);
4670
5097
  console.log(` All FlyDocs projects on this machine will use this key.`);
4671
5098
  console.log();
4672
5099
  }
@@ -4679,10 +5106,10 @@ var sync_exports = {};
4679
5106
  __export(sync_exports, {
4680
5107
  default: () => sync_default
4681
5108
  });
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";
5109
+ import { defineCommand as defineCommand10 } from "citty";
5110
+ import pc15 from "picocolors";
5111
+ import { join as join24 } from "path";
5112
+ import { mkdir as mkdir11, writeFile as writeFile14 } from "fs/promises";
4686
5113
  var sync_default;
4687
5114
  var init_sync = __esm({
4688
5115
  "src/commands/sync.ts"() {
@@ -4694,7 +5121,7 @@ var init_sync = __esm({
4694
5121
  init_fs_ops();
4695
5122
  init_types();
4696
5123
  init_relay_client();
4697
- sync_default = defineCommand9({
5124
+ sync_default = defineCommand10({
4698
5125
  meta: {
4699
5126
  name: "sync",
4700
5127
  description: "Pull latest config and templates from server"
@@ -4745,14 +5172,14 @@ var init_sync = __esm({
4745
5172
  } else {
4746
5173
  printWarning("Server unreachable, using cached config.");
4747
5174
  }
4748
- console.log(` ${pc14.dim("Config may be stale. Retry when connected.")}`);
5175
+ console.log(` ${pc15.dim("Config may be stale. Retry when connected.")}`);
4749
5176
  return;
4750
5177
  }
4751
5178
  if (!serverResponse.valid) {
4752
5179
  printWarning("Server returned invalid config. Keeping current config.");
4753
5180
  return;
4754
5181
  }
4755
- await mkdir11(join23(targetDir, ".flydocs"), { recursive: true });
5182
+ await mkdir11(join24(targetDir, ".flydocs"), { recursive: true });
4756
5183
  const configWithHash = applyConfigHash(serverResponse.config);
4757
5184
  await writeConfig(targetDir, configWithHash);
4758
5185
  changes.push("Updated .flydocs/config.json");
@@ -4765,7 +5192,7 @@ var init_sync = __esm({
4765
5192
  workspaceId
4766
5193
  );
4767
5194
  if (templatesResponse.templates.length > 0) {
4768
- const templatesDir = join23(
5195
+ const templatesDir = join24(
4769
5196
  targetDir,
4770
5197
  ".claude",
4771
5198
  "skills",
@@ -4776,10 +5203,10 @@ var init_sync = __esm({
4776
5203
  for (const template of templatesResponse.templates) {
4777
5204
  const filename = `${template.type}.md`;
4778
5205
  const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
4779
- const templateDir = join23(templatesDir, subdir);
5206
+ const templateDir = join24(templatesDir, subdir);
4780
5207
  await mkdir11(templateDir, { recursive: true });
4781
- await writeFile13(
4782
- join23(templateDir, filename),
5208
+ await writeFile14(
5209
+ join24(templateDir, filename),
4783
5210
  template.content,
4784
5211
  "utf-8"
4785
5212
  );
@@ -4809,221 +5236,6 @@ var init_sync = __esm({
4809
5236
  }
4810
5237
  });
4811
5238
 
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")
4985
- );
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
- ]);
5021
- console.log();
5022
- }
5023
- });
5024
- }
5025
- });
5026
-
5027
5239
  // src/commands/upgrade.ts
5028
5240
  var upgrade_exports = {};
5029
5241
  __export(upgrade_exports, {