@flydocs/cli 0.6.0-alpha.32 → 0.6.0-alpha.34

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.32";
18
+ CLI_VERSION = "0.6.0-alpha.34";
19
19
  CLI_NAME = "flydocs";
20
20
  PACKAGE_NAME = "@flydocs/cli";
21
21
  POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
@@ -281,7 +281,7 @@ var init_types = __esm({
281
281
  // src/lib/config-integrity.ts
282
282
  import { createHash } from "crypto";
283
283
  function computeConfigHash(config) {
284
- const { configHash: _, ...rest } = config;
284
+ const { configHash: _, artifactOverrides: _overrides, ...rest } = config;
285
285
  const serialized = JSON.stringify(rest, Object.keys(rest).sort());
286
286
  return `sha256:${createHash("sha256").update(serialized, "utf-8").digest("hex")}`;
287
287
  }
@@ -423,7 +423,7 @@ async function installOwnedSkills(templateDir, targetDir, _tier) {
423
423
  async function replaceOwnedSkills(templateDir, targetDir, _tier) {
424
424
  const skillsDir = join4(targetDir, ".claude", "skills");
425
425
  const templateSkillsDir = join4(templateDir, ".claude", "skills");
426
- const { rm: rm7 } = await import("fs/promises");
426
+ const { rm: rm9 } = await import("fs/promises");
427
427
  await replaceDirectory(
428
428
  join4(templateSkillsDir, OWNED_SKILL),
429
429
  join4(skillsDir, OWNED_SKILL)
@@ -431,7 +431,7 @@ async function replaceOwnedSkills(templateDir, targetDir, _tier) {
431
431
  for (const legacy of LEGACY_SKILLS) {
432
432
  const legacyPath = join4(skillsDir, legacy);
433
433
  if (await pathExists(legacyPath)) {
434
- await rm7(legacyPath, { recursive: true, force: true });
434
+ await rm9(legacyPath, { recursive: true, force: true });
435
435
  }
436
436
  }
437
437
  const readmeSrc = join4(templateSkillsDir, "README.md");
@@ -440,9 +440,9 @@ async function replaceOwnedSkills(templateDir, targetDir, _tier) {
440
440
  }
441
441
  }
442
442
  async function copyCursorRules(targetDir) {
443
- const { mkdir: mkdir15 } = await import("fs/promises");
443
+ const { mkdir: mkdir16 } = await import("fs/promises");
444
444
  const rulesDir = join4(targetDir, ".cursor", "rules");
445
- await mkdir15(rulesDir, { recursive: true });
445
+ await mkdir16(rulesDir, { recursive: true });
446
446
  const workflowRule = join4(
447
447
  targetDir,
448
448
  ".claude",
@@ -453,11 +453,11 @@ async function copyCursorRules(targetDir) {
453
453
  if (await pathExists(workflowRule)) {
454
454
  await copyFile(workflowRule, join4(rulesDir, "flydocs-workflow.mdc"));
455
455
  }
456
- const { rm: rm7 } = await import("fs/promises");
456
+ const { rm: rm9 } = await import("fs/promises");
457
457
  for (const legacy of ["flydocs-mechanism.mdc", "flydocs-context7.mdc"]) {
458
458
  const legacyRule = join4(rulesDir, legacy);
459
459
  if (await pathExists(legacyRule)) {
460
- await rm7(legacyRule, { force: true });
460
+ await rm9(legacyRule, { force: true });
461
461
  }
462
462
  }
463
463
  }
@@ -1328,6 +1328,11 @@ async function scanDeprecated(targetDir) {
1328
1328
  found.push(item);
1329
1329
  }
1330
1330
  }
1331
+ for (const file of DEPRECATED_CONTEXT_FILES) {
1332
+ if (await pathExists(join10(targetDir, file))) {
1333
+ found.push(file);
1334
+ }
1335
+ }
1331
1336
  for (const skill of DEPRECATED_SKILLS) {
1332
1337
  const p = join10(targetDir, ".claude", "skills", skill);
1333
1338
  if (await pathExists(p)) {
@@ -1436,7 +1441,7 @@ async function promptCleanup(targetDir, paths) {
1436
1441
  printStatus(`Deleted: ${p}`);
1437
1442
  }
1438
1443
  }
1439
- var DEPRECATED_DIRS, DEPRECATED_FILES, DEPRECATED_SKILLS, DEPRECATED_RULES_DIR, DEPRECATED_FLYDOCS_DIRS, DEPRECATED_HOOKS, DEPRECATED_CURSOR_RULES, DEPRECATED_CURSOR_RULE_DIRS, DEPRECATED_COMMANDS;
1444
+ var DEPRECATED_DIRS, DEPRECATED_FILES, DEPRECATED_CONTEXT_FILES, DEPRECATED_SKILLS, DEPRECATED_RULES_DIR, DEPRECATED_FLYDOCS_DIRS, DEPRECATED_HOOKS, DEPRECATED_CURSOR_RULES, DEPRECATED_CURSOR_RULE_DIRS, DEPRECATED_COMMANDS;
1440
1445
  var init_deprecated = __esm({
1441
1446
  "src/lib/deprecated.ts"() {
1442
1447
  "use strict";
@@ -1444,6 +1449,11 @@ var init_deprecated = __esm({
1444
1449
  init_ui();
1445
1450
  DEPRECATED_DIRS = [".docflow", "docflow"];
1446
1451
  DEPRECATED_FILES = ["AGENTS.md.bak", ".cursor/mcp.json"];
1452
+ DEPRECATED_CONTEXT_FILES = [
1453
+ "flydocs/context/overview.md",
1454
+ "flydocs/context/stack.md",
1455
+ "flydocs/context/standards.md"
1456
+ ];
1447
1457
  DEPRECATED_SKILLS = [
1448
1458
  "linear-workflow",
1449
1459
  "session-workflow",
@@ -3027,11 +3037,11 @@ async function fetchTemplates(apiKey, sinceVersion, workspaceId) {
3027
3037
  }
3028
3038
  return await response.json();
3029
3039
  }
3030
- async function fetchArtifacts(apiKey, sinceVersion) {
3040
+ async function fetchArtifacts(apiKey, sinceVersion, workspaceId) {
3031
3041
  const baseUrl = resolveRelayUrl();
3032
3042
  const response = await fetch(`${baseUrl}/artifacts?since=${sinceVersion}`, {
3033
3043
  method: "GET",
3034
- headers: headers(apiKey),
3044
+ headers: headers(apiKey, workspaceId),
3035
3045
  signal: AbortSignal.timeout(3e4)
3036
3046
  });
3037
3047
  if (!response.ok) {
@@ -3044,23 +3054,6 @@ async function fetchArtifacts(apiKey, sinceVersion) {
3044
3054
  }
3045
3055
  return await response.json();
3046
3056
  }
3047
- async function fetchAgentConfigs(apiKey, workspaceId) {
3048
- const baseUrl = resolveRelayUrl();
3049
- const response = await fetch(`${baseUrl}/artifacts/agent-configs`, {
3050
- method: "GET",
3051
- headers: headers(apiKey, workspaceId),
3052
- signal: AbortSignal.timeout(15e3)
3053
- });
3054
- if (!response.ok) {
3055
- const body = await response.text().catch(() => "");
3056
- throw new RelayError(
3057
- `agent-configs fetch failed (${response.status})`,
3058
- response.status,
3059
- body
3060
- );
3061
- }
3062
- return await response.json();
3063
- }
3064
3057
  async function triggerScan(apiKey, repoName, options) {
3065
3058
  const baseUrl = resolveRelayUrl();
3066
3059
  const body = {
@@ -3102,78 +3095,156 @@ var init_relay_client = __esm({
3102
3095
  }
3103
3096
  });
3104
3097
 
3098
+ // src/lib/managed-paths.ts
3099
+ import { join as join18 } from "path";
3100
+ import { readdir as readdir4, rm as rm4, unlink } from "fs/promises";
3101
+ function isSkipped(path, skipPaths) {
3102
+ return skipPaths.some((skip) => path === skip || path.startsWith(skip));
3103
+ }
3104
+ async function wipeManagedPaths(targetDir, skipPaths = []) {
3105
+ let deleted = 0;
3106
+ let skipped = 0;
3107
+ for (const dir of MANAGED_DIRECTORIES) {
3108
+ const fullPath = join18(targetDir, dir);
3109
+ if (isSkipped(dir + "/", skipPaths) || isSkipped(dir, skipPaths)) {
3110
+ if (await pathExists(fullPath)) {
3111
+ skipped++;
3112
+ }
3113
+ continue;
3114
+ }
3115
+ if (await pathExists(fullPath)) {
3116
+ await rm4(fullPath, { recursive: true, force: true });
3117
+ deleted++;
3118
+ }
3119
+ }
3120
+ for (const file of MANAGED_FILES) {
3121
+ if (isSkipped(file, skipPaths)) {
3122
+ skipped++;
3123
+ continue;
3124
+ }
3125
+ const fullPath = join18(targetDir, file);
3126
+ if (await pathExists(fullPath)) {
3127
+ await unlink(fullPath);
3128
+ deleted++;
3129
+ }
3130
+ }
3131
+ const skillsDir = join18(targetDir, ".claude", "skills");
3132
+ if (await pathExists(skillsDir)) {
3133
+ const entries = await readdir4(skillsDir, { withFileTypes: true });
3134
+ for (const entry of entries) {
3135
+ if (!entry.isDirectory()) continue;
3136
+ if (!entry.name.startsWith(CORE_SKILL_PREFIX)) continue;
3137
+ const skillPath = `.claude/skills/${entry.name}/`;
3138
+ if (isSkipped(skillPath, skipPaths)) {
3139
+ skipped++;
3140
+ continue;
3141
+ }
3142
+ await rm4(join18(skillsDir, entry.name), { recursive: true, force: true });
3143
+ deleted++;
3144
+ }
3145
+ }
3146
+ if (deleted > 0) {
3147
+ printStatus(`Wiped ${deleted} managed path(s)`);
3148
+ }
3149
+ if (skipped > 0) {
3150
+ printInfo(`Preserved ${skipped} path(s) via skipPaths override`);
3151
+ }
3152
+ return { deleted, skipped };
3153
+ }
3154
+ var MANAGED_DIRECTORIES, MANAGED_FILES, CORE_SKILL_PREFIX;
3155
+ var init_managed_paths = __esm({
3156
+ "src/lib/managed-paths.ts"() {
3157
+ "use strict";
3158
+ init_fs_ops();
3159
+ init_ui();
3160
+ MANAGED_DIRECTORIES = [
3161
+ ".claude/hooks",
3162
+ ".claude/commands",
3163
+ ".claude/agents",
3164
+ ".cursor/rules",
3165
+ ".cursor/commands",
3166
+ ".cursor/agents"
3167
+ ];
3168
+ MANAGED_FILES = [
3169
+ ".claude/settings.json",
3170
+ ".claude/CLAUDE.md",
3171
+ ".cursor/hooks.json",
3172
+ "AGENTS.md"
3173
+ ];
3174
+ CORE_SKILL_PREFIX = "flydocs-";
3175
+ }
3176
+ });
3177
+
3105
3178
  // src/lib/artifacts.ts
3106
3179
  import { mkdir as mkdir9, writeFile as writeFile11 } from "fs/promises";
3107
- import { join as join18, dirname as dirname3 } from "path";
3108
- async function syncArtifacts(targetDir, apiKey, workspaceId, currentVersion) {
3109
- const response = await fetchArtifacts(apiKey, currentVersion);
3180
+ import { join as join19, dirname as dirname3 } from "path";
3181
+ function isPathSkipped(artifactPath, skipPaths) {
3182
+ return skipPaths.some(
3183
+ (skip) => artifactPath === skip || artifactPath.startsWith(skip)
3184
+ );
3185
+ }
3186
+ async function syncArtifacts(targetDir, apiKey, workspaceId, currentVersion, skipPaths = []) {
3187
+ const response = await fetchArtifacts(apiKey, currentVersion, workspaceId);
3110
3188
  if (response.artifacts.length === 0) {
3111
- return { written: 0, version: currentVersion };
3189
+ return { written: 0, skipped: 0, version: currentVersion };
3112
3190
  }
3113
3191
  let written = 0;
3192
+ let skipped = 0;
3114
3193
  for (const artifact of response.artifacts) {
3194
+ if (skipPaths.length > 0 && isPathSkipped(artifact.path, skipPaths)) {
3195
+ skipped++;
3196
+ continue;
3197
+ }
3115
3198
  await writeArtifact(targetDir, artifact);
3116
3199
  written++;
3117
3200
  }
3118
3201
  printStatus(
3119
3202
  `Synced ${written} artifact(s) (v${currentVersion} \u2192 v${response.version})`
3120
3203
  );
3121
- return { written, version: response.version };
3122
- }
3123
- async function syncAgentConfigs(targetDir, apiKey, workspaceId) {
3124
- const response = await fetchAgentConfigs(apiKey, workspaceId);
3125
- let written = 0;
3126
- for (const file of response.files) {
3127
- const filePath = join18(targetDir, file.path);
3128
- await mkdir9(dirname3(filePath), { recursive: true });
3129
- await writeFile11(filePath, file.content, "utf-8");
3130
- written++;
3204
+ if (skipped > 0) {
3205
+ printInfo(`Skipped ${skipped} artifact(s) via artifactOverrides.skipPaths`);
3131
3206
  }
3132
- if (written > 0) {
3133
- printStatus(`Wrote ${written} agent config(s) (CLAUDE.md, AGENTS.md)`);
3134
- }
3135
- return written;
3207
+ return { written, skipped, version: response.version };
3136
3208
  }
3137
3209
  async function writeArtifact(targetDir, artifact) {
3138
- const filePath = join18(targetDir, artifact.path);
3210
+ const filePath = join19(targetDir, artifact.path);
3139
3211
  await mkdir9(dirname3(filePath), { recursive: true });
3140
3212
  await writeFile11(filePath, artifact.content, "utf-8");
3141
3213
  }
3142
- async function syncAllArtifacts(targetDir, apiKey, workspaceId, currentArtifactVersion) {
3214
+ async function syncAllArtifacts(targetDir, apiKey, workspaceId, currentArtifactVersion, skipPaths = []) {
3143
3215
  const changes = [];
3144
3216
  let artifactVersion = currentArtifactVersion;
3217
+ const { deleted, skipped } = await wipeManagedPaths(targetDir, skipPaths);
3218
+ if (deleted > 0) {
3219
+ changes.push(`Wiped ${deleted} managed path(s)`);
3220
+ }
3221
+ if (skipped > 0) {
3222
+ changes.push(`Preserved ${skipped} path(s) (skipPaths override)`);
3223
+ }
3145
3224
  try {
3146
3225
  const result = await syncArtifacts(
3147
3226
  targetDir,
3148
3227
  apiKey,
3149
3228
  workspaceId,
3150
- currentArtifactVersion
3229
+ currentArtifactVersion,
3230
+ skipPaths
3151
3231
  );
3152
3232
  if (result.written > 0) {
3153
3233
  changes.push(`Synced ${result.written} artifact(s)`);
3154
3234
  artifactVersion = result.version;
3155
3235
  }
3156
- } catch (err) {
3157
- if (err instanceof RelayError) {
3158
- printWarning(
3159
- `Could not fetch artifacts (${err.status}). Will retry on next sync.`
3236
+ if (result.skipped > 0) {
3237
+ changes.push(
3238
+ `Skipped ${result.skipped} artifact(s) (skipPaths override)`
3160
3239
  );
3161
- } else {
3162
- printWarning("Artifact sync failed. Will retry on next sync.");
3163
- }
3164
- }
3165
- try {
3166
- const configCount = await syncAgentConfigs(targetDir, apiKey, workspaceId);
3167
- if (configCount > 0) {
3168
- changes.push(`Wrote ${configCount} agent config(s)`);
3169
3240
  }
3170
3241
  } catch (err) {
3171
3242
  if (err instanceof RelayError) {
3172
3243
  printWarning(
3173
- `Could not fetch agent configs (${err.status}). Will retry on next sync.`
3244
+ `Could not fetch artifacts (${err.status}). Will retry on next sync.`
3174
3245
  );
3175
3246
  } else {
3176
- printWarning("Agent config sync failed. Will retry on next sync.");
3247
+ printWarning("Artifact sync failed. Will retry on next sync.");
3177
3248
  }
3178
3249
  }
3179
3250
  return { artifactVersion, changes };
@@ -3183,6 +3254,7 @@ var init_artifacts = __esm({
3183
3254
  "use strict";
3184
3255
  init_ui();
3185
3256
  init_relay_client();
3257
+ init_managed_paths();
3186
3258
  }
3187
3259
  });
3188
3260
 
@@ -3195,11 +3267,11 @@ __export(cleanup_exports, {
3195
3267
  });
3196
3268
  import { defineCommand as defineCommand2 } from "citty";
3197
3269
  import pc7 from "picocolors";
3198
- import { join as join19 } from "path";
3199
- import { readFile as readFile13, writeFile as writeFile12, rm as rm4 } from "fs/promises";
3270
+ import { join as join20 } from "path";
3271
+ import { readFile as readFile13, writeFile as writeFile12, rm as rm5 } from "fs/promises";
3200
3272
  async function scanArtifacts(targetDir) {
3201
3273
  const actions = [];
3202
- const localMePath = join19(targetDir, ".flydocs", "me.json");
3274
+ const localMePath = join20(targetDir, ".flydocs", "me.json");
3203
3275
  if (await pathExists(localMePath) && await pathExists(globalMePath())) {
3204
3276
  actions.push({
3205
3277
  description: "Remove .flydocs/me.json (migrated to ~/.flydocs/me.json)",
@@ -3207,7 +3279,7 @@ async function scanArtifacts(targetDir) {
3207
3279
  type: "file"
3208
3280
  });
3209
3281
  }
3210
- const validationCachePath = join19(
3282
+ const validationCachePath = join20(
3211
3283
  targetDir,
3212
3284
  ".flydocs",
3213
3285
  "validation-cache.json"
@@ -3221,7 +3293,7 @@ async function scanArtifacts(targetDir) {
3221
3293
  }
3222
3294
  const hasGlobalCreds = await pathExists(credentialsPath());
3223
3295
  for (const envFile of [".env", ".env.local"]) {
3224
- const envPath = join19(targetDir, envFile);
3296
+ const envPath = join20(targetDir, envFile);
3225
3297
  if (!await pathExists(envPath)) continue;
3226
3298
  const content = await readFile13(envPath, "utf-8");
3227
3299
  const lines = content.split("\n");
@@ -3246,7 +3318,7 @@ async function scanArtifacts(targetDir) {
3246
3318
  try {
3247
3319
  const config = await readAnyConfig(targetDir);
3248
3320
  const rawContent = await readFile13(
3249
- join19(targetDir, ".flydocs", "config.json"),
3321
+ join20(targetDir, ".flydocs", "config.json"),
3250
3322
  "utf-8"
3251
3323
  );
3252
3324
  const raw = JSON.parse(rawContent);
@@ -3255,7 +3327,7 @@ async function scanArtifacts(targetDir) {
3255
3327
  if (field in raw) {
3256
3328
  actions.push({
3257
3329
  description: `Remove ghost field "${field}" from config.json`,
3258
- path: join19(targetDir, ".flydocs", "config.json"),
3330
+ path: join20(targetDir, ".flydocs", "config.json"),
3259
3331
  type: "field"
3260
3332
  });
3261
3333
  }
@@ -3274,7 +3346,7 @@ async function scanArtifacts(targetDir) {
3274
3346
  if (field in raw) {
3275
3347
  actions.push({
3276
3348
  description: `Remove v1 field "${field}" from config.json (server-owned in v2)`,
3277
- path: join19(targetDir, ".flydocs", "config.json"),
3349
+ path: join20(targetDir, ".flydocs", "config.json"),
3278
3350
  type: "field"
3279
3351
  });
3280
3352
  }
@@ -3289,7 +3361,7 @@ async function executeCleanup(targetDir, actions) {
3289
3361
  const lineRemovals = actions.filter((a) => a.type === "line");
3290
3362
  const fieldRemovals = actions.filter((a) => a.type === "field");
3291
3363
  for (const action of fileRemovals) {
3292
- await rm4(action.path, { force: true });
3364
+ await rm5(action.path, { force: true });
3293
3365
  }
3294
3366
  const envFiles = /* @__PURE__ */ new Map();
3295
3367
  for (const action of lineRemovals) {
@@ -3307,13 +3379,13 @@ async function executeCleanup(targetDir, actions) {
3307
3379
  (l) => l.trim().length > 0 && !l.trim().startsWith("#")
3308
3380
  );
3309
3381
  if (!hasContent) {
3310
- await rm4(envPath, { force: true });
3382
+ await rm5(envPath, { force: true });
3311
3383
  } else {
3312
3384
  await writeFile12(envPath, filtered.join("\n"), "utf-8");
3313
3385
  }
3314
3386
  }
3315
3387
  if (fieldRemovals.length > 0) {
3316
- const configPath = join19(targetDir, ".flydocs", "config.json");
3388
+ const configPath = join20(targetDir, ".flydocs", "config.json");
3317
3389
  const content = await readFile13(configPath, "utf-8");
3318
3390
  const config = JSON.parse(content);
3319
3391
  for (const action of fieldRemovals) {
@@ -3360,7 +3432,7 @@ var init_cleanup = __esm({
3360
3432
  console.log(` ${pc7.bold("FlyDocs Cleanup")}`);
3361
3433
  console.log();
3362
3434
  const hasConfig = await pathExists(
3363
- join19(targetDir, ".flydocs", "config.json")
3435
+ join20(targetDir, ".flydocs", "config.json")
3364
3436
  );
3365
3437
  if (!hasConfig) {
3366
3438
  printError("No .flydocs/config.json found. Run flydocs init first.");
@@ -3404,16 +3476,16 @@ var init_cleanup = __esm({
3404
3476
  });
3405
3477
 
3406
3478
  // src/lib/ide-archive.ts
3407
- import { readFile as readFile14, writeFile as writeFile13, mkdir as mkdir10, readdir as readdir4 } from "fs/promises";
3408
- import { join as join20, basename } from "path";
3479
+ import { readFile as readFile14, writeFile as writeFile13, mkdir as mkdir10, readdir as readdir5 } from "fs/promises";
3480
+ import { join as join21, basename } from "path";
3409
3481
  async function archiveIdeConfigs(targetDir) {
3410
3482
  const archived = [];
3411
3483
  for (const cfg of SINGLE_FILE_CONFIGS) {
3412
- const absSource = join20(targetDir, cfg.source);
3484
+ const absSource = join21(targetDir, cfg.source);
3413
3485
  if (!await pathExists(absSource)) continue;
3414
3486
  const content = await readFile14(absSource, "utf-8");
3415
- const absDest = join20(targetDir, cfg.archive);
3416
- await mkdir10(join20(absDest, ".."), { recursive: true });
3487
+ const absDest = join21(targetDir, cfg.archive);
3488
+ await mkdir10(join21(absDest, ".."), { recursive: true });
3417
3489
  await writeFile13(absDest, content, "utf-8");
3418
3490
  archived.push({
3419
3491
  sourcePath: cfg.source,
@@ -3422,17 +3494,17 @@ async function archiveIdeConfigs(targetDir) {
3422
3494
  });
3423
3495
  printStatus(`Archived ${cfg.source}`);
3424
3496
  }
3425
- const cursorRulesDir = join20(targetDir, ".cursor", "rules");
3497
+ const cursorRulesDir = join21(targetDir, ".cursor", "rules");
3426
3498
  if (await pathExists(cursorRulesDir)) {
3427
3499
  let entries;
3428
3500
  try {
3429
- entries = await readdir4(cursorRulesDir);
3501
+ entries = await readdir5(cursorRulesDir);
3430
3502
  } catch {
3431
3503
  entries = [];
3432
3504
  }
3433
3505
  const mdFiles = entries.filter((f) => f.endsWith(".md"));
3434
3506
  if (mdFiles.length > 0) {
3435
- const archiveSubdir = join20(
3507
+ const archiveSubdir = join21(
3436
3508
  targetDir,
3437
3509
  "flydocs",
3438
3510
  "knowledge",
@@ -3441,10 +3513,10 @@ async function archiveIdeConfigs(targetDir) {
3441
3513
  );
3442
3514
  await mkdir10(archiveSubdir, { recursive: true });
3443
3515
  for (const file of mdFiles) {
3444
- const absSource = join20(cursorRulesDir, file);
3516
+ const absSource = join21(cursorRulesDir, file);
3445
3517
  const content = await readFile14(absSource, "utf-8");
3446
3518
  const archivePath = `flydocs/knowledge/archived/cursor-rules/${basename(file)}`;
3447
- const absDest = join20(targetDir, archivePath);
3519
+ const absDest = join21(targetDir, archivePath);
3448
3520
  await writeFile13(absDest, content, "utf-8");
3449
3521
  archived.push({
3450
3522
  sourcePath: `.cursor/rules/${file}`,
@@ -3477,10 +3549,10 @@ var init_ide_archive = __esm({
3477
3549
  });
3478
3550
 
3479
3551
  // src/lib/workspace.ts
3480
- import { readFile as readFile15, writeFile as writeFile14, readdir as readdir5, stat as stat2 } from "fs/promises";
3481
- import { join as join21, dirname as dirname4, relative, resolve as resolve3 } from "path";
3552
+ import { readFile as readFile15, writeFile as writeFile14, readdir as readdir6, stat as stat2 } from "fs/promises";
3553
+ import { join as join22, dirname as dirname4, relative, resolve as resolve3 } from "path";
3482
3554
  async function readWorkspaceFile(dir) {
3483
- const filePath = join21(dir, WORKSPACE_FILENAME);
3555
+ const filePath = join22(dir, WORKSPACE_FILENAME);
3484
3556
  if (!await pathExists(filePath)) return null;
3485
3557
  const content = await readFile15(filePath, "utf-8");
3486
3558
  const parsed = JSON.parse(content);
@@ -3488,21 +3560,21 @@ async function readWorkspaceFile(dir) {
3488
3560
  return parsed;
3489
3561
  }
3490
3562
  async function writeWorkspaceFile(dir, workspace) {
3491
- const filePath = join21(dir, WORKSPACE_FILENAME);
3563
+ const filePath = join22(dir, WORKSPACE_FILENAME);
3492
3564
  const content = JSON.stringify(workspace, null, 2) + "\n";
3493
3565
  await writeFile14(filePath, content, "utf-8");
3494
3566
  }
3495
3567
  async function detectSiblingRepos(parentDir) {
3496
3568
  const repos = {};
3497
- const entries = await readdir5(parentDir);
3569
+ const entries = await readdir6(parentDir);
3498
3570
  for (const entry of entries) {
3499
3571
  if (entry.startsWith(".")) continue;
3500
- const entryPath = join21(parentDir, entry);
3572
+ const entryPath = join22(parentDir, entry);
3501
3573
  const entryStat = await stat2(entryPath).catch(() => null);
3502
3574
  if (!entryStat?.isDirectory()) continue;
3503
- const hasGit = await pathExists(join21(entryPath, ".git"));
3575
+ const hasGit = await pathExists(join22(entryPath, ".git"));
3504
3576
  const hasFlydocs = await pathExists(
3505
- join21(entryPath, ".flydocs", "config.json")
3577
+ join22(entryPath, ".flydocs", "config.json")
3506
3578
  );
3507
3579
  if (hasGit || hasFlydocs) {
3508
3580
  repos[entry] = { path: `./${entry}` };
@@ -3517,7 +3589,7 @@ async function readSiblingDescriptors(workspaceDir, repos) {
3517
3589
  const descriptors = [];
3518
3590
  for (const [name, entry] of Object.entries(repos)) {
3519
3591
  const repoDir = resolve3(workspaceDir, entry.path);
3520
- const serviceJsonPath = join21(repoDir, "flydocs", "context", "service.json");
3592
+ const serviceJsonPath = join22(repoDir, "flydocs", "context", "service.json");
3521
3593
  if (!await pathExists(serviceJsonPath)) continue;
3522
3594
  try {
3523
3595
  const content = await readFile15(serviceJsonPath, "utf-8");
@@ -3632,7 +3704,7 @@ __export(scan_exports, {
3632
3704
  import { defineCommand as defineCommand3 } from "citty";
3633
3705
  import { spinner } from "@clack/prompts";
3634
3706
  import pc8 from "picocolors";
3635
- import { join as join22 } from "path";
3707
+ import { join as join23 } from "path";
3636
3708
  import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
3637
3709
  async function resolveRepoName(targetDir, repoArg) {
3638
3710
  if (repoArg) return repoArg;
@@ -3661,15 +3733,15 @@ async function resolveRepoName(targetDir, repoArg) {
3661
3733
  }
3662
3734
  async function writeScanResults(targetDir, response) {
3663
3735
  const written = [];
3664
- const contextDir = join22(targetDir, "flydocs", "context");
3736
+ const contextDir = join23(targetDir, "flydocs", "context");
3665
3737
  await mkdir11(contextDir, { recursive: true });
3666
3738
  if (response.projectMd) {
3667
- const projectMdPath = join22(contextDir, "project.md");
3739
+ const projectMdPath = join23(contextDir, "project.md");
3668
3740
  await writeFile15(projectMdPath, response.projectMd, "utf-8");
3669
3741
  written.push("flydocs/context/project.md");
3670
3742
  }
3671
3743
  if (response.serviceJson) {
3672
- const serviceJsonPath = join22(contextDir, "service.json");
3744
+ const serviceJsonPath = join23(contextDir, "service.json");
3673
3745
  await writeFile15(
3674
3746
  serviceJsonPath,
3675
3747
  JSON.stringify(response.serviceJson, null, 2) + "\n",
@@ -3825,7 +3897,7 @@ __export(init_exports, {
3825
3897
  import { defineCommand as defineCommand4 } from "citty";
3826
3898
  import { text as text2, confirm as confirm3, select as select2, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
3827
3899
  import pc9 from "picocolors";
3828
- import { join as join23 } from "path";
3900
+ import { join as join24 } from "path";
3829
3901
  import { mkdir as mkdir12, writeFile as writeFile16 } from "fs/promises";
3830
3902
  import { execFile } from "child_process";
3831
3903
  import { promisify } from "util";
@@ -3906,7 +3978,7 @@ async function resolveAndValidateKey(keyArg, targetDir) {
3906
3978
  }
3907
3979
  async function pullServerConfig(apiKey, targetDir, workspaceId, forceIncludeContext = false) {
3908
3980
  printInfo("Pulling config from server...");
3909
- const isFirstInit = forceIncludeContext || !await pathExists(join23(targetDir, ".flydocs", "config.json"));
3981
+ const isFirstInit = forceIncludeContext || !await pathExists(join24(targetDir, ".flydocs", "config.json"));
3910
3982
  try {
3911
3983
  const response = await fetchConfigV2(apiKey, {
3912
3984
  includeContext: isFirstInit,
@@ -3935,7 +4007,7 @@ async function initSingleRepo(targetDir, apiKey, serverResponse, options = {}) {
3935
4007
  const actions = [];
3936
4008
  const skipped = [];
3937
4009
  const { childRepoMode = false } = options;
3938
- await mkdir12(join23(targetDir, ".flydocs"), { recursive: true });
4010
+ await mkdir12(join24(targetDir, ".flydocs"), { recursive: true });
3939
4011
  const configWithHash = applyConfigHash(serverResponse.config);
3940
4012
  await writeConfig(targetDir, configWithHash);
3941
4013
  actions.push("Wrote .flydocs/config.json (from server)");
@@ -3948,16 +4020,16 @@ async function initSingleRepo(targetDir, apiKey, serverResponse, options = {}) {
3948
4020
  actions.push("Wrote ~/.flydocs/me.json");
3949
4021
  }
3950
4022
  if (serverResponse.context) {
3951
- const contextDir = join23(targetDir, "flydocs", "context");
4023
+ const contextDir = join24(targetDir, "flydocs", "context");
3952
4024
  await mkdir12(contextDir, { recursive: true });
3953
- const projectMdPath = join23(contextDir, "project.md");
4025
+ const projectMdPath = join24(contextDir, "project.md");
3954
4026
  if (!await pathExists(projectMdPath)) {
3955
4027
  await writeFile16(projectMdPath, serverResponse.context.projectMd, "utf-8");
3956
4028
  actions.push("Wrote flydocs/context/project.md");
3957
4029
  } else {
3958
4030
  skipped.push("flydocs/context/project.md (already exists)");
3959
4031
  }
3960
- const serviceJsonPath = join23(contextDir, "service.json");
4032
+ const serviceJsonPath = join24(contextDir, "service.json");
3961
4033
  if (serverResponse.context.serviceJson && !await pathExists(serviceJsonPath)) {
3962
4034
  await writeFile16(
3963
4035
  serviceJsonPath,
@@ -4004,26 +4076,26 @@ async function initSingleRepo(targetDir, apiKey, serverResponse, options = {}) {
4004
4076
  };
4005
4077
  }
4006
4078
  async function cleanServerManagedFiles(targetDir) {
4007
- const { rm: rm7 } = await import("fs/promises");
4079
+ const { rm: rm9 } = await import("fs/promises");
4008
4080
  let cleaned = 0;
4009
4081
  for (const dir of SERVER_MANAGED_DIRS) {
4010
- const fullPath = join23(targetDir, dir);
4082
+ const fullPath = join24(targetDir, dir);
4011
4083
  if (await pathExists(fullPath)) {
4012
- await rm7(fullPath, { recursive: true, force: true });
4084
+ await rm9(fullPath, { recursive: true, force: true });
4013
4085
  cleaned++;
4014
4086
  }
4015
4087
  }
4016
4088
  for (const file of SERVER_MANAGED_FILES) {
4017
- const fullPath = join23(targetDir, file);
4089
+ const fullPath = join24(targetDir, file);
4018
4090
  if (await pathExists(fullPath)) {
4019
- await rm7(fullPath, { force: true });
4091
+ await rm9(fullPath, { force: true });
4020
4092
  cleaned++;
4021
4093
  }
4022
4094
  }
4023
4095
  return cleaned;
4024
4096
  }
4025
4097
  async function checkGitFreshness(repoDir) {
4026
- const hasGit = await pathExists(join23(repoDir, ".git"));
4098
+ const hasGit = await pathExists(join24(repoDir, ".git"));
4027
4099
  if (!hasGit) return;
4028
4100
  try {
4029
4101
  await execFileAsync("git", ["-C", repoDir, "fetch", "--quiet"], {
@@ -4052,7 +4124,7 @@ async function checkGitFreshness(repoDir) {
4052
4124
  }
4053
4125
  }
4054
4126
  async function isEmptyOrNonRepo(dir) {
4055
- const hasGit = await pathExists(join23(dir, ".git"));
4127
+ const hasGit = await pathExists(join24(dir, ".git"));
4056
4128
  if (hasGit) return false;
4057
4129
  const siblings = await detectSiblingRepos(dir);
4058
4130
  if (Object.keys(siblings).length >= 2) return false;
@@ -4122,9 +4194,9 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
4122
4194
  for (let i = 0; i < repos.length; i++) {
4123
4195
  const repo = repos[i];
4124
4196
  const shortName = repoNames[i];
4125
- const cloneDir = join23(targetDir, shortName);
4197
+ const cloneDir = join24(targetDir, shortName);
4126
4198
  console.log();
4127
- if (await pathExists(join23(cloneDir, ".git"))) {
4199
+ if (await pathExists(join24(cloneDir, ".git"))) {
4128
4200
  printInfo(`${pc9.bold(shortName)} already cloned, initializing...`);
4129
4201
  await checkGitFreshness(cloneDir);
4130
4202
  } else {
@@ -4167,8 +4239,8 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
4167
4239
  );
4168
4240
  await writeWorkspaceFile(targetDir, workspaceFile);
4169
4241
  allActions.push(`.flydocs-workspace.json (${repos.length} repos)`);
4170
- const contextDir = join23(targetDir, "flydocs", "context");
4171
- const workspaceMdPath = join23(contextDir, "workspace.md");
4242
+ const contextDir = join24(targetDir, "flydocs", "context");
4243
+ const workspaceMdPath = join24(contextDir, "workspace.md");
4172
4244
  if (!await pathExists(workspaceMdPath)) {
4173
4245
  await mkdir12(contextDir, { recursive: true });
4174
4246
  const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(targetDir, workspaceFile);
@@ -4187,7 +4259,7 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
4187
4259
  }
4188
4260
  }
4189
4261
  async function isParentDirectory(dir) {
4190
- const hasGit = await pathExists(join23(dir, ".git"));
4262
+ const hasGit = await pathExists(join24(dir, ".git"));
4191
4263
  if (hasGit) return false;
4192
4264
  const repos = await detectSiblingRepos(dir);
4193
4265
  return Object.keys(repos).length >= 2;
@@ -4207,7 +4279,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
4207
4279
  cancel3("Init cancelled.");
4208
4280
  process.exit(0);
4209
4281
  }
4210
- const firstRepoDir = join23(parentDir, repoNames[0]);
4282
+ const firstRepoDir = join24(parentDir, repoNames[0]);
4211
4283
  const { apiKey, workspaceId } = await resolveAndValidateKey(
4212
4284
  keyArg,
4213
4285
  firstRepoDir
@@ -4240,7 +4312,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
4240
4312
  await ensureGitignore(parentDir);
4241
4313
  allActions.push("Updated workspace root .gitignore");
4242
4314
  for (const repoName of repoNames) {
4243
- const repoDir = join23(parentDir, repoName);
4315
+ const repoDir = join24(parentDir, repoName);
4244
4316
  console.log();
4245
4317
  printInfo(`Initializing ${pc9.bold(repoName)}...`);
4246
4318
  await checkGitFreshness(repoDir);
@@ -4272,8 +4344,8 @@ async function runMultiRepoInit(parentDir, keyArg) {
4272
4344
  await writeWorkspaceFile(parentDir, workspaceFile);
4273
4345
  allActions.push(`.flydocs-workspace.json (${repoNames.length} repos)`);
4274
4346
  }
4275
- const contextDir = join23(parentDir, "flydocs", "context");
4276
- const workspaceMdPath = join23(contextDir, "workspace.md");
4347
+ const contextDir = join24(parentDir, "flydocs", "context");
4348
+ const workspaceMdPath = join24(contextDir, "workspace.md");
4277
4349
  if (await pathExists(workspaceMdPath)) {
4278
4350
  allSkipped.push("flydocs/context/workspace.md (already exists)");
4279
4351
  } else {
@@ -4426,7 +4498,7 @@ var init_init = __esm({
4426
4498
  actions.unshift("Stored credential globally (~/.flydocs/credentials)");
4427
4499
  const topology = serverResponse.config.topology;
4428
4500
  if (topology?.type === 4 && topology.label === "sibling-repos") {
4429
- const parentDir = join23(targetDir, "..");
4501
+ const parentDir = join24(targetDir, "..");
4430
4502
  const existing = await readWorkspaceFile(parentDir);
4431
4503
  if (existing) {
4432
4504
  skipped.push(".flydocs-workspace.json (already exists)");
@@ -4547,16 +4619,320 @@ var init_init = __esm({
4547
4619
  }
4548
4620
  });
4549
4621
 
4622
+ // src/lib/skill-conflicts.ts
4623
+ import { join as join25 } from "path";
4624
+ import { readdir as readdir7, rm as rm6, cp as cp2, mkdir as mkdir13 } from "fs/promises";
4625
+ import { select as select3, isCancel as isCancel5 } from "@clack/prompts";
4626
+ async function resolveSkillConflicts(targetDir, keptPaths = []) {
4627
+ const result = {
4628
+ newKeptPaths: [],
4629
+ archived: 0,
4630
+ deleted: 0
4631
+ };
4632
+ const skillsDir = join25(targetDir, ".claude", "skills");
4633
+ if (!await pathExists(skillsDir)) return result;
4634
+ const entries = await readdir7(skillsDir, { withFileTypes: true });
4635
+ for (const entry of entries) {
4636
+ if (!entry.isDirectory()) continue;
4637
+ if (entry.name.startsWith(CORE_SKILL_PREFIX2)) continue;
4638
+ const relPath = `.claude/skills/${entry.name}`;
4639
+ if (keptPaths.includes(relPath) || keptPaths.includes(relPath + "/")) {
4640
+ continue;
4641
+ }
4642
+ await promptConflict(targetDir, skillsDir, entry.name, relPath, result);
4643
+ }
4644
+ if (result.archived > 0 || result.deleted > 0 || result.newKeptPaths.length > 0) {
4645
+ const parts = [];
4646
+ if (result.archived > 0) parts.push(`${result.archived} archived`);
4647
+ if (result.deleted > 0) parts.push(`${result.deleted} deleted`);
4648
+ if (result.newKeptPaths.length > 0)
4649
+ parts.push(`${result.newKeptPaths.length} kept`);
4650
+ printInfo(`Non-core skills: ${parts.join(", ")}`);
4651
+ }
4652
+ return result;
4653
+ }
4654
+ async function promptConflict(targetDir, skillsDir, name, relPath, result) {
4655
+ const choice = await select3({
4656
+ message: `Found non-managed skill: ${name}. What should we do?`,
4657
+ options: [
4658
+ {
4659
+ value: "keep",
4660
+ label: "Keep \u2014 leave in place, don't ask again"
4661
+ },
4662
+ {
4663
+ value: "archive",
4664
+ label: "Archive \u2014 move to flydocs/knowledge/archived/skills/"
4665
+ },
4666
+ {
4667
+ value: "delete",
4668
+ label: "Delete \u2014 remove permanently"
4669
+ }
4670
+ ]
4671
+ });
4672
+ if (isCancel5(choice)) {
4673
+ result.newKeptPaths.push(relPath);
4674
+ return;
4675
+ }
4676
+ const fullPath = join25(skillsDir, name);
4677
+ switch (choice) {
4678
+ case "keep":
4679
+ result.newKeptPaths.push(relPath);
4680
+ printInfo(`Keeping ${name} (won't ask again)`);
4681
+ break;
4682
+ case "archive": {
4683
+ const archiveDir = join25(
4684
+ targetDir,
4685
+ "flydocs",
4686
+ "knowledge",
4687
+ "archived",
4688
+ "skills"
4689
+ );
4690
+ await mkdir13(archiveDir, { recursive: true });
4691
+ const archiveDest = join25(archiveDir, name);
4692
+ if (await pathExists(archiveDest)) {
4693
+ await rm6(archiveDest, { recursive: true, force: true });
4694
+ }
4695
+ await cp2(fullPath, archiveDest, { recursive: true });
4696
+ await rm6(fullPath, { recursive: true, force: true });
4697
+ result.archived++;
4698
+ printStatus(`Archived ${name} \u2192 flydocs/knowledge/archived/skills/`);
4699
+ break;
4700
+ }
4701
+ case "delete":
4702
+ await rm6(fullPath, { recursive: true, force: true });
4703
+ result.deleted++;
4704
+ printStatus(`Deleted ${name}`);
4705
+ break;
4706
+ }
4707
+ }
4708
+ var CORE_SKILL_PREFIX2;
4709
+ var init_skill_conflicts = __esm({
4710
+ "src/lib/skill-conflicts.ts"() {
4711
+ "use strict";
4712
+ init_fs_ops();
4713
+ init_ui();
4714
+ CORE_SKILL_PREFIX2 = "flydocs-";
4715
+ }
4716
+ });
4717
+
4718
+ // src/commands/sync.ts
4719
+ var sync_exports = {};
4720
+ __export(sync_exports, {
4721
+ default: () => sync_default,
4722
+ runSync: () => runSync
4723
+ });
4724
+ import { defineCommand as defineCommand5 } from "citty";
4725
+ import pc10 from "picocolors";
4726
+ import { join as join26, resolve as resolve4 } from "path";
4727
+ import { mkdir as mkdir14, writeFile as writeFile17 } from "fs/promises";
4728
+ async function runSync(targetDir) {
4729
+ const changes = [];
4730
+ printInfo("Syncing with server...");
4731
+ const resolved = await resolveApiKey(void 0, targetDir);
4732
+ if (!resolved) {
4733
+ printError("No API key found. Run `flydocs auth` or `flydocs init` first.");
4734
+ process.exit(1);
4735
+ }
4736
+ const apiKey = resolved.key;
4737
+ const cred = await readGlobalCredential();
4738
+ const workspaceId = cred?.workspaceId;
4739
+ if (!workspaceId) {
4740
+ printError(
4741
+ "No workspace ID found. Run `flydocs init` to set up your workspace."
4742
+ );
4743
+ process.exit(1);
4744
+ }
4745
+ let currentTemplateVersion = 0;
4746
+ let currentArtifactVersion = 0;
4747
+ let localOverrides;
4748
+ try {
4749
+ const currentConfig = await readAnyConfig(targetDir);
4750
+ if (isConfigV2(currentConfig)) {
4751
+ currentTemplateVersion = currentConfig.configVersion ?? 0;
4752
+ currentArtifactVersion = currentConfig.artifactVersion ?? 0;
4753
+ localOverrides = currentConfig.artifactOverrides;
4754
+ }
4755
+ } catch {
4756
+ }
4757
+ let serverResponse;
4758
+ try {
4759
+ serverResponse = await fetchConfigV2(apiKey, { workspaceId });
4760
+ } catch (err) {
4761
+ if (err instanceof RelayError) {
4762
+ printWarning(`Server unavailable (${err.status}), using cached config.`);
4763
+ } else {
4764
+ printWarning("Server unreachable, using cached config.");
4765
+ }
4766
+ console.log(` ${pc10.dim("Config may be stale. Retry when connected.")}`);
4767
+ return changes;
4768
+ }
4769
+ if (!serverResponse.valid) {
4770
+ printWarning("Server returned invalid config. Keeping current config.");
4771
+ return changes;
4772
+ }
4773
+ await mkdir14(join26(targetDir, ".flydocs"), { recursive: true });
4774
+ const serverConfig = serverResponse.config;
4775
+ if (localOverrides) {
4776
+ serverConfig.artifactOverrides = localOverrides;
4777
+ }
4778
+ const configWithHash = applyConfigHash(serverConfig);
4779
+ await writeConfig(targetDir, configWithHash);
4780
+ changes.push("Updated .flydocs/config.json");
4781
+ const serverTemplateVersion = serverResponse.templates.version;
4782
+ if (serverTemplateVersion > currentTemplateVersion) {
4783
+ try {
4784
+ const templatesResponse = await fetchTemplates(
4785
+ apiKey,
4786
+ currentTemplateVersion,
4787
+ workspaceId
4788
+ );
4789
+ if (templatesResponse.templates.length > 0) {
4790
+ const templatesDir = join26(
4791
+ targetDir,
4792
+ ".claude",
4793
+ "skills",
4794
+ "flydocs-workflow",
4795
+ "templates"
4796
+ );
4797
+ await mkdir14(templatesDir, { recursive: true });
4798
+ for (const template of templatesResponse.templates) {
4799
+ const filename = `${template.type}.md`;
4800
+ const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
4801
+ const templateDir = join26(templatesDir, subdir);
4802
+ await mkdir14(templateDir, { recursive: true });
4803
+ await writeFile17(
4804
+ join26(templateDir, filename),
4805
+ template.content,
4806
+ "utf-8"
4807
+ );
4808
+ }
4809
+ changes.push(
4810
+ `Updated ${templatesResponse.templates.length} templates (v${currentTemplateVersion} \u2192 v${serverTemplateVersion})`
4811
+ );
4812
+ }
4813
+ } catch (err) {
4814
+ if (err instanceof RelayError) {
4815
+ printWarning("Could not fetch templates. Will retry on next sync.");
4816
+ } else {
4817
+ printWarning("Template sync failed. Will retry on next sync.");
4818
+ }
4819
+ }
4820
+ }
4821
+ const serverArtifactVersion = serverResponse.artifactVersion ?? 0;
4822
+ const skipPaths = localOverrides?.skipPaths ?? [];
4823
+ if (serverArtifactVersion > currentArtifactVersion) {
4824
+ const artifactWriteRoot = await resolveArtifactWriteRoot(
4825
+ targetDir,
4826
+ serverResponse.config
4827
+ );
4828
+ const { changes: artifactChanges } = await syncAllArtifacts(
4829
+ artifactWriteRoot,
4830
+ apiKey,
4831
+ workspaceId,
4832
+ currentArtifactVersion,
4833
+ skipPaths
4834
+ );
4835
+ changes.push(...artifactChanges);
4836
+ if (artifactWriteRoot !== targetDir) {
4837
+ changes.push(`Artifacts written to workspace root`);
4838
+ }
4839
+ const keptPaths = localOverrides?.keptPaths ?? [];
4840
+ const conflicts = await resolveSkillConflicts(artifactWriteRoot, keptPaths);
4841
+ if (conflicts.newKeptPaths.length > 0) {
4842
+ try {
4843
+ const updatedConfig = await readAnyConfig(targetDir);
4844
+ if (isConfigV2(updatedConfig)) {
4845
+ const existingKept = updatedConfig.artifactOverrides?.keptPaths ?? [];
4846
+ const mergedKept = [
4847
+ .../* @__PURE__ */ new Set([...existingKept, ...conflicts.newKeptPaths])
4848
+ ];
4849
+ updatedConfig.artifactOverrides = {
4850
+ ...updatedConfig.artifactOverrides,
4851
+ skipPaths: updatedConfig.artifactOverrides?.skipPaths ?? [],
4852
+ keptPaths: mergedKept
4853
+ };
4854
+ const hashed = applyConfigHash(updatedConfig);
4855
+ await writeConfig(targetDir, hashed);
4856
+ changes.push(
4857
+ `Persisted ${conflicts.newKeptPaths.length} kept path(s) to config`
4858
+ );
4859
+ }
4860
+ } catch {
4861
+ }
4862
+ }
4863
+ if (conflicts.archived > 0) {
4864
+ changes.push(`Archived ${conflicts.archived} non-core skill(s)`);
4865
+ }
4866
+ if (conflicts.deleted > 0) {
4867
+ changes.push(`Deleted ${conflicts.deleted} non-core skill(s)`);
4868
+ }
4869
+ }
4870
+ await migrateGitignore(targetDir);
4871
+ return changes;
4872
+ }
4873
+ async function resolveArtifactWriteRoot(targetDir, config) {
4874
+ const topology = config.topology;
4875
+ if (topology && topology.type === 4 && topology.label === "sibling-repos") {
4876
+ const parentDir = join26(targetDir, "..");
4877
+ const workspaceFile = await readWorkspaceFile(parentDir);
4878
+ if (workspaceFile) {
4879
+ return resolve4(parentDir);
4880
+ }
4881
+ }
4882
+ return targetDir;
4883
+ }
4884
+ var sync_default;
4885
+ var init_sync = __esm({
4886
+ "src/commands/sync.ts"() {
4887
+ "use strict";
4888
+ init_ui();
4889
+ init_global_config();
4890
+ init_config();
4891
+ init_config_integrity();
4892
+ init_types();
4893
+ init_gitignore();
4894
+ init_artifacts();
4895
+ init_workspace();
4896
+ init_relay_client();
4897
+ init_skill_conflicts();
4898
+ sync_default = defineCommand5({
4899
+ meta: {
4900
+ name: "sync",
4901
+ description: "Pull latest config and templates from server"
4902
+ },
4903
+ args: {
4904
+ path: {
4905
+ type: "string",
4906
+ description: "Path to project directory"
4907
+ }
4908
+ },
4909
+ async run({ args }) {
4910
+ const targetDir = args.path ?? process.cwd();
4911
+ console.log();
4912
+ const changes = await runSync(targetDir);
4913
+ if (changes.length === 0) {
4914
+ printStatus("Already up to date.");
4915
+ } else {
4916
+ for (const change of changes) {
4917
+ printStatus(change);
4918
+ }
4919
+ }
4920
+ console.log();
4921
+ }
4922
+ });
4923
+ }
4924
+ });
4925
+
4550
4926
  // src/commands/update.ts
4551
4927
  var update_exports = {};
4552
4928
  __export(update_exports, {
4553
4929
  default: () => update_default
4554
4930
  });
4555
- import { defineCommand as defineCommand5 } from "citty";
4556
- import { resolve as resolve4, join as join24 } from "path";
4557
- import { mkdir as mkdir13, cp as cp2, readFile as readFile16, readdir as readdir6, rm as rm5 } from "fs/promises";
4558
- import { select as select3, text as text3, confirm as confirm4, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
4559
- import pc10 from "picocolors";
4931
+ import { defineCommand as defineCommand6 } from "citty";
4932
+ import { resolve as resolve5, join as join27 } from "path";
4933
+ import { mkdir as mkdir15, cp as cp3, readFile as readFile16, readdir as readdir8, rm as rm7 } from "fs/promises";
4934
+ import { select as select4, text as text3, confirm as confirm4, isCancel as isCancel6, cancel as cancel4 } from "@clack/prompts";
4935
+ import pc11 from "picocolors";
4560
4936
  var update_default;
4561
4937
  var init_update = __esm({
4562
4938
  "src/commands/update.ts"() {
@@ -4575,7 +4951,11 @@ var init_update = __esm({
4575
4951
  init_update_check();
4576
4952
  init_telemetry();
4577
4953
  init_integrity();
4578
- update_default = defineCommand5({
4954
+ init_config();
4955
+ init_types();
4956
+ init_managed_paths();
4957
+ init_sync();
4958
+ update_default = defineCommand6({
4579
4959
  meta: {
4580
4960
  name: "update",
4581
4961
  description: "Update an existing FlyDocs installation"
@@ -4620,11 +5000,11 @@ var init_update = __esm({
4620
5000
  await capture("update_started", { template_version: version });
4621
5001
  let targetDir;
4622
5002
  if (args.path) {
4623
- targetDir = resolve4(args.path.replace(/^~/, process.env.HOME ?? "~"));
5003
+ targetDir = resolve5(args.path.replace(/^~/, process.env.HOME ?? "~"));
4624
5004
  } else if (args.here) {
4625
5005
  targetDir = process.cwd();
4626
5006
  } else {
4627
- const choice = await select3({
5007
+ const choice = await select4({
4628
5008
  message: "Which project would you like to update?",
4629
5009
  options: [
4630
5010
  {
@@ -4637,7 +5017,7 @@ var init_update = __esm({
4637
5017
  }
4638
5018
  ]
4639
5019
  });
4640
- if (isCancel5(choice)) {
5020
+ if (isCancel6(choice)) {
4641
5021
  cancel4("Update cancelled.");
4642
5022
  process.exit(0);
4643
5023
  }
@@ -4647,11 +5027,11 @@ var init_update = __esm({
4647
5027
  const enteredPath = await text3({
4648
5028
  message: "Enter project path:"
4649
5029
  });
4650
- if (isCancel5(enteredPath)) {
5030
+ if (isCancel6(enteredPath)) {
4651
5031
  cancel4("Update cancelled.");
4652
5032
  process.exit(0);
4653
5033
  }
4654
- targetDir = resolve4(
5034
+ targetDir = resolve5(
4655
5035
  enteredPath.replace(/^~/, process.env.HOME ?? "~")
4656
5036
  );
4657
5037
  }
@@ -4660,11 +5040,11 @@ var init_update = __esm({
4660
5040
  printError(`Directory does not exist: ${targetDir}`);
4661
5041
  process.exit(1);
4662
5042
  }
4663
- targetDir = resolve4(targetDir);
5043
+ targetDir = resolve5(targetDir);
4664
5044
  process.chdir(targetDir);
4665
- const hasVersion = await pathExists(join24(targetDir, ".flydocs", "version"));
5045
+ const hasVersion = await pathExists(join27(targetDir, ".flydocs", "version"));
4666
5046
  const hasConfig = await pathExists(
4667
- join24(targetDir, ".flydocs", "config.json")
5047
+ join27(targetDir, ".flydocs", "config.json")
4668
5048
  );
4669
5049
  if (!hasVersion && !hasConfig) {
4670
5050
  printError(`Not a FlyDocs project: ${targetDir}`);
@@ -4676,7 +5056,7 @@ var init_update = __esm({
4676
5056
  let currentVersion = "0.1.0";
4677
5057
  if (hasVersion) {
4678
5058
  const vContent = await readFile16(
4679
- join24(targetDir, ".flydocs", "version"),
5059
+ join27(targetDir, ".flydocs", "version"),
4680
5060
  "utf-8"
4681
5061
  );
4682
5062
  currentVersion = vContent.trim();
@@ -4698,7 +5078,7 @@ var init_update = __esm({
4698
5078
  const shouldContinue = await confirm4({
4699
5079
  message: "Continue anyway?"
4700
5080
  });
4701
- if (isCancel5(shouldContinue) || !shouldContinue) {
5081
+ if (isCancel6(shouldContinue) || !shouldContinue) {
4702
5082
  process.exit(0);
4703
5083
  }
4704
5084
  }
@@ -4710,35 +5090,65 @@ var init_update = __esm({
4710
5090
  });
4711
5091
  console.log(`Updating: v${currentVersion} \u2192 v${version}`);
4712
5092
  console.log();
4713
- const changelogPath = join24(templateDir, "CHANGELOG.md");
5093
+ const changelogPath = join27(templateDir, "CHANGELOG.md");
4714
5094
  const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
4715
5095
  if (whatsNew.length > 0) {
4716
- console.log(pc10.cyan("What's new:"));
5096
+ console.log(pc11.cyan("What's new:"));
4717
5097
  console.log();
4718
5098
  for (const entry of whatsNew) {
4719
5099
  console.log(` ${entry}`);
4720
5100
  }
4721
5101
  console.log();
4722
5102
  }
5103
+ if (hasConfig) {
5104
+ try {
5105
+ const existingConfig = await readAnyConfig(targetDir);
5106
+ if (isConfigV2(existingConfig)) {
5107
+ printInfo(
5108
+ "Cloud project detected \u2014 running sync flow (wipe-and-replace)"
5109
+ );
5110
+ console.log();
5111
+ const syncChanges = await runSync(targetDir);
5112
+ if (syncChanges.length > 0) {
5113
+ for (const change of syncChanges) {
5114
+ printStatus(change);
5115
+ }
5116
+ }
5117
+ console.log();
5118
+ console.log(
5119
+ ` ${pc11.dim("Tip: use")} ${pc11.cyan("flydocs sync")} ${pc11.dim("for file-only refresh (no CLI update).")}`
5120
+ );
5121
+ await capture("update_completed", {
5122
+ current_version: currentVersion,
5123
+ version,
5124
+ tier: "cloud",
5125
+ mode: "sync-delegate"
5126
+ });
5127
+ await flush();
5128
+ return;
5129
+ }
5130
+ } catch {
5131
+ }
5132
+ }
4723
5133
  const now = /* @__PURE__ */ new Date();
4724
5134
  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")}`;
4725
- const backupDir = join24(targetDir, ".flydocs", `backup-${ts}`);
4726
- await mkdir13(backupDir, { recursive: true });
5135
+ const backupDir = join27(targetDir, ".flydocs", `backup-${ts}`);
5136
+ await mkdir15(backupDir, { recursive: true });
4727
5137
  if (hasConfig) {
4728
- await cp2(
4729
- join24(targetDir, ".flydocs", "config.json"),
4730
- join24(backupDir, "config.json")
5138
+ await cp3(
5139
+ join27(targetDir, ".flydocs", "config.json"),
5140
+ join27(backupDir, "config.json")
4731
5141
  );
4732
5142
  printStatus(`Config backed up to .flydocs/backup-${ts}/`);
4733
5143
  }
4734
5144
  try {
4735
- const flydocsDir = join24(targetDir, ".flydocs");
4736
- const entries = await readdir6(flydocsDir);
5145
+ const flydocsDir = join27(targetDir, ".flydocs");
5146
+ const entries = await readdir8(flydocsDir);
4737
5147
  const backups = entries.filter((e) => e.startsWith("backup-")).sort();
4738
5148
  if (backups.length > 3) {
4739
5149
  const toRemove = backups.slice(0, backups.length - 3);
4740
5150
  for (const old of toRemove) {
4741
- await rm5(join24(flydocsDir, old), { recursive: true, force: true });
5151
+ await rm7(join27(flydocsDir, old), { recursive: true, force: true });
4742
5152
  }
4743
5153
  }
4744
5154
  } catch {
@@ -4775,19 +5185,21 @@ var init_update = __esm({
4775
5185
  effectiveTier = preserved.tier;
4776
5186
  }
4777
5187
  await ensureDirectories(targetDir, effectiveTier);
5188
+ console.log("Wiping server-managed paths...");
5189
+ await wipeManagedPaths(targetDir);
4778
5190
  console.log("Replacing framework directories...");
4779
5191
  await replaceDirectory(
4780
- join24(templateDir, ".flydocs", "templates"),
4781
- join24(targetDir, ".flydocs", "templates")
5192
+ join27(templateDir, ".flydocs", "templates"),
5193
+ join27(targetDir, ".flydocs", "templates")
4782
5194
  );
4783
5195
  printStatus(".flydocs/templates");
4784
5196
  await replaceDirectory(
4785
- join24(templateDir, ".claude", "hooks"),
4786
- join24(targetDir, ".claude", "hooks")
5197
+ join27(templateDir, ".claude", "hooks"),
5198
+ join27(targetDir, ".claude", "hooks")
4787
5199
  );
4788
5200
  printStatus(".claude/hooks");
4789
5201
  const hasExistingAgents = await pathExists(
4790
- join24(targetDir, ".claude", "agents")
5202
+ join27(targetDir, ".claude", "agents")
4791
5203
  );
4792
5204
  let installAgents;
4793
5205
  if (args.yes) {
@@ -4796,7 +5208,7 @@ var init_update = __esm({
4796
5208
  installAgents = true;
4797
5209
  } else {
4798
5210
  console.log();
4799
- console.log(` ${pc10.bold(pc10.yellow("Sub-Agents (Recommended)"))}`);
5211
+ console.log(` ${pc11.bold(pc11.yellow("Sub-Agents (Recommended)"))}`);
4800
5212
  console.log();
4801
5213
  console.log(
4802
5214
  " Sub-agents are specialized roles (PM, implementation, review,"
@@ -4812,27 +5224,27 @@ var init_update = __esm({
4812
5224
  message: "Install sub-agents?",
4813
5225
  initialValue: true
4814
5226
  });
4815
- if (isCancel5(agentConfirm)) {
5227
+ if (isCancel6(agentConfirm)) {
4816
5228
  installAgents = false;
4817
5229
  } else {
4818
5230
  installAgents = agentConfirm;
4819
5231
  }
4820
5232
  }
4821
5233
  if (installAgents) {
4822
- const claudeAgentsSrc = join24(templateDir, ".claude", "agents");
5234
+ const claudeAgentsSrc = join27(templateDir, ".claude", "agents");
4823
5235
  if (await pathExists(claudeAgentsSrc)) {
4824
- await mkdir13(join24(targetDir, ".claude", "agents"), { recursive: true });
5236
+ await mkdir15(join27(targetDir, ".claude", "agents"), { recursive: true });
4825
5237
  await copyDirectoryContents(
4826
5238
  claudeAgentsSrc,
4827
- join24(targetDir, ".claude", "agents")
5239
+ join27(targetDir, ".claude", "agents")
4828
5240
  );
4829
5241
  }
4830
- const cursorAgentsSrc = join24(templateDir, ".cursor", "agents");
5242
+ const cursorAgentsSrc = join27(templateDir, ".cursor", "agents");
4831
5243
  if (await pathExists(cursorAgentsSrc)) {
4832
- await mkdir13(join24(targetDir, ".cursor", "agents"), { recursive: true });
5244
+ await mkdir15(join27(targetDir, ".cursor", "agents"), { recursive: true });
4833
5245
  await copyDirectoryContents(
4834
5246
  cursorAgentsSrc,
4835
- join24(targetDir, ".cursor", "agents")
5247
+ join27(targetDir, ".cursor", "agents")
4836
5248
  );
4837
5249
  }
4838
5250
  printStatus(
@@ -4846,58 +5258,58 @@ var init_update = __esm({
4846
5258
  console.log();
4847
5259
  console.log("Replacing framework files...");
4848
5260
  await copyFile(
4849
- join24(templateDir, ".claude", "CLAUDE.md"),
4850
- join24(targetDir, ".claude", "CLAUDE.md")
5261
+ join27(templateDir, ".claude", "CLAUDE.md"),
5262
+ join27(targetDir, ".claude", "CLAUDE.md")
4851
5263
  );
4852
5264
  await copyFile(
4853
- join24(templateDir, ".claude", "settings.json"),
4854
- join24(targetDir, ".claude", "settings.json")
5265
+ join27(templateDir, ".claude", "settings.json"),
5266
+ join27(targetDir, ".claude", "settings.json")
4855
5267
  );
4856
5268
  printStatus(".claude/CLAUDE.md, settings.json");
4857
5269
  await copyDirectoryContents(
4858
- join24(templateDir, ".claude", "commands"),
4859
- join24(targetDir, ".claude", "commands")
5270
+ join27(templateDir, ".claude", "commands"),
5271
+ join27(targetDir, ".claude", "commands")
4860
5272
  );
4861
5273
  await copyDirectoryContents(
4862
- join24(templateDir, ".claude", "commands"),
4863
- join24(targetDir, ".cursor", "commands")
5274
+ join27(templateDir, ".claude", "commands"),
5275
+ join27(targetDir, ".cursor", "commands")
4864
5276
  );
4865
5277
  printStatus(".claude/commands, .cursor/commands");
4866
- const skillsReadmeSrc = join24(templateDir, ".claude", "skills", "README.md");
5278
+ const skillsReadmeSrc = join27(templateDir, ".claude", "skills", "README.md");
4867
5279
  if (await pathExists(skillsReadmeSrc)) {
4868
5280
  await copyFile(
4869
5281
  skillsReadmeSrc,
4870
- join24(targetDir, ".claude", "skills", "README.md")
5282
+ join27(targetDir, ".claude", "skills", "README.md")
4871
5283
  );
4872
5284
  }
4873
5285
  printStatus(".claude/skills/README.md");
4874
5286
  await copyFile(
4875
- join24(templateDir, ".cursor", "hooks.json"),
4876
- join24(targetDir, ".cursor", "hooks.json")
5287
+ join27(templateDir, ".cursor", "hooks.json"),
5288
+ join27(targetDir, ".cursor", "hooks.json")
4877
5289
  );
4878
5290
  printStatus(".cursor/hooks.json");
4879
5291
  await copyFile(
4880
- join24(templateDir, "AGENTS.md"),
4881
- join24(targetDir, "AGENTS.md")
5292
+ join27(templateDir, "AGENTS.md"),
5293
+ join27(targetDir, "AGENTS.md")
4882
5294
  );
4883
5295
  printStatus("AGENTS.md");
4884
- const envExampleSrc = join24(templateDir, ".env.example");
5296
+ const envExampleSrc = join27(templateDir, ".env.example");
4885
5297
  if (await pathExists(envExampleSrc)) {
4886
- await copyFile(envExampleSrc, join24(targetDir, ".env.example"));
5298
+ await copyFile(envExampleSrc, join27(targetDir, ".env.example"));
4887
5299
  printStatus(".env.example");
4888
5300
  }
4889
- const knowledgeTemplatesDir = join24(
5301
+ const knowledgeTemplatesDir = join27(
4890
5302
  targetDir,
4891
5303
  "flydocs",
4892
5304
  "knowledge",
4893
5305
  "templates"
4894
5306
  );
4895
5307
  if (!await pathExists(knowledgeTemplatesDir)) {
4896
- await mkdir13(knowledgeTemplatesDir, { recursive: true });
5308
+ await mkdir15(knowledgeTemplatesDir, { recursive: true });
4897
5309
  }
4898
5310
  for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
4899
- const src = join24(templateDir, "flydocs", "knowledge", "templates", tmpl);
4900
- const dest = join24(knowledgeTemplatesDir, tmpl);
5311
+ const src = join27(templateDir, "flydocs", "knowledge", "templates", tmpl);
5312
+ const dest = join27(knowledgeTemplatesDir, tmpl);
4901
5313
  if (await pathExists(src) && !await pathExists(dest)) {
4902
5314
  await copyFile(src, dest);
4903
5315
  }
@@ -4924,18 +5336,18 @@ var init_update = __esm({
4924
5336
  printWarning("Config merge failed \u2014 config.json preserved as-is");
4925
5337
  }
4926
5338
  await copyFile(
4927
- join24(templateDir, ".flydocs", "version"),
4928
- join24(targetDir, ".flydocs", "version")
5339
+ join27(templateDir, ".flydocs", "version"),
5340
+ join27(targetDir, ".flydocs", "version")
4929
5341
  );
4930
5342
  printStatus(`.flydocs/version \u2192 ${version}`);
4931
- const clSrc = join24(templateDir, "CHANGELOG.md");
5343
+ const clSrc = join27(templateDir, "CHANGELOG.md");
4932
5344
  if (await pathExists(clSrc)) {
4933
- await copyFile(clSrc, join24(targetDir, ".flydocs", "CHANGELOG.md"));
5345
+ await copyFile(clSrc, join27(targetDir, ".flydocs", "CHANGELOG.md"));
4934
5346
  printStatus(".flydocs/CHANGELOG.md");
4935
5347
  }
4936
- const mfSrc = join24(templateDir, "manifest.json");
5348
+ const mfSrc = join27(templateDir, "manifest.json");
4937
5349
  if (await pathExists(mfSrc)) {
4938
- await copyFile(mfSrc, join24(targetDir, ".flydocs", "manifest.json"));
5350
+ await copyFile(mfSrc, join27(targetDir, ".flydocs", "manifest.json"));
4939
5351
  printStatus(".flydocs/manifest.json");
4940
5352
  }
4941
5353
  await generateIntegrity(targetDir, version);
@@ -4997,22 +5409,22 @@ var uninstall_exports = {};
4997
5409
  __export(uninstall_exports, {
4998
5410
  default: () => uninstall_default
4999
5411
  });
5000
- import { defineCommand as defineCommand6 } from "citty";
5001
- import { resolve as resolve5, join as join25 } from "path";
5002
- import { readdir as readdir7, rm as rm6, rename as rename2 } from "fs/promises";
5003
- import { confirm as confirm5, select as select4, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
5004
- import pc11 from "picocolors";
5412
+ import { defineCommand as defineCommand7 } from "citty";
5413
+ import { resolve as resolve6, join as join28 } from "path";
5414
+ import { readdir as readdir9, rm as rm8, rename as rename2 } from "fs/promises";
5415
+ import { confirm as confirm5, select as select5, isCancel as isCancel7, cancel as cancel5 } from "@clack/prompts";
5416
+ import pc12 from "picocolors";
5005
5417
  async function removeOwnedSkills(targetDir) {
5006
- const skillsDir = join25(targetDir, ".claude", "skills");
5418
+ const skillsDir = join28(targetDir, ".claude", "skills");
5007
5419
  const removed = [];
5008
5420
  if (!await pathExists(skillsDir)) {
5009
5421
  return removed;
5010
5422
  }
5011
5423
  try {
5012
- const entries = await readdir7(skillsDir);
5424
+ const entries = await readdir9(skillsDir);
5013
5425
  for (const entry of entries) {
5014
5426
  if (entry.startsWith(OWNED_SKILL_PREFIX)) {
5015
- await rm6(join25(skillsDir, entry), { recursive: true, force: true });
5427
+ await rm8(join28(skillsDir, entry), { recursive: true, force: true });
5016
5428
  removed.push(`.claude/skills/${entry}`);
5017
5429
  }
5018
5430
  }
@@ -5021,16 +5433,16 @@ async function removeOwnedSkills(targetDir) {
5021
5433
  return removed;
5022
5434
  }
5023
5435
  async function removeOwnedCursorRules(targetDir) {
5024
- const rulesDir = join25(targetDir, ".cursor", "rules");
5436
+ const rulesDir = join28(targetDir, ".cursor", "rules");
5025
5437
  const removed = [];
5026
5438
  if (!await pathExists(rulesDir)) {
5027
5439
  return removed;
5028
5440
  }
5029
5441
  try {
5030
- const entries = await readdir7(rulesDir);
5442
+ const entries = await readdir9(rulesDir);
5031
5443
  for (const entry of entries) {
5032
5444
  if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
5033
- await rm6(join25(rulesDir, entry), { force: true });
5445
+ await rm8(join28(rulesDir, entry), { force: true });
5034
5446
  removed.push(`.cursor/rules/${entry}`);
5035
5447
  }
5036
5448
  }
@@ -5040,7 +5452,7 @@ async function removeOwnedCursorRules(targetDir) {
5040
5452
  }
5041
5453
  async function isEmptyDir(dirPath) {
5042
5454
  try {
5043
- const entries = await readdir7(dirPath);
5455
+ const entries = await readdir9(dirPath);
5044
5456
  return entries.length === 0;
5045
5457
  } catch {
5046
5458
  return false;
@@ -5049,9 +5461,9 @@ async function isEmptyDir(dirPath) {
5049
5461
  async function cleanupEmptyParents(targetDir, dirs) {
5050
5462
  const cleaned = [];
5051
5463
  for (const dir of dirs) {
5052
- const fullPath = join25(targetDir, dir);
5464
+ const fullPath = join28(targetDir, dir);
5053
5465
  if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
5054
- await rm6(fullPath, { recursive: true, force: true });
5466
+ await rm8(fullPath, { recursive: true, force: true });
5055
5467
  cleaned.push(dir);
5056
5468
  }
5057
5469
  }
@@ -5081,7 +5493,7 @@ var init_uninstall = __esm({
5081
5493
  ];
5082
5494
  OWNED_SKILL_PREFIX = "flydocs-";
5083
5495
  OWNED_RULE_PREFIX = "flydocs-";
5084
- uninstall_default = defineCommand6({
5496
+ uninstall_default = defineCommand7({
5085
5497
  meta: {
5086
5498
  name: "uninstall",
5087
5499
  description: "Remove FlyDocs from a project directory"
@@ -5117,7 +5529,7 @@ var init_uninstall = __esm({
5117
5529
  printBanner(CLI_VERSION);
5118
5530
  let targetDir;
5119
5531
  if (args.path) {
5120
- targetDir = resolve5(args.path.replace(/^~/, process.env.HOME ?? "~"));
5532
+ targetDir = resolve6(args.path.replace(/^~/, process.env.HOME ?? "~"));
5121
5533
  } else if (args.here) {
5122
5534
  targetDir = process.cwd();
5123
5535
  } else {
@@ -5127,9 +5539,9 @@ var init_uninstall = __esm({
5127
5539
  printError(`Directory does not exist: ${targetDir}`);
5128
5540
  process.exit(1);
5129
5541
  }
5130
- targetDir = resolve5(targetDir);
5131
- const hasFlydocs = await pathExists(join25(targetDir, ".flydocs"));
5132
- const hasAgentsMd = await pathExists(join25(targetDir, "AGENTS.md"));
5542
+ targetDir = resolve6(targetDir);
5543
+ const hasFlydocs = await pathExists(join28(targetDir, ".flydocs"));
5544
+ const hasAgentsMd = await pathExists(join28(targetDir, "AGENTS.md"));
5133
5545
  if (!hasFlydocs && !hasAgentsMd) {
5134
5546
  printError(`Not a FlyDocs project: ${targetDir}`);
5135
5547
  printInfo("No .flydocs/ directory or AGENTS.md found.");
@@ -5141,12 +5553,12 @@ var init_uninstall = __esm({
5141
5553
  const removeAll = forceAll || args.all;
5142
5554
  const skipPrompts = forceAll || args.yes;
5143
5555
  let contentAction = "preserve";
5144
- const hasUserContent = await pathExists(join25(targetDir, "flydocs"));
5556
+ const hasUserContent = await pathExists(join28(targetDir, "flydocs"));
5145
5557
  if (hasUserContent) {
5146
5558
  if (removeAll) {
5147
5559
  contentAction = "remove";
5148
5560
  } else if (!skipPrompts) {
5149
- const choice = await select4({
5561
+ const choice = await select5({
5150
5562
  message: "What should happen to your flydocs/ content (project docs, knowledge base)?",
5151
5563
  options: [
5152
5564
  {
@@ -5166,7 +5578,7 @@ var init_uninstall = __esm({
5166
5578
  }
5167
5579
  ]
5168
5580
  });
5169
- if (isCancel6(choice)) {
5581
+ if (isCancel7(choice)) {
5170
5582
  cancel5("Uninstall cancelled.");
5171
5583
  process.exit(0);
5172
5584
  }
@@ -5175,39 +5587,39 @@ var init_uninstall = __esm({
5175
5587
  }
5176
5588
  if (!skipPrompts) {
5177
5589
  console.log();
5178
- console.log(pc11.bold("The following will be removed:"));
5590
+ console.log(pc12.bold("The following will be removed:"));
5179
5591
  console.log();
5180
5592
  console.log(" Framework files:");
5181
5593
  for (const [path] of ALWAYS_REMOVED) {
5182
- console.log(` ${pc11.dim(path)}`);
5594
+ console.log(` ${pc12.dim(path)}`);
5183
5595
  }
5184
- console.log(` ${pc11.dim(".claude/skills/flydocs-*")}`);
5185
- console.log(` ${pc11.dim(".cursor/rules/flydocs-*.mdc")}`);
5596
+ console.log(` ${pc12.dim(".claude/skills/flydocs-*")}`);
5597
+ console.log(` ${pc12.dim(".cursor/rules/flydocs-*.mdc")}`);
5186
5598
  if (hasUserContent) {
5187
5599
  if (contentAction === "archive") {
5188
5600
  console.log();
5189
5601
  console.log(
5190
- ` User content: ${pc11.yellow("flydocs/ -> flydocs-archive/")}`
5602
+ ` User content: ${pc12.yellow("flydocs/ -> flydocs-archive/")}`
5191
5603
  );
5192
5604
  } else if (contentAction === "remove") {
5193
5605
  console.log();
5194
- console.log(` User content: ${pc11.red("flydocs/ (deleted)")}`);
5606
+ console.log(` User content: ${pc12.red("flydocs/ (deleted)")}`);
5195
5607
  } else {
5196
5608
  console.log();
5197
- console.log(` User content: ${pc11.green("flydocs/ (preserved)")}`);
5609
+ console.log(` User content: ${pc12.green("flydocs/ (preserved)")}`);
5198
5610
  }
5199
5611
  }
5200
5612
  console.log();
5201
- console.log(pc11.bold("Preserved:"));
5613
+ console.log(pc12.bold("Preserved:"));
5202
5614
  console.log(
5203
- ` ${pc11.dim(".claude/skills/ (non-flydocs community skills)")}`
5615
+ ` ${pc12.dim(".claude/skills/ (non-flydocs community skills)")}`
5204
5616
  );
5205
- console.log(` ${pc11.dim(".env, .env.local")}`);
5617
+ console.log(` ${pc12.dim(".env, .env.local")}`);
5206
5618
  console.log();
5207
5619
  const shouldContinue = await confirm5({
5208
5620
  message: "Proceed with uninstall?"
5209
5621
  });
5210
- if (isCancel6(shouldContinue) || !shouldContinue) {
5622
+ if (isCancel7(shouldContinue) || !shouldContinue) {
5211
5623
  cancel5("Uninstall cancelled.");
5212
5624
  process.exit(0);
5213
5625
  }
@@ -5224,16 +5636,16 @@ var init_uninstall = __esm({
5224
5636
  const removedRules = await removeOwnedCursorRules(targetDir);
5225
5637
  result.removed.push(...removedRules);
5226
5638
  for (const [relativePath, type] of ALWAYS_REMOVED) {
5227
- const fullPath = join25(targetDir, relativePath);
5639
+ const fullPath = join28(targetDir, relativePath);
5228
5640
  if (!await pathExists(fullPath)) {
5229
5641
  result.skipped.push(relativePath);
5230
5642
  continue;
5231
5643
  }
5232
5644
  try {
5233
5645
  if (type === "dir") {
5234
- await rm6(fullPath, { recursive: true, force: true });
5646
+ await rm8(fullPath, { recursive: true, force: true });
5235
5647
  } else {
5236
- await rm6(fullPath, { force: true });
5648
+ await rm8(fullPath, { force: true });
5237
5649
  }
5238
5650
  result.removed.push(relativePath);
5239
5651
  } catch {
@@ -5242,16 +5654,16 @@ var init_uninstall = __esm({
5242
5654
  }
5243
5655
  }
5244
5656
  if (hasUserContent) {
5245
- const flydocsPath = join25(targetDir, "flydocs");
5657
+ const flydocsPath = join28(targetDir, "flydocs");
5246
5658
  if (contentAction === "archive") {
5247
- const archivePath = join25(targetDir, "flydocs-archive");
5659
+ const archivePath = join28(targetDir, "flydocs-archive");
5248
5660
  if (await pathExists(archivePath)) {
5249
- await rm6(archivePath, { recursive: true, force: true });
5661
+ await rm8(archivePath, { recursive: true, force: true });
5250
5662
  }
5251
5663
  await rename2(flydocsPath, archivePath);
5252
5664
  result.archived.push("flydocs/ -> flydocs-archive/");
5253
5665
  } else if (contentAction === "remove") {
5254
- await rm6(flydocsPath, { recursive: true, force: true });
5666
+ await rm8(flydocsPath, { recursive: true, force: true });
5255
5667
  result.removed.push("flydocs/");
5256
5668
  }
5257
5669
  }
@@ -5270,17 +5682,17 @@ var init_uninstall = __esm({
5270
5682
  result.restored = originals.map((f) => f.relativePath);
5271
5683
  }
5272
5684
  console.log();
5273
- console.log(pc11.bold("Uninstall Summary"));
5685
+ console.log(pc12.bold("Uninstall Summary"));
5274
5686
  console.log();
5275
5687
  if (result.removed.length > 0) {
5276
- console.log(` ${pc11.green("Removed")} (${result.removed.length}):`);
5688
+ console.log(` ${pc12.green("Removed")} (${result.removed.length}):`);
5277
5689
  for (const item of result.removed) {
5278
5690
  printStatus(item);
5279
5691
  }
5280
5692
  }
5281
5693
  if (result.archived.length > 0) {
5282
5694
  console.log();
5283
- console.log(` ${pc11.yellow("Archived")} (${result.archived.length}):`);
5695
+ console.log(` ${pc12.yellow("Archived")} (${result.archived.length}):`);
5284
5696
  for (const item of result.archived) {
5285
5697
  printInfo(item);
5286
5698
  }
@@ -5288,7 +5700,7 @@ var init_uninstall = __esm({
5288
5700
  if (result.restored.length > 0) {
5289
5701
  console.log();
5290
5702
  console.log(
5291
- ` ${pc11.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
5703
+ ` ${pc12.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
5292
5704
  );
5293
5705
  for (const item of result.restored) {
5294
5706
  printInfo(item);
@@ -5297,16 +5709,16 @@ var init_uninstall = __esm({
5297
5709
  if (result.skipped.length > 0) {
5298
5710
  console.log();
5299
5711
  console.log(
5300
- ` ${pc11.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
5712
+ ` ${pc12.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
5301
5713
  );
5302
5714
  for (const item of result.skipped) {
5303
- console.log(` ${pc11.dim(item)}`);
5715
+ console.log(` ${pc12.dim(item)}`);
5304
5716
  }
5305
5717
  }
5306
5718
  console.log();
5307
5719
  printStatus("FlyDocs has been removed from this project.");
5308
5720
  console.log();
5309
- printInfo(`To reinstall: ${pc11.cyan("npx @flydocs/cli install --here")}`);
5721
+ printInfo(`To reinstall: ${pc12.cyan("npx @flydocs/cli install --here")}`);
5310
5722
  console.log();
5311
5723
  }
5312
5724
  });
@@ -5318,28 +5730,28 @@ var setup_exports = {};
5318
5730
  __export(setup_exports, {
5319
5731
  default: () => setup_default
5320
5732
  });
5321
- import { defineCommand as defineCommand7 } from "citty";
5322
- import pc12 from "picocolors";
5733
+ import { defineCommand as defineCommand8 } from "citty";
5734
+ import pc13 from "picocolors";
5323
5735
  var setup_default;
5324
5736
  var init_setup = __esm({
5325
5737
  "src/commands/setup.ts"() {
5326
5738
  "use strict";
5327
- setup_default = defineCommand7({
5739
+ setup_default = defineCommand8({
5328
5740
  meta: {
5329
5741
  name: "setup",
5330
5742
  description: "Configure FlyDocs settings for this project"
5331
5743
  },
5332
5744
  run() {
5333
5745
  console.log();
5334
- console.log(` ${pc12.bold("FlyDocs Setup")}`);
5746
+ console.log(` ${pc13.bold("FlyDocs Setup")}`);
5335
5747
  console.log();
5336
5748
  console.log(` Setup runs inside your IDE as an interactive AI command.`);
5337
5749
  console.log();
5338
5750
  console.log(
5339
- ` ${pc12.cyan("Claude Code:")} Type ${pc12.bold("/flydocs-setup")} in chat`
5751
+ ` ${pc13.cyan("Claude Code:")} Type ${pc13.bold("/flydocs-setup")} in chat`
5340
5752
  );
5341
5753
  console.log(
5342
- ` ${pc12.cyan("Cursor:")} Type ${pc12.bold("/flydocs-setup")} in chat`
5754
+ ` ${pc13.cyan("Cursor:")} Type ${pc13.bold("/flydocs-setup")} in chat`
5343
5755
  );
5344
5756
  console.log();
5345
5757
  console.log(` This configures your project context, detects your stack,`);
@@ -5355,14 +5767,14 @@ var skills_exports = {};
5355
5767
  __export(skills_exports, {
5356
5768
  default: () => skills_default
5357
5769
  });
5358
- import { defineCommand as defineCommand8 } from "citty";
5359
- import pc13 from "picocolors";
5770
+ import { defineCommand as defineCommand9 } from "citty";
5771
+ import pc14 from "picocolors";
5360
5772
  var list, search, add, remove, skills_default;
5361
5773
  var init_skills2 = __esm({
5362
5774
  "src/commands/skills.ts"() {
5363
5775
  "use strict";
5364
5776
  init_skill_manager();
5365
- list = defineCommand8({
5777
+ list = defineCommand9({
5366
5778
  meta: {
5367
5779
  name: "list",
5368
5780
  description: "List installed skills"
@@ -5378,26 +5790,26 @@ var init_skills2 = __esm({
5378
5790
  console.log(`${total} skill(s) installed:`);
5379
5791
  if (result.platform.length > 0) {
5380
5792
  console.log();
5381
- console.log(pc13.bold("Platform"));
5793
+ console.log(pc14.bold("Platform"));
5382
5794
  for (const skill of result.platform) {
5383
5795
  console.log(
5384
- ` ${skill.name} ${pc13.dim(`(${skill.triggers} triggers)`)}`
5796
+ ` ${skill.name} ${pc14.dim(`(${skill.triggers} triggers)`)}`
5385
5797
  );
5386
5798
  }
5387
5799
  }
5388
5800
  if (result.community.length > 0) {
5389
5801
  console.log();
5390
- console.log(pc13.bold("Community"));
5802
+ console.log(pc14.bold("Community"));
5391
5803
  for (const skill of result.community) {
5392
5804
  console.log(
5393
- ` ${skill.name} ${pc13.dim(`(${skill.triggers} triggers)`)}`
5805
+ ` ${skill.name} ${pc14.dim(`(${skill.triggers} triggers)`)}`
5394
5806
  );
5395
5807
  }
5396
5808
  }
5397
5809
  console.log();
5398
5810
  }
5399
5811
  });
5400
- search = defineCommand8({
5812
+ search = defineCommand9({
5401
5813
  meta: {
5402
5814
  name: "search",
5403
5815
  description: "Search community skills"
@@ -5413,24 +5825,24 @@ var init_skills2 = __esm({
5413
5825
  const results = await searchCatalog(args.keyword);
5414
5826
  if (results.length === 0) {
5415
5827
  console.log(`No skills found for "${args.keyword}".`);
5416
- console.log(` Browse the catalog at: ${pc13.cyan("https://skills.sh/")}`);
5828
+ console.log(` Browse the catalog at: ${pc14.cyan("https://skills.sh/")}`);
5417
5829
  return;
5418
5830
  }
5419
5831
  console.log();
5420
5832
  console.log(`${results.length} skill(s) matching "${args.keyword}":`);
5421
5833
  console.log();
5422
5834
  for (const skill of results) {
5423
- console.log(` ${pc13.bold(skill.name)}`);
5835
+ console.log(` ${pc14.bold(skill.name)}`);
5424
5836
  console.log(` ${skill.description}`);
5425
- console.log(` ${pc13.dim(skill.repo)}`);
5837
+ console.log(` ${pc14.dim(skill.repo)}`);
5426
5838
  if (skill.tags.length > 0) {
5427
- console.log(` ${pc13.dim(skill.tags.join(", "))}`);
5839
+ console.log(` ${pc14.dim(skill.tags.join(", "))}`);
5428
5840
  }
5429
5841
  console.log();
5430
5842
  }
5431
5843
  }
5432
5844
  });
5433
- add = defineCommand8({
5845
+ add = defineCommand9({
5434
5846
  meta: {
5435
5847
  name: "add",
5436
5848
  description: "Install a community skill"
@@ -5446,7 +5858,7 @@ var init_skills2 = __esm({
5446
5858
  await addSkill(process.cwd(), args.source);
5447
5859
  }
5448
5860
  });
5449
- remove = defineCommand8({
5861
+ remove = defineCommand9({
5450
5862
  meta: {
5451
5863
  name: "remove",
5452
5864
  description: "Remove an installed community skill"
@@ -5462,7 +5874,7 @@ var init_skills2 = __esm({
5462
5874
  await removeSkill(process.cwd(), args.name);
5463
5875
  }
5464
5876
  });
5465
- skills_default = defineCommand8({
5877
+ skills_default = defineCommand9({
5466
5878
  meta: {
5467
5879
  name: "skills",
5468
5880
  description: "Manage FlyDocs skills (list, search, add, remove)"
@@ -5482,10 +5894,10 @@ var connect_exports = {};
5482
5894
  __export(connect_exports, {
5483
5895
  default: () => connect_default
5484
5896
  });
5485
- import { defineCommand as defineCommand9 } from "citty";
5486
- import { text as text4, confirm as confirm6, isCancel as isCancel7, cancel as cancel6 } from "@clack/prompts";
5487
- import pc14 from "picocolors";
5488
- import { join as join26 } from "path";
5897
+ import { defineCommand as defineCommand10 } from "citty";
5898
+ import { text as text4, confirm as confirm6, isCancel as isCancel8, cancel as cancel6 } from "@clack/prompts";
5899
+ import pc15 from "picocolors";
5900
+ import { join as join29 } from "path";
5489
5901
  var connect_default;
5490
5902
  var init_connect = __esm({
5491
5903
  "src/commands/connect.ts"() {
@@ -5495,7 +5907,7 @@ var init_connect = __esm({
5495
5907
  init_template();
5496
5908
  init_ui();
5497
5909
  init_api_key();
5498
- connect_default = defineCommand9({
5910
+ connect_default = defineCommand10({
5499
5911
  meta: {
5500
5912
  name: "connect",
5501
5913
  description: "Connect FlyDocs to a cloud provider"
@@ -5520,11 +5932,11 @@ var init_connect = __esm({
5520
5932
  },
5521
5933
  async run({ args }) {
5522
5934
  const targetDir = args.path ?? process.cwd();
5523
- const configPath = join26(targetDir, ".flydocs", "config.json");
5935
+ const configPath = join29(targetDir, ".flydocs", "config.json");
5524
5936
  if (!await pathExists(configPath)) {
5525
5937
  printError("Not a FlyDocs project (.flydocs/config.json not found).");
5526
5938
  console.log(
5527
- ` Run ${pc14.cyan("flydocs")} first to install FlyDocs in this project.`
5939
+ ` Run ${pc15.cyan("flydocs")} first to install FlyDocs in this project.`
5528
5940
  );
5529
5941
  process.exit(1);
5530
5942
  }
@@ -5535,16 +5947,16 @@ var init_connect = __esm({
5535
5947
  const reconnect = await confirm6({
5536
5948
  message: "Want to update your API key?"
5537
5949
  });
5538
- if (isCancel7(reconnect) || !reconnect) {
5950
+ if (isCancel8(reconnect) || !reconnect) {
5539
5951
  console.log(` No changes made.`);
5540
5952
  return;
5541
5953
  }
5542
5954
  }
5543
5955
  console.log();
5544
- console.log(` ${pc14.bold("Connect to FlyDocs Cloud")}`);
5956
+ console.log(` ${pc15.bold("Connect to FlyDocs Cloud")}`);
5545
5957
  console.log();
5546
5958
  console.log(
5547
- ` ${pc14.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
5959
+ ` ${pc15.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
5548
5960
  );
5549
5961
  console.log();
5550
5962
  let apiKey = args.key ?? "";
@@ -5560,7 +5972,7 @@ var init_connect = __esm({
5560
5972
  return void 0;
5561
5973
  }
5562
5974
  });
5563
- if (isCancel7(keyInput)) {
5975
+ if (isCancel8(keyInput)) {
5564
5976
  cancel6("Connection cancelled.");
5565
5977
  process.exit(0);
5566
5978
  }
@@ -5582,7 +5994,7 @@ var init_connect = __esm({
5582
5994
  console.log(` Check your key and try again.`);
5583
5995
  process.exit(1);
5584
5996
  }
5585
- printStatus(`Connected to ${pc14.bold(result.org)}`);
5997
+ printStatus(`Connected to ${pc15.bold(result.org)}`);
5586
5998
  } catch {
5587
5999
  printError(
5588
6000
  "Could not reach relay API. Check your network and try again."
@@ -5590,7 +6002,7 @@ var init_connect = __esm({
5590
6002
  process.exit(1);
5591
6003
  }
5592
6004
  const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
5593
- printStatus(`API key stored in ${pc14.dim(envFile)}`);
6005
+ printStatus(`API key stored in ${pc15.dim(envFile)}`);
5594
6006
  } else {
5595
6007
  try {
5596
6008
  const result = await validateLinearKey(apiKey);
@@ -5600,7 +6012,7 @@ var init_connect = __esm({
5600
6012
  process.exit(1);
5601
6013
  }
5602
6014
  printStatus(
5603
- `Authenticated as ${pc14.bold(result.name)} (${result.email})`
6015
+ `Authenticated as ${pc15.bold(result.name)} (${result.email})`
5604
6016
  );
5605
6017
  } catch {
5606
6018
  printError("Invalid API key or network error.");
@@ -5608,7 +6020,7 @@ var init_connect = __esm({
5608
6020
  process.exit(1);
5609
6021
  }
5610
6022
  const envFile = await storeEnvKey(targetDir, "LINEAR_API_KEY", apiKey);
5611
- printStatus(`API key stored in ${pc14.dim(envFile)}`);
6023
+ printStatus(`API key stored in ${pc15.dim(envFile)}`);
5612
6024
  }
5613
6025
  const wasLocal = config.tier === "local";
5614
6026
  config.tier = "cloud";
@@ -5624,14 +6036,14 @@ var init_connect = __esm({
5624
6036
  }
5625
6037
  console.log();
5626
6038
  console.log(
5627
- ` ${pc14.bold("Connected!")} Your project is now on the cloud tier.`
6039
+ ` ${pc15.bold("Connected!")} Your project is now on the cloud tier.`
5628
6040
  );
5629
6041
  console.log();
5630
6042
  console.log(` Next steps:`);
5631
6043
  console.log(
5632
- ` 1. Run ${pc14.cyan("/flydocs-setup")} in your IDE to configure your project`
6044
+ ` 1. Run ${pc15.cyan("/flydocs-setup")} in your IDE to configure your project`
5633
6045
  );
5634
- console.log(` 2. Run ${pc14.cyan("/start-session")} to begin working`);
6046
+ console.log(` 2. Run ${pc15.cyan("/start-session")} to begin working`);
5635
6047
  console.log();
5636
6048
  }
5637
6049
  });
@@ -5643,9 +6055,9 @@ var auth_exports = {};
5643
6055
  __export(auth_exports, {
5644
6056
  default: () => auth_default
5645
6057
  });
5646
- import { defineCommand as defineCommand10 } from "citty";
5647
- import { text as text5, confirm as confirm7, isCancel as isCancel8, cancel as cancel7 } from "@clack/prompts";
5648
- import pc15 from "picocolors";
6058
+ import { defineCommand as defineCommand11 } from "citty";
6059
+ import { text as text5, confirm as confirm7, isCancel as isCancel9, cancel as cancel7 } from "@clack/prompts";
6060
+ import pc16 from "picocolors";
5649
6061
  var auth_default;
5650
6062
  var init_auth = __esm({
5651
6063
  "src/commands/auth.ts"() {
@@ -5653,7 +6065,7 @@ var init_auth = __esm({
5653
6065
  init_ui();
5654
6066
  init_api_key();
5655
6067
  init_global_config();
5656
- auth_default = defineCommand10({
6068
+ auth_default = defineCommand11({
5657
6069
  meta: {
5658
6070
  name: "auth",
5659
6071
  description: "Store API key globally (~/.flydocs/credentials)"
@@ -5667,25 +6079,25 @@ var init_auth = __esm({
5667
6079
  },
5668
6080
  async run({ args }) {
5669
6081
  console.log();
5670
- console.log(` ${pc15.bold("FlyDocs Authentication")}`);
6082
+ console.log(` ${pc16.bold("FlyDocs Authentication")}`);
5671
6083
  console.log();
5672
6084
  let apiKey = args.key ?? "";
5673
6085
  const existing = await readGlobalCredential();
5674
6086
  if (existing?.apiKey && !apiKey) {
5675
6087
  printInfo(
5676
- `Existing key found: ${pc15.dim(existing.apiKey.slice(0, 8) + "...")}`
6088
+ `Existing key found: ${pc16.dim(existing.apiKey.slice(0, 8) + "...")}`
5677
6089
  );
5678
6090
  const replace = await confirm7({
5679
6091
  message: "Replace with a new key?"
5680
6092
  });
5681
- if (isCancel8(replace) || !replace) {
6093
+ if (isCancel9(replace) || !replace) {
5682
6094
  console.log(` No changes made.`);
5683
6095
  return;
5684
6096
  }
5685
6097
  }
5686
6098
  if (!apiKey) {
5687
6099
  console.log(
5688
- ` ${pc15.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
6100
+ ` ${pc16.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
5689
6101
  );
5690
6102
  console.log();
5691
6103
  const keyInput = await text5({
@@ -5699,7 +6111,7 @@ var init_auth = __esm({
5699
6111
  return void 0;
5700
6112
  }
5701
6113
  });
5702
- if (isCancel8(keyInput)) {
6114
+ if (isCancel9(keyInput)) {
5703
6115
  cancel7("Authentication cancelled.");
5704
6116
  process.exit(0);
5705
6117
  }
@@ -5721,7 +6133,7 @@ var init_auth = __esm({
5721
6133
  printError("Invalid API key. Check your key and try again.");
5722
6134
  process.exit(1);
5723
6135
  }
5724
- printStatus(`Authenticated with ${pc15.bold(result.org)}`);
6136
+ printStatus(`Authenticated with ${pc16.bold(result.org)}`);
5725
6137
  } catch {
5726
6138
  printError(
5727
6139
  "Could not reach FlyDocs API. Check your network and try again."
@@ -5736,10 +6148,10 @@ var init_auth = __esm({
5736
6148
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
5737
6149
  lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5738
6150
  });
5739
- printStatus(`Key stored at ${pc15.dim(credentialsPath())}`);
6151
+ printStatus(`Key stored at ${pc16.dim(credentialsPath())}`);
5740
6152
  await checkCredentialPermissions();
5741
6153
  console.log();
5742
- console.log(` ${pc15.bold("Authenticated!")} Key stored globally.`);
6154
+ console.log(` ${pc16.bold("Authenticated!")} Key stored globally.`);
5743
6155
  console.log(` All FlyDocs projects on this machine will use this key.`);
5744
6156
  console.log();
5745
6157
  }
@@ -5747,175 +6159,6 @@ var init_auth = __esm({
5747
6159
  }
5748
6160
  });
5749
6161
 
5750
- // src/commands/sync.ts
5751
- var sync_exports = {};
5752
- __export(sync_exports, {
5753
- default: () => sync_default
5754
- });
5755
- import { defineCommand as defineCommand11 } from "citty";
5756
- import pc16 from "picocolors";
5757
- import { join as join27, resolve as resolve6 } from "path";
5758
- import { mkdir as mkdir14, writeFile as writeFile17 } from "fs/promises";
5759
- async function resolveArtifactWriteRoot(targetDir, config) {
5760
- const topology = config.topology;
5761
- if (topology && topology.type === 4 && topology.label === "sibling-repos") {
5762
- const parentDir = join27(targetDir, "..");
5763
- const workspaceFile = await readWorkspaceFile(parentDir);
5764
- if (workspaceFile) {
5765
- return resolve6(parentDir);
5766
- }
5767
- }
5768
- return targetDir;
5769
- }
5770
- var sync_default;
5771
- var init_sync = __esm({
5772
- "src/commands/sync.ts"() {
5773
- "use strict";
5774
- init_ui();
5775
- init_global_config();
5776
- init_config();
5777
- init_config_integrity();
5778
- init_fs_ops();
5779
- init_types();
5780
- init_gitignore();
5781
- init_artifacts();
5782
- init_workspace();
5783
- init_relay_client();
5784
- sync_default = defineCommand11({
5785
- meta: {
5786
- name: "sync",
5787
- description: "Pull latest config and templates from server"
5788
- },
5789
- args: {
5790
- path: {
5791
- type: "string",
5792
- description: "Path to project directory"
5793
- }
5794
- },
5795
- async run({ args }) {
5796
- const targetDir = args.path ?? process.cwd();
5797
- const changes = [];
5798
- console.log();
5799
- printInfo("Syncing with server...");
5800
- const resolved = await resolveApiKey(void 0, targetDir);
5801
- if (!resolved) {
5802
- printError(
5803
- "No API key found. Run `flydocs auth` or `flydocs init` first."
5804
- );
5805
- process.exit(1);
5806
- }
5807
- const apiKey = resolved.key;
5808
- const cred = await readGlobalCredential();
5809
- const workspaceId = cred?.workspaceId;
5810
- if (!workspaceId) {
5811
- printError(
5812
- "No workspace ID found. Run `flydocs init` to set up your workspace."
5813
- );
5814
- process.exit(1);
5815
- }
5816
- let currentTemplateVersion = 0;
5817
- let currentArtifactVersion = 0;
5818
- try {
5819
- const currentConfig = await readAnyConfig(targetDir);
5820
- if (isConfigV2(currentConfig)) {
5821
- currentTemplateVersion = currentConfig.configVersion ?? 0;
5822
- currentArtifactVersion = currentConfig.artifactVersion ?? 0;
5823
- }
5824
- } catch {
5825
- }
5826
- let serverResponse;
5827
- try {
5828
- serverResponse = await fetchConfigV2(apiKey, { workspaceId });
5829
- } catch (err) {
5830
- if (err instanceof RelayError) {
5831
- printWarning(
5832
- `Server unavailable (${err.status}), using cached config.`
5833
- );
5834
- } else {
5835
- printWarning("Server unreachable, using cached config.");
5836
- }
5837
- console.log(` ${pc16.dim("Config may be stale. Retry when connected.")}`);
5838
- return;
5839
- }
5840
- if (!serverResponse.valid) {
5841
- printWarning("Server returned invalid config. Keeping current config.");
5842
- return;
5843
- }
5844
- await mkdir14(join27(targetDir, ".flydocs"), { recursive: true });
5845
- const configWithHash = applyConfigHash(serverResponse.config);
5846
- await writeConfig(targetDir, configWithHash);
5847
- changes.push("Updated .flydocs/config.json");
5848
- const serverTemplateVersion = serverResponse.templates.version;
5849
- if (serverTemplateVersion > currentTemplateVersion) {
5850
- try {
5851
- const templatesResponse = await fetchTemplates(
5852
- apiKey,
5853
- currentTemplateVersion,
5854
- workspaceId
5855
- );
5856
- if (templatesResponse.templates.length > 0) {
5857
- const templatesDir = join27(
5858
- targetDir,
5859
- ".claude",
5860
- "skills",
5861
- "flydocs-workflow",
5862
- "templates"
5863
- );
5864
- await mkdir14(templatesDir, { recursive: true });
5865
- for (const template of templatesResponse.templates) {
5866
- const filename = `${template.type}.md`;
5867
- const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
5868
- const templateDir = join27(templatesDir, subdir);
5869
- await mkdir14(templateDir, { recursive: true });
5870
- await writeFile17(
5871
- join27(templateDir, filename),
5872
- template.content,
5873
- "utf-8"
5874
- );
5875
- }
5876
- changes.push(
5877
- `Updated ${templatesResponse.templates.length} templates (v${currentTemplateVersion} \u2192 v${serverTemplateVersion})`
5878
- );
5879
- }
5880
- } catch (err) {
5881
- if (err instanceof RelayError) {
5882
- printWarning("Could not fetch templates. Will retry on next sync.");
5883
- } else {
5884
- printWarning("Template sync failed. Will retry on next sync.");
5885
- }
5886
- }
5887
- }
5888
- const serverArtifactVersion = serverResponse.artifactVersion ?? 0;
5889
- if (serverArtifactVersion > currentArtifactVersion) {
5890
- const artifactWriteRoot = await resolveArtifactWriteRoot(
5891
- targetDir,
5892
- serverResponse.config
5893
- );
5894
- const { changes: artifactChanges } = await syncAllArtifacts(
5895
- artifactWriteRoot,
5896
- apiKey,
5897
- workspaceId,
5898
- currentArtifactVersion
5899
- );
5900
- changes.push(...artifactChanges);
5901
- if (artifactWriteRoot !== targetDir) {
5902
- changes.push(`Artifacts written to workspace root`);
5903
- }
5904
- }
5905
- await migrateGitignore(targetDir);
5906
- if (changes.length === 0) {
5907
- printStatus("Already up to date.");
5908
- } else {
5909
- for (const change of changes) {
5910
- printStatus(change);
5911
- }
5912
- }
5913
- console.log();
5914
- }
5915
- });
5916
- }
5917
- });
5918
-
5919
6162
  // src/commands/upgrade.ts
5920
6163
  var upgrade_exports = {};
5921
6164
  __export(upgrade_exports, {