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

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.28";
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,44 @@ 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
+ }
3295
3548
  async function isParentDirectory(dir) {
3296
- const hasGit = await pathExists(join19(dir, ".git"));
3549
+ const hasGit = await pathExists(join20(dir, ".git"));
3297
3550
  if (hasGit) return false;
3298
3551
  const repos = await detectSiblingRepos(dir);
3299
3552
  return Object.keys(repos).length >= 2;
@@ -3303,7 +3556,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
3303
3556
  const repoNames = Object.keys(repos).sort();
3304
3557
  printInfo(`Detected ${repoNames.length} repos:`);
3305
3558
  for (const name of repoNames) {
3306
- console.log(` ${pc7.cyan(name)}`);
3559
+ console.log(` ${pc8.cyan(name)}`);
3307
3560
  }
3308
3561
  console.log();
3309
3562
  const shouldContinue = await confirm3({
@@ -3313,7 +3566,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
3313
3566
  cancel3("Init cancelled.");
3314
3567
  process.exit(0);
3315
3568
  }
3316
- const firstRepoDir = join19(parentDir, repoNames[0]);
3569
+ const firstRepoDir = join20(parentDir, repoNames[0]);
3317
3570
  const { apiKey, workspaceId } = await resolveAndValidateKey(
3318
3571
  keyArg,
3319
3572
  firstRepoDir
@@ -3325,9 +3578,10 @@ async function runMultiRepoInit(parentDir, keyArg) {
3325
3578
  let allWarnings = [];
3326
3579
  let firstResponse;
3327
3580
  for (const repoName of repoNames) {
3328
- const repoDir = join19(parentDir, repoName);
3581
+ const repoDir = join20(parentDir, repoName);
3329
3582
  console.log();
3330
- printInfo(`Initializing ${pc7.bold(repoName)}...`);
3583
+ printInfo(`Initializing ${pc8.bold(repoName)}...`);
3584
+ await checkGitFreshness(repoDir);
3331
3585
  const serverResponse = await pullServerConfig(apiKey, repoDir, workspaceId);
3332
3586
  if (!firstResponse) firstResponse = serverResponse;
3333
3587
  const { actions, skipped } = await initSingleRepo(
@@ -3356,47 +3610,60 @@ async function runMultiRepoInit(parentDir, keyArg) {
3356
3610
  await writeWorkspaceFile(parentDir, workspaceFile);
3357
3611
  allActions.push(`.flydocs-workspace.json (${repoNames.length} repos)`);
3358
3612
  }
3359
- const contextDir = join19(parentDir, "flydocs", "context");
3360
- const workspaceMdPath = join19(contextDir, "workspace.md");
3613
+ const contextDir = join20(parentDir, "flydocs", "context");
3614
+ const workspaceMdPath = join20(contextDir, "workspace.md");
3361
3615
  if (await pathExists(workspaceMdPath)) {
3362
3616
  allSkipped.push("flydocs/context/workspace.md (already exists)");
3363
3617
  } else {
3364
3618
  await mkdir9(contextDir, { recursive: true });
3365
3619
  const workspaceJson = await readWorkspaceFile(parentDir) ?? buildWorkspaceFile(firstResponse.workspaceId, repos);
3366
3620
  const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(parentDir, workspaceJson);
3367
- await writeFile12(workspaceMdPath, content, "utf-8");
3621
+ await writeFile13(workspaceMdPath, content, "utf-8");
3368
3622
  const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
3369
3623
  allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
3370
3624
  }
3371
3625
  }
3372
3626
  console.log();
3373
- printInitReport(allActions, allSkipped, allWarnings);
3627
+ printInitReport(
3628
+ allActions,
3629
+ allSkipped,
3630
+ allWarnings,
3631
+ !!firstResponse?.context
3632
+ );
3374
3633
  }
3375
- function printInitReport(actions, skipped, warnings) {
3634
+ function printInitReport(actions, skipped, warnings, hasContext) {
3376
3635
  console.log();
3377
3636
  const lines = [];
3378
3637
  for (const action of actions) {
3379
- lines.push(`${pc7.green("+")} ${action}`);
3638
+ lines.push(`${pc8.green("+")} ${action}`);
3380
3639
  }
3381
3640
  for (const skip of skipped) {
3382
- lines.push(`${pc7.dim("-")} ${skip}`);
3641
+ lines.push(`${pc8.dim("-")} ${skip}`);
3642
+ }
3643
+ if (!hasContext) {
3644
+ lines.push("");
3645
+ lines.push(
3646
+ `${pc8.yellow("!")} No generated context yet. An admin can generate it from the portal.`
3647
+ );
3383
3648
  }
3384
3649
  if (warnings.length > 0) {
3385
3650
  lines.push("");
3386
3651
  for (const warning of warnings) {
3387
- lines.push(`${pc7.yellow("!")} ${warning}`);
3652
+ lines.push(`${pc8.yellow("!")} ${warning}`);
3388
3653
  }
3389
3654
  }
3390
3655
  printCompletionBox("FlyDocs Initialized", lines);
3391
3656
  console.log();
3392
3657
  console.log(` Next steps:`);
3393
3658
  console.log(
3394
- ` ${pc7.cyan("flydocs sync")} \u2014 pull latest config and templates`
3659
+ ` ${pc8.cyan("flydocs sync")} Pull latest config and templates`
3660
+ );
3661
+ console.log(
3662
+ ` ${pc8.cyan("cursor .")} ${pc8.dim("or")} ${pc8.cyan("code .")} Open your IDE, then use ${pc8.bold("/start-session")}`
3395
3663
  );
3396
- console.log(` ${pc7.cyan("/start-session")} \u2014 begin working`);
3397
3664
  console.log();
3398
3665
  }
3399
- var init_default;
3666
+ var execFileAsync, init_default;
3400
3667
  var init_init = __esm({
3401
3668
  "src/commands/init.ts"() {
3402
3669
  "use strict";
@@ -3408,8 +3675,10 @@ var init_init = __esm({
3408
3675
  init_gitignore();
3409
3676
  init_fs_ops();
3410
3677
  init_relay_client();
3678
+ init_cleanup();
3411
3679
  init_workspace();
3412
- init_default = defineCommand2({
3680
+ execFileAsync = promisify(execFile);
3681
+ init_default = defineCommand3({
3413
3682
  meta: {
3414
3683
  name: "init",
3415
3684
  description: "Initialize FlyDocs in this project (unified setup)"
@@ -3427,12 +3696,13 @@ var init_init = __esm({
3427
3696
  async run({ args }) {
3428
3697
  const targetDir = args.path ?? process.cwd();
3429
3698
  console.log();
3430
- console.log(` ${pc7.bold("FlyDocs Init")}`);
3699
+ console.log(` ${pc8.bold("FlyDocs Init")}`);
3431
3700
  console.log();
3432
3701
  if (await isParentDirectory(targetDir)) {
3433
3702
  await runMultiRepoInit(targetDir, args.key);
3434
3703
  return;
3435
3704
  }
3705
+ await checkGitFreshness(targetDir);
3436
3706
  const { apiKey, workspaceId } = await resolveAndValidateKey(
3437
3707
  args.key,
3438
3708
  targetDir
@@ -3450,7 +3720,7 @@ var init_init = __esm({
3450
3720
  actions.unshift("Stored credential globally (~/.flydocs/credentials)");
3451
3721
  const topology = serverResponse.config.topology;
3452
3722
  if (topology?.type === 4 && topology.label === "sibling-repos") {
3453
- const parentDir = join19(targetDir, "..");
3723
+ const parentDir = join20(targetDir, "..");
3454
3724
  const existing = await readWorkspaceFile(parentDir);
3455
3725
  if (existing) {
3456
3726
  skipped.push(".flydocs-workspace.json (already exists)");
@@ -3468,7 +3738,12 @@ var init_init = __esm({
3468
3738
  }
3469
3739
  }
3470
3740
  }
3471
- printInitReport(actions, skipped, serverResponse.warnings);
3741
+ printInitReport(
3742
+ actions,
3743
+ skipped,
3744
+ serverResponse.warnings,
3745
+ !!serverResponse.context
3746
+ );
3472
3747
  }
3473
3748
  });
3474
3749
  }
@@ -3479,11 +3754,11 @@ var update_exports = {};
3479
3754
  __export(update_exports, {
3480
3755
  default: () => update_default
3481
3756
  });
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";
3757
+ import { defineCommand as defineCommand4 } from "citty";
3758
+ import { resolve as resolve4, join as join21 } from "path";
3759
+ import { mkdir as mkdir10, cp as cp2, readFile as readFile15, readdir as readdir5, rm as rm5 } from "fs/promises";
3485
3760
  import { select as select3, text as text3, confirm as confirm4, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
3486
- import pc8 from "picocolors";
3761
+ import pc9 from "picocolors";
3487
3762
  var update_default;
3488
3763
  var init_update = __esm({
3489
3764
  "src/commands/update.ts"() {
@@ -3502,7 +3777,7 @@ var init_update = __esm({
3502
3777
  init_update_check();
3503
3778
  init_telemetry();
3504
3779
  init_integrity();
3505
- update_default = defineCommand3({
3780
+ update_default = defineCommand4({
3506
3781
  meta: {
3507
3782
  name: "update",
3508
3783
  description: "Update an existing FlyDocs installation"
@@ -3589,9 +3864,9 @@ var init_update = __esm({
3589
3864
  }
3590
3865
  targetDir = resolve4(targetDir);
3591
3866
  process.chdir(targetDir);
3592
- const hasVersion = await pathExists(join20(targetDir, ".flydocs", "version"));
3867
+ const hasVersion = await pathExists(join21(targetDir, ".flydocs", "version"));
3593
3868
  const hasConfig = await pathExists(
3594
- join20(targetDir, ".flydocs", "config.json")
3869
+ join21(targetDir, ".flydocs", "config.json")
3595
3870
  );
3596
3871
  if (!hasVersion && !hasConfig) {
3597
3872
  printError(`Not a FlyDocs project: ${targetDir}`);
@@ -3602,8 +3877,8 @@ var init_update = __esm({
3602
3877
  console.log();
3603
3878
  let currentVersion = "0.1.0";
3604
3879
  if (hasVersion) {
3605
- const vContent = await readFile14(
3606
- join20(targetDir, ".flydocs", "version"),
3880
+ const vContent = await readFile15(
3881
+ join21(targetDir, ".flydocs", "version"),
3607
3882
  "utf-8"
3608
3883
  );
3609
3884
  currentVersion = vContent.trim();
@@ -3637,10 +3912,10 @@ var init_update = __esm({
3637
3912
  });
3638
3913
  console.log(`Updating: v${currentVersion} \u2192 v${version}`);
3639
3914
  console.log();
3640
- const changelogPath = join20(templateDir, "CHANGELOG.md");
3915
+ const changelogPath = join21(templateDir, "CHANGELOG.md");
3641
3916
  const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
3642
3917
  if (whatsNew.length > 0) {
3643
- console.log(pc8.cyan("What's new:"));
3918
+ console.log(pc9.cyan("What's new:"));
3644
3919
  console.log();
3645
3920
  for (const entry of whatsNew) {
3646
3921
  console.log(` ${entry}`);
@@ -3649,23 +3924,23 @@ var init_update = __esm({
3649
3924
  }
3650
3925
  const now = /* @__PURE__ */ new Date();
3651
3926
  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}`);
3927
+ const backupDir = join21(targetDir, ".flydocs", `backup-${ts}`);
3653
3928
  await mkdir10(backupDir, { recursive: true });
3654
3929
  if (hasConfig) {
3655
3930
  await cp2(
3656
- join20(targetDir, ".flydocs", "config.json"),
3657
- join20(backupDir, "config.json")
3931
+ join21(targetDir, ".flydocs", "config.json"),
3932
+ join21(backupDir, "config.json")
3658
3933
  );
3659
3934
  printStatus(`Config backed up to .flydocs/backup-${ts}/`);
3660
3935
  }
3661
3936
  try {
3662
- const flydocsDir = join20(targetDir, ".flydocs");
3937
+ const flydocsDir = join21(targetDir, ".flydocs");
3663
3938
  const entries = await readdir5(flydocsDir);
3664
3939
  const backups = entries.filter((e) => e.startsWith("backup-")).sort();
3665
3940
  if (backups.length > 3) {
3666
3941
  const toRemove = backups.slice(0, backups.length - 3);
3667
3942
  for (const old of toRemove) {
3668
- await rm4(join20(flydocsDir, old), { recursive: true, force: true });
3943
+ await rm5(join21(flydocsDir, old), { recursive: true, force: true });
3669
3944
  }
3670
3945
  }
3671
3946
  } catch {
@@ -3704,17 +3979,17 @@ var init_update = __esm({
3704
3979
  await ensureDirectories(targetDir, effectiveTier);
3705
3980
  console.log("Replacing framework directories...");
3706
3981
  await replaceDirectory(
3707
- join20(templateDir, ".flydocs", "templates"),
3708
- join20(targetDir, ".flydocs", "templates")
3982
+ join21(templateDir, ".flydocs", "templates"),
3983
+ join21(targetDir, ".flydocs", "templates")
3709
3984
  );
3710
3985
  printStatus(".flydocs/templates");
3711
3986
  await replaceDirectory(
3712
- join20(templateDir, ".claude", "hooks"),
3713
- join20(targetDir, ".claude", "hooks")
3987
+ join21(templateDir, ".claude", "hooks"),
3988
+ join21(targetDir, ".claude", "hooks")
3714
3989
  );
3715
3990
  printStatus(".claude/hooks");
3716
3991
  const hasExistingAgents = await pathExists(
3717
- join20(targetDir, ".claude", "agents")
3992
+ join21(targetDir, ".claude", "agents")
3718
3993
  );
3719
3994
  let installAgents;
3720
3995
  if (args.yes) {
@@ -3723,7 +3998,7 @@ var init_update = __esm({
3723
3998
  installAgents = true;
3724
3999
  } else {
3725
4000
  console.log();
3726
- console.log(` ${pc8.bold(pc8.yellow("Sub-Agents (Recommended)"))}`);
4001
+ console.log(` ${pc9.bold(pc9.yellow("Sub-Agents (Recommended)"))}`);
3727
4002
  console.log();
3728
4003
  console.log(
3729
4004
  " Sub-agents are specialized roles (PM, implementation, review,"
@@ -3746,20 +4021,20 @@ var init_update = __esm({
3746
4021
  }
3747
4022
  }
3748
4023
  if (installAgents) {
3749
- const claudeAgentsSrc = join20(templateDir, ".claude", "agents");
4024
+ const claudeAgentsSrc = join21(templateDir, ".claude", "agents");
3750
4025
  if (await pathExists(claudeAgentsSrc)) {
3751
- await mkdir10(join20(targetDir, ".claude", "agents"), { recursive: true });
4026
+ await mkdir10(join21(targetDir, ".claude", "agents"), { recursive: true });
3752
4027
  await copyDirectoryContents(
3753
4028
  claudeAgentsSrc,
3754
- join20(targetDir, ".claude", "agents")
4029
+ join21(targetDir, ".claude", "agents")
3755
4030
  );
3756
4031
  }
3757
- const cursorAgentsSrc = join20(templateDir, ".cursor", "agents");
4032
+ const cursorAgentsSrc = join21(templateDir, ".cursor", "agents");
3758
4033
  if (await pathExists(cursorAgentsSrc)) {
3759
- await mkdir10(join20(targetDir, ".cursor", "agents"), { recursive: true });
4034
+ await mkdir10(join21(targetDir, ".cursor", "agents"), { recursive: true });
3760
4035
  await copyDirectoryContents(
3761
4036
  cursorAgentsSrc,
3762
- join20(targetDir, ".cursor", "agents")
4037
+ join21(targetDir, ".cursor", "agents")
3763
4038
  );
3764
4039
  }
3765
4040
  printStatus(
@@ -3773,47 +4048,47 @@ var init_update = __esm({
3773
4048
  console.log();
3774
4049
  console.log("Replacing framework files...");
3775
4050
  await copyFile(
3776
- join20(templateDir, ".claude", "CLAUDE.md"),
3777
- join20(targetDir, ".claude", "CLAUDE.md")
4051
+ join21(templateDir, ".claude", "CLAUDE.md"),
4052
+ join21(targetDir, ".claude", "CLAUDE.md")
3778
4053
  );
3779
4054
  await copyFile(
3780
- join20(templateDir, ".claude", "settings.json"),
3781
- join20(targetDir, ".claude", "settings.json")
4055
+ join21(templateDir, ".claude", "settings.json"),
4056
+ join21(targetDir, ".claude", "settings.json")
3782
4057
  );
3783
4058
  printStatus(".claude/CLAUDE.md, settings.json");
3784
4059
  await copyDirectoryContents(
3785
- join20(templateDir, ".claude", "commands"),
3786
- join20(targetDir, ".claude", "commands")
4060
+ join21(templateDir, ".claude", "commands"),
4061
+ join21(targetDir, ".claude", "commands")
3787
4062
  );
3788
4063
  await copyDirectoryContents(
3789
- join20(templateDir, ".claude", "commands"),
3790
- join20(targetDir, ".cursor", "commands")
4064
+ join21(templateDir, ".claude", "commands"),
4065
+ join21(targetDir, ".cursor", "commands")
3791
4066
  );
3792
4067
  printStatus(".claude/commands, .cursor/commands");
3793
- const skillsReadmeSrc = join20(templateDir, ".claude", "skills", "README.md");
4068
+ const skillsReadmeSrc = join21(templateDir, ".claude", "skills", "README.md");
3794
4069
  if (await pathExists(skillsReadmeSrc)) {
3795
4070
  await copyFile(
3796
4071
  skillsReadmeSrc,
3797
- join20(targetDir, ".claude", "skills", "README.md")
4072
+ join21(targetDir, ".claude", "skills", "README.md")
3798
4073
  );
3799
4074
  }
3800
4075
  printStatus(".claude/skills/README.md");
3801
4076
  await copyFile(
3802
- join20(templateDir, ".cursor", "hooks.json"),
3803
- join20(targetDir, ".cursor", "hooks.json")
4077
+ join21(templateDir, ".cursor", "hooks.json"),
4078
+ join21(targetDir, ".cursor", "hooks.json")
3804
4079
  );
3805
4080
  printStatus(".cursor/hooks.json");
3806
4081
  await copyFile(
3807
- join20(templateDir, "AGENTS.md"),
3808
- join20(targetDir, "AGENTS.md")
4082
+ join21(templateDir, "AGENTS.md"),
4083
+ join21(targetDir, "AGENTS.md")
3809
4084
  );
3810
4085
  printStatus("AGENTS.md");
3811
- const envExampleSrc = join20(templateDir, ".env.example");
4086
+ const envExampleSrc = join21(templateDir, ".env.example");
3812
4087
  if (await pathExists(envExampleSrc)) {
3813
- await copyFile(envExampleSrc, join20(targetDir, ".env.example"));
4088
+ await copyFile(envExampleSrc, join21(targetDir, ".env.example"));
3814
4089
  printStatus(".env.example");
3815
4090
  }
3816
- const knowledgeTemplatesDir = join20(
4091
+ const knowledgeTemplatesDir = join21(
3817
4092
  targetDir,
3818
4093
  "flydocs",
3819
4094
  "knowledge",
@@ -3823,8 +4098,8 @@ var init_update = __esm({
3823
4098
  await mkdir10(knowledgeTemplatesDir, { recursive: true });
3824
4099
  }
3825
4100
  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);
4101
+ const src = join21(templateDir, "flydocs", "knowledge", "templates", tmpl);
4102
+ const dest = join21(knowledgeTemplatesDir, tmpl);
3828
4103
  if (await pathExists(src) && !await pathExists(dest)) {
3829
4104
  await copyFile(src, dest);
3830
4105
  }
@@ -3851,18 +4126,18 @@ var init_update = __esm({
3851
4126
  printWarning("Config merge failed \u2014 config.json preserved as-is");
3852
4127
  }
3853
4128
  await copyFile(
3854
- join20(templateDir, ".flydocs", "version"),
3855
- join20(targetDir, ".flydocs", "version")
4129
+ join21(templateDir, ".flydocs", "version"),
4130
+ join21(targetDir, ".flydocs", "version")
3856
4131
  );
3857
4132
  printStatus(`.flydocs/version \u2192 ${version}`);
3858
- const clSrc = join20(templateDir, "CHANGELOG.md");
4133
+ const clSrc = join21(templateDir, "CHANGELOG.md");
3859
4134
  if (await pathExists(clSrc)) {
3860
- await copyFile(clSrc, join20(targetDir, ".flydocs", "CHANGELOG.md"));
4135
+ await copyFile(clSrc, join21(targetDir, ".flydocs", "CHANGELOG.md"));
3861
4136
  printStatus(".flydocs/CHANGELOG.md");
3862
4137
  }
3863
- const mfSrc = join20(templateDir, "manifest.json");
4138
+ const mfSrc = join21(templateDir, "manifest.json");
3864
4139
  if (await pathExists(mfSrc)) {
3865
- await copyFile(mfSrc, join20(targetDir, ".flydocs", "manifest.json"));
4140
+ await copyFile(mfSrc, join21(targetDir, ".flydocs", "manifest.json"));
3866
4141
  printStatus(".flydocs/manifest.json");
3867
4142
  }
3868
4143
  await generateIntegrity(targetDir, version);
@@ -3924,13 +4199,13 @@ var uninstall_exports = {};
3924
4199
  __export(uninstall_exports, {
3925
4200
  default: () => uninstall_default
3926
4201
  });
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";
4202
+ import { defineCommand as defineCommand5 } from "citty";
4203
+ import { resolve as resolve5, join as join22 } from "path";
4204
+ import { readdir as readdir6, rm as rm6, rename as rename2 } from "fs/promises";
3930
4205
  import { confirm as confirm5, select as select4, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
3931
- import pc9 from "picocolors";
4206
+ import pc10 from "picocolors";
3932
4207
  async function removeOwnedSkills(targetDir) {
3933
- const skillsDir = join21(targetDir, ".claude", "skills");
4208
+ const skillsDir = join22(targetDir, ".claude", "skills");
3934
4209
  const removed = [];
3935
4210
  if (!await pathExists(skillsDir)) {
3936
4211
  return removed;
@@ -3939,7 +4214,7 @@ async function removeOwnedSkills(targetDir) {
3939
4214
  const entries = await readdir6(skillsDir);
3940
4215
  for (const entry of entries) {
3941
4216
  if (entry.startsWith(OWNED_SKILL_PREFIX)) {
3942
- await rm5(join21(skillsDir, entry), { recursive: true, force: true });
4217
+ await rm6(join22(skillsDir, entry), { recursive: true, force: true });
3943
4218
  removed.push(`.claude/skills/${entry}`);
3944
4219
  }
3945
4220
  }
@@ -3948,7 +4223,7 @@ async function removeOwnedSkills(targetDir) {
3948
4223
  return removed;
3949
4224
  }
3950
4225
  async function removeOwnedCursorRules(targetDir) {
3951
- const rulesDir = join21(targetDir, ".cursor", "rules");
4226
+ const rulesDir = join22(targetDir, ".cursor", "rules");
3952
4227
  const removed = [];
3953
4228
  if (!await pathExists(rulesDir)) {
3954
4229
  return removed;
@@ -3957,7 +4232,7 @@ async function removeOwnedCursorRules(targetDir) {
3957
4232
  const entries = await readdir6(rulesDir);
3958
4233
  for (const entry of entries) {
3959
4234
  if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
3960
- await rm5(join21(rulesDir, entry), { force: true });
4235
+ await rm6(join22(rulesDir, entry), { force: true });
3961
4236
  removed.push(`.cursor/rules/${entry}`);
3962
4237
  }
3963
4238
  }
@@ -3976,9 +4251,9 @@ async function isEmptyDir(dirPath) {
3976
4251
  async function cleanupEmptyParents(targetDir, dirs) {
3977
4252
  const cleaned = [];
3978
4253
  for (const dir of dirs) {
3979
- const fullPath = join21(targetDir, dir);
4254
+ const fullPath = join22(targetDir, dir);
3980
4255
  if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
3981
- await rm5(fullPath, { recursive: true, force: true });
4256
+ await rm6(fullPath, { recursive: true, force: true });
3982
4257
  cleaned.push(dir);
3983
4258
  }
3984
4259
  }
@@ -4008,7 +4283,7 @@ var init_uninstall = __esm({
4008
4283
  ];
4009
4284
  OWNED_SKILL_PREFIX = "flydocs-";
4010
4285
  OWNED_RULE_PREFIX = "flydocs-";
4011
- uninstall_default = defineCommand4({
4286
+ uninstall_default = defineCommand5({
4012
4287
  meta: {
4013
4288
  name: "uninstall",
4014
4289
  description: "Remove FlyDocs from a project directory"
@@ -4055,8 +4330,8 @@ var init_uninstall = __esm({
4055
4330
  process.exit(1);
4056
4331
  }
4057
4332
  targetDir = resolve5(targetDir);
4058
- const hasFlydocs = await pathExists(join21(targetDir, ".flydocs"));
4059
- const hasAgentsMd = await pathExists(join21(targetDir, "AGENTS.md"));
4333
+ const hasFlydocs = await pathExists(join22(targetDir, ".flydocs"));
4334
+ const hasAgentsMd = await pathExists(join22(targetDir, "AGENTS.md"));
4060
4335
  if (!hasFlydocs && !hasAgentsMd) {
4061
4336
  printError(`Not a FlyDocs project: ${targetDir}`);
4062
4337
  printInfo("No .flydocs/ directory or AGENTS.md found.");
@@ -4068,7 +4343,7 @@ var init_uninstall = __esm({
4068
4343
  const removeAll = forceAll || args.all;
4069
4344
  const skipPrompts = forceAll || args.yes;
4070
4345
  let contentAction = "preserve";
4071
- const hasUserContent = await pathExists(join21(targetDir, "flydocs"));
4346
+ const hasUserContent = await pathExists(join22(targetDir, "flydocs"));
4072
4347
  if (hasUserContent) {
4073
4348
  if (removeAll) {
4074
4349
  contentAction = "remove";
@@ -4102,34 +4377,34 @@ var init_uninstall = __esm({
4102
4377
  }
4103
4378
  if (!skipPrompts) {
4104
4379
  console.log();
4105
- console.log(pc9.bold("The following will be removed:"));
4380
+ console.log(pc10.bold("The following will be removed:"));
4106
4381
  console.log();
4107
4382
  console.log(" Framework files:");
4108
4383
  for (const [path] of ALWAYS_REMOVED) {
4109
- console.log(` ${pc9.dim(path)}`);
4384
+ console.log(` ${pc10.dim(path)}`);
4110
4385
  }
4111
- console.log(` ${pc9.dim(".claude/skills/flydocs-*")}`);
4112
- console.log(` ${pc9.dim(".cursor/rules/flydocs-*.mdc")}`);
4386
+ console.log(` ${pc10.dim(".claude/skills/flydocs-*")}`);
4387
+ console.log(` ${pc10.dim(".cursor/rules/flydocs-*.mdc")}`);
4113
4388
  if (hasUserContent) {
4114
4389
  if (contentAction === "archive") {
4115
4390
  console.log();
4116
4391
  console.log(
4117
- ` User content: ${pc9.yellow("flydocs/ -> flydocs-archive/")}`
4392
+ ` User content: ${pc10.yellow("flydocs/ -> flydocs-archive/")}`
4118
4393
  );
4119
4394
  } else if (contentAction === "remove") {
4120
4395
  console.log();
4121
- console.log(` User content: ${pc9.red("flydocs/ (deleted)")}`);
4396
+ console.log(` User content: ${pc10.red("flydocs/ (deleted)")}`);
4122
4397
  } else {
4123
4398
  console.log();
4124
- console.log(` User content: ${pc9.green("flydocs/ (preserved)")}`);
4399
+ console.log(` User content: ${pc10.green("flydocs/ (preserved)")}`);
4125
4400
  }
4126
4401
  }
4127
4402
  console.log();
4128
- console.log(pc9.bold("Preserved:"));
4403
+ console.log(pc10.bold("Preserved:"));
4129
4404
  console.log(
4130
- ` ${pc9.dim(".claude/skills/ (non-flydocs community skills)")}`
4405
+ ` ${pc10.dim(".claude/skills/ (non-flydocs community skills)")}`
4131
4406
  );
4132
- console.log(` ${pc9.dim(".env, .env.local")}`);
4407
+ console.log(` ${pc10.dim(".env, .env.local")}`);
4133
4408
  console.log();
4134
4409
  const shouldContinue = await confirm5({
4135
4410
  message: "Proceed with uninstall?"
@@ -4151,16 +4426,16 @@ var init_uninstall = __esm({
4151
4426
  const removedRules = await removeOwnedCursorRules(targetDir);
4152
4427
  result.removed.push(...removedRules);
4153
4428
  for (const [relativePath, type] of ALWAYS_REMOVED) {
4154
- const fullPath = join21(targetDir, relativePath);
4429
+ const fullPath = join22(targetDir, relativePath);
4155
4430
  if (!await pathExists(fullPath)) {
4156
4431
  result.skipped.push(relativePath);
4157
4432
  continue;
4158
4433
  }
4159
4434
  try {
4160
4435
  if (type === "dir") {
4161
- await rm5(fullPath, { recursive: true, force: true });
4436
+ await rm6(fullPath, { recursive: true, force: true });
4162
4437
  } else {
4163
- await rm5(fullPath, { force: true });
4438
+ await rm6(fullPath, { force: true });
4164
4439
  }
4165
4440
  result.removed.push(relativePath);
4166
4441
  } catch {
@@ -4169,16 +4444,16 @@ var init_uninstall = __esm({
4169
4444
  }
4170
4445
  }
4171
4446
  if (hasUserContent) {
4172
- const flydocsPath = join21(targetDir, "flydocs");
4447
+ const flydocsPath = join22(targetDir, "flydocs");
4173
4448
  if (contentAction === "archive") {
4174
- const archivePath = join21(targetDir, "flydocs-archive");
4449
+ const archivePath = join22(targetDir, "flydocs-archive");
4175
4450
  if (await pathExists(archivePath)) {
4176
- await rm5(archivePath, { recursive: true, force: true });
4451
+ await rm6(archivePath, { recursive: true, force: true });
4177
4452
  }
4178
4453
  await rename2(flydocsPath, archivePath);
4179
4454
  result.archived.push("flydocs/ -> flydocs-archive/");
4180
4455
  } else if (contentAction === "remove") {
4181
- await rm5(flydocsPath, { recursive: true, force: true });
4456
+ await rm6(flydocsPath, { recursive: true, force: true });
4182
4457
  result.removed.push("flydocs/");
4183
4458
  }
4184
4459
  }
@@ -4197,17 +4472,17 @@ var init_uninstall = __esm({
4197
4472
  result.restored = originals.map((f) => f.relativePath);
4198
4473
  }
4199
4474
  console.log();
4200
- console.log(pc9.bold("Uninstall Summary"));
4475
+ console.log(pc10.bold("Uninstall Summary"));
4201
4476
  console.log();
4202
4477
  if (result.removed.length > 0) {
4203
- console.log(` ${pc9.green("Removed")} (${result.removed.length}):`);
4478
+ console.log(` ${pc10.green("Removed")} (${result.removed.length}):`);
4204
4479
  for (const item of result.removed) {
4205
4480
  printStatus(item);
4206
4481
  }
4207
4482
  }
4208
4483
  if (result.archived.length > 0) {
4209
4484
  console.log();
4210
- console.log(` ${pc9.yellow("Archived")} (${result.archived.length}):`);
4485
+ console.log(` ${pc10.yellow("Archived")} (${result.archived.length}):`);
4211
4486
  for (const item of result.archived) {
4212
4487
  printInfo(item);
4213
4488
  }
@@ -4215,7 +4490,7 @@ var init_uninstall = __esm({
4215
4490
  if (result.restored.length > 0) {
4216
4491
  console.log();
4217
4492
  console.log(
4218
- ` ${pc9.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
4493
+ ` ${pc10.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
4219
4494
  );
4220
4495
  for (const item of result.restored) {
4221
4496
  printInfo(item);
@@ -4224,16 +4499,16 @@ var init_uninstall = __esm({
4224
4499
  if (result.skipped.length > 0) {
4225
4500
  console.log();
4226
4501
  console.log(
4227
- ` ${pc9.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
4502
+ ` ${pc10.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
4228
4503
  );
4229
4504
  for (const item of result.skipped) {
4230
- console.log(` ${pc9.dim(item)}`);
4505
+ console.log(` ${pc10.dim(item)}`);
4231
4506
  }
4232
4507
  }
4233
4508
  console.log();
4234
4509
  printStatus("FlyDocs has been removed from this project.");
4235
4510
  console.log();
4236
- printInfo(`To reinstall: ${pc9.cyan("npx @flydocs/cli install --here")}`);
4511
+ printInfo(`To reinstall: ${pc10.cyan("npx @flydocs/cli install --here")}`);
4237
4512
  console.log();
4238
4513
  }
4239
4514
  });
@@ -4245,28 +4520,28 @@ var setup_exports = {};
4245
4520
  __export(setup_exports, {
4246
4521
  default: () => setup_default
4247
4522
  });
4248
- import { defineCommand as defineCommand5 } from "citty";
4249
- import pc10 from "picocolors";
4523
+ import { defineCommand as defineCommand6 } from "citty";
4524
+ import pc11 from "picocolors";
4250
4525
  var setup_default;
4251
4526
  var init_setup = __esm({
4252
4527
  "src/commands/setup.ts"() {
4253
4528
  "use strict";
4254
- setup_default = defineCommand5({
4529
+ setup_default = defineCommand6({
4255
4530
  meta: {
4256
4531
  name: "setup",
4257
4532
  description: "Configure FlyDocs settings for this project"
4258
4533
  },
4259
4534
  run() {
4260
4535
  console.log();
4261
- console.log(` ${pc10.bold("FlyDocs Setup")}`);
4536
+ console.log(` ${pc11.bold("FlyDocs Setup")}`);
4262
4537
  console.log();
4263
4538
  console.log(` Setup runs inside your IDE as an interactive AI command.`);
4264
4539
  console.log();
4265
4540
  console.log(
4266
- ` ${pc10.cyan("Claude Code:")} Type ${pc10.bold("/flydocs-setup")} in chat`
4541
+ ` ${pc11.cyan("Claude Code:")} Type ${pc11.bold("/flydocs-setup")} in chat`
4267
4542
  );
4268
4543
  console.log(
4269
- ` ${pc10.cyan("Cursor:")} Type ${pc10.bold("/flydocs-setup")} in chat`
4544
+ ` ${pc11.cyan("Cursor:")} Type ${pc11.bold("/flydocs-setup")} in chat`
4270
4545
  );
4271
4546
  console.log();
4272
4547
  console.log(` This configures your project context, detects your stack,`);
@@ -4282,14 +4557,14 @@ var skills_exports = {};
4282
4557
  __export(skills_exports, {
4283
4558
  default: () => skills_default
4284
4559
  });
4285
- import { defineCommand as defineCommand6 } from "citty";
4286
- import pc11 from "picocolors";
4560
+ import { defineCommand as defineCommand7 } from "citty";
4561
+ import pc12 from "picocolors";
4287
4562
  var list, search, add, remove, skills_default;
4288
4563
  var init_skills2 = __esm({
4289
4564
  "src/commands/skills.ts"() {
4290
4565
  "use strict";
4291
4566
  init_skill_manager();
4292
- list = defineCommand6({
4567
+ list = defineCommand7({
4293
4568
  meta: {
4294
4569
  name: "list",
4295
4570
  description: "List installed skills"
@@ -4305,26 +4580,26 @@ var init_skills2 = __esm({
4305
4580
  console.log(`${total} skill(s) installed:`);
4306
4581
  if (result.platform.length > 0) {
4307
4582
  console.log();
4308
- console.log(pc11.bold("Platform"));
4583
+ console.log(pc12.bold("Platform"));
4309
4584
  for (const skill of result.platform) {
4310
4585
  console.log(
4311
- ` ${skill.name} ${pc11.dim(`(${skill.triggers} triggers)`)}`
4586
+ ` ${skill.name} ${pc12.dim(`(${skill.triggers} triggers)`)}`
4312
4587
  );
4313
4588
  }
4314
4589
  }
4315
4590
  if (result.community.length > 0) {
4316
4591
  console.log();
4317
- console.log(pc11.bold("Community"));
4592
+ console.log(pc12.bold("Community"));
4318
4593
  for (const skill of result.community) {
4319
4594
  console.log(
4320
- ` ${skill.name} ${pc11.dim(`(${skill.triggers} triggers)`)}`
4595
+ ` ${skill.name} ${pc12.dim(`(${skill.triggers} triggers)`)}`
4321
4596
  );
4322
4597
  }
4323
4598
  }
4324
4599
  console.log();
4325
4600
  }
4326
4601
  });
4327
- search = defineCommand6({
4602
+ search = defineCommand7({
4328
4603
  meta: {
4329
4604
  name: "search",
4330
4605
  description: "Search community skills"
@@ -4340,24 +4615,24 @@ var init_skills2 = __esm({
4340
4615
  const results = await searchCatalog(args.keyword);
4341
4616
  if (results.length === 0) {
4342
4617
  console.log(`No skills found for "${args.keyword}".`);
4343
- console.log(` Browse the catalog at: ${pc11.cyan("https://skills.sh/")}`);
4618
+ console.log(` Browse the catalog at: ${pc12.cyan("https://skills.sh/")}`);
4344
4619
  return;
4345
4620
  }
4346
4621
  console.log();
4347
4622
  console.log(`${results.length} skill(s) matching "${args.keyword}":`);
4348
4623
  console.log();
4349
4624
  for (const skill of results) {
4350
- console.log(` ${pc11.bold(skill.name)}`);
4625
+ console.log(` ${pc12.bold(skill.name)}`);
4351
4626
  console.log(` ${skill.description}`);
4352
- console.log(` ${pc11.dim(skill.repo)}`);
4627
+ console.log(` ${pc12.dim(skill.repo)}`);
4353
4628
  if (skill.tags.length > 0) {
4354
- console.log(` ${pc11.dim(skill.tags.join(", "))}`);
4629
+ console.log(` ${pc12.dim(skill.tags.join(", "))}`);
4355
4630
  }
4356
4631
  console.log();
4357
4632
  }
4358
4633
  }
4359
4634
  });
4360
- add = defineCommand6({
4635
+ add = defineCommand7({
4361
4636
  meta: {
4362
4637
  name: "add",
4363
4638
  description: "Install a community skill"
@@ -4373,7 +4648,7 @@ var init_skills2 = __esm({
4373
4648
  await addSkill(process.cwd(), args.source);
4374
4649
  }
4375
4650
  });
4376
- remove = defineCommand6({
4651
+ remove = defineCommand7({
4377
4652
  meta: {
4378
4653
  name: "remove",
4379
4654
  description: "Remove an installed community skill"
@@ -4389,7 +4664,7 @@ var init_skills2 = __esm({
4389
4664
  await removeSkill(process.cwd(), args.name);
4390
4665
  }
4391
4666
  });
4392
- skills_default = defineCommand6({
4667
+ skills_default = defineCommand7({
4393
4668
  meta: {
4394
4669
  name: "skills",
4395
4670
  description: "Manage FlyDocs skills (list, search, add, remove)"
@@ -4409,10 +4684,10 @@ var connect_exports = {};
4409
4684
  __export(connect_exports, {
4410
4685
  default: () => connect_default
4411
4686
  });
4412
- import { defineCommand as defineCommand7 } from "citty";
4687
+ import { defineCommand as defineCommand8 } from "citty";
4413
4688
  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";
4689
+ import pc13 from "picocolors";
4690
+ import { join as join23 } from "path";
4416
4691
  var connect_default;
4417
4692
  var init_connect = __esm({
4418
4693
  "src/commands/connect.ts"() {
@@ -4422,7 +4697,7 @@ var init_connect = __esm({
4422
4697
  init_template();
4423
4698
  init_ui();
4424
4699
  init_api_key();
4425
- connect_default = defineCommand7({
4700
+ connect_default = defineCommand8({
4426
4701
  meta: {
4427
4702
  name: "connect",
4428
4703
  description: "Connect FlyDocs to a cloud provider"
@@ -4447,11 +4722,11 @@ var init_connect = __esm({
4447
4722
  },
4448
4723
  async run({ args }) {
4449
4724
  const targetDir = args.path ?? process.cwd();
4450
- const configPath = join22(targetDir, ".flydocs", "config.json");
4725
+ const configPath = join23(targetDir, ".flydocs", "config.json");
4451
4726
  if (!await pathExists(configPath)) {
4452
4727
  printError("Not a FlyDocs project (.flydocs/config.json not found).");
4453
4728
  console.log(
4454
- ` Run ${pc12.cyan("flydocs")} first to install FlyDocs in this project.`
4729
+ ` Run ${pc13.cyan("flydocs")} first to install FlyDocs in this project.`
4455
4730
  );
4456
4731
  process.exit(1);
4457
4732
  }
@@ -4468,10 +4743,10 @@ var init_connect = __esm({
4468
4743
  }
4469
4744
  }
4470
4745
  console.log();
4471
- console.log(` ${pc12.bold("Connect to FlyDocs Cloud")}`);
4746
+ console.log(` ${pc13.bold("Connect to FlyDocs Cloud")}`);
4472
4747
  console.log();
4473
4748
  console.log(
4474
- ` ${pc12.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
4749
+ ` ${pc13.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
4475
4750
  );
4476
4751
  console.log();
4477
4752
  let apiKey = args.key ?? "";
@@ -4509,7 +4784,7 @@ var init_connect = __esm({
4509
4784
  console.log(` Check your key and try again.`);
4510
4785
  process.exit(1);
4511
4786
  }
4512
- printStatus(`Connected to ${pc12.bold(result.org)}`);
4787
+ printStatus(`Connected to ${pc13.bold(result.org)}`);
4513
4788
  } catch {
4514
4789
  printError(
4515
4790
  "Could not reach relay API. Check your network and try again."
@@ -4517,7 +4792,7 @@ var init_connect = __esm({
4517
4792
  process.exit(1);
4518
4793
  }
4519
4794
  const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
4520
- printStatus(`API key stored in ${pc12.dim(envFile)}`);
4795
+ printStatus(`API key stored in ${pc13.dim(envFile)}`);
4521
4796
  } else {
4522
4797
  try {
4523
4798
  const result = await validateLinearKey(apiKey);
@@ -4527,7 +4802,7 @@ var init_connect = __esm({
4527
4802
  process.exit(1);
4528
4803
  }
4529
4804
  printStatus(
4530
- `Authenticated as ${pc12.bold(result.name)} (${result.email})`
4805
+ `Authenticated as ${pc13.bold(result.name)} (${result.email})`
4531
4806
  );
4532
4807
  } catch {
4533
4808
  printError("Invalid API key or network error.");
@@ -4535,7 +4810,7 @@ var init_connect = __esm({
4535
4810
  process.exit(1);
4536
4811
  }
4537
4812
  const envFile = await storeEnvKey(targetDir, "LINEAR_API_KEY", apiKey);
4538
- printStatus(`API key stored in ${pc12.dim(envFile)}`);
4813
+ printStatus(`API key stored in ${pc13.dim(envFile)}`);
4539
4814
  }
4540
4815
  const wasLocal = config.tier === "local";
4541
4816
  config.tier = "cloud";
@@ -4551,14 +4826,14 @@ var init_connect = __esm({
4551
4826
  }
4552
4827
  console.log();
4553
4828
  console.log(
4554
- ` ${pc12.bold("Connected!")} Your project is now on the cloud tier.`
4829
+ ` ${pc13.bold("Connected!")} Your project is now on the cloud tier.`
4555
4830
  );
4556
4831
  console.log();
4557
4832
  console.log(` Next steps:`);
4558
4833
  console.log(
4559
- ` 1. Run ${pc12.cyan("/flydocs-setup")} in your IDE to configure your project`
4834
+ ` 1. Run ${pc13.cyan("/flydocs-setup")} in your IDE to configure your project`
4560
4835
  );
4561
- console.log(` 2. Run ${pc12.cyan("/start-session")} to begin working`);
4836
+ console.log(` 2. Run ${pc13.cyan("/start-session")} to begin working`);
4562
4837
  console.log();
4563
4838
  }
4564
4839
  });
@@ -4570,9 +4845,9 @@ var auth_exports = {};
4570
4845
  __export(auth_exports, {
4571
4846
  default: () => auth_default
4572
4847
  });
4573
- import { defineCommand as defineCommand8 } from "citty";
4848
+ import { defineCommand as defineCommand9 } from "citty";
4574
4849
  import { text as text5, confirm as confirm7, isCancel as isCancel8, cancel as cancel7 } from "@clack/prompts";
4575
- import pc13 from "picocolors";
4850
+ import pc14 from "picocolors";
4576
4851
  var auth_default;
4577
4852
  var init_auth = __esm({
4578
4853
  "src/commands/auth.ts"() {
@@ -4580,7 +4855,7 @@ var init_auth = __esm({
4580
4855
  init_ui();
4581
4856
  init_api_key();
4582
4857
  init_global_config();
4583
- auth_default = defineCommand8({
4858
+ auth_default = defineCommand9({
4584
4859
  meta: {
4585
4860
  name: "auth",
4586
4861
  description: "Store API key globally (~/.flydocs/credentials)"
@@ -4594,13 +4869,13 @@ var init_auth = __esm({
4594
4869
  },
4595
4870
  async run({ args }) {
4596
4871
  console.log();
4597
- console.log(` ${pc13.bold("FlyDocs Authentication")}`);
4872
+ console.log(` ${pc14.bold("FlyDocs Authentication")}`);
4598
4873
  console.log();
4599
4874
  let apiKey = args.key ?? "";
4600
4875
  const existing = await readGlobalCredential();
4601
4876
  if (existing?.apiKey && !apiKey) {
4602
4877
  printInfo(
4603
- `Existing key found: ${pc13.dim(existing.apiKey.slice(0, 8) + "...")}`
4878
+ `Existing key found: ${pc14.dim(existing.apiKey.slice(0, 8) + "...")}`
4604
4879
  );
4605
4880
  const replace = await confirm7({
4606
4881
  message: "Replace with a new key?"
@@ -4612,7 +4887,7 @@ var init_auth = __esm({
4612
4887
  }
4613
4888
  if (!apiKey) {
4614
4889
  console.log(
4615
- ` ${pc13.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
4890
+ ` ${pc14.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
4616
4891
  );
4617
4892
  console.log();
4618
4893
  const keyInput = await text5({
@@ -4648,7 +4923,7 @@ var init_auth = __esm({
4648
4923
  printError("Invalid API key. Check your key and try again.");
4649
4924
  process.exit(1);
4650
4925
  }
4651
- printStatus(`Authenticated with ${pc13.bold(result.org)}`);
4926
+ printStatus(`Authenticated with ${pc14.bold(result.org)}`);
4652
4927
  } catch {
4653
4928
  printError(
4654
4929
  "Could not reach FlyDocs API. Check your network and try again."
@@ -4663,10 +4938,10 @@ var init_auth = __esm({
4663
4938
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4664
4939
  lastValidated: (/* @__PURE__ */ new Date()).toISOString()
4665
4940
  });
4666
- printStatus(`Key stored at ${pc13.dim(credentialsPath())}`);
4941
+ printStatus(`Key stored at ${pc14.dim(credentialsPath())}`);
4667
4942
  await checkCredentialPermissions();
4668
4943
  console.log();
4669
- console.log(` ${pc13.bold("Authenticated!")} Key stored globally.`);
4944
+ console.log(` ${pc14.bold("Authenticated!")} Key stored globally.`);
4670
4945
  console.log(` All FlyDocs projects on this machine will use this key.`);
4671
4946
  console.log();
4672
4947
  }
@@ -4679,10 +4954,10 @@ var sync_exports = {};
4679
4954
  __export(sync_exports, {
4680
4955
  default: () => sync_default
4681
4956
  });
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";
4957
+ import { defineCommand as defineCommand10 } from "citty";
4958
+ import pc15 from "picocolors";
4959
+ import { join as join24 } from "path";
4960
+ import { mkdir as mkdir11, writeFile as writeFile14 } from "fs/promises";
4686
4961
  var sync_default;
4687
4962
  var init_sync = __esm({
4688
4963
  "src/commands/sync.ts"() {
@@ -4694,7 +4969,7 @@ var init_sync = __esm({
4694
4969
  init_fs_ops();
4695
4970
  init_types();
4696
4971
  init_relay_client();
4697
- sync_default = defineCommand9({
4972
+ sync_default = defineCommand10({
4698
4973
  meta: {
4699
4974
  name: "sync",
4700
4975
  description: "Pull latest config and templates from server"
@@ -4745,14 +5020,14 @@ var init_sync = __esm({
4745
5020
  } else {
4746
5021
  printWarning("Server unreachable, using cached config.");
4747
5022
  }
4748
- console.log(` ${pc14.dim("Config may be stale. Retry when connected.")}`);
5023
+ console.log(` ${pc15.dim("Config may be stale. Retry when connected.")}`);
4749
5024
  return;
4750
5025
  }
4751
5026
  if (!serverResponse.valid) {
4752
5027
  printWarning("Server returned invalid config. Keeping current config.");
4753
5028
  return;
4754
5029
  }
4755
- await mkdir11(join23(targetDir, ".flydocs"), { recursive: true });
5030
+ await mkdir11(join24(targetDir, ".flydocs"), { recursive: true });
4756
5031
  const configWithHash = applyConfigHash(serverResponse.config);
4757
5032
  await writeConfig(targetDir, configWithHash);
4758
5033
  changes.push("Updated .flydocs/config.json");
@@ -4765,7 +5040,7 @@ var init_sync = __esm({
4765
5040
  workspaceId
4766
5041
  );
4767
5042
  if (templatesResponse.templates.length > 0) {
4768
- const templatesDir = join23(
5043
+ const templatesDir = join24(
4769
5044
  targetDir,
4770
5045
  ".claude",
4771
5046
  "skills",
@@ -4776,10 +5051,10 @@ var init_sync = __esm({
4776
5051
  for (const template of templatesResponse.templates) {
4777
5052
  const filename = `${template.type}.md`;
4778
5053
  const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
4779
- const templateDir = join23(templatesDir, subdir);
5054
+ const templateDir = join24(templatesDir, subdir);
4780
5055
  await mkdir11(templateDir, { recursive: true });
4781
- await writeFile13(
4782
- join23(templateDir, filename),
5056
+ await writeFile14(
5057
+ join24(templateDir, filename),
4783
5058
  template.content,
4784
5059
  "utf-8"
4785
5060
  );
@@ -4809,221 +5084,6 @@ var init_sync = __esm({
4809
5084
  }
4810
5085
  });
4811
5086
 
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
5087
  // src/commands/upgrade.ts
5028
5088
  var upgrade_exports = {};
5029
5089
  __export(upgrade_exports, {