@flydocs/cli 0.6.0-alpha.31 → 0.6.0-alpha.33

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.31";
18
+ CLI_VERSION = "0.6.0-alpha.33";
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: mkdir12 } = await import("fs/promises");
443
+ const { mkdir: mkdir16 } = await import("fs/promises");
444
444
  const rulesDir = join4(targetDir, ".cursor", "rules");
445
- await mkdir12(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
  }
@@ -1492,10 +1492,20 @@ import { join as join11 } from "path";
1492
1492
  async function ensureGitignore(targetDir) {
1493
1493
  const gitignorePath = join11(targetDir, ".gitignore");
1494
1494
  if (await pathExists(gitignorePath)) {
1495
- const content = await readFile6(gitignorePath, "utf-8");
1495
+ let content = await readFile6(gitignorePath, "utf-8");
1496
+ let modified = false;
1496
1497
  if (!content.includes("# FlyDocs")) {
1497
- const section = "\n# FlyDocs\n" + FLYDOCS_GITIGNORE_ENTRIES.join("\n") + "\n";
1498
- await appendFile(gitignorePath, section, "utf-8");
1498
+ const section = "\n# FlyDocs \u2014 local state\n" + FLYDOCS_GITIGNORE_ENTRIES.join("\n") + "\n";
1499
+ content += section;
1500
+ modified = true;
1501
+ }
1502
+ if (!content.includes("# FlyDocs \u2014 server-managed")) {
1503
+ const section = "\n# FlyDocs \u2014 server-managed (rebuilt by flydocs sync)\n" + SERVER_MANAGED_GITIGNORE_ENTRIES.join("\n") + "\n";
1504
+ content += section;
1505
+ modified = true;
1506
+ }
1507
+ if (modified) {
1508
+ await writeFile4(gitignorePath, content, "utf-8");
1499
1509
  printStatus("Added FlyDocs entries to .gitignore");
1500
1510
  }
1501
1511
  } else {
@@ -1507,33 +1517,55 @@ async function migrateGitignore(targetDir) {
1507
1517
  const gitignorePath = join11(targetDir, ".gitignore");
1508
1518
  if (!await pathExists(gitignorePath)) return;
1509
1519
  let content = await readFile6(gitignorePath, "utf-8");
1510
- for (const entry of FLYDOCS_GITIGNORE_ENTRIES) {
1520
+ let modified = false;
1521
+ content = migrateEntriesToSection(
1522
+ content,
1523
+ FLYDOCS_GITIGNORE_ENTRIES,
1524
+ "# FlyDocs"
1525
+ );
1526
+ if (!content.includes("# FlyDocs \u2014 server-managed")) {
1527
+ const section = "\n# FlyDocs \u2014 server-managed (rebuilt by flydocs sync)\n" + SERVER_MANAGED_GITIGNORE_ENTRIES.join("\n") + "\n";
1528
+ content += section;
1529
+ modified = true;
1530
+ } else {
1531
+ const before = content;
1532
+ content = migrateEntriesToSection(
1533
+ content,
1534
+ SERVER_MANAGED_GITIGNORE_ENTRIES,
1535
+ "# FlyDocs \u2014 server-managed"
1536
+ );
1537
+ if (content !== before) modified = true;
1538
+ }
1539
+ if (!modified) {
1540
+ const original = await readFile6(gitignorePath, "utf-8");
1541
+ if (content !== original) modified = true;
1542
+ }
1543
+ if (modified) {
1544
+ await writeFile4(gitignorePath, content, "utf-8");
1545
+ printStatus("Updated .gitignore with new FlyDocs entries");
1546
+ }
1547
+ }
1548
+ function migrateEntriesToSection(content, entries, sectionMarker) {
1549
+ for (const entry of entries) {
1511
1550
  if (content.includes(entry)) continue;
1512
- if (content.includes("# FlyDocs")) {
1513
- const lines = content.split("\n");
1514
- const flyDocsIdx = lines.findIndex((l) => l.trim() === "# FlyDocs");
1515
- if (flyDocsIdx !== -1) {
1516
- let insertIdx = flyDocsIdx + 1;
1517
- while (insertIdx < lines.length && lines[insertIdx].trim() !== "" && !lines[insertIdx].startsWith("#")) {
1518
- insertIdx++;
1519
- }
1520
- lines.splice(insertIdx, 0, entry);
1521
- content = lines.join("\n");
1522
- await writeFile4(gitignorePath, content, "utf-8");
1523
- } else {
1524
- content += `
1525
- ${entry}
1526
- `;
1527
- await writeFile4(gitignorePath, content, "utf-8");
1551
+ const lines = content.split("\n");
1552
+ const sectionIdx = lines.findIndex(
1553
+ (l) => l.trim().startsWith(sectionMarker)
1554
+ );
1555
+ if (sectionIdx !== -1) {
1556
+ let insertIdx = sectionIdx + 1;
1557
+ while (insertIdx < lines.length && lines[insertIdx].trim() !== "" && !lines[insertIdx].startsWith("#")) {
1558
+ insertIdx++;
1528
1559
  }
1560
+ lines.splice(insertIdx, 0, entry);
1561
+ content = lines.join("\n");
1529
1562
  } else {
1530
1563
  content += `
1531
1564
  ${entry}
1532
1565
  `;
1533
- await writeFile4(gitignorePath, content, "utf-8");
1534
1566
  }
1535
- printStatus(`Added ${entry} to .gitignore`);
1536
1567
  }
1568
+ return content;
1537
1569
  }
1538
1570
  async function ensurePlatformIgnores(targetDir) {
1539
1571
  for (const filename of PLATFORM_IGNORE_FILES) {
@@ -1546,7 +1578,7 @@ async function ensurePlatformIgnores(targetDir) {
1546
1578
  printStatus(`Added FlyDocs exclusions to ${filename}`);
1547
1579
  }
1548
1580
  }
1549
- var PLATFORM_IGNORE_FILES, FLYDOCS_DEPLOY_EXCLUSIONS, FLYDOCS_GITIGNORE_ENTRIES, FULL_GITIGNORE_TEMPLATE;
1581
+ var PLATFORM_IGNORE_FILES, FLYDOCS_DEPLOY_EXCLUSIONS, FLYDOCS_GITIGNORE_ENTRIES, SERVER_MANAGED_GITIGNORE_ENTRIES, FULL_GITIGNORE_TEMPLATE;
1550
1582
  var init_gitignore = __esm({
1551
1583
  "src/lib/gitignore.ts"() {
1552
1584
  "use strict";
@@ -1582,12 +1614,23 @@ var init_gitignore = __esm({
1582
1614
  ".flydocs/session/",
1583
1615
  "flydocs/context/graph.json"
1584
1616
  ];
1617
+ SERVER_MANAGED_GITIGNORE_ENTRIES = [
1618
+ ".claude/skills/",
1619
+ ".claude/commands/",
1620
+ ".claude/hooks/",
1621
+ ".claude/agents/",
1622
+ ".claude/settings.json",
1623
+ ".claude/CLAUDE.md",
1624
+ ".cursor/rules/",
1625
+ ".cursor/commands/",
1626
+ "AGENTS.md"
1627
+ ];
1585
1628
  FULL_GITIGNORE_TEMPLATE = `# Environment
1586
1629
  .env
1587
1630
  .env.local
1588
1631
  .env.*.local
1589
1632
 
1590
- # FlyDocs
1633
+ # FlyDocs \u2014 local state
1591
1634
  .flydocs/session.json
1592
1635
  .flydocs/logs/
1593
1636
  .flydocs/backup-*/
@@ -1597,6 +1640,17 @@ var init_gitignore = __esm({
1597
1640
  .flydocs/session/
1598
1641
  flydocs/context/graph.json
1599
1642
 
1643
+ # FlyDocs \u2014 server-managed (rebuilt by flydocs sync)
1644
+ .claude/skills/
1645
+ .claude/commands/
1646
+ .claude/hooks/
1647
+ .claude/agents/
1648
+ .claude/settings.json
1649
+ .claude/CLAUDE.md
1650
+ .cursor/rules/
1651
+ .cursor/commands/
1652
+ AGENTS.md
1653
+
1600
1654
  # Dependencies
1601
1655
  node_modules/
1602
1656
 
@@ -2973,6 +3027,65 @@ async function fetchTemplates(apiKey, sinceVersion, workspaceId) {
2973
3027
  }
2974
3028
  return await response.json();
2975
3029
  }
3030
+ async function fetchArtifacts(apiKey, sinceVersion) {
3031
+ const baseUrl = resolveRelayUrl();
3032
+ const response = await fetch(`${baseUrl}/artifacts?since=${sinceVersion}`, {
3033
+ method: "GET",
3034
+ headers: headers(apiKey),
3035
+ signal: AbortSignal.timeout(3e4)
3036
+ });
3037
+ if (!response.ok) {
3038
+ const body = await response.text().catch(() => "");
3039
+ throw new RelayError(
3040
+ `artifacts fetch failed (${response.status})`,
3041
+ response.status,
3042
+ body
3043
+ );
3044
+ }
3045
+ return await response.json();
3046
+ }
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
+ async function triggerScan(apiKey, repoName, options) {
3065
+ const baseUrl = resolveRelayUrl();
3066
+ const body = {
3067
+ repoName,
3068
+ options: {
3069
+ regenerate: options.regenerate ?? false,
3070
+ includeIdeConfigs: options.includeIdeConfigs ?? false
3071
+ }
3072
+ };
3073
+ const response = await fetch(`${baseUrl}/ai/generate-context`, {
3074
+ method: "POST",
3075
+ headers: headers(apiKey, options.workspaceId),
3076
+ body: JSON.stringify(body),
3077
+ signal: AbortSignal.timeout(6e4)
3078
+ });
3079
+ if (!response.ok) {
3080
+ const responseBody = await response.text().catch(() => "");
3081
+ throw new RelayError(
3082
+ `ai/generate-context failed (${response.status})`,
3083
+ response.status,
3084
+ responseBody
3085
+ );
3086
+ }
3087
+ return await response.json();
3088
+ }
2976
3089
  var DEFAULT_RELAY_URL, RelayError;
2977
3090
  var init_relay_client = __esm({
2978
3091
  "src/lib/relay-client.ts"() {
@@ -2989,6 +3102,205 @@ var init_relay_client = __esm({
2989
3102
  }
2990
3103
  });
2991
3104
 
3105
+ // src/lib/managed-paths.ts
3106
+ import { join as join18 } from "path";
3107
+ import { readdir as readdir4, rm as rm4, unlink } from "fs/promises";
3108
+ function isSkipped(path, skipPaths) {
3109
+ return skipPaths.some((skip) => path === skip || path.startsWith(skip));
3110
+ }
3111
+ async function wipeManagedPaths(targetDir, skipPaths = []) {
3112
+ let deleted = 0;
3113
+ let skipped = 0;
3114
+ for (const dir of MANAGED_DIRECTORIES) {
3115
+ const fullPath = join18(targetDir, dir);
3116
+ if (isSkipped(dir + "/", skipPaths) || isSkipped(dir, skipPaths)) {
3117
+ if (await pathExists(fullPath)) {
3118
+ skipped++;
3119
+ }
3120
+ continue;
3121
+ }
3122
+ if (await pathExists(fullPath)) {
3123
+ await rm4(fullPath, { recursive: true, force: true });
3124
+ deleted++;
3125
+ }
3126
+ }
3127
+ for (const file of MANAGED_FILES) {
3128
+ if (isSkipped(file, skipPaths)) {
3129
+ skipped++;
3130
+ continue;
3131
+ }
3132
+ const fullPath = join18(targetDir, file);
3133
+ if (await pathExists(fullPath)) {
3134
+ await unlink(fullPath);
3135
+ deleted++;
3136
+ }
3137
+ }
3138
+ const skillsDir = join18(targetDir, ".claude", "skills");
3139
+ if (await pathExists(skillsDir)) {
3140
+ const entries = await readdir4(skillsDir, { withFileTypes: true });
3141
+ for (const entry of entries) {
3142
+ if (!entry.isDirectory()) continue;
3143
+ if (!entry.name.startsWith(CORE_SKILL_PREFIX)) continue;
3144
+ const skillPath = `.claude/skills/${entry.name}/`;
3145
+ if (isSkipped(skillPath, skipPaths)) {
3146
+ skipped++;
3147
+ continue;
3148
+ }
3149
+ await rm4(join18(skillsDir, entry.name), { recursive: true, force: true });
3150
+ deleted++;
3151
+ }
3152
+ }
3153
+ if (deleted > 0) {
3154
+ printStatus(`Wiped ${deleted} managed path(s)`);
3155
+ }
3156
+ if (skipped > 0) {
3157
+ printInfo(`Preserved ${skipped} path(s) via skipPaths override`);
3158
+ }
3159
+ return { deleted, skipped };
3160
+ }
3161
+ var MANAGED_DIRECTORIES, MANAGED_FILES, CORE_SKILL_PREFIX;
3162
+ var init_managed_paths = __esm({
3163
+ "src/lib/managed-paths.ts"() {
3164
+ "use strict";
3165
+ init_fs_ops();
3166
+ init_ui();
3167
+ MANAGED_DIRECTORIES = [
3168
+ ".claude/hooks",
3169
+ ".claude/commands",
3170
+ ".claude/agents",
3171
+ ".cursor/rules",
3172
+ ".cursor/commands",
3173
+ ".cursor/agents"
3174
+ ];
3175
+ MANAGED_FILES = [
3176
+ ".claude/settings.json",
3177
+ ".claude/CLAUDE.md",
3178
+ ".cursor/hooks.json",
3179
+ "AGENTS.md"
3180
+ ];
3181
+ CORE_SKILL_PREFIX = "flydocs-";
3182
+ }
3183
+ });
3184
+
3185
+ // src/lib/artifacts.ts
3186
+ import { mkdir as mkdir9, writeFile as writeFile11 } from "fs/promises";
3187
+ import { join as join19, dirname as dirname3 } from "path";
3188
+ function isPathSkipped(artifactPath, skipPaths) {
3189
+ return skipPaths.some(
3190
+ (skip) => artifactPath === skip || artifactPath.startsWith(skip)
3191
+ );
3192
+ }
3193
+ async function syncArtifacts(targetDir, apiKey, workspaceId, currentVersion, skipPaths = []) {
3194
+ const response = await fetchArtifacts(apiKey, currentVersion);
3195
+ if (response.artifacts.length === 0) {
3196
+ return { written: 0, skipped: 0, version: currentVersion };
3197
+ }
3198
+ let written = 0;
3199
+ let skipped = 0;
3200
+ for (const artifact of response.artifacts) {
3201
+ if (skipPaths.length > 0 && isPathSkipped(artifact.path, skipPaths)) {
3202
+ skipped++;
3203
+ continue;
3204
+ }
3205
+ await writeArtifact(targetDir, artifact);
3206
+ written++;
3207
+ }
3208
+ printStatus(
3209
+ `Synced ${written} artifact(s) (v${currentVersion} \u2192 v${response.version})`
3210
+ );
3211
+ if (skipped > 0) {
3212
+ printInfo(`Skipped ${skipped} artifact(s) via artifactOverrides.skipPaths`);
3213
+ }
3214
+ return { written, skipped, version: response.version };
3215
+ }
3216
+ async function syncAgentConfigs(targetDir, apiKey, workspaceId, skipPaths = []) {
3217
+ const response = await fetchAgentConfigs(apiKey, workspaceId);
3218
+ let written = 0;
3219
+ for (const file of response.files) {
3220
+ if (skipPaths.length > 0 && isPathSkipped(file.path, skipPaths)) {
3221
+ continue;
3222
+ }
3223
+ const filePath = join19(targetDir, file.path);
3224
+ await mkdir9(dirname3(filePath), { recursive: true });
3225
+ await writeFile11(filePath, file.content, "utf-8");
3226
+ written++;
3227
+ }
3228
+ if (written > 0) {
3229
+ printStatus(`Wrote ${written} agent config(s) (CLAUDE.md, AGENTS.md)`);
3230
+ }
3231
+ return written;
3232
+ }
3233
+ async function writeArtifact(targetDir, artifact) {
3234
+ const filePath = join19(targetDir, artifact.path);
3235
+ await mkdir9(dirname3(filePath), { recursive: true });
3236
+ await writeFile11(filePath, artifact.content, "utf-8");
3237
+ }
3238
+ async function syncAllArtifacts(targetDir, apiKey, workspaceId, currentArtifactVersion, skipPaths = []) {
3239
+ const changes = [];
3240
+ let artifactVersion = currentArtifactVersion;
3241
+ const { deleted, skipped } = await wipeManagedPaths(targetDir, skipPaths);
3242
+ if (deleted > 0) {
3243
+ changes.push(`Wiped ${deleted} managed path(s)`);
3244
+ }
3245
+ if (skipped > 0) {
3246
+ changes.push(`Preserved ${skipped} path(s) (skipPaths override)`);
3247
+ }
3248
+ try {
3249
+ const result = await syncArtifacts(
3250
+ targetDir,
3251
+ apiKey,
3252
+ workspaceId,
3253
+ currentArtifactVersion,
3254
+ skipPaths
3255
+ );
3256
+ if (result.written > 0) {
3257
+ changes.push(`Synced ${result.written} artifact(s)`);
3258
+ artifactVersion = result.version;
3259
+ }
3260
+ if (result.skipped > 0) {
3261
+ changes.push(
3262
+ `Skipped ${result.skipped} artifact(s) (skipPaths override)`
3263
+ );
3264
+ }
3265
+ } catch (err) {
3266
+ if (err instanceof RelayError) {
3267
+ printWarning(
3268
+ `Could not fetch artifacts (${err.status}). Will retry on next sync.`
3269
+ );
3270
+ } else {
3271
+ printWarning("Artifact sync failed. Will retry on next sync.");
3272
+ }
3273
+ }
3274
+ try {
3275
+ const configCount = await syncAgentConfigs(
3276
+ targetDir,
3277
+ apiKey,
3278
+ workspaceId,
3279
+ skipPaths
3280
+ );
3281
+ if (configCount > 0) {
3282
+ changes.push(`Wrote ${configCount} agent config(s)`);
3283
+ }
3284
+ } catch (err) {
3285
+ if (err instanceof RelayError) {
3286
+ printWarning(
3287
+ `Could not fetch agent configs (${err.status}). Will retry on next sync.`
3288
+ );
3289
+ } else {
3290
+ printWarning("Agent config sync failed. Will retry on next sync.");
3291
+ }
3292
+ }
3293
+ return { artifactVersion, changes };
3294
+ }
3295
+ var init_artifacts = __esm({
3296
+ "src/lib/artifacts.ts"() {
3297
+ "use strict";
3298
+ init_ui();
3299
+ init_relay_client();
3300
+ init_managed_paths();
3301
+ }
3302
+ });
3303
+
2992
3304
  // src/commands/cleanup.ts
2993
3305
  var cleanup_exports = {};
2994
3306
  __export(cleanup_exports, {
@@ -2998,11 +3310,11 @@ __export(cleanup_exports, {
2998
3310
  });
2999
3311
  import { defineCommand as defineCommand2 } from "citty";
3000
3312
  import pc7 from "picocolors";
3001
- import { join as join18 } from "path";
3002
- import { readFile as readFile13, writeFile as writeFile11, rm as rm4 } from "fs/promises";
3313
+ import { join as join20 } from "path";
3314
+ import { readFile as readFile13, writeFile as writeFile12, rm as rm5 } from "fs/promises";
3003
3315
  async function scanArtifacts(targetDir) {
3004
3316
  const actions = [];
3005
- const localMePath = join18(targetDir, ".flydocs", "me.json");
3317
+ const localMePath = join20(targetDir, ".flydocs", "me.json");
3006
3318
  if (await pathExists(localMePath) && await pathExists(globalMePath())) {
3007
3319
  actions.push({
3008
3320
  description: "Remove .flydocs/me.json (migrated to ~/.flydocs/me.json)",
@@ -3010,7 +3322,7 @@ async function scanArtifacts(targetDir) {
3010
3322
  type: "file"
3011
3323
  });
3012
3324
  }
3013
- const validationCachePath = join18(
3325
+ const validationCachePath = join20(
3014
3326
  targetDir,
3015
3327
  ".flydocs",
3016
3328
  "validation-cache.json"
@@ -3024,7 +3336,7 @@ async function scanArtifacts(targetDir) {
3024
3336
  }
3025
3337
  const hasGlobalCreds = await pathExists(credentialsPath());
3026
3338
  for (const envFile of [".env", ".env.local"]) {
3027
- const envPath = join18(targetDir, envFile);
3339
+ const envPath = join20(targetDir, envFile);
3028
3340
  if (!await pathExists(envPath)) continue;
3029
3341
  const content = await readFile13(envPath, "utf-8");
3030
3342
  const lines = content.split("\n");
@@ -3049,7 +3361,7 @@ async function scanArtifacts(targetDir) {
3049
3361
  try {
3050
3362
  const config = await readAnyConfig(targetDir);
3051
3363
  const rawContent = await readFile13(
3052
- join18(targetDir, ".flydocs", "config.json"),
3364
+ join20(targetDir, ".flydocs", "config.json"),
3053
3365
  "utf-8"
3054
3366
  );
3055
3367
  const raw = JSON.parse(rawContent);
@@ -3058,7 +3370,7 @@ async function scanArtifacts(targetDir) {
3058
3370
  if (field in raw) {
3059
3371
  actions.push({
3060
3372
  description: `Remove ghost field "${field}" from config.json`,
3061
- path: join18(targetDir, ".flydocs", "config.json"),
3373
+ path: join20(targetDir, ".flydocs", "config.json"),
3062
3374
  type: "field"
3063
3375
  });
3064
3376
  }
@@ -3077,7 +3389,7 @@ async function scanArtifacts(targetDir) {
3077
3389
  if (field in raw) {
3078
3390
  actions.push({
3079
3391
  description: `Remove v1 field "${field}" from config.json (server-owned in v2)`,
3080
- path: join18(targetDir, ".flydocs", "config.json"),
3392
+ path: join20(targetDir, ".flydocs", "config.json"),
3081
3393
  type: "field"
3082
3394
  });
3083
3395
  }
@@ -3092,7 +3404,7 @@ async function executeCleanup(targetDir, actions) {
3092
3404
  const lineRemovals = actions.filter((a) => a.type === "line");
3093
3405
  const fieldRemovals = actions.filter((a) => a.type === "field");
3094
3406
  for (const action of fileRemovals) {
3095
- await rm4(action.path, { force: true });
3407
+ await rm5(action.path, { force: true });
3096
3408
  }
3097
3409
  const envFiles = /* @__PURE__ */ new Map();
3098
3410
  for (const action of lineRemovals) {
@@ -3110,13 +3422,13 @@ async function executeCleanup(targetDir, actions) {
3110
3422
  (l) => l.trim().length > 0 && !l.trim().startsWith("#")
3111
3423
  );
3112
3424
  if (!hasContent) {
3113
- await rm4(envPath, { force: true });
3425
+ await rm5(envPath, { force: true });
3114
3426
  } else {
3115
- await writeFile11(envPath, filtered.join("\n"), "utf-8");
3427
+ await writeFile12(envPath, filtered.join("\n"), "utf-8");
3116
3428
  }
3117
3429
  }
3118
3430
  if (fieldRemovals.length > 0) {
3119
- const configPath = join18(targetDir, ".flydocs", "config.json");
3431
+ const configPath = join20(targetDir, ".flydocs", "config.json");
3120
3432
  const content = await readFile13(configPath, "utf-8");
3121
3433
  const config = JSON.parse(content);
3122
3434
  for (const action of fieldRemovals) {
@@ -3125,7 +3437,7 @@ async function executeCleanup(targetDir, actions) {
3125
3437
  delete config[match[1]];
3126
3438
  }
3127
3439
  }
3128
- await writeFile11(
3440
+ await writeFile12(
3129
3441
  configPath,
3130
3442
  JSON.stringify(config, null, 2) + "\n",
3131
3443
  "utf-8"
@@ -3163,7 +3475,7 @@ var init_cleanup = __esm({
3163
3475
  console.log(` ${pc7.bold("FlyDocs Cleanup")}`);
3164
3476
  console.log();
3165
3477
  const hasConfig = await pathExists(
3166
- join18(targetDir, ".flydocs", "config.json")
3478
+ join20(targetDir, ".flydocs", "config.json")
3167
3479
  );
3168
3480
  if (!hasConfig) {
3169
3481
  printError("No .flydocs/config.json found. Run flydocs init first.");
@@ -3206,33 +3518,106 @@ var init_cleanup = __esm({
3206
3518
  }
3207
3519
  });
3208
3520
 
3521
+ // src/lib/ide-archive.ts
3522
+ import { readFile as readFile14, writeFile as writeFile13, mkdir as mkdir10, readdir as readdir5 } from "fs/promises";
3523
+ import { join as join21, basename } from "path";
3524
+ async function archiveIdeConfigs(targetDir) {
3525
+ const archived = [];
3526
+ for (const cfg of SINGLE_FILE_CONFIGS) {
3527
+ const absSource = join21(targetDir, cfg.source);
3528
+ if (!await pathExists(absSource)) continue;
3529
+ const content = await readFile14(absSource, "utf-8");
3530
+ const absDest = join21(targetDir, cfg.archive);
3531
+ await mkdir10(join21(absDest, ".."), { recursive: true });
3532
+ await writeFile13(absDest, content, "utf-8");
3533
+ archived.push({
3534
+ sourcePath: cfg.source,
3535
+ archivePath: cfg.archive,
3536
+ content
3537
+ });
3538
+ printStatus(`Archived ${cfg.source}`);
3539
+ }
3540
+ const cursorRulesDir = join21(targetDir, ".cursor", "rules");
3541
+ if (await pathExists(cursorRulesDir)) {
3542
+ let entries;
3543
+ try {
3544
+ entries = await readdir5(cursorRulesDir);
3545
+ } catch {
3546
+ entries = [];
3547
+ }
3548
+ const mdFiles = entries.filter((f) => f.endsWith(".md"));
3549
+ if (mdFiles.length > 0) {
3550
+ const archiveSubdir = join21(
3551
+ targetDir,
3552
+ "flydocs",
3553
+ "knowledge",
3554
+ "archived",
3555
+ "cursor-rules"
3556
+ );
3557
+ await mkdir10(archiveSubdir, { recursive: true });
3558
+ for (const file of mdFiles) {
3559
+ const absSource = join21(cursorRulesDir, file);
3560
+ const content = await readFile14(absSource, "utf-8");
3561
+ const archivePath = `flydocs/knowledge/archived/cursor-rules/${basename(file)}`;
3562
+ const absDest = join21(targetDir, archivePath);
3563
+ await writeFile13(absDest, content, "utf-8");
3564
+ archived.push({
3565
+ sourcePath: `.cursor/rules/${file}`,
3566
+ archivePath,
3567
+ content
3568
+ });
3569
+ printStatus(`Archived .cursor/rules/${file}`);
3570
+ }
3571
+ }
3572
+ }
3573
+ return archived;
3574
+ }
3575
+ var SINGLE_FILE_CONFIGS;
3576
+ var init_ide_archive = __esm({
3577
+ "src/lib/ide-archive.ts"() {
3578
+ "use strict";
3579
+ init_fs_ops();
3580
+ init_ui();
3581
+ SINGLE_FILE_CONFIGS = [
3582
+ {
3583
+ source: ".cursorrules",
3584
+ archive: "flydocs/knowledge/archived/cursorrules.md"
3585
+ },
3586
+ {
3587
+ source: ".claude/CLAUDE.md",
3588
+ archive: "flydocs/knowledge/archived/claude-md.md"
3589
+ }
3590
+ ];
3591
+ }
3592
+ });
3593
+
3209
3594
  // src/lib/workspace.ts
3210
- import { readFile as readFile14, writeFile as writeFile12, readdir as readdir4, stat as stat2 } from "fs/promises";
3211
- import { join as join19, dirname as dirname3, relative, resolve as resolve3 } from "path";
3595
+ import { readFile as readFile15, writeFile as writeFile14, readdir as readdir6, stat as stat2 } from "fs/promises";
3596
+ import { join as join22, dirname as dirname4, relative, resolve as resolve3 } from "path";
3212
3597
  async function readWorkspaceFile(dir) {
3213
- const filePath = join19(dir, WORKSPACE_FILENAME);
3598
+ const filePath = join22(dir, WORKSPACE_FILENAME);
3214
3599
  if (!await pathExists(filePath)) return null;
3215
- const content = await readFile14(filePath, "utf-8");
3600
+ const content = await readFile15(filePath, "utf-8");
3216
3601
  const parsed = JSON.parse(content);
3217
3602
  validateWorkspaceFile(parsed);
3218
3603
  return parsed;
3219
3604
  }
3220
3605
  async function writeWorkspaceFile(dir, workspace) {
3221
- const filePath = join19(dir, WORKSPACE_FILENAME);
3606
+ const filePath = join22(dir, WORKSPACE_FILENAME);
3222
3607
  const content = JSON.stringify(workspace, null, 2) + "\n";
3223
- await writeFile12(filePath, content, "utf-8");
3608
+ await writeFile14(filePath, content, "utf-8");
3224
3609
  }
3225
3610
  async function detectSiblingRepos(parentDir) {
3226
3611
  const repos = {};
3227
- const entries = await readdir4(parentDir);
3612
+ const entries = await readdir6(parentDir);
3228
3613
  for (const entry of entries) {
3229
3614
  if (entry.startsWith(".")) continue;
3230
- const entryPath = join19(parentDir, entry);
3615
+ const entryPath = join22(parentDir, entry);
3231
3616
  const entryStat = await stat2(entryPath).catch(() => null);
3232
3617
  if (!entryStat?.isDirectory()) continue;
3233
- const hasGit = await pathExists(join19(entryPath, ".git"));
3618
+ const hasGit = await pathExists(join22(entryPath, ".git"));
3234
3619
  const hasFlydocs = await pathExists(
3235
- join19(entryPath, ".flydocs", "config.json")
3620
+ join22(entryPath, ".flydocs", "config.json")
3236
3621
  );
3237
3622
  if (hasGit || hasFlydocs) {
3238
3623
  repos[entry] = { path: `./${entry}` };
@@ -3247,10 +3632,10 @@ async function readSiblingDescriptors(workspaceDir, repos) {
3247
3632
  const descriptors = [];
3248
3633
  for (const [name, entry] of Object.entries(repos)) {
3249
3634
  const repoDir = resolve3(workspaceDir, entry.path);
3250
- const serviceJsonPath = join19(repoDir, "flydocs", "context", "service.json");
3635
+ const serviceJsonPath = join22(repoDir, "flydocs", "context", "service.json");
3251
3636
  if (!await pathExists(serviceJsonPath)) continue;
3252
3637
  try {
3253
- const content = await readFile14(serviceJsonPath, "utf-8");
3638
+ const content = await readFile15(serviceJsonPath, "utf-8");
3254
3639
  const descriptor = JSON.parse(content);
3255
3640
  descriptors.push({ name, path: entry.path, descriptor });
3256
3641
  } catch {
@@ -3353,96 +3738,290 @@ var init_workspace = __esm({
3353
3738
  }
3354
3739
  });
3355
3740
 
3356
- // src/commands/init.ts
3357
- var init_exports = {};
3358
- __export(init_exports, {
3359
- default: () => init_default
3741
+ // src/commands/scan.ts
3742
+ var scan_exports = {};
3743
+ __export(scan_exports, {
3744
+ default: () => scan_default,
3745
+ runScan: () => runScan
3360
3746
  });
3361
3747
  import { defineCommand as defineCommand3 } from "citty";
3362
- import { text as text2, confirm as confirm3, select as select2, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
3748
+ import { spinner } from "@clack/prompts";
3363
3749
  import pc8 from "picocolors";
3364
- import { join as join20 } from "path";
3365
- import { mkdir as mkdir9, writeFile as writeFile13 } from "fs/promises";
3366
- import { execFile } from "child_process";
3367
- import { promisify } from "util";
3368
- async function resolveAndValidateKey(keyArg, targetDir) {
3369
- let resolved = await resolveApiKey(keyArg, targetDir);
3370
- if (!resolved) {
3371
- console.log(
3372
- ` ${pc8.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
3373
- );
3374
- console.log();
3375
- const keyInput = await text2({
3376
- message: "Enter your API key",
3377
- placeholder: "fdk_...",
3378
- validate(value) {
3379
- if (!value.trim()) return "API key is required";
3380
- if (detectKeyType(value.trim()) !== "relay") {
3381
- return "Key must start with fdk_ (FlyDocs API key)";
3382
- }
3383
- return void 0;
3384
- }
3385
- });
3386
- if (isCancel4(keyInput)) {
3387
- cancel3("Init cancelled.");
3388
- process.exit(0);
3389
- }
3390
- resolved = { key: keyInput.trim(), source: "prompt" };
3391
- } else {
3392
- printInfo(`Using key from ${resolved.source}`);
3393
- }
3394
- const apiKey = resolved.key;
3395
- printInfo("Validating API key...");
3750
+ import { join as join23 } from "path";
3751
+ import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
3752
+ async function resolveRepoName(targetDir, repoArg) {
3753
+ if (repoArg) return repoArg;
3396
3754
  try {
3397
- const result = await validateRelayKey(apiKey);
3398
- if (!result.valid) {
3399
- printError("Invalid API key. Check your key and try again.");
3400
- process.exit(1);
3755
+ const config = await readAnyConfig(targetDir);
3756
+ if (!isConfigV2(config) && config.sourceRepo) {
3757
+ return config.sourceRepo;
3401
3758
  }
3402
- printStatus(`Authenticated with ${pc8.bold(result.org)}`);
3403
3759
  } catch {
3404
- printError(
3405
- "Could not reach FlyDocs API. Check your network and try again."
3760
+ }
3761
+ try {
3762
+ const { execFile: execFile2 } = await import("child_process");
3763
+ const { promisify: promisify2 } = await import("util");
3764
+ const execFileAsync2 = promisify2(execFile2);
3765
+ const { stdout } = await execFileAsync2(
3766
+ "git",
3767
+ ["-C", targetDir, "remote", "get-url", "origin"],
3768
+ { timeout: 5e3 }
3406
3769
  );
3407
- console.log(
3408
- ` ${pc8.dim("flydocs init requires server access. For offline use, run flydocs install instead.")}`
3770
+ const url = stdout.trim();
3771
+ const match = url.match(/[/:]([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
3772
+ if (match) return match[1];
3773
+ } catch {
3774
+ }
3775
+ return null;
3776
+ }
3777
+ async function writeScanResults(targetDir, response) {
3778
+ const written = [];
3779
+ const contextDir = join23(targetDir, "flydocs", "context");
3780
+ await mkdir11(contextDir, { recursive: true });
3781
+ if (response.projectMd) {
3782
+ const projectMdPath = join23(contextDir, "project.md");
3783
+ await writeFile15(projectMdPath, response.projectMd, "utf-8");
3784
+ written.push("flydocs/context/project.md");
3785
+ }
3786
+ if (response.serviceJson) {
3787
+ const serviceJsonPath = join23(contextDir, "service.json");
3788
+ await writeFile15(
3789
+ serviceJsonPath,
3790
+ JSON.stringify(response.serviceJson, null, 2) + "\n",
3791
+ "utf-8"
3409
3792
  );
3410
- process.exit(1);
3793
+ written.push("flydocs/context/service.json");
3411
3794
  }
3412
- const workspaces = await fetchWorkspaces(apiKey);
3413
- let workspaceId;
3414
- if (workspaces.length === 0) {
3415
- printError("No workspaces found. Create one at app.flydocs.ai first.");
3416
- process.exit(1);
3417
- } else if (workspaces.length === 1) {
3418
- workspaceId = workspaces[0].id;
3419
- printStatus(`Workspace: ${pc8.bold(workspaces[0].name)}`);
3420
- } else {
3421
- const choice = await select2({
3422
- message: "Select workspace",
3423
- options: workspaces.map((ws) => ({
3424
- value: ws.id,
3425
- label: ws.name
3426
- }))
3795
+ return written;
3796
+ }
3797
+ async function runScan(apiKey, workspaceId, targetDir, repoName, regenerate) {
3798
+ const s = spinner();
3799
+ s.start("Running AI scan (this may take 5-20 seconds)...");
3800
+ try {
3801
+ const response = await triggerScan(apiKey, repoName, {
3802
+ workspaceId,
3803
+ regenerate
3427
3804
  });
3428
- if (isCancel4(choice)) {
3429
- cancel3("Init cancelled.");
3430
- process.exit(0);
3805
+ if (!response.success) {
3806
+ s.stop("Scan returned unsuccessful", 1);
3807
+ return { success: false, written: [] };
3431
3808
  }
3432
- workspaceId = choice;
3809
+ s.stop("Scan complete");
3810
+ const written = await writeScanResults(targetDir, response);
3811
+ return { success: true, written };
3812
+ } catch (err) {
3813
+ if (err instanceof RelayError) {
3814
+ if (err.status === 403) {
3815
+ s.stop("Permission denied", 1);
3816
+ printError(
3817
+ "Admin role required. Only workspace admins can trigger AI scans."
3818
+ );
3819
+ } else {
3820
+ s.stop("Scan failed", 1);
3821
+ printError(`Server error: ${err.message}`);
3822
+ }
3823
+ } else {
3824
+ s.stop("Scan failed", 1);
3825
+ printError("Could not reach server. Check your network and try again.");
3826
+ }
3827
+ return { success: false, written: [] };
3433
3828
  }
3434
- await writeGlobalCredential({
3435
- apiKey,
3436
- workspaceId,
3437
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3438
- lastValidated: (/* @__PURE__ */ new Date()).toISOString()
3829
+ }
3830
+ var scan_default;
3831
+ var init_scan = __esm({
3832
+ "src/commands/scan.ts"() {
3833
+ "use strict";
3834
+ init_ui();
3835
+ init_global_config();
3836
+ init_config();
3837
+ init_types();
3838
+ init_relay_client();
3839
+ scan_default = defineCommand3({
3840
+ meta: {
3841
+ name: "scan",
3842
+ description: "Trigger server-side AI scan to generate project context"
3843
+ },
3844
+ args: {
3845
+ force: {
3846
+ type: "boolean",
3847
+ description: "Regenerate context even if it already exists",
3848
+ default: false
3849
+ },
3850
+ path: {
3851
+ type: "string",
3852
+ description: "Path to project directory"
3853
+ },
3854
+ repo: {
3855
+ type: "string",
3856
+ description: "Override repo name (owner/repo format)"
3857
+ }
3858
+ },
3859
+ async run({ args }) {
3860
+ const targetDir = args.path ?? process.cwd();
3861
+ console.log();
3862
+ printInfo("FlyDocs AI Scan");
3863
+ console.log();
3864
+ const resolved = await resolveApiKey(void 0, targetDir);
3865
+ if (!resolved) {
3866
+ printError(
3867
+ "No API key found. Run `flydocs auth` or `flydocs init` first."
3868
+ );
3869
+ process.exit(1);
3870
+ }
3871
+ const apiKey = resolved.key;
3872
+ const cred = await readGlobalCredential();
3873
+ const workspaceId = cred?.workspaceId;
3874
+ if (!workspaceId) {
3875
+ printError(
3876
+ "No workspace ID found. Run `flydocs init` to set up your workspace."
3877
+ );
3878
+ process.exit(1);
3879
+ }
3880
+ printInfo("Checking permissions...");
3881
+ try {
3882
+ const configResponse = await fetchConfigV2(apiKey, { workspaceId });
3883
+ if (configResponse.identity?.role !== "admin") {
3884
+ printError(
3885
+ "Admin role required. Only workspace admins can trigger AI scans."
3886
+ );
3887
+ console.log(
3888
+ ` ${pc8.dim("Your role: " + (configResponse.identity?.role ?? "unknown"))}`
3889
+ );
3890
+ console.log(
3891
+ ` ${pc8.dim("Ask a workspace admin to run this command or promote your role.")}`
3892
+ );
3893
+ process.exit(1);
3894
+ }
3895
+ } catch (err) {
3896
+ if (err instanceof RelayError) {
3897
+ printError(`Could not verify permissions: ${err.message}`);
3898
+ } else {
3899
+ printError("Could not reach server. Check your network.");
3900
+ }
3901
+ process.exit(1);
3902
+ }
3903
+ const repoName = await resolveRepoName(targetDir, args.repo);
3904
+ if (!repoName) {
3905
+ printError(
3906
+ "Could not determine repo name. Use --repo owner/name to specify it."
3907
+ );
3908
+ process.exit(1);
3909
+ }
3910
+ printInfo(`Repo: ${pc8.bold(repoName)}`);
3911
+ const { success, written } = await runScan(
3912
+ apiKey,
3913
+ workspaceId,
3914
+ targetDir,
3915
+ repoName,
3916
+ args.force
3917
+ );
3918
+ if (success) {
3919
+ console.log();
3920
+ for (const file of written) {
3921
+ printStatus(`Wrote ${file}`);
3922
+ }
3923
+ if (written.length === 0) {
3924
+ printWarning("Scan completed but no context files were returned.");
3925
+ }
3926
+ console.log();
3927
+ } else {
3928
+ process.exit(1);
3929
+ }
3930
+ }
3931
+ });
3932
+ }
3933
+ });
3934
+
3935
+ // src/commands/init.ts
3936
+ var init_exports = {};
3937
+ __export(init_exports, {
3938
+ default: () => init_default
3939
+ });
3940
+ import { defineCommand as defineCommand4 } from "citty";
3941
+ import { text as text2, confirm as confirm3, select as select2, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
3942
+ import pc9 from "picocolors";
3943
+ import { join as join24 } from "path";
3944
+ import { mkdir as mkdir12, writeFile as writeFile16 } from "fs/promises";
3945
+ import { execFile } from "child_process";
3946
+ import { promisify } from "util";
3947
+ async function resolveAndValidateKey(keyArg, targetDir) {
3948
+ let resolved = await resolveApiKey(keyArg, targetDir);
3949
+ if (!resolved) {
3950
+ console.log(
3951
+ ` ${pc9.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
3952
+ );
3953
+ console.log();
3954
+ const keyInput = await text2({
3955
+ message: "Enter your API key",
3956
+ placeholder: "fdk_...",
3957
+ validate(value) {
3958
+ if (!value.trim()) return "API key is required";
3959
+ if (detectKeyType(value.trim()) !== "relay") {
3960
+ return "Key must start with fdk_ (FlyDocs API key)";
3961
+ }
3962
+ return void 0;
3963
+ }
3964
+ });
3965
+ if (isCancel4(keyInput)) {
3966
+ cancel3("Init cancelled.");
3967
+ process.exit(0);
3968
+ }
3969
+ resolved = { key: keyInput.trim(), source: "prompt" };
3970
+ } else {
3971
+ printInfo(`Using key from ${resolved.source}`);
3972
+ }
3973
+ const apiKey = resolved.key;
3974
+ printInfo("Validating API key...");
3975
+ try {
3976
+ const result = await validateRelayKey(apiKey);
3977
+ if (!result.valid) {
3978
+ printError("Invalid API key. Check your key and try again.");
3979
+ process.exit(1);
3980
+ }
3981
+ printStatus(`Authenticated with ${pc9.bold(result.org)}`);
3982
+ } catch {
3983
+ printError(
3984
+ "Could not reach FlyDocs API. Check your network and try again."
3985
+ );
3986
+ console.log(
3987
+ ` ${pc9.dim("flydocs init requires server access. For offline use, run flydocs install instead.")}`
3988
+ );
3989
+ process.exit(1);
3990
+ }
3991
+ const workspaces = await fetchWorkspaces(apiKey);
3992
+ let workspaceId;
3993
+ if (workspaces.length === 0) {
3994
+ printError("No workspaces found. Create one at app.flydocs.ai first.");
3995
+ process.exit(1);
3996
+ } else if (workspaces.length === 1) {
3997
+ workspaceId = workspaces[0].id;
3998
+ printStatus(`Workspace: ${pc9.bold(workspaces[0].name)}`);
3999
+ } else {
4000
+ const choice = await select2({
4001
+ message: "Select workspace",
4002
+ options: workspaces.map((ws) => ({
4003
+ value: ws.id,
4004
+ label: ws.name
4005
+ }))
4006
+ });
4007
+ if (isCancel4(choice)) {
4008
+ cancel3("Init cancelled.");
4009
+ process.exit(0);
4010
+ }
4011
+ workspaceId = choice;
4012
+ }
4013
+ await writeGlobalCredential({
4014
+ apiKey,
4015
+ workspaceId,
4016
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4017
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
3439
4018
  });
3440
4019
  await checkCredentialPermissions();
3441
4020
  return { apiKey, workspaceId };
3442
4021
  }
3443
4022
  async function pullServerConfig(apiKey, targetDir, workspaceId, forceIncludeContext = false) {
3444
4023
  printInfo("Pulling config from server...");
3445
- const isFirstInit = forceIncludeContext || !await pathExists(join20(targetDir, ".flydocs", "config.json"));
4024
+ const isFirstInit = forceIncludeContext || !await pathExists(join24(targetDir, ".flydocs", "config.json"));
3446
4025
  try {
3447
4026
  const response = await fetchConfigV2(apiKey, {
3448
4027
  includeContext: isFirstInit,
@@ -3458,7 +4037,7 @@ async function pullServerConfig(apiKey, targetDir, workspaceId, forceIncludeCont
3458
4037
  printError(`Server error: ${err.message}`);
3459
4038
  if (err.status === 401) {
3460
4039
  console.log(
3461
- ` ${pc8.dim("Your API key may be revoked. Run flydocs auth with a new key.")}`
4040
+ ` ${pc9.dim("Your API key may be revoked. Run flydocs auth with a new key.")}`
3462
4041
  );
3463
4042
  }
3464
4043
  } else {
@@ -3467,10 +4046,11 @@ async function pullServerConfig(apiKey, targetDir, workspaceId, forceIncludeCont
3467
4046
  process.exit(1);
3468
4047
  }
3469
4048
  }
3470
- async function initSingleRepo(targetDir, apiKey, serverResponse) {
4049
+ async function initSingleRepo(targetDir, apiKey, serverResponse, options = {}) {
3471
4050
  const actions = [];
3472
4051
  const skipped = [];
3473
- await mkdir9(join20(targetDir, ".flydocs"), { recursive: true });
4052
+ const { childRepoMode = false } = options;
4053
+ await mkdir12(join24(targetDir, ".flydocs"), { recursive: true });
3474
4054
  const configWithHash = applyConfigHash(serverResponse.config);
3475
4055
  await writeConfig(targetDir, configWithHash);
3476
4056
  actions.push("Wrote .flydocs/config.json (from server)");
@@ -3483,18 +4063,18 @@ async function initSingleRepo(targetDir, apiKey, serverResponse) {
3483
4063
  actions.push("Wrote ~/.flydocs/me.json");
3484
4064
  }
3485
4065
  if (serverResponse.context) {
3486
- const contextDir = join20(targetDir, "flydocs", "context");
3487
- await mkdir9(contextDir, { recursive: true });
3488
- const projectMdPath = join20(contextDir, "project.md");
4066
+ const contextDir = join24(targetDir, "flydocs", "context");
4067
+ await mkdir12(contextDir, { recursive: true });
4068
+ const projectMdPath = join24(contextDir, "project.md");
3489
4069
  if (!await pathExists(projectMdPath)) {
3490
- await writeFile13(projectMdPath, serverResponse.context.projectMd, "utf-8");
4070
+ await writeFile16(projectMdPath, serverResponse.context.projectMd, "utf-8");
3491
4071
  actions.push("Wrote flydocs/context/project.md");
3492
4072
  } else {
3493
4073
  skipped.push("flydocs/context/project.md (already exists)");
3494
4074
  }
3495
- const serviceJsonPath = join20(contextDir, "service.json");
4075
+ const serviceJsonPath = join24(contextDir, "service.json");
3496
4076
  if (serverResponse.context.serviceJson && !await pathExists(serviceJsonPath)) {
3497
- await writeFile13(
4077
+ await writeFile16(
3498
4078
  serviceJsonPath,
3499
4079
  JSON.stringify(serverResponse.context.serviceJson, null, 2) + "\n",
3500
4080
  "utf-8"
@@ -3504,6 +4084,27 @@ async function initSingleRepo(targetDir, apiKey, serverResponse) {
3504
4084
  skipped.push("flydocs/context/service.json (already exists)");
3505
4085
  }
3506
4086
  }
4087
+ if (childRepoMode) {
4088
+ const cleaned = await cleanServerManagedFiles(targetDir);
4089
+ if (cleaned > 0) {
4090
+ actions.push(
4091
+ `Removed ${cleaned} server-managed file(s) (moved to workspace root)`
4092
+ );
4093
+ }
4094
+ } else {
4095
+ const archivedConfigs = await archiveIdeConfigs(targetDir);
4096
+ if (archivedConfigs.length > 0) {
4097
+ actions.push(`Archived ${archivedConfigs.length} existing IDE config(s)`);
4098
+ }
4099
+ const { changes: artifactChanges } = await syncAllArtifacts(
4100
+ targetDir,
4101
+ apiKey,
4102
+ serverResponse.workspaceId,
4103
+ 0
4104
+ // fresh init — fetch all artifacts
4105
+ );
4106
+ actions.push(...artifactChanges);
4107
+ }
3507
4108
  await ensureGitignore(targetDir);
3508
4109
  await ensurePlatformIgnores(targetDir);
3509
4110
  actions.push("Updated ignore files");
@@ -3512,10 +4113,32 @@ async function initSingleRepo(targetDir, apiKey, serverResponse) {
3512
4113
  await executeCleanup(targetDir, artifacts);
3513
4114
  actions.push(`Cleaned ${artifacts.length} legacy artifact(s)`);
3514
4115
  }
3515
- return { actions, skipped };
4116
+ return {
4117
+ actions,
4118
+ skipped
4119
+ };
4120
+ }
4121
+ async function cleanServerManagedFiles(targetDir) {
4122
+ const { rm: rm9 } = await import("fs/promises");
4123
+ let cleaned = 0;
4124
+ for (const dir of SERVER_MANAGED_DIRS) {
4125
+ const fullPath = join24(targetDir, dir);
4126
+ if (await pathExists(fullPath)) {
4127
+ await rm9(fullPath, { recursive: true, force: true });
4128
+ cleaned++;
4129
+ }
4130
+ }
4131
+ for (const file of SERVER_MANAGED_FILES) {
4132
+ const fullPath = join24(targetDir, file);
4133
+ if (await pathExists(fullPath)) {
4134
+ await rm9(fullPath, { force: true });
4135
+ cleaned++;
4136
+ }
4137
+ }
4138
+ return cleaned;
3516
4139
  }
3517
4140
  async function checkGitFreshness(repoDir) {
3518
- const hasGit = await pathExists(join20(repoDir, ".git"));
4141
+ const hasGit = await pathExists(join24(repoDir, ".git"));
3519
4142
  if (!hasGit) return;
3520
4143
  try {
3521
4144
  await execFileAsync("git", ["-C", repoDir, "fetch", "--quiet"], {
@@ -3544,7 +4167,7 @@ async function checkGitFreshness(repoDir) {
3544
4167
  }
3545
4168
  }
3546
4169
  async function isEmptyOrNonRepo(dir) {
3547
- const hasGit = await pathExists(join20(dir, ".git"));
4170
+ const hasGit = await pathExists(join24(dir, ".git"));
3548
4171
  if (hasGit) return false;
3549
4172
  const siblings = await detectSiblingRepos(dir);
3550
4173
  if (Object.keys(siblings).length >= 2) return false;
@@ -3557,7 +4180,7 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
3557
4180
  if (repos.length === 1) {
3558
4181
  const repo = repos[0];
3559
4182
  const shortName = repoShortName(repo.name);
3560
- printInfo(`Workspace has 1 repo: ${pc8.bold(shortName)}`);
4183
+ printInfo(`Workspace has 1 repo: ${pc9.bold(shortName)}`);
3561
4184
  const shouldClone = await confirm3({
3562
4185
  message: `Clone ${shortName} into this directory and initialize?`
3563
4186
  });
@@ -3595,7 +4218,7 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
3595
4218
  const repoNames = repos.map((r) => repoShortName(r.name));
3596
4219
  printInfo(`Workspace has ${repos.length} repos:`);
3597
4220
  for (const name of repoNames) {
3598
- console.log(` ${pc8.cyan(name)}`);
4221
+ console.log(` ${pc9.cyan(name)}`);
3599
4222
  }
3600
4223
  console.log();
3601
4224
  const shouldClone = await confirm3({
@@ -3614,13 +4237,13 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
3614
4237
  for (let i = 0; i < repos.length; i++) {
3615
4238
  const repo = repos[i];
3616
4239
  const shortName = repoNames[i];
3617
- const cloneDir = join20(targetDir, shortName);
4240
+ const cloneDir = join24(targetDir, shortName);
3618
4241
  console.log();
3619
- if (await pathExists(join20(cloneDir, ".git"))) {
3620
- printInfo(`${pc8.bold(shortName)} already cloned, initializing...`);
4242
+ if (await pathExists(join24(cloneDir, ".git"))) {
4243
+ printInfo(`${pc9.bold(shortName)} already cloned, initializing...`);
3621
4244
  await checkGitFreshness(cloneDir);
3622
4245
  } else {
3623
- printInfo(`Cloning ${pc8.bold(shortName)}...`);
4246
+ printInfo(`Cloning ${pc9.bold(shortName)}...`);
3624
4247
  await execFileAsync("git", ["clone", repo.cloneUrl, cloneDir], {
3625
4248
  timeout: 6e4
3626
4249
  });
@@ -3659,12 +4282,12 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
3659
4282
  );
3660
4283
  await writeWorkspaceFile(targetDir, workspaceFile);
3661
4284
  allActions.push(`.flydocs-workspace.json (${repos.length} repos)`);
3662
- const contextDir = join20(targetDir, "flydocs", "context");
3663
- const workspaceMdPath = join20(contextDir, "workspace.md");
4285
+ const contextDir = join24(targetDir, "flydocs", "context");
4286
+ const workspaceMdPath = join24(contextDir, "workspace.md");
3664
4287
  if (!await pathExists(workspaceMdPath)) {
3665
- await mkdir9(contextDir, { recursive: true });
4288
+ await mkdir12(contextDir, { recursive: true });
3666
4289
  const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(targetDir, workspaceFile);
3667
- await writeFile13(workspaceMdPath, content, "utf-8");
4290
+ await writeFile16(workspaceMdPath, content, "utf-8");
3668
4291
  const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
3669
4292
  allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
3670
4293
  }
@@ -3679,7 +4302,7 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
3679
4302
  }
3680
4303
  }
3681
4304
  async function isParentDirectory(dir) {
3682
- const hasGit = await pathExists(join20(dir, ".git"));
4305
+ const hasGit = await pathExists(join24(dir, ".git"));
3683
4306
  if (hasGit) return false;
3684
4307
  const repos = await detectSiblingRepos(dir);
3685
4308
  return Object.keys(repos).length >= 2;
@@ -3689,7 +4312,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
3689
4312
  const repoNames = Object.keys(repos).sort();
3690
4313
  printInfo(`Detected ${repoNames.length} repos:`);
3691
4314
  for (const name of repoNames) {
3692
- console.log(` ${pc8.cyan(name)}`);
4315
+ console.log(` ${pc9.cyan(name)}`);
3693
4316
  }
3694
4317
  console.log();
3695
4318
  const shouldContinue = await confirm3({
@@ -3699,7 +4322,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
3699
4322
  cancel3("Init cancelled.");
3700
4323
  process.exit(0);
3701
4324
  }
3702
- const firstRepoDir = join20(parentDir, repoNames[0]);
4325
+ const firstRepoDir = join24(parentDir, repoNames[0]);
3703
4326
  const { apiKey, workspaceId } = await resolveAndValidateKey(
3704
4327
  keyArg,
3705
4328
  firstRepoDir
@@ -3710,17 +4333,38 @@ async function runMultiRepoInit(parentDir, keyArg) {
3710
4333
  const allSkipped = [];
3711
4334
  let allWarnings = [];
3712
4335
  let firstResponse;
4336
+ const workspaceResponse = await pullServerConfig(
4337
+ apiKey,
4338
+ firstRepoDir,
4339
+ workspaceId
4340
+ );
4341
+ firstResponse = workspaceResponse;
4342
+ const archivedConfigs = await archiveIdeConfigs(parentDir);
4343
+ if (archivedConfigs.length > 0) {
4344
+ allActions.push(
4345
+ `Archived ${archivedConfigs.length} existing IDE config(s) at workspace root`
4346
+ );
4347
+ }
4348
+ const { changes: artifactChanges } = await syncAllArtifacts(
4349
+ parentDir,
4350
+ apiKey,
4351
+ workspaceId,
4352
+ 0
4353
+ );
4354
+ allActions.push(...artifactChanges);
4355
+ await ensureGitignore(parentDir);
4356
+ allActions.push("Updated workspace root .gitignore");
3713
4357
  for (const repoName of repoNames) {
3714
- const repoDir = join20(parentDir, repoName);
4358
+ const repoDir = join24(parentDir, repoName);
3715
4359
  console.log();
3716
- printInfo(`Initializing ${pc8.bold(repoName)}...`);
4360
+ printInfo(`Initializing ${pc9.bold(repoName)}...`);
3717
4361
  await checkGitFreshness(repoDir);
3718
4362
  const serverResponse = await pullServerConfig(apiKey, repoDir, workspaceId);
3719
- if (!firstResponse) firstResponse = serverResponse;
3720
4363
  const { actions, skipped } = await initSingleRepo(
3721
4364
  repoDir,
3722
4365
  apiKey,
3723
- serverResponse
4366
+ serverResponse,
4367
+ { childRepoMode: true }
3724
4368
  );
3725
4369
  for (const action of actions) {
3726
4370
  allActions.push(`[${repoName}] ${action}`);
@@ -3743,15 +4387,15 @@ async function runMultiRepoInit(parentDir, keyArg) {
3743
4387
  await writeWorkspaceFile(parentDir, workspaceFile);
3744
4388
  allActions.push(`.flydocs-workspace.json (${repoNames.length} repos)`);
3745
4389
  }
3746
- const contextDir = join20(parentDir, "flydocs", "context");
3747
- const workspaceMdPath = join20(contextDir, "workspace.md");
4390
+ const contextDir = join24(parentDir, "flydocs", "context");
4391
+ const workspaceMdPath = join24(contextDir, "workspace.md");
3748
4392
  if (await pathExists(workspaceMdPath)) {
3749
4393
  allSkipped.push("flydocs/context/workspace.md (already exists)");
3750
4394
  } else {
3751
- await mkdir9(contextDir, { recursive: true });
4395
+ await mkdir12(contextDir, { recursive: true });
3752
4396
  const workspaceJson = await readWorkspaceFile(parentDir) ?? buildWorkspaceFile(firstResponse.workspaceId, repos);
3753
4397
  const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(parentDir, workspaceJson);
3754
- await writeFile13(workspaceMdPath, content, "utf-8");
4398
+ await writeFile16(workspaceMdPath, content, "utf-8");
3755
4399
  const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
3756
4400
  allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
3757
4401
  }
@@ -3768,35 +4412,35 @@ function printInitReport(actions, skipped, warnings, hasContext) {
3768
4412
  console.log();
3769
4413
  const lines = [];
3770
4414
  for (const action of actions) {
3771
- lines.push(`${pc8.green("+")} ${action}`);
4415
+ lines.push(`${pc9.green("+")} ${action}`);
3772
4416
  }
3773
4417
  for (const skip of skipped) {
3774
- lines.push(`${pc8.dim("-")} ${skip}`);
4418
+ lines.push(`${pc9.dim("-")} ${skip}`);
3775
4419
  }
3776
4420
  if (!hasContext) {
3777
4421
  lines.push("");
3778
4422
  lines.push(
3779
- `${pc8.yellow("!")} No generated context yet. An admin can generate it from the portal.`
4423
+ `${pc9.yellow("!")} No generated context yet. An admin can generate it from the portal.`
3780
4424
  );
3781
4425
  }
3782
4426
  if (warnings.length > 0) {
3783
4427
  lines.push("");
3784
4428
  for (const warning of warnings) {
3785
- lines.push(`${pc8.yellow("!")} ${warning}`);
4429
+ lines.push(`${pc9.yellow("!")} ${warning}`);
3786
4430
  }
3787
4431
  }
3788
4432
  printCompletionBox("FlyDocs Initialized", lines);
3789
4433
  console.log();
3790
4434
  console.log(` Next steps:`);
3791
4435
  console.log(
3792
- ` ${pc8.cyan("flydocs sync")} Pull latest config and templates`
4436
+ ` ${pc9.cyan("flydocs sync")} Pull latest config and templates`
3793
4437
  );
3794
4438
  console.log(
3795
- ` ${pc8.cyan("cursor .")} ${pc8.dim("or")} ${pc8.cyan("code .")} Open your IDE, then use ${pc8.bold("/start-session")}`
4439
+ ` ${pc9.cyan("cursor .")} ${pc9.dim("or")} ${pc9.cyan("code .")} Open your IDE, then use ${pc9.bold("/start-session")}`
3796
4440
  );
3797
4441
  console.log();
3798
4442
  }
3799
- var execFileAsync, init_default;
4443
+ var SERVER_MANAGED_DIRS, SERVER_MANAGED_FILES, execFileAsync, init_default;
3800
4444
  var init_init = __esm({
3801
4445
  "src/commands/init.ts"() {
3802
4446
  "use strict";
@@ -3808,10 +4452,26 @@ var init_init = __esm({
3808
4452
  init_gitignore();
3809
4453
  init_fs_ops();
3810
4454
  init_relay_client();
4455
+ init_artifacts();
3811
4456
  init_cleanup();
4457
+ init_ide_archive();
3812
4458
  init_workspace();
4459
+ init_scan();
4460
+ SERVER_MANAGED_DIRS = [
4461
+ ".claude/skills",
4462
+ ".claude/commands",
4463
+ ".claude/hooks",
4464
+ ".claude/agents",
4465
+ ".cursor/rules",
4466
+ ".cursor/commands"
4467
+ ];
4468
+ SERVER_MANAGED_FILES = [
4469
+ ".claude/settings.json",
4470
+ ".claude/CLAUDE.md",
4471
+ "AGENTS.md"
4472
+ ];
3813
4473
  execFileAsync = promisify(execFile);
3814
- init_default = defineCommand3({
4474
+ init_default = defineCommand4({
3815
4475
  meta: {
3816
4476
  name: "init",
3817
4477
  description: "Initialize FlyDocs in this project (unified setup)"
@@ -3824,12 +4484,17 @@ var init_init = __esm({
3824
4484
  path: {
3825
4485
  type: "string",
3826
4486
  description: "Path to project directory"
4487
+ },
4488
+ scan: {
4489
+ type: "boolean",
4490
+ description: "Run AI scan after init to generate project context",
4491
+ default: false
3827
4492
  }
3828
4493
  },
3829
4494
  async run({ args }) {
3830
4495
  const targetDir = args.path ?? process.cwd();
3831
4496
  console.log();
3832
- console.log(` ${pc8.bold("FlyDocs Init")}`);
4497
+ console.log(` ${pc9.bold("FlyDocs Init")}`);
3833
4498
  console.log();
3834
4499
  if (await isParentDirectory(targetDir)) {
3835
4500
  await runMultiRepoInit(targetDir, args.key);
@@ -3876,7 +4541,7 @@ var init_init = __esm({
3876
4541
  actions.unshift("Stored credential globally (~/.flydocs/credentials)");
3877
4542
  const topology = serverResponse.config.topology;
3878
4543
  if (topology?.type === 4 && topology.label === "sibling-repos") {
3879
- const parentDir = join20(targetDir, "..");
4544
+ const parentDir = join24(targetDir, "..");
3880
4545
  const existing = await readWorkspaceFile(parentDir);
3881
4546
  if (existing) {
3882
4547
  skipped.push(".flydocs-workspace.json (already exists)");
@@ -3900,6 +4565,402 @@ var init_init = __esm({
3900
4565
  serverResponse.warnings,
3901
4566
  !!serverResponse.context
3902
4567
  );
4568
+ const shouldAutoScan = !args.scan && serverResponse.contextVersion === 0 && !serverResponse.context?.projectMd;
4569
+ if (shouldAutoScan) {
4570
+ const isAdmin = serverResponse.identity?.role === "admin";
4571
+ if (isAdmin) {
4572
+ console.log();
4573
+ printInfo(
4574
+ "No generated context found. AI scanning creates project.md and service.json."
4575
+ );
4576
+ const shouldScan = await confirm3({
4577
+ message: "Run AI scan now?"
4578
+ });
4579
+ if (!isCancel4(shouldScan) && shouldScan) {
4580
+ let repoName = null;
4581
+ try {
4582
+ const { stdout } = await execFileAsync(
4583
+ "git",
4584
+ ["-C", targetDir, "remote", "get-url", "origin"],
4585
+ { timeout: 5e3 }
4586
+ );
4587
+ const url = stdout.trim();
4588
+ const match = url.match(/[/:]([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
4589
+ if (match) repoName = match[1];
4590
+ } catch {
4591
+ }
4592
+ if (repoName) {
4593
+ const { success, written } = await runScan(
4594
+ apiKey,
4595
+ workspaceId,
4596
+ targetDir,
4597
+ repoName,
4598
+ false
4599
+ );
4600
+ if (success) {
4601
+ for (const file of written) {
4602
+ printStatus(`Wrote ${file}`);
4603
+ }
4604
+ }
4605
+ } else {
4606
+ printWarning(
4607
+ "Could not determine repo name from git remote. Run `flydocs scan --repo owner/name` manually."
4608
+ );
4609
+ }
4610
+ }
4611
+ } else {
4612
+ console.log();
4613
+ printInfo(
4614
+ "No generated context yet. Ask a workspace admin to generate it from the portal, then run `flydocs sync`."
4615
+ );
4616
+ }
4617
+ }
4618
+ if (args.scan) {
4619
+ const isAdmin = serverResponse.identity?.role === "admin";
4620
+ if (!isAdmin) {
4621
+ printWarning(
4622
+ "Skipping scan: admin role required. Only workspace admins can trigger AI scans."
4623
+ );
4624
+ } else {
4625
+ let repoName = null;
4626
+ try {
4627
+ const { stdout } = await execFileAsync(
4628
+ "git",
4629
+ ["-C", targetDir, "remote", "get-url", "origin"],
4630
+ { timeout: 5e3 }
4631
+ );
4632
+ const url = stdout.trim();
4633
+ const match = url.match(/[/:]([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
4634
+ if (match) repoName = match[1];
4635
+ } catch {
4636
+ }
4637
+ if (!repoName) {
4638
+ printWarning(
4639
+ "Skipping scan: could not determine repo name from git remote."
4640
+ );
4641
+ } else {
4642
+ console.log();
4643
+ printInfo(`Running AI scan for ${pc9.bold(repoName)}...`);
4644
+ const { success, written } = await runScan(
4645
+ apiKey,
4646
+ workspaceId,
4647
+ targetDir,
4648
+ repoName,
4649
+ false
4650
+ // not a forced regeneration on init
4651
+ );
4652
+ if (success) {
4653
+ for (const file of written) {
4654
+ printStatus(`Wrote ${file}`);
4655
+ }
4656
+ }
4657
+ }
4658
+ }
4659
+ }
4660
+ }
4661
+ });
4662
+ }
4663
+ });
4664
+
4665
+ // src/lib/skill-conflicts.ts
4666
+ import { join as join25 } from "path";
4667
+ import { readdir as readdir7, rm as rm6, cp as cp2, mkdir as mkdir13 } from "fs/promises";
4668
+ import { select as select3, isCancel as isCancel5 } from "@clack/prompts";
4669
+ async function resolveSkillConflicts(targetDir, keptPaths = []) {
4670
+ const result = {
4671
+ newKeptPaths: [],
4672
+ archived: 0,
4673
+ deleted: 0
4674
+ };
4675
+ const skillsDir = join25(targetDir, ".claude", "skills");
4676
+ if (!await pathExists(skillsDir)) return result;
4677
+ const entries = await readdir7(skillsDir, { withFileTypes: true });
4678
+ for (const entry of entries) {
4679
+ if (!entry.isDirectory()) continue;
4680
+ if (entry.name.startsWith(CORE_SKILL_PREFIX2)) continue;
4681
+ const relPath = `.claude/skills/${entry.name}`;
4682
+ if (keptPaths.includes(relPath) || keptPaths.includes(relPath + "/")) {
4683
+ continue;
4684
+ }
4685
+ await promptConflict(targetDir, skillsDir, entry.name, relPath, result);
4686
+ }
4687
+ if (result.archived > 0 || result.deleted > 0 || result.newKeptPaths.length > 0) {
4688
+ const parts = [];
4689
+ if (result.archived > 0) parts.push(`${result.archived} archived`);
4690
+ if (result.deleted > 0) parts.push(`${result.deleted} deleted`);
4691
+ if (result.newKeptPaths.length > 0)
4692
+ parts.push(`${result.newKeptPaths.length} kept`);
4693
+ printInfo(`Non-core skills: ${parts.join(", ")}`);
4694
+ }
4695
+ return result;
4696
+ }
4697
+ async function promptConflict(targetDir, skillsDir, name, relPath, result) {
4698
+ const choice = await select3({
4699
+ message: `Found non-managed skill: ${name}. What should we do?`,
4700
+ options: [
4701
+ {
4702
+ value: "keep",
4703
+ label: "Keep \u2014 leave in place, don't ask again"
4704
+ },
4705
+ {
4706
+ value: "archive",
4707
+ label: "Archive \u2014 move to flydocs/knowledge/archived/skills/"
4708
+ },
4709
+ {
4710
+ value: "delete",
4711
+ label: "Delete \u2014 remove permanently"
4712
+ }
4713
+ ]
4714
+ });
4715
+ if (isCancel5(choice)) {
4716
+ result.newKeptPaths.push(relPath);
4717
+ return;
4718
+ }
4719
+ const fullPath = join25(skillsDir, name);
4720
+ switch (choice) {
4721
+ case "keep":
4722
+ result.newKeptPaths.push(relPath);
4723
+ printInfo(`Keeping ${name} (won't ask again)`);
4724
+ break;
4725
+ case "archive": {
4726
+ const archiveDir = join25(
4727
+ targetDir,
4728
+ "flydocs",
4729
+ "knowledge",
4730
+ "archived",
4731
+ "skills"
4732
+ );
4733
+ await mkdir13(archiveDir, { recursive: true });
4734
+ const archiveDest = join25(archiveDir, name);
4735
+ if (await pathExists(archiveDest)) {
4736
+ await rm6(archiveDest, { recursive: true, force: true });
4737
+ }
4738
+ await cp2(fullPath, archiveDest, { recursive: true });
4739
+ await rm6(fullPath, { recursive: true, force: true });
4740
+ result.archived++;
4741
+ printStatus(`Archived ${name} \u2192 flydocs/knowledge/archived/skills/`);
4742
+ break;
4743
+ }
4744
+ case "delete":
4745
+ await rm6(fullPath, { recursive: true, force: true });
4746
+ result.deleted++;
4747
+ printStatus(`Deleted ${name}`);
4748
+ break;
4749
+ }
4750
+ }
4751
+ var CORE_SKILL_PREFIX2;
4752
+ var init_skill_conflicts = __esm({
4753
+ "src/lib/skill-conflicts.ts"() {
4754
+ "use strict";
4755
+ init_fs_ops();
4756
+ init_ui();
4757
+ CORE_SKILL_PREFIX2 = "flydocs-";
4758
+ }
4759
+ });
4760
+
4761
+ // src/commands/sync.ts
4762
+ var sync_exports = {};
4763
+ __export(sync_exports, {
4764
+ default: () => sync_default,
4765
+ runSync: () => runSync
4766
+ });
4767
+ import { defineCommand as defineCommand5 } from "citty";
4768
+ import pc10 from "picocolors";
4769
+ import { join as join26, resolve as resolve4 } from "path";
4770
+ import { mkdir as mkdir14, writeFile as writeFile17 } from "fs/promises";
4771
+ async function runSync(targetDir) {
4772
+ const changes = [];
4773
+ printInfo("Syncing with server...");
4774
+ const resolved = await resolveApiKey(void 0, targetDir);
4775
+ if (!resolved) {
4776
+ printError("No API key found. Run `flydocs auth` or `flydocs init` first.");
4777
+ process.exit(1);
4778
+ }
4779
+ const apiKey = resolved.key;
4780
+ const cred = await readGlobalCredential();
4781
+ const workspaceId = cred?.workspaceId;
4782
+ if (!workspaceId) {
4783
+ printError(
4784
+ "No workspace ID found. Run `flydocs init` to set up your workspace."
4785
+ );
4786
+ process.exit(1);
4787
+ }
4788
+ let currentTemplateVersion = 0;
4789
+ let currentArtifactVersion = 0;
4790
+ let localOverrides;
4791
+ try {
4792
+ const currentConfig = await readAnyConfig(targetDir);
4793
+ if (isConfigV2(currentConfig)) {
4794
+ currentTemplateVersion = currentConfig.configVersion ?? 0;
4795
+ currentArtifactVersion = currentConfig.artifactVersion ?? 0;
4796
+ localOverrides = currentConfig.artifactOverrides;
4797
+ }
4798
+ } catch {
4799
+ }
4800
+ let serverResponse;
4801
+ try {
4802
+ serverResponse = await fetchConfigV2(apiKey, { workspaceId });
4803
+ } catch (err) {
4804
+ if (err instanceof RelayError) {
4805
+ printWarning(`Server unavailable (${err.status}), using cached config.`);
4806
+ } else {
4807
+ printWarning("Server unreachable, using cached config.");
4808
+ }
4809
+ console.log(` ${pc10.dim("Config may be stale. Retry when connected.")}`);
4810
+ return changes;
4811
+ }
4812
+ if (!serverResponse.valid) {
4813
+ printWarning("Server returned invalid config. Keeping current config.");
4814
+ return changes;
4815
+ }
4816
+ await mkdir14(join26(targetDir, ".flydocs"), { recursive: true });
4817
+ const serverConfig = serverResponse.config;
4818
+ if (localOverrides) {
4819
+ serverConfig.artifactOverrides = localOverrides;
4820
+ }
4821
+ const configWithHash = applyConfigHash(serverConfig);
4822
+ await writeConfig(targetDir, configWithHash);
4823
+ changes.push("Updated .flydocs/config.json");
4824
+ const serverTemplateVersion = serverResponse.templates.version;
4825
+ if (serverTemplateVersion > currentTemplateVersion) {
4826
+ try {
4827
+ const templatesResponse = await fetchTemplates(
4828
+ apiKey,
4829
+ currentTemplateVersion,
4830
+ workspaceId
4831
+ );
4832
+ if (templatesResponse.templates.length > 0) {
4833
+ const templatesDir = join26(
4834
+ targetDir,
4835
+ ".claude",
4836
+ "skills",
4837
+ "flydocs-workflow",
4838
+ "templates"
4839
+ );
4840
+ await mkdir14(templatesDir, { recursive: true });
4841
+ for (const template of templatesResponse.templates) {
4842
+ const filename = `${template.type}.md`;
4843
+ const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
4844
+ const templateDir = join26(templatesDir, subdir);
4845
+ await mkdir14(templateDir, { recursive: true });
4846
+ await writeFile17(
4847
+ join26(templateDir, filename),
4848
+ template.content,
4849
+ "utf-8"
4850
+ );
4851
+ }
4852
+ changes.push(
4853
+ `Updated ${templatesResponse.templates.length} templates (v${currentTemplateVersion} \u2192 v${serverTemplateVersion})`
4854
+ );
4855
+ }
4856
+ } catch (err) {
4857
+ if (err instanceof RelayError) {
4858
+ printWarning("Could not fetch templates. Will retry on next sync.");
4859
+ } else {
4860
+ printWarning("Template sync failed. Will retry on next sync.");
4861
+ }
4862
+ }
4863
+ }
4864
+ const serverArtifactVersion = serverResponse.artifactVersion ?? 0;
4865
+ const skipPaths = localOverrides?.skipPaths ?? [];
4866
+ if (serverArtifactVersion > currentArtifactVersion) {
4867
+ const artifactWriteRoot = await resolveArtifactWriteRoot(
4868
+ targetDir,
4869
+ serverResponse.config
4870
+ );
4871
+ const { changes: artifactChanges } = await syncAllArtifacts(
4872
+ artifactWriteRoot,
4873
+ apiKey,
4874
+ workspaceId,
4875
+ currentArtifactVersion,
4876
+ skipPaths
4877
+ );
4878
+ changes.push(...artifactChanges);
4879
+ if (artifactWriteRoot !== targetDir) {
4880
+ changes.push(`Artifacts written to workspace root`);
4881
+ }
4882
+ const keptPaths = localOverrides?.keptPaths ?? [];
4883
+ const conflicts = await resolveSkillConflicts(artifactWriteRoot, keptPaths);
4884
+ if (conflicts.newKeptPaths.length > 0) {
4885
+ try {
4886
+ const updatedConfig = await readAnyConfig(targetDir);
4887
+ if (isConfigV2(updatedConfig)) {
4888
+ const existingKept = updatedConfig.artifactOverrides?.keptPaths ?? [];
4889
+ const mergedKept = [
4890
+ .../* @__PURE__ */ new Set([...existingKept, ...conflicts.newKeptPaths])
4891
+ ];
4892
+ updatedConfig.artifactOverrides = {
4893
+ ...updatedConfig.artifactOverrides,
4894
+ skipPaths: updatedConfig.artifactOverrides?.skipPaths ?? [],
4895
+ keptPaths: mergedKept
4896
+ };
4897
+ const hashed = applyConfigHash(updatedConfig);
4898
+ await writeConfig(targetDir, hashed);
4899
+ changes.push(
4900
+ `Persisted ${conflicts.newKeptPaths.length} kept path(s) to config`
4901
+ );
4902
+ }
4903
+ } catch {
4904
+ }
4905
+ }
4906
+ if (conflicts.archived > 0) {
4907
+ changes.push(`Archived ${conflicts.archived} non-core skill(s)`);
4908
+ }
4909
+ if (conflicts.deleted > 0) {
4910
+ changes.push(`Deleted ${conflicts.deleted} non-core skill(s)`);
4911
+ }
4912
+ }
4913
+ await migrateGitignore(targetDir);
4914
+ return changes;
4915
+ }
4916
+ async function resolveArtifactWriteRoot(targetDir, config) {
4917
+ const topology = config.topology;
4918
+ if (topology && topology.type === 4 && topology.label === "sibling-repos") {
4919
+ const parentDir = join26(targetDir, "..");
4920
+ const workspaceFile = await readWorkspaceFile(parentDir);
4921
+ if (workspaceFile) {
4922
+ return resolve4(parentDir);
4923
+ }
4924
+ }
4925
+ return targetDir;
4926
+ }
4927
+ var sync_default;
4928
+ var init_sync = __esm({
4929
+ "src/commands/sync.ts"() {
4930
+ "use strict";
4931
+ init_ui();
4932
+ init_global_config();
4933
+ init_config();
4934
+ init_config_integrity();
4935
+ init_types();
4936
+ init_gitignore();
4937
+ init_artifacts();
4938
+ init_workspace();
4939
+ init_relay_client();
4940
+ init_skill_conflicts();
4941
+ sync_default = defineCommand5({
4942
+ meta: {
4943
+ name: "sync",
4944
+ description: "Pull latest config and templates from server"
4945
+ },
4946
+ args: {
4947
+ path: {
4948
+ type: "string",
4949
+ description: "Path to project directory"
4950
+ }
4951
+ },
4952
+ async run({ args }) {
4953
+ const targetDir = args.path ?? process.cwd();
4954
+ console.log();
4955
+ const changes = await runSync(targetDir);
4956
+ if (changes.length === 0) {
4957
+ printStatus("Already up to date.");
4958
+ } else {
4959
+ for (const change of changes) {
4960
+ printStatus(change);
4961
+ }
4962
+ }
4963
+ console.log();
3903
4964
  }
3904
4965
  });
3905
4966
  }
@@ -3910,11 +4971,11 @@ var update_exports = {};
3910
4971
  __export(update_exports, {
3911
4972
  default: () => update_default
3912
4973
  });
3913
- import { defineCommand as defineCommand4 } from "citty";
3914
- import { resolve as resolve4, join as join21 } from "path";
3915
- import { mkdir as mkdir10, cp as cp2, readFile as readFile15, readdir as readdir5, rm as rm5 } from "fs/promises";
3916
- import { select as select3, text as text3, confirm as confirm4, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
3917
- import pc9 from "picocolors";
4974
+ import { defineCommand as defineCommand6 } from "citty";
4975
+ import { resolve as resolve5, join as join27 } from "path";
4976
+ import { mkdir as mkdir15, cp as cp3, readFile as readFile16, readdir as readdir8, rm as rm7 } from "fs/promises";
4977
+ import { select as select4, text as text3, confirm as confirm4, isCancel as isCancel6, cancel as cancel4 } from "@clack/prompts";
4978
+ import pc11 from "picocolors";
3918
4979
  var update_default;
3919
4980
  var init_update = __esm({
3920
4981
  "src/commands/update.ts"() {
@@ -3933,7 +4994,11 @@ var init_update = __esm({
3933
4994
  init_update_check();
3934
4995
  init_telemetry();
3935
4996
  init_integrity();
3936
- update_default = defineCommand4({
4997
+ init_config();
4998
+ init_types();
4999
+ init_managed_paths();
5000
+ init_sync();
5001
+ update_default = defineCommand6({
3937
5002
  meta: {
3938
5003
  name: "update",
3939
5004
  description: "Update an existing FlyDocs installation"
@@ -3978,11 +5043,11 @@ var init_update = __esm({
3978
5043
  await capture("update_started", { template_version: version });
3979
5044
  let targetDir;
3980
5045
  if (args.path) {
3981
- targetDir = resolve4(args.path.replace(/^~/, process.env.HOME ?? "~"));
5046
+ targetDir = resolve5(args.path.replace(/^~/, process.env.HOME ?? "~"));
3982
5047
  } else if (args.here) {
3983
5048
  targetDir = process.cwd();
3984
5049
  } else {
3985
- const choice = await select3({
5050
+ const choice = await select4({
3986
5051
  message: "Which project would you like to update?",
3987
5052
  options: [
3988
5053
  {
@@ -3995,7 +5060,7 @@ var init_update = __esm({
3995
5060
  }
3996
5061
  ]
3997
5062
  });
3998
- if (isCancel5(choice)) {
5063
+ if (isCancel6(choice)) {
3999
5064
  cancel4("Update cancelled.");
4000
5065
  process.exit(0);
4001
5066
  }
@@ -4005,11 +5070,11 @@ var init_update = __esm({
4005
5070
  const enteredPath = await text3({
4006
5071
  message: "Enter project path:"
4007
5072
  });
4008
- if (isCancel5(enteredPath)) {
5073
+ if (isCancel6(enteredPath)) {
4009
5074
  cancel4("Update cancelled.");
4010
5075
  process.exit(0);
4011
5076
  }
4012
- targetDir = resolve4(
5077
+ targetDir = resolve5(
4013
5078
  enteredPath.replace(/^~/, process.env.HOME ?? "~")
4014
5079
  );
4015
5080
  }
@@ -4018,11 +5083,11 @@ var init_update = __esm({
4018
5083
  printError(`Directory does not exist: ${targetDir}`);
4019
5084
  process.exit(1);
4020
5085
  }
4021
- targetDir = resolve4(targetDir);
5086
+ targetDir = resolve5(targetDir);
4022
5087
  process.chdir(targetDir);
4023
- const hasVersion = await pathExists(join21(targetDir, ".flydocs", "version"));
5088
+ const hasVersion = await pathExists(join27(targetDir, ".flydocs", "version"));
4024
5089
  const hasConfig = await pathExists(
4025
- join21(targetDir, ".flydocs", "config.json")
5090
+ join27(targetDir, ".flydocs", "config.json")
4026
5091
  );
4027
5092
  if (!hasVersion && !hasConfig) {
4028
5093
  printError(`Not a FlyDocs project: ${targetDir}`);
@@ -4033,8 +5098,8 @@ var init_update = __esm({
4033
5098
  console.log();
4034
5099
  let currentVersion = "0.1.0";
4035
5100
  if (hasVersion) {
4036
- const vContent = await readFile15(
4037
- join21(targetDir, ".flydocs", "version"),
5101
+ const vContent = await readFile16(
5102
+ join27(targetDir, ".flydocs", "version"),
4038
5103
  "utf-8"
4039
5104
  );
4040
5105
  currentVersion = vContent.trim();
@@ -4056,7 +5121,7 @@ var init_update = __esm({
4056
5121
  const shouldContinue = await confirm4({
4057
5122
  message: "Continue anyway?"
4058
5123
  });
4059
- if (isCancel5(shouldContinue) || !shouldContinue) {
5124
+ if (isCancel6(shouldContinue) || !shouldContinue) {
4060
5125
  process.exit(0);
4061
5126
  }
4062
5127
  }
@@ -4068,35 +5133,65 @@ var init_update = __esm({
4068
5133
  });
4069
5134
  console.log(`Updating: v${currentVersion} \u2192 v${version}`);
4070
5135
  console.log();
4071
- const changelogPath = join21(templateDir, "CHANGELOG.md");
5136
+ const changelogPath = join27(templateDir, "CHANGELOG.md");
4072
5137
  const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
4073
5138
  if (whatsNew.length > 0) {
4074
- console.log(pc9.cyan("What's new:"));
5139
+ console.log(pc11.cyan("What's new:"));
4075
5140
  console.log();
4076
5141
  for (const entry of whatsNew) {
4077
5142
  console.log(` ${entry}`);
4078
5143
  }
4079
5144
  console.log();
4080
5145
  }
5146
+ if (hasConfig) {
5147
+ try {
5148
+ const existingConfig = await readAnyConfig(targetDir);
5149
+ if (isConfigV2(existingConfig)) {
5150
+ printInfo(
5151
+ "Cloud project detected \u2014 running sync flow (wipe-and-replace)"
5152
+ );
5153
+ console.log();
5154
+ const syncChanges = await runSync(targetDir);
5155
+ if (syncChanges.length > 0) {
5156
+ for (const change of syncChanges) {
5157
+ printStatus(change);
5158
+ }
5159
+ }
5160
+ console.log();
5161
+ console.log(
5162
+ ` ${pc11.dim("Tip: use")} ${pc11.cyan("flydocs sync")} ${pc11.dim("for file-only refresh (no CLI update).")}`
5163
+ );
5164
+ await capture("update_completed", {
5165
+ current_version: currentVersion,
5166
+ version,
5167
+ tier: "cloud",
5168
+ mode: "sync-delegate"
5169
+ });
5170
+ await flush();
5171
+ return;
5172
+ }
5173
+ } catch {
5174
+ }
5175
+ }
4081
5176
  const now = /* @__PURE__ */ new Date();
4082
5177
  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")}`;
4083
- const backupDir = join21(targetDir, ".flydocs", `backup-${ts}`);
4084
- await mkdir10(backupDir, { recursive: true });
5178
+ const backupDir = join27(targetDir, ".flydocs", `backup-${ts}`);
5179
+ await mkdir15(backupDir, { recursive: true });
4085
5180
  if (hasConfig) {
4086
- await cp2(
4087
- join21(targetDir, ".flydocs", "config.json"),
4088
- join21(backupDir, "config.json")
5181
+ await cp3(
5182
+ join27(targetDir, ".flydocs", "config.json"),
5183
+ join27(backupDir, "config.json")
4089
5184
  );
4090
5185
  printStatus(`Config backed up to .flydocs/backup-${ts}/`);
4091
5186
  }
4092
5187
  try {
4093
- const flydocsDir = join21(targetDir, ".flydocs");
4094
- const entries = await readdir5(flydocsDir);
5188
+ const flydocsDir = join27(targetDir, ".flydocs");
5189
+ const entries = await readdir8(flydocsDir);
4095
5190
  const backups = entries.filter((e) => e.startsWith("backup-")).sort();
4096
5191
  if (backups.length > 3) {
4097
5192
  const toRemove = backups.slice(0, backups.length - 3);
4098
5193
  for (const old of toRemove) {
4099
- await rm5(join21(flydocsDir, old), { recursive: true, force: true });
5194
+ await rm7(join27(flydocsDir, old), { recursive: true, force: true });
4100
5195
  }
4101
5196
  }
4102
5197
  } catch {
@@ -4133,19 +5228,21 @@ var init_update = __esm({
4133
5228
  effectiveTier = preserved.tier;
4134
5229
  }
4135
5230
  await ensureDirectories(targetDir, effectiveTier);
5231
+ console.log("Wiping server-managed paths...");
5232
+ await wipeManagedPaths(targetDir);
4136
5233
  console.log("Replacing framework directories...");
4137
5234
  await replaceDirectory(
4138
- join21(templateDir, ".flydocs", "templates"),
4139
- join21(targetDir, ".flydocs", "templates")
5235
+ join27(templateDir, ".flydocs", "templates"),
5236
+ join27(targetDir, ".flydocs", "templates")
4140
5237
  );
4141
5238
  printStatus(".flydocs/templates");
4142
5239
  await replaceDirectory(
4143
- join21(templateDir, ".claude", "hooks"),
4144
- join21(targetDir, ".claude", "hooks")
5240
+ join27(templateDir, ".claude", "hooks"),
5241
+ join27(targetDir, ".claude", "hooks")
4145
5242
  );
4146
5243
  printStatus(".claude/hooks");
4147
5244
  const hasExistingAgents = await pathExists(
4148
- join21(targetDir, ".claude", "agents")
5245
+ join27(targetDir, ".claude", "agents")
4149
5246
  );
4150
5247
  let installAgents;
4151
5248
  if (args.yes) {
@@ -4154,7 +5251,7 @@ var init_update = __esm({
4154
5251
  installAgents = true;
4155
5252
  } else {
4156
5253
  console.log();
4157
- console.log(` ${pc9.bold(pc9.yellow("Sub-Agents (Recommended)"))}`);
5254
+ console.log(` ${pc11.bold(pc11.yellow("Sub-Agents (Recommended)"))}`);
4158
5255
  console.log();
4159
5256
  console.log(
4160
5257
  " Sub-agents are specialized roles (PM, implementation, review,"
@@ -4170,27 +5267,27 @@ var init_update = __esm({
4170
5267
  message: "Install sub-agents?",
4171
5268
  initialValue: true
4172
5269
  });
4173
- if (isCancel5(agentConfirm)) {
5270
+ if (isCancel6(agentConfirm)) {
4174
5271
  installAgents = false;
4175
5272
  } else {
4176
5273
  installAgents = agentConfirm;
4177
5274
  }
4178
5275
  }
4179
5276
  if (installAgents) {
4180
- const claudeAgentsSrc = join21(templateDir, ".claude", "agents");
5277
+ const claudeAgentsSrc = join27(templateDir, ".claude", "agents");
4181
5278
  if (await pathExists(claudeAgentsSrc)) {
4182
- await mkdir10(join21(targetDir, ".claude", "agents"), { recursive: true });
5279
+ await mkdir15(join27(targetDir, ".claude", "agents"), { recursive: true });
4183
5280
  await copyDirectoryContents(
4184
5281
  claudeAgentsSrc,
4185
- join21(targetDir, ".claude", "agents")
5282
+ join27(targetDir, ".claude", "agents")
4186
5283
  );
4187
5284
  }
4188
- const cursorAgentsSrc = join21(templateDir, ".cursor", "agents");
5285
+ const cursorAgentsSrc = join27(templateDir, ".cursor", "agents");
4189
5286
  if (await pathExists(cursorAgentsSrc)) {
4190
- await mkdir10(join21(targetDir, ".cursor", "agents"), { recursive: true });
5287
+ await mkdir15(join27(targetDir, ".cursor", "agents"), { recursive: true });
4191
5288
  await copyDirectoryContents(
4192
5289
  cursorAgentsSrc,
4193
- join21(targetDir, ".cursor", "agents")
5290
+ join27(targetDir, ".cursor", "agents")
4194
5291
  );
4195
5292
  }
4196
5293
  printStatus(
@@ -4204,58 +5301,58 @@ var init_update = __esm({
4204
5301
  console.log();
4205
5302
  console.log("Replacing framework files...");
4206
5303
  await copyFile(
4207
- join21(templateDir, ".claude", "CLAUDE.md"),
4208
- join21(targetDir, ".claude", "CLAUDE.md")
5304
+ join27(templateDir, ".claude", "CLAUDE.md"),
5305
+ join27(targetDir, ".claude", "CLAUDE.md")
4209
5306
  );
4210
5307
  await copyFile(
4211
- join21(templateDir, ".claude", "settings.json"),
4212
- join21(targetDir, ".claude", "settings.json")
5308
+ join27(templateDir, ".claude", "settings.json"),
5309
+ join27(targetDir, ".claude", "settings.json")
4213
5310
  );
4214
5311
  printStatus(".claude/CLAUDE.md, settings.json");
4215
5312
  await copyDirectoryContents(
4216
- join21(templateDir, ".claude", "commands"),
4217
- join21(targetDir, ".claude", "commands")
5313
+ join27(templateDir, ".claude", "commands"),
5314
+ join27(targetDir, ".claude", "commands")
4218
5315
  );
4219
5316
  await copyDirectoryContents(
4220
- join21(templateDir, ".claude", "commands"),
4221
- join21(targetDir, ".cursor", "commands")
5317
+ join27(templateDir, ".claude", "commands"),
5318
+ join27(targetDir, ".cursor", "commands")
4222
5319
  );
4223
5320
  printStatus(".claude/commands, .cursor/commands");
4224
- const skillsReadmeSrc = join21(templateDir, ".claude", "skills", "README.md");
5321
+ const skillsReadmeSrc = join27(templateDir, ".claude", "skills", "README.md");
4225
5322
  if (await pathExists(skillsReadmeSrc)) {
4226
5323
  await copyFile(
4227
5324
  skillsReadmeSrc,
4228
- join21(targetDir, ".claude", "skills", "README.md")
5325
+ join27(targetDir, ".claude", "skills", "README.md")
4229
5326
  );
4230
5327
  }
4231
5328
  printStatus(".claude/skills/README.md");
4232
5329
  await copyFile(
4233
- join21(templateDir, ".cursor", "hooks.json"),
4234
- join21(targetDir, ".cursor", "hooks.json")
5330
+ join27(templateDir, ".cursor", "hooks.json"),
5331
+ join27(targetDir, ".cursor", "hooks.json")
4235
5332
  );
4236
5333
  printStatus(".cursor/hooks.json");
4237
5334
  await copyFile(
4238
- join21(templateDir, "AGENTS.md"),
4239
- join21(targetDir, "AGENTS.md")
5335
+ join27(templateDir, "AGENTS.md"),
5336
+ join27(targetDir, "AGENTS.md")
4240
5337
  );
4241
5338
  printStatus("AGENTS.md");
4242
- const envExampleSrc = join21(templateDir, ".env.example");
5339
+ const envExampleSrc = join27(templateDir, ".env.example");
4243
5340
  if (await pathExists(envExampleSrc)) {
4244
- await copyFile(envExampleSrc, join21(targetDir, ".env.example"));
5341
+ await copyFile(envExampleSrc, join27(targetDir, ".env.example"));
4245
5342
  printStatus(".env.example");
4246
5343
  }
4247
- const knowledgeTemplatesDir = join21(
5344
+ const knowledgeTemplatesDir = join27(
4248
5345
  targetDir,
4249
5346
  "flydocs",
4250
5347
  "knowledge",
4251
5348
  "templates"
4252
5349
  );
4253
5350
  if (!await pathExists(knowledgeTemplatesDir)) {
4254
- await mkdir10(knowledgeTemplatesDir, { recursive: true });
5351
+ await mkdir15(knowledgeTemplatesDir, { recursive: true });
4255
5352
  }
4256
5353
  for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
4257
- const src = join21(templateDir, "flydocs", "knowledge", "templates", tmpl);
4258
- const dest = join21(knowledgeTemplatesDir, tmpl);
5354
+ const src = join27(templateDir, "flydocs", "knowledge", "templates", tmpl);
5355
+ const dest = join27(knowledgeTemplatesDir, tmpl);
4259
5356
  if (await pathExists(src) && !await pathExists(dest)) {
4260
5357
  await copyFile(src, dest);
4261
5358
  }
@@ -4282,18 +5379,18 @@ var init_update = __esm({
4282
5379
  printWarning("Config merge failed \u2014 config.json preserved as-is");
4283
5380
  }
4284
5381
  await copyFile(
4285
- join21(templateDir, ".flydocs", "version"),
4286
- join21(targetDir, ".flydocs", "version")
5382
+ join27(templateDir, ".flydocs", "version"),
5383
+ join27(targetDir, ".flydocs", "version")
4287
5384
  );
4288
5385
  printStatus(`.flydocs/version \u2192 ${version}`);
4289
- const clSrc = join21(templateDir, "CHANGELOG.md");
5386
+ const clSrc = join27(templateDir, "CHANGELOG.md");
4290
5387
  if (await pathExists(clSrc)) {
4291
- await copyFile(clSrc, join21(targetDir, ".flydocs", "CHANGELOG.md"));
5388
+ await copyFile(clSrc, join27(targetDir, ".flydocs", "CHANGELOG.md"));
4292
5389
  printStatus(".flydocs/CHANGELOG.md");
4293
5390
  }
4294
- const mfSrc = join21(templateDir, "manifest.json");
5391
+ const mfSrc = join27(templateDir, "manifest.json");
4295
5392
  if (await pathExists(mfSrc)) {
4296
- await copyFile(mfSrc, join21(targetDir, ".flydocs", "manifest.json"));
5393
+ await copyFile(mfSrc, join27(targetDir, ".flydocs", "manifest.json"));
4297
5394
  printStatus(".flydocs/manifest.json");
4298
5395
  }
4299
5396
  await generateIntegrity(targetDir, version);
@@ -4355,22 +5452,22 @@ var uninstall_exports = {};
4355
5452
  __export(uninstall_exports, {
4356
5453
  default: () => uninstall_default
4357
5454
  });
4358
- import { defineCommand as defineCommand5 } from "citty";
4359
- import { resolve as resolve5, join as join22 } from "path";
4360
- import { readdir as readdir6, rm as rm6, rename as rename2 } from "fs/promises";
4361
- import { confirm as confirm5, select as select4, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
4362
- import pc10 from "picocolors";
5455
+ import { defineCommand as defineCommand7 } from "citty";
5456
+ import { resolve as resolve6, join as join28 } from "path";
5457
+ import { readdir as readdir9, rm as rm8, rename as rename2 } from "fs/promises";
5458
+ import { confirm as confirm5, select as select5, isCancel as isCancel7, cancel as cancel5 } from "@clack/prompts";
5459
+ import pc12 from "picocolors";
4363
5460
  async function removeOwnedSkills(targetDir) {
4364
- const skillsDir = join22(targetDir, ".claude", "skills");
5461
+ const skillsDir = join28(targetDir, ".claude", "skills");
4365
5462
  const removed = [];
4366
5463
  if (!await pathExists(skillsDir)) {
4367
5464
  return removed;
4368
5465
  }
4369
5466
  try {
4370
- const entries = await readdir6(skillsDir);
5467
+ const entries = await readdir9(skillsDir);
4371
5468
  for (const entry of entries) {
4372
5469
  if (entry.startsWith(OWNED_SKILL_PREFIX)) {
4373
- await rm6(join22(skillsDir, entry), { recursive: true, force: true });
5470
+ await rm8(join28(skillsDir, entry), { recursive: true, force: true });
4374
5471
  removed.push(`.claude/skills/${entry}`);
4375
5472
  }
4376
5473
  }
@@ -4379,16 +5476,16 @@ async function removeOwnedSkills(targetDir) {
4379
5476
  return removed;
4380
5477
  }
4381
5478
  async function removeOwnedCursorRules(targetDir) {
4382
- const rulesDir = join22(targetDir, ".cursor", "rules");
5479
+ const rulesDir = join28(targetDir, ".cursor", "rules");
4383
5480
  const removed = [];
4384
5481
  if (!await pathExists(rulesDir)) {
4385
5482
  return removed;
4386
5483
  }
4387
5484
  try {
4388
- const entries = await readdir6(rulesDir);
5485
+ const entries = await readdir9(rulesDir);
4389
5486
  for (const entry of entries) {
4390
5487
  if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
4391
- await rm6(join22(rulesDir, entry), { force: true });
5488
+ await rm8(join28(rulesDir, entry), { force: true });
4392
5489
  removed.push(`.cursor/rules/${entry}`);
4393
5490
  }
4394
5491
  }
@@ -4398,7 +5495,7 @@ async function removeOwnedCursorRules(targetDir) {
4398
5495
  }
4399
5496
  async function isEmptyDir(dirPath) {
4400
5497
  try {
4401
- const entries = await readdir6(dirPath);
5498
+ const entries = await readdir9(dirPath);
4402
5499
  return entries.length === 0;
4403
5500
  } catch {
4404
5501
  return false;
@@ -4407,9 +5504,9 @@ async function isEmptyDir(dirPath) {
4407
5504
  async function cleanupEmptyParents(targetDir, dirs) {
4408
5505
  const cleaned = [];
4409
5506
  for (const dir of dirs) {
4410
- const fullPath = join22(targetDir, dir);
5507
+ const fullPath = join28(targetDir, dir);
4411
5508
  if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
4412
- await rm6(fullPath, { recursive: true, force: true });
5509
+ await rm8(fullPath, { recursive: true, force: true });
4413
5510
  cleaned.push(dir);
4414
5511
  }
4415
5512
  }
@@ -4439,7 +5536,7 @@ var init_uninstall = __esm({
4439
5536
  ];
4440
5537
  OWNED_SKILL_PREFIX = "flydocs-";
4441
5538
  OWNED_RULE_PREFIX = "flydocs-";
4442
- uninstall_default = defineCommand5({
5539
+ uninstall_default = defineCommand7({
4443
5540
  meta: {
4444
5541
  name: "uninstall",
4445
5542
  description: "Remove FlyDocs from a project directory"
@@ -4475,7 +5572,7 @@ var init_uninstall = __esm({
4475
5572
  printBanner(CLI_VERSION);
4476
5573
  let targetDir;
4477
5574
  if (args.path) {
4478
- targetDir = resolve5(args.path.replace(/^~/, process.env.HOME ?? "~"));
5575
+ targetDir = resolve6(args.path.replace(/^~/, process.env.HOME ?? "~"));
4479
5576
  } else if (args.here) {
4480
5577
  targetDir = process.cwd();
4481
5578
  } else {
@@ -4485,9 +5582,9 @@ var init_uninstall = __esm({
4485
5582
  printError(`Directory does not exist: ${targetDir}`);
4486
5583
  process.exit(1);
4487
5584
  }
4488
- targetDir = resolve5(targetDir);
4489
- const hasFlydocs = await pathExists(join22(targetDir, ".flydocs"));
4490
- const hasAgentsMd = await pathExists(join22(targetDir, "AGENTS.md"));
5585
+ targetDir = resolve6(targetDir);
5586
+ const hasFlydocs = await pathExists(join28(targetDir, ".flydocs"));
5587
+ const hasAgentsMd = await pathExists(join28(targetDir, "AGENTS.md"));
4491
5588
  if (!hasFlydocs && !hasAgentsMd) {
4492
5589
  printError(`Not a FlyDocs project: ${targetDir}`);
4493
5590
  printInfo("No .flydocs/ directory or AGENTS.md found.");
@@ -4499,12 +5596,12 @@ var init_uninstall = __esm({
4499
5596
  const removeAll = forceAll || args.all;
4500
5597
  const skipPrompts = forceAll || args.yes;
4501
5598
  let contentAction = "preserve";
4502
- const hasUserContent = await pathExists(join22(targetDir, "flydocs"));
5599
+ const hasUserContent = await pathExists(join28(targetDir, "flydocs"));
4503
5600
  if (hasUserContent) {
4504
5601
  if (removeAll) {
4505
5602
  contentAction = "remove";
4506
5603
  } else if (!skipPrompts) {
4507
- const choice = await select4({
5604
+ const choice = await select5({
4508
5605
  message: "What should happen to your flydocs/ content (project docs, knowledge base)?",
4509
5606
  options: [
4510
5607
  {
@@ -4524,7 +5621,7 @@ var init_uninstall = __esm({
4524
5621
  }
4525
5622
  ]
4526
5623
  });
4527
- if (isCancel6(choice)) {
5624
+ if (isCancel7(choice)) {
4528
5625
  cancel5("Uninstall cancelled.");
4529
5626
  process.exit(0);
4530
5627
  }
@@ -4533,39 +5630,39 @@ var init_uninstall = __esm({
4533
5630
  }
4534
5631
  if (!skipPrompts) {
4535
5632
  console.log();
4536
- console.log(pc10.bold("The following will be removed:"));
5633
+ console.log(pc12.bold("The following will be removed:"));
4537
5634
  console.log();
4538
5635
  console.log(" Framework files:");
4539
5636
  for (const [path] of ALWAYS_REMOVED) {
4540
- console.log(` ${pc10.dim(path)}`);
5637
+ console.log(` ${pc12.dim(path)}`);
4541
5638
  }
4542
- console.log(` ${pc10.dim(".claude/skills/flydocs-*")}`);
4543
- console.log(` ${pc10.dim(".cursor/rules/flydocs-*.mdc")}`);
5639
+ console.log(` ${pc12.dim(".claude/skills/flydocs-*")}`);
5640
+ console.log(` ${pc12.dim(".cursor/rules/flydocs-*.mdc")}`);
4544
5641
  if (hasUserContent) {
4545
5642
  if (contentAction === "archive") {
4546
5643
  console.log();
4547
5644
  console.log(
4548
- ` User content: ${pc10.yellow("flydocs/ -> flydocs-archive/")}`
5645
+ ` User content: ${pc12.yellow("flydocs/ -> flydocs-archive/")}`
4549
5646
  );
4550
5647
  } else if (contentAction === "remove") {
4551
5648
  console.log();
4552
- console.log(` User content: ${pc10.red("flydocs/ (deleted)")}`);
5649
+ console.log(` User content: ${pc12.red("flydocs/ (deleted)")}`);
4553
5650
  } else {
4554
5651
  console.log();
4555
- console.log(` User content: ${pc10.green("flydocs/ (preserved)")}`);
5652
+ console.log(` User content: ${pc12.green("flydocs/ (preserved)")}`);
4556
5653
  }
4557
5654
  }
4558
5655
  console.log();
4559
- console.log(pc10.bold("Preserved:"));
5656
+ console.log(pc12.bold("Preserved:"));
4560
5657
  console.log(
4561
- ` ${pc10.dim(".claude/skills/ (non-flydocs community skills)")}`
5658
+ ` ${pc12.dim(".claude/skills/ (non-flydocs community skills)")}`
4562
5659
  );
4563
- console.log(` ${pc10.dim(".env, .env.local")}`);
5660
+ console.log(` ${pc12.dim(".env, .env.local")}`);
4564
5661
  console.log();
4565
5662
  const shouldContinue = await confirm5({
4566
5663
  message: "Proceed with uninstall?"
4567
5664
  });
4568
- if (isCancel6(shouldContinue) || !shouldContinue) {
5665
+ if (isCancel7(shouldContinue) || !shouldContinue) {
4569
5666
  cancel5("Uninstall cancelled.");
4570
5667
  process.exit(0);
4571
5668
  }
@@ -4582,16 +5679,16 @@ var init_uninstall = __esm({
4582
5679
  const removedRules = await removeOwnedCursorRules(targetDir);
4583
5680
  result.removed.push(...removedRules);
4584
5681
  for (const [relativePath, type] of ALWAYS_REMOVED) {
4585
- const fullPath = join22(targetDir, relativePath);
5682
+ const fullPath = join28(targetDir, relativePath);
4586
5683
  if (!await pathExists(fullPath)) {
4587
5684
  result.skipped.push(relativePath);
4588
5685
  continue;
4589
5686
  }
4590
5687
  try {
4591
5688
  if (type === "dir") {
4592
- await rm6(fullPath, { recursive: true, force: true });
5689
+ await rm8(fullPath, { recursive: true, force: true });
4593
5690
  } else {
4594
- await rm6(fullPath, { force: true });
5691
+ await rm8(fullPath, { force: true });
4595
5692
  }
4596
5693
  result.removed.push(relativePath);
4597
5694
  } catch {
@@ -4600,16 +5697,16 @@ var init_uninstall = __esm({
4600
5697
  }
4601
5698
  }
4602
5699
  if (hasUserContent) {
4603
- const flydocsPath = join22(targetDir, "flydocs");
5700
+ const flydocsPath = join28(targetDir, "flydocs");
4604
5701
  if (contentAction === "archive") {
4605
- const archivePath = join22(targetDir, "flydocs-archive");
5702
+ const archivePath = join28(targetDir, "flydocs-archive");
4606
5703
  if (await pathExists(archivePath)) {
4607
- await rm6(archivePath, { recursive: true, force: true });
5704
+ await rm8(archivePath, { recursive: true, force: true });
4608
5705
  }
4609
5706
  await rename2(flydocsPath, archivePath);
4610
5707
  result.archived.push("flydocs/ -> flydocs-archive/");
4611
5708
  } else if (contentAction === "remove") {
4612
- await rm6(flydocsPath, { recursive: true, force: true });
5709
+ await rm8(flydocsPath, { recursive: true, force: true });
4613
5710
  result.removed.push("flydocs/");
4614
5711
  }
4615
5712
  }
@@ -4628,17 +5725,17 @@ var init_uninstall = __esm({
4628
5725
  result.restored = originals.map((f) => f.relativePath);
4629
5726
  }
4630
5727
  console.log();
4631
- console.log(pc10.bold("Uninstall Summary"));
5728
+ console.log(pc12.bold("Uninstall Summary"));
4632
5729
  console.log();
4633
5730
  if (result.removed.length > 0) {
4634
- console.log(` ${pc10.green("Removed")} (${result.removed.length}):`);
5731
+ console.log(` ${pc12.green("Removed")} (${result.removed.length}):`);
4635
5732
  for (const item of result.removed) {
4636
5733
  printStatus(item);
4637
5734
  }
4638
5735
  }
4639
5736
  if (result.archived.length > 0) {
4640
5737
  console.log();
4641
- console.log(` ${pc10.yellow("Archived")} (${result.archived.length}):`);
5738
+ console.log(` ${pc12.yellow("Archived")} (${result.archived.length}):`);
4642
5739
  for (const item of result.archived) {
4643
5740
  printInfo(item);
4644
5741
  }
@@ -4646,7 +5743,7 @@ var init_uninstall = __esm({
4646
5743
  if (result.restored.length > 0) {
4647
5744
  console.log();
4648
5745
  console.log(
4649
- ` ${pc10.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
5746
+ ` ${pc12.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
4650
5747
  );
4651
5748
  for (const item of result.restored) {
4652
5749
  printInfo(item);
@@ -4655,16 +5752,16 @@ var init_uninstall = __esm({
4655
5752
  if (result.skipped.length > 0) {
4656
5753
  console.log();
4657
5754
  console.log(
4658
- ` ${pc10.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
5755
+ ` ${pc12.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
4659
5756
  );
4660
5757
  for (const item of result.skipped) {
4661
- console.log(` ${pc10.dim(item)}`);
5758
+ console.log(` ${pc12.dim(item)}`);
4662
5759
  }
4663
5760
  }
4664
5761
  console.log();
4665
5762
  printStatus("FlyDocs has been removed from this project.");
4666
5763
  console.log();
4667
- printInfo(`To reinstall: ${pc10.cyan("npx @flydocs/cli install --here")}`);
5764
+ printInfo(`To reinstall: ${pc12.cyan("npx @flydocs/cli install --here")}`);
4668
5765
  console.log();
4669
5766
  }
4670
5767
  });
@@ -4676,28 +5773,28 @@ var setup_exports = {};
4676
5773
  __export(setup_exports, {
4677
5774
  default: () => setup_default
4678
5775
  });
4679
- import { defineCommand as defineCommand6 } from "citty";
4680
- import pc11 from "picocolors";
5776
+ import { defineCommand as defineCommand8 } from "citty";
5777
+ import pc13 from "picocolors";
4681
5778
  var setup_default;
4682
5779
  var init_setup = __esm({
4683
5780
  "src/commands/setup.ts"() {
4684
5781
  "use strict";
4685
- setup_default = defineCommand6({
5782
+ setup_default = defineCommand8({
4686
5783
  meta: {
4687
5784
  name: "setup",
4688
5785
  description: "Configure FlyDocs settings for this project"
4689
5786
  },
4690
5787
  run() {
4691
5788
  console.log();
4692
- console.log(` ${pc11.bold("FlyDocs Setup")}`);
5789
+ console.log(` ${pc13.bold("FlyDocs Setup")}`);
4693
5790
  console.log();
4694
5791
  console.log(` Setup runs inside your IDE as an interactive AI command.`);
4695
5792
  console.log();
4696
5793
  console.log(
4697
- ` ${pc11.cyan("Claude Code:")} Type ${pc11.bold("/flydocs-setup")} in chat`
5794
+ ` ${pc13.cyan("Claude Code:")} Type ${pc13.bold("/flydocs-setup")} in chat`
4698
5795
  );
4699
5796
  console.log(
4700
- ` ${pc11.cyan("Cursor:")} Type ${pc11.bold("/flydocs-setup")} in chat`
5797
+ ` ${pc13.cyan("Cursor:")} Type ${pc13.bold("/flydocs-setup")} in chat`
4701
5798
  );
4702
5799
  console.log();
4703
5800
  console.log(` This configures your project context, detects your stack,`);
@@ -4713,14 +5810,14 @@ var skills_exports = {};
4713
5810
  __export(skills_exports, {
4714
5811
  default: () => skills_default
4715
5812
  });
4716
- import { defineCommand as defineCommand7 } from "citty";
4717
- import pc12 from "picocolors";
5813
+ import { defineCommand as defineCommand9 } from "citty";
5814
+ import pc14 from "picocolors";
4718
5815
  var list, search, add, remove, skills_default;
4719
5816
  var init_skills2 = __esm({
4720
5817
  "src/commands/skills.ts"() {
4721
5818
  "use strict";
4722
5819
  init_skill_manager();
4723
- list = defineCommand7({
5820
+ list = defineCommand9({
4724
5821
  meta: {
4725
5822
  name: "list",
4726
5823
  description: "List installed skills"
@@ -4736,26 +5833,26 @@ var init_skills2 = __esm({
4736
5833
  console.log(`${total} skill(s) installed:`);
4737
5834
  if (result.platform.length > 0) {
4738
5835
  console.log();
4739
- console.log(pc12.bold("Platform"));
5836
+ console.log(pc14.bold("Platform"));
4740
5837
  for (const skill of result.platform) {
4741
5838
  console.log(
4742
- ` ${skill.name} ${pc12.dim(`(${skill.triggers} triggers)`)}`
5839
+ ` ${skill.name} ${pc14.dim(`(${skill.triggers} triggers)`)}`
4743
5840
  );
4744
5841
  }
4745
5842
  }
4746
5843
  if (result.community.length > 0) {
4747
5844
  console.log();
4748
- console.log(pc12.bold("Community"));
5845
+ console.log(pc14.bold("Community"));
4749
5846
  for (const skill of result.community) {
4750
5847
  console.log(
4751
- ` ${skill.name} ${pc12.dim(`(${skill.triggers} triggers)`)}`
5848
+ ` ${skill.name} ${pc14.dim(`(${skill.triggers} triggers)`)}`
4752
5849
  );
4753
5850
  }
4754
5851
  }
4755
5852
  console.log();
4756
5853
  }
4757
5854
  });
4758
- search = defineCommand7({
5855
+ search = defineCommand9({
4759
5856
  meta: {
4760
5857
  name: "search",
4761
5858
  description: "Search community skills"
@@ -4771,24 +5868,24 @@ var init_skills2 = __esm({
4771
5868
  const results = await searchCatalog(args.keyword);
4772
5869
  if (results.length === 0) {
4773
5870
  console.log(`No skills found for "${args.keyword}".`);
4774
- console.log(` Browse the catalog at: ${pc12.cyan("https://skills.sh/")}`);
5871
+ console.log(` Browse the catalog at: ${pc14.cyan("https://skills.sh/")}`);
4775
5872
  return;
4776
5873
  }
4777
5874
  console.log();
4778
5875
  console.log(`${results.length} skill(s) matching "${args.keyword}":`);
4779
5876
  console.log();
4780
5877
  for (const skill of results) {
4781
- console.log(` ${pc12.bold(skill.name)}`);
5878
+ console.log(` ${pc14.bold(skill.name)}`);
4782
5879
  console.log(` ${skill.description}`);
4783
- console.log(` ${pc12.dim(skill.repo)}`);
5880
+ console.log(` ${pc14.dim(skill.repo)}`);
4784
5881
  if (skill.tags.length > 0) {
4785
- console.log(` ${pc12.dim(skill.tags.join(", "))}`);
5882
+ console.log(` ${pc14.dim(skill.tags.join(", "))}`);
4786
5883
  }
4787
5884
  console.log();
4788
5885
  }
4789
5886
  }
4790
5887
  });
4791
- add = defineCommand7({
5888
+ add = defineCommand9({
4792
5889
  meta: {
4793
5890
  name: "add",
4794
5891
  description: "Install a community skill"
@@ -4804,7 +5901,7 @@ var init_skills2 = __esm({
4804
5901
  await addSkill(process.cwd(), args.source);
4805
5902
  }
4806
5903
  });
4807
- remove = defineCommand7({
5904
+ remove = defineCommand9({
4808
5905
  meta: {
4809
5906
  name: "remove",
4810
5907
  description: "Remove an installed community skill"
@@ -4820,7 +5917,7 @@ var init_skills2 = __esm({
4820
5917
  await removeSkill(process.cwd(), args.name);
4821
5918
  }
4822
5919
  });
4823
- skills_default = defineCommand7({
5920
+ skills_default = defineCommand9({
4824
5921
  meta: {
4825
5922
  name: "skills",
4826
5923
  description: "Manage FlyDocs skills (list, search, add, remove)"
@@ -4840,10 +5937,10 @@ var connect_exports = {};
4840
5937
  __export(connect_exports, {
4841
5938
  default: () => connect_default
4842
5939
  });
4843
- import { defineCommand as defineCommand8 } from "citty";
4844
- import { text as text4, confirm as confirm6, isCancel as isCancel7, cancel as cancel6 } from "@clack/prompts";
4845
- import pc13 from "picocolors";
4846
- import { join as join23 } from "path";
5940
+ import { defineCommand as defineCommand10 } from "citty";
5941
+ import { text as text4, confirm as confirm6, isCancel as isCancel8, cancel as cancel6 } from "@clack/prompts";
5942
+ import pc15 from "picocolors";
5943
+ import { join as join29 } from "path";
4847
5944
  var connect_default;
4848
5945
  var init_connect = __esm({
4849
5946
  "src/commands/connect.ts"() {
@@ -4853,7 +5950,7 @@ var init_connect = __esm({
4853
5950
  init_template();
4854
5951
  init_ui();
4855
5952
  init_api_key();
4856
- connect_default = defineCommand8({
5953
+ connect_default = defineCommand10({
4857
5954
  meta: {
4858
5955
  name: "connect",
4859
5956
  description: "Connect FlyDocs to a cloud provider"
@@ -4878,11 +5975,11 @@ var init_connect = __esm({
4878
5975
  },
4879
5976
  async run({ args }) {
4880
5977
  const targetDir = args.path ?? process.cwd();
4881
- const configPath = join23(targetDir, ".flydocs", "config.json");
5978
+ const configPath = join29(targetDir, ".flydocs", "config.json");
4882
5979
  if (!await pathExists(configPath)) {
4883
5980
  printError("Not a FlyDocs project (.flydocs/config.json not found).");
4884
5981
  console.log(
4885
- ` Run ${pc13.cyan("flydocs")} first to install FlyDocs in this project.`
5982
+ ` Run ${pc15.cyan("flydocs")} first to install FlyDocs in this project.`
4886
5983
  );
4887
5984
  process.exit(1);
4888
5985
  }
@@ -4893,16 +5990,16 @@ var init_connect = __esm({
4893
5990
  const reconnect = await confirm6({
4894
5991
  message: "Want to update your API key?"
4895
5992
  });
4896
- if (isCancel7(reconnect) || !reconnect) {
5993
+ if (isCancel8(reconnect) || !reconnect) {
4897
5994
  console.log(` No changes made.`);
4898
5995
  return;
4899
5996
  }
4900
5997
  }
4901
5998
  console.log();
4902
- console.log(` ${pc13.bold("Connect to FlyDocs Cloud")}`);
5999
+ console.log(` ${pc15.bold("Connect to FlyDocs Cloud")}`);
4903
6000
  console.log();
4904
6001
  console.log(
4905
- ` ${pc13.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
6002
+ ` ${pc15.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
4906
6003
  );
4907
6004
  console.log();
4908
6005
  let apiKey = args.key ?? "";
@@ -4918,7 +6015,7 @@ var init_connect = __esm({
4918
6015
  return void 0;
4919
6016
  }
4920
6017
  });
4921
- if (isCancel7(keyInput)) {
6018
+ if (isCancel8(keyInput)) {
4922
6019
  cancel6("Connection cancelled.");
4923
6020
  process.exit(0);
4924
6021
  }
@@ -4940,7 +6037,7 @@ var init_connect = __esm({
4940
6037
  console.log(` Check your key and try again.`);
4941
6038
  process.exit(1);
4942
6039
  }
4943
- printStatus(`Connected to ${pc13.bold(result.org)}`);
6040
+ printStatus(`Connected to ${pc15.bold(result.org)}`);
4944
6041
  } catch {
4945
6042
  printError(
4946
6043
  "Could not reach relay API. Check your network and try again."
@@ -4948,7 +6045,7 @@ var init_connect = __esm({
4948
6045
  process.exit(1);
4949
6046
  }
4950
6047
  const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
4951
- printStatus(`API key stored in ${pc13.dim(envFile)}`);
6048
+ printStatus(`API key stored in ${pc15.dim(envFile)}`);
4952
6049
  } else {
4953
6050
  try {
4954
6051
  const result = await validateLinearKey(apiKey);
@@ -4958,7 +6055,7 @@ var init_connect = __esm({
4958
6055
  process.exit(1);
4959
6056
  }
4960
6057
  printStatus(
4961
- `Authenticated as ${pc13.bold(result.name)} (${result.email})`
6058
+ `Authenticated as ${pc15.bold(result.name)} (${result.email})`
4962
6059
  );
4963
6060
  } catch {
4964
6061
  printError("Invalid API key or network error.");
@@ -4966,7 +6063,7 @@ var init_connect = __esm({
4966
6063
  process.exit(1);
4967
6064
  }
4968
6065
  const envFile = await storeEnvKey(targetDir, "LINEAR_API_KEY", apiKey);
4969
- printStatus(`API key stored in ${pc13.dim(envFile)}`);
6066
+ printStatus(`API key stored in ${pc15.dim(envFile)}`);
4970
6067
  }
4971
6068
  const wasLocal = config.tier === "local";
4972
6069
  config.tier = "cloud";
@@ -4982,14 +6079,14 @@ var init_connect = __esm({
4982
6079
  }
4983
6080
  console.log();
4984
6081
  console.log(
4985
- ` ${pc13.bold("Connected!")} Your project is now on the cloud tier.`
6082
+ ` ${pc15.bold("Connected!")} Your project is now on the cloud tier.`
4986
6083
  );
4987
6084
  console.log();
4988
6085
  console.log(` Next steps:`);
4989
6086
  console.log(
4990
- ` 1. Run ${pc13.cyan("/flydocs-setup")} in your IDE to configure your project`
6087
+ ` 1. Run ${pc15.cyan("/flydocs-setup")} in your IDE to configure your project`
4991
6088
  );
4992
- console.log(` 2. Run ${pc13.cyan("/start-session")} to begin working`);
6089
+ console.log(` 2. Run ${pc15.cyan("/start-session")} to begin working`);
4993
6090
  console.log();
4994
6091
  }
4995
6092
  });
@@ -5001,9 +6098,9 @@ var auth_exports = {};
5001
6098
  __export(auth_exports, {
5002
6099
  default: () => auth_default
5003
6100
  });
5004
- import { defineCommand as defineCommand9 } from "citty";
5005
- import { text as text5, confirm as confirm7, isCancel as isCancel8, cancel as cancel7 } from "@clack/prompts";
5006
- import pc14 from "picocolors";
6101
+ import { defineCommand as defineCommand11 } from "citty";
6102
+ import { text as text5, confirm as confirm7, isCancel as isCancel9, cancel as cancel7 } from "@clack/prompts";
6103
+ import pc16 from "picocolors";
5007
6104
  var auth_default;
5008
6105
  var init_auth = __esm({
5009
6106
  "src/commands/auth.ts"() {
@@ -5011,7 +6108,7 @@ var init_auth = __esm({
5011
6108
  init_ui();
5012
6109
  init_api_key();
5013
6110
  init_global_config();
5014
- auth_default = defineCommand9({
6111
+ auth_default = defineCommand11({
5015
6112
  meta: {
5016
6113
  name: "auth",
5017
6114
  description: "Store API key globally (~/.flydocs/credentials)"
@@ -5025,25 +6122,25 @@ var init_auth = __esm({
5025
6122
  },
5026
6123
  async run({ args }) {
5027
6124
  console.log();
5028
- console.log(` ${pc14.bold("FlyDocs Authentication")}`);
6125
+ console.log(` ${pc16.bold("FlyDocs Authentication")}`);
5029
6126
  console.log();
5030
6127
  let apiKey = args.key ?? "";
5031
6128
  const existing = await readGlobalCredential();
5032
6129
  if (existing?.apiKey && !apiKey) {
5033
6130
  printInfo(
5034
- `Existing key found: ${pc14.dim(existing.apiKey.slice(0, 8) + "...")}`
6131
+ `Existing key found: ${pc16.dim(existing.apiKey.slice(0, 8) + "...")}`
5035
6132
  );
5036
6133
  const replace = await confirm7({
5037
6134
  message: "Replace with a new key?"
5038
6135
  });
5039
- if (isCancel8(replace) || !replace) {
6136
+ if (isCancel9(replace) || !replace) {
5040
6137
  console.log(` No changes made.`);
5041
6138
  return;
5042
6139
  }
5043
6140
  }
5044
6141
  if (!apiKey) {
5045
6142
  console.log(
5046
- ` ${pc14.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
6143
+ ` ${pc16.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
5047
6144
  );
5048
6145
  console.log();
5049
6146
  const keyInput = await text5({
@@ -5057,7 +6154,7 @@ var init_auth = __esm({
5057
6154
  return void 0;
5058
6155
  }
5059
6156
  });
5060
- if (isCancel8(keyInput)) {
6157
+ if (isCancel9(keyInput)) {
5061
6158
  cancel7("Authentication cancelled.");
5062
6159
  process.exit(0);
5063
6160
  }
@@ -5079,7 +6176,7 @@ var init_auth = __esm({
5079
6176
  printError("Invalid API key. Check your key and try again.");
5080
6177
  process.exit(1);
5081
6178
  }
5082
- printStatus(`Authenticated with ${pc14.bold(result.org)}`);
6179
+ printStatus(`Authenticated with ${pc16.bold(result.org)}`);
5083
6180
  } catch {
5084
6181
  printError(
5085
6182
  "Could not reach FlyDocs API. Check your network and try again."
@@ -5094,10 +6191,10 @@ var init_auth = __esm({
5094
6191
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
5095
6192
  lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5096
6193
  });
5097
- printStatus(`Key stored at ${pc14.dim(credentialsPath())}`);
6194
+ printStatus(`Key stored at ${pc16.dim(credentialsPath())}`);
5098
6195
  await checkCredentialPermissions();
5099
6196
  console.log();
5100
- console.log(` ${pc14.bold("Authenticated!")} Key stored globally.`);
6197
+ console.log(` ${pc16.bold("Authenticated!")} Key stored globally.`);
5101
6198
  console.log(` All FlyDocs projects on this machine will use this key.`);
5102
6199
  console.log();
5103
6200
  }
@@ -5105,155 +6202,20 @@ var init_auth = __esm({
5105
6202
  }
5106
6203
  });
5107
6204
 
5108
- // src/commands/sync.ts
5109
- var sync_exports = {};
5110
- __export(sync_exports, {
5111
- default: () => sync_default
5112
- });
5113
- import { defineCommand as defineCommand10 } from "citty";
5114
- import pc15 from "picocolors";
5115
- import { join as join24 } from "path";
5116
- import { mkdir as mkdir11, writeFile as writeFile14 } from "fs/promises";
5117
- var sync_default;
5118
- var init_sync = __esm({
5119
- "src/commands/sync.ts"() {
5120
- "use strict";
5121
- init_ui();
5122
- init_global_config();
5123
- init_config();
5124
- init_config_integrity();
5125
- init_fs_ops();
5126
- init_types();
5127
- init_relay_client();
5128
- sync_default = defineCommand10({
5129
- meta: {
5130
- name: "sync",
5131
- description: "Pull latest config and templates from server"
5132
- },
5133
- args: {
5134
- path: {
5135
- type: "string",
5136
- description: "Path to project directory"
5137
- }
5138
- },
5139
- async run({ args }) {
5140
- const targetDir = args.path ?? process.cwd();
5141
- const changes = [];
5142
- console.log();
5143
- printInfo("Syncing with server...");
5144
- const resolved = await resolveApiKey(void 0, targetDir);
5145
- if (!resolved) {
5146
- printError(
5147
- "No API key found. Run `flydocs auth` or `flydocs init` first."
5148
- );
5149
- process.exit(1);
5150
- }
5151
- const apiKey = resolved.key;
5152
- const cred = await readGlobalCredential();
5153
- const workspaceId = cred?.workspaceId;
5154
- if (!workspaceId) {
5155
- printError(
5156
- "No workspace ID found. Run `flydocs init` to set up your workspace."
5157
- );
5158
- process.exit(1);
5159
- }
5160
- let currentTemplateVersion = 0;
5161
- try {
5162
- const currentConfig = await readAnyConfig(targetDir);
5163
- if (isConfigV2(currentConfig)) {
5164
- currentTemplateVersion = currentConfig.configVersion ?? 0;
5165
- }
5166
- } catch {
5167
- }
5168
- let serverResponse;
5169
- try {
5170
- serverResponse = await fetchConfigV2(apiKey, { workspaceId });
5171
- } catch (err) {
5172
- if (err instanceof RelayError) {
5173
- printWarning(
5174
- `Server unavailable (${err.status}), using cached config.`
5175
- );
5176
- } else {
5177
- printWarning("Server unreachable, using cached config.");
5178
- }
5179
- console.log(` ${pc15.dim("Config may be stale. Retry when connected.")}`);
5180
- return;
5181
- }
5182
- if (!serverResponse.valid) {
5183
- printWarning("Server returned invalid config. Keeping current config.");
5184
- return;
5185
- }
5186
- await mkdir11(join24(targetDir, ".flydocs"), { recursive: true });
5187
- const configWithHash = applyConfigHash(serverResponse.config);
5188
- await writeConfig(targetDir, configWithHash);
5189
- changes.push("Updated .flydocs/config.json");
5190
- const serverTemplateVersion = serverResponse.templates.version;
5191
- if (serverTemplateVersion > currentTemplateVersion) {
5192
- try {
5193
- const templatesResponse = await fetchTemplates(
5194
- apiKey,
5195
- currentTemplateVersion,
5196
- workspaceId
5197
- );
5198
- if (templatesResponse.templates.length > 0) {
5199
- const templatesDir = join24(
5200
- targetDir,
5201
- ".claude",
5202
- "skills",
5203
- "flydocs-workflow",
5204
- "templates"
5205
- );
5206
- await mkdir11(templatesDir, { recursive: true });
5207
- for (const template of templatesResponse.templates) {
5208
- const filename = `${template.type}.md`;
5209
- const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
5210
- const templateDir = join24(templatesDir, subdir);
5211
- await mkdir11(templateDir, { recursive: true });
5212
- await writeFile14(
5213
- join24(templateDir, filename),
5214
- template.content,
5215
- "utf-8"
5216
- );
5217
- }
5218
- changes.push(
5219
- `Updated ${templatesResponse.templates.length} templates (v${currentTemplateVersion} \u2192 v${serverTemplateVersion})`
5220
- );
5221
- }
5222
- } catch (err) {
5223
- if (err instanceof RelayError) {
5224
- printWarning("Could not fetch templates. Will retry on next sync.");
5225
- } else {
5226
- printWarning("Template sync failed. Will retry on next sync.");
5227
- }
5228
- }
5229
- }
5230
- if (changes.length === 0) {
5231
- printStatus("Already up to date.");
5232
- } else {
5233
- for (const change of changes) {
5234
- printStatus(change);
5235
- }
5236
- }
5237
- console.log();
5238
- }
5239
- });
5240
- }
5241
- });
5242
-
5243
6205
  // src/commands/upgrade.ts
5244
6206
  var upgrade_exports = {};
5245
6207
  __export(upgrade_exports, {
5246
6208
  default: () => upgrade_default
5247
6209
  });
5248
- import { defineCommand as defineCommand11 } from "citty";
5249
- import pc16 from "picocolors";
6210
+ import { defineCommand as defineCommand12 } from "citty";
6211
+ import pc17 from "picocolors";
5250
6212
  var upgrade_default;
5251
6213
  var init_upgrade = __esm({
5252
6214
  "src/commands/upgrade.ts"() {
5253
6215
  "use strict";
5254
6216
  init_config();
5255
6217
  init_fs_ops();
5256
- upgrade_default = defineCommand11({
6218
+ upgrade_default = defineCommand12({
5257
6219
  meta: {
5258
6220
  name: "upgrade",
5259
6221
  description: "Learn about FlyDocs Cloud tier and upgrade from local"
@@ -5282,34 +6244,34 @@ var init_upgrade = __esm({
5282
6244
  console.log();
5283
6245
  if (currentTier === "cloud") {
5284
6246
  console.log(
5285
- ` ${pc16.green("\u2713")} You're already on the ${pc16.bold("cloud")} tier.`
6247
+ ` ${pc17.green("\u2713")} You're already on the ${pc17.bold("cloud")} tier.`
5286
6248
  );
5287
6249
  console.log();
5288
6250
  console.log(` Your issues sync with your provider via the relay API.`);
5289
6251
  console.log(
5290
- ` Run ${pc16.cyan("flydocs connect")} to update your connection settings.`
6252
+ ` Run ${pc17.cyan("flydocs connect")} to update your connection settings.`
5291
6253
  );
5292
6254
  console.log();
5293
6255
  return;
5294
6256
  }
5295
- console.log(` ${pc16.bold("FlyDocs Cloud Tier")}`);
6257
+ console.log(` ${pc17.bold("FlyDocs Cloud Tier")}`);
5296
6258
  console.log();
5297
- console.log(` You're currently on the ${pc16.yellow("local")} tier.`);
6259
+ console.log(` You're currently on the ${pc17.yellow("local")} tier.`);
5298
6260
  console.log(` Upgrade to cloud for:`);
5299
6261
  console.log();
5300
- console.log(` ${pc16.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
5301
- console.log(` ${pc16.cyan("\u2192")} Project milestones and cycle management`);
5302
- console.log(` ${pc16.cyan("\u2192")} Team assignment and priority tracking`);
5303
- console.log(` ${pc16.cyan("\u2192")} Project health updates and dashboards`);
5304
- console.log(` ${pc16.cyan("\u2192")} Cross-project issue linking`);
6262
+ console.log(` ${pc17.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
6263
+ console.log(` ${pc17.cyan("\u2192")} Project milestones and cycle management`);
6264
+ console.log(` ${pc17.cyan("\u2192")} Team assignment and priority tracking`);
6265
+ console.log(` ${pc17.cyan("\u2192")} Project health updates and dashboards`);
6266
+ console.log(` ${pc17.cyan("\u2192")} Cross-project issue linking`);
5305
6267
  console.log();
5306
- console.log(` ${pc16.bold("How to upgrade:")}`);
6268
+ console.log(` ${pc17.bold("How to upgrade:")}`);
5307
6269
  console.log();
5308
- console.log(` Option 1: Run ${pc16.cyan("/flydocs-upgrade")} in your IDE`);
6270
+ console.log(` Option 1: Run ${pc17.cyan("/flydocs-upgrade")} in your IDE`);
5309
6271
  console.log(` Guided migration with issue transfer`);
5310
6272
  console.log();
5311
6273
  console.log(
5312
- ` Option 2: Run ${pc16.cyan("flydocs connect")} from terminal`
6274
+ ` Option 2: Run ${pc17.cyan("flydocs connect")} from terminal`
5313
6275
  );
5314
6276
  console.log(` Quick tier swap (no issue migration)`);
5315
6277
  console.log();
@@ -5323,23 +6285,23 @@ var self_update_exports = {};
5323
6285
  __export(self_update_exports, {
5324
6286
  default: () => self_update_default
5325
6287
  });
5326
- import { defineCommand as defineCommand12 } from "citty";
6288
+ import { defineCommand as defineCommand13 } from "citty";
5327
6289
  import { execSync } from "child_process";
5328
- import pc17 from "picocolors";
6290
+ import pc18 from "picocolors";
5329
6291
  var self_update_default;
5330
6292
  var init_self_update = __esm({
5331
6293
  "src/commands/self-update.ts"() {
5332
6294
  "use strict";
5333
6295
  init_constants();
5334
6296
  init_ui();
5335
- self_update_default = defineCommand12({
6297
+ self_update_default = defineCommand13({
5336
6298
  meta: {
5337
6299
  name: "self-update",
5338
6300
  description: "Update FlyDocs CLI to the latest version"
5339
6301
  },
5340
6302
  async run() {
5341
6303
  console.log();
5342
- console.log(` Updating ${pc17.cyan(PACKAGE_NAME)}...`);
6304
+ console.log(` Updating ${pc18.cyan(PACKAGE_NAME)}...`);
5343
6305
  console.log();
5344
6306
  try {
5345
6307
  execSync(`npm install -g ${PACKAGE_NAME}@beta`, {
@@ -5364,15 +6326,15 @@ var telemetry_exports = {};
5364
6326
  __export(telemetry_exports, {
5365
6327
  default: () => telemetry_default
5366
6328
  });
5367
- import { defineCommand as defineCommand13 } from "citty";
5368
- import pc18 from "picocolors";
6329
+ import { defineCommand as defineCommand14 } from "citty";
6330
+ import pc19 from "picocolors";
5369
6331
  var enable, disable, status, telemetry_default;
5370
6332
  var init_telemetry2 = __esm({
5371
6333
  "src/commands/telemetry.ts"() {
5372
6334
  "use strict";
5373
6335
  init_telemetry();
5374
6336
  init_ui();
5375
- enable = defineCommand13({
6337
+ enable = defineCommand14({
5376
6338
  meta: {
5377
6339
  name: "enable",
5378
6340
  description: "Enable anonymous usage analytics"
@@ -5387,7 +6349,7 @@ var init_telemetry2 = __esm({
5387
6349
  }
5388
6350
  }
5389
6351
  });
5390
- disable = defineCommand13({
6352
+ disable = defineCommand14({
5391
6353
  meta: {
5392
6354
  name: "disable",
5393
6355
  description: "Disable anonymous usage analytics"
@@ -5405,7 +6367,7 @@ var init_telemetry2 = __esm({
5405
6367
  }
5406
6368
  }
5407
6369
  });
5408
- status = defineCommand13({
6370
+ status = defineCommand14({
5409
6371
  meta: {
5410
6372
  name: "status",
5411
6373
  description: "Show current telemetry status"
@@ -5413,37 +6375,37 @@ var init_telemetry2 = __esm({
5413
6375
  async run() {
5414
6376
  const info = await getStatus();
5415
6377
  console.log();
5416
- console.log(pc18.bold("Telemetry Status"));
6378
+ console.log(pc19.bold("Telemetry Status"));
5417
6379
  console.log();
5418
6380
  const effectivelyEnabled = info.enabled && !info.envOverride;
5419
6381
  console.log(
5420
- ` Enabled: ${effectivelyEnabled ? pc18.green("yes") : pc18.yellow("no")}`
6382
+ ` Enabled: ${effectivelyEnabled ? pc19.green("yes") : pc19.yellow("no")}`
5421
6383
  );
5422
6384
  if (info.envOverride) {
5423
6385
  console.log(
5424
- ` Env override: ${pc18.yellow("FLYDOCS_TELEMETRY=0 (disabled via env)")}`
6386
+ ` Env override: ${pc19.yellow("FLYDOCS_TELEMETRY=0 (disabled via env)")}`
5425
6387
  );
5426
6388
  }
5427
6389
  if (info.anonymousId) {
5428
- console.log(` Anonymous ID: ${pc18.dim(info.anonymousId)}`);
6390
+ console.log(` Anonymous ID: ${pc19.dim(info.anonymousId)}`);
5429
6391
  } else {
5430
6392
  console.log(
5431
- ` Anonymous ID: ${pc18.dim("(not yet created \u2014 generated on first run)")}`
6393
+ ` Anonymous ID: ${pc19.dim("(not yet created \u2014 generated on first run)")}`
5432
6394
  );
5433
6395
  }
5434
6396
  console.log();
5435
6397
  console.log(
5436
- pc18.dim(
6398
+ pc19.dim(
5437
6399
  " FlyDocs collects anonymous usage analytics to improve the CLI."
5438
6400
  )
5439
6401
  );
5440
6402
  console.log(
5441
- pc18.dim(" No personal data, file contents, or code is ever collected.")
6403
+ pc19.dim(" No personal data, file contents, or code is ever collected.")
5442
6404
  );
5443
6405
  console.log();
5444
6406
  }
5445
6407
  });
5446
- telemetry_default = defineCommand13({
6408
+ telemetry_default = defineCommand14({
5447
6409
  meta: {
5448
6410
  name: "telemetry",
5449
6411
  description: "Manage anonymous usage analytics (enable, disable, status)"
@@ -5459,7 +6421,7 @@ var init_telemetry2 = __esm({
5459
6421
 
5460
6422
  // src/cli.ts
5461
6423
  init_constants();
5462
- import { defineCommand as defineCommand14, runMain } from "citty";
6424
+ import { defineCommand as defineCommand15, runMain } from "citty";
5463
6425
  var SUB_COMMANDS = /* @__PURE__ */ new Set([
5464
6426
  "install",
5465
6427
  "init",
@@ -5470,6 +6432,7 @@ var SUB_COMMANDS = /* @__PURE__ */ new Set([
5470
6432
  "connect",
5471
6433
  "auth",
5472
6434
  "sync",
6435
+ "scan",
5473
6436
  "cleanup",
5474
6437
  "upgrade",
5475
6438
  "self-update",
@@ -5483,7 +6446,7 @@ var firstPositional = userArgs.find((a) => !a.startsWith("-"));
5483
6446
  if (!hasMetaFlag && (!firstPositional || !SUB_COMMANDS.has(firstPositional))) {
5484
6447
  process.argv.splice(2, 0, "install");
5485
6448
  }
5486
- var main = defineCommand14({
6449
+ var main = defineCommand15({
5487
6450
  meta: {
5488
6451
  name: CLI_NAME,
5489
6452
  version: CLI_VERSION,
@@ -5499,6 +6462,7 @@ var main = defineCommand14({
5499
6462
  connect: () => Promise.resolve().then(() => (init_connect(), connect_exports)).then((m) => m.default),
5500
6463
  auth: () => Promise.resolve().then(() => (init_auth(), auth_exports)).then((m) => m.default),
5501
6464
  sync: () => Promise.resolve().then(() => (init_sync(), sync_exports)).then((m) => m.default),
6465
+ scan: () => Promise.resolve().then(() => (init_scan(), scan_exports)).then((m) => m.default),
5502
6466
  cleanup: () => Promise.resolve().then(() => (init_cleanup(), cleanup_exports)).then((m) => m.default),
5503
6467
  upgrade: () => Promise.resolve().then(() => (init_upgrade(), upgrade_exports)).then((m) => m.default),
5504
6468
  "self-update": () => Promise.resolve().then(() => (init_self_update(), self_update_exports)).then((m) => m.default),