@flydocs/cli 0.6.0-alpha.2 → 0.6.0-alpha.21

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.
Files changed (148) hide show
  1. package/dist/cli.js +705 -393
  2. package/package.json +1 -1
  3. package/template/.claude/CLAUDE.md +62 -63
  4. package/template/.claude/agents/implementation-agent.md +1 -1
  5. package/template/.claude/agents/pm-agent.md +1 -1
  6. package/template/.claude/commands/activate.md +1 -1
  7. package/template/.claude/commands/attach.md +1 -1
  8. package/template/.claude/commands/block.md +2 -2
  9. package/template/.claude/commands/capture.md +1 -1
  10. package/template/.claude/commands/close.md +1 -1
  11. package/template/.claude/commands/flydocs-setup.md +387 -74
  12. package/template/.claude/commands/flydocs-upgrade.md +48 -37
  13. package/template/.claude/commands/implement.md +1 -1
  14. package/template/.claude/commands/knowledge.md +61 -0
  15. package/template/.claude/commands/new-project.md +1 -1
  16. package/template/.claude/commands/onboard.md +275 -0
  17. package/template/.claude/commands/project-update.md +1 -1
  18. package/template/.claude/commands/refine.md +1 -1
  19. package/template/.claude/commands/review.md +1 -1
  20. package/template/.claude/commands/start-session.md +1 -1
  21. package/template/.claude/commands/status.md +1 -1
  22. package/template/.claude/commands/validate.md +1 -1
  23. package/template/.claude/commands/wrap-session.md +1 -1
  24. package/template/.claude/hooks/auto-approve.py +132 -0
  25. package/template/.claude/hooks/post-pr-check.py +108 -0
  26. package/template/.claude/hooks/post-transition-check.py +94 -0
  27. package/template/.claude/hooks/prompt-submit.py +513 -0
  28. package/template/.claude/hooks/session-start.py +146 -0
  29. package/template/.claude/hooks/stop-gate.py +109 -0
  30. package/template/.claude/settings.json +41 -4
  31. package/template/.claude/skills/README.md +23 -25
  32. package/template/.claude/skills/flydocs-workflow/SKILL.md +134 -42
  33. package/template/.claude/skills/flydocs-workflow/cursor-rule.mdc +9 -8
  34. package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
  35. package/template/.claude/skills/flydocs-workflow/reference/golden-rules.md +28 -17
  36. package/template/.claude/skills/flydocs-workflow/reference/graph-schema.md +116 -0
  37. package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +120 -0
  38. package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
  39. package/template/.claude/skills/flydocs-workflow/reference/service-descriptor-schema.md +251 -0
  40. package/template/.claude/skills/flydocs-workflow/reference/status-workflow.md +26 -26
  41. package/template/.claude/skills/flydocs-workflow/scripts/_local/__init__.py +0 -0
  42. package/template/.claude/skills/{flydocs-local/scripts/flydocs_api.py → flydocs-workflow/scripts/_local/file_store.py} +137 -47
  43. package/template/.claude/skills/flydocs-workflow/scripts/flydocs_api.py +693 -0
  44. package/template/{.flydocs → .claude/skills/flydocs-workflow}/scripts/generate_manifest.py +4 -4
  45. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_build.py +132 -1
  46. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_query.py +18 -5
  47. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_session.py +1 -10
  48. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_update.py +4 -4
  49. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_utils.py +2 -1
  50. package/template/.claude/skills/flydocs-workflow/scripts/issues.py +489 -0
  51. package/template/.claude/skills/flydocs-workflow/scripts/projects.py +144 -0
  52. package/template/.claude/skills/flydocs-workflow/scripts/pull_services.py +128 -0
  53. package/template/.claude/skills/flydocs-workflow/scripts/push_service.py +132 -0
  54. package/template/.claude/skills/flydocs-workflow/scripts/session.py +54 -0
  55. package/template/.claude/skills/flydocs-workflow/scripts/workspace.py +860 -0
  56. package/template/.claude/skills/flydocs-workflow/session.md +63 -25
  57. package/template/.claude/skills/flydocs-workflow/stages/activate.md +18 -7
  58. package/template/.claude/skills/flydocs-workflow/stages/capture.md +10 -5
  59. package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
  60. package/template/.claude/skills/flydocs-workflow/stages/implement.md +33 -9
  61. package/template/.claude/skills/flydocs-workflow/stages/refine.md +22 -6
  62. package/template/.claude/skills/flydocs-workflow/stages/review.md +16 -4
  63. package/template/.claude/skills/flydocs-workflow/stages/validate.md +3 -1
  64. package/template/.claude/skills/flydocs-workflow/templates/pr/default.md +33 -0
  65. package/template/.cursor/agents/implementation-agent.md +1 -1
  66. package/template/.cursor/agents/pm-agent.md +2 -2
  67. package/template/.cursor/hooks.json +10 -3
  68. package/template/.env.example +6 -6
  69. package/template/.flydocs/config.json +5 -18
  70. package/template/.flydocs/templates/README.md +13 -14
  71. package/template/.flydocs/templates/quick-capture.md +4 -8
  72. package/template/.flydocs/version +1 -1
  73. package/template/AGENTS.md +39 -32
  74. package/template/CHANGELOG.md +39 -0
  75. package/template/flydocs/README.md +1 -3
  76. package/template/flydocs/context/project.md +6 -3
  77. package/template/flydocs/design-system/README.md +3 -3
  78. package/template/flydocs/knowledge/INDEX.md +38 -53
  79. package/template/flydocs/knowledge/README.md +60 -9
  80. package/template/flydocs/knowledge/templates/decision.md +47 -0
  81. package/template/flydocs/knowledge/templates/feature.md +35 -0
  82. package/template/flydocs/knowledge/templates/note.md +25 -0
  83. package/template/manifest.json +24 -20
  84. package/template/.claude/skills/flydocs-cloud/SKILL.md +0 -111
  85. package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +0 -50
  86. package/template/.claude/skills/flydocs-cloud/scripts/assign.py +0 -22
  87. package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +0 -28
  88. package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +0 -22
  89. package/template/.claude/skills/flydocs-cloud/scripts/comment.py +0 -29
  90. package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +0 -63
  91. package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +0 -35
  92. package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +0 -33
  93. package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +0 -39
  94. package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +0 -29
  95. package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +0 -210
  96. package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +0 -24
  97. package/template/.claude/skills/flydocs-cloud/scripts/link.py +0 -28
  98. package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +0 -28
  99. package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +0 -44
  100. package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +0 -19
  101. package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +0 -28
  102. package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +0 -31
  103. package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +0 -19
  104. package/template/.claude/skills/flydocs-cloud/scripts/priority.py +0 -29
  105. package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +0 -45
  106. package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +0 -68
  107. package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +0 -41
  108. package/template/.claude/skills/flydocs-cloud/scripts/transition.py +0 -26
  109. package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +0 -36
  110. package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +0 -82
  111. package/template/.claude/skills/flydocs-context-graph/SKILL.md +0 -87
  112. package/template/.claude/skills/flydocs-context-graph/schema.md +0 -78
  113. package/template/.claude/skills/flydocs-context-graph/scripts/graph_context.py +0 -338
  114. package/template/.claude/skills/flydocs-context7/SKILL.md +0 -105
  115. package/template/.claude/skills/flydocs-context7/cursor-rule.mdc +0 -49
  116. package/template/.claude/skills/flydocs-context7/scripts/context7.py +0 -293
  117. package/template/.claude/skills/flydocs-estimates/SKILL.md +0 -384
  118. package/template/.claude/skills/flydocs-figma/SKILL.md +0 -377
  119. package/template/.claude/skills/flydocs-figma/references/PROMPTING.md +0 -108
  120. package/template/.claude/skills/flydocs-figma/references/TROUBLESHOOTING.md +0 -112
  121. package/template/.claude/skills/flydocs-local/SKILL.md +0 -103
  122. package/template/.claude/skills/flydocs-local/cursor-rule.mdc +0 -43
  123. package/template/.claude/skills/flydocs-local/scripts/assign.py +0 -20
  124. package/template/.claude/skills/flydocs-local/scripts/comment.py +0 -27
  125. package/template/.claude/skills/flydocs-local/scripts/create_issue.py +0 -44
  126. package/template/.claude/skills/flydocs-local/scripts/estimate.py +0 -37
  127. package/template/.claude/skills/flydocs-local/scripts/get_issue.py +0 -20
  128. package/template/.claude/skills/flydocs-local/scripts/link.py +0 -41
  129. package/template/.claude/skills/flydocs-local/scripts/list_issues.py +0 -34
  130. package/template/.claude/skills/flydocs-local/scripts/priority.py +0 -37
  131. package/template/.claude/skills/flydocs-local/scripts/project_update.py +0 -67
  132. package/template/.claude/skills/flydocs-local/scripts/status_summary.py +0 -16
  133. package/template/.claude/skills/flydocs-local/scripts/transition.py +0 -24
  134. package/template/.claude/skills/flydocs-local/scripts/update_description.py +0 -35
  135. package/template/.claude/skills/flydocs-local/scripts/update_issue.py +0 -84
  136. package/template/.flydocs/hooks/auto-approve.py +0 -71
  137. package/template/.flydocs/hooks/prompt-submit.py +0 -277
  138. package/template/.flydocs/scripts/skill_manager.py +0 -541
  139. package/template/.flydocs/templates/bug.md +0 -166
  140. package/template/.flydocs/templates/chore.md +0 -110
  141. package/template/.flydocs/templates/feature.md +0 -173
  142. package/template/.flydocs/templates/idea.md +0 -122
  143. /package/template/{.flydocs → .claude}/hooks/post-edit.py +0 -0
  144. /package/template/.claude/skills/{flydocs-estimates/references → flydocs-workflow/reference}/provider-costs.md +0 -0
  145. /package/template/.claude/skills/flydocs-workflow/templates/{bug.md → issues/bug.md} +0 -0
  146. /package/template/.claude/skills/flydocs-workflow/templates/{chore.md → issues/chore.md} +0 -0
  147. /package/template/.claude/skills/flydocs-workflow/templates/{feature.md → issues/feature.md} +0 -0
  148. /package/template/.claude/skills/flydocs-workflow/templates/{idea.md → issues/idea.md} +0 -0
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.2";
18
+ CLI_VERSION = "0.6.0-alpha.21";
19
19
  CLI_NAME = "flydocs";
20
20
  PACKAGE_NAME = "@flydocs/cli";
21
21
  POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
@@ -50,6 +50,7 @@ async function ensureDirectories(targetDir, tier) {
50
50
  "flydocs/knowledge/decisions",
51
51
  "flydocs/knowledge/notes",
52
52
  "flydocs/knowledge/product",
53
+ "flydocs/knowledge/templates",
53
54
  "flydocs/design-system"
54
55
  ];
55
56
  if (tier === "local") {
@@ -135,8 +136,8 @@ var init_template = __esm({
135
136
 
136
137
  // src/lib/ui.ts
137
138
  import pc2 from "picocolors";
138
- function shadow(text3) {
139
- return `\x1B[38;2;55;45;70m${text3}\x1B[0m`;
139
+ function shadow(text4) {
140
+ return `\x1B[38;2;55;45;70m${text4}\x1B[0m`;
140
141
  }
141
142
  function renderBannerBlock() {
142
143
  const height = BANNER_ROWS.length;
@@ -292,12 +293,13 @@ function extractPreservedValues(config) {
292
293
  return {
293
294
  tier: config.tier,
294
295
  setupComplete: config.setupComplete ?? false,
295
- providerTeamId: config.provider?.teamId ?? null,
296
+ workspaceId: config.workspaceId ?? null,
297
+ configVersion: config.configVersion,
296
298
  workspace: config.workspace ?? {},
297
299
  issueLabels: config.issueLabels ?? {},
298
- statusMapping: config.statusMapping ?? {},
299
300
  detectedStack: config.detectedStack ?? {},
300
301
  skills: config.skills ?? {},
302
+ topology: config.topology,
301
303
  designSystem: config.designSystem ?? null,
302
304
  aiLabor: config.aiLabor ?? {}
303
305
  };
@@ -311,11 +313,9 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
311
313
  config.version = version;
312
314
  config.tier = tierFlag ?? preserved.tier;
313
315
  config.setupComplete = preserved.setupComplete;
314
- if (preserved.providerTeamId !== null) {
315
- if (!config.provider) {
316
- config.provider = { type: "linear", teamId: null };
317
- }
318
- config.provider.teamId = preserved.providerTeamId;
316
+ config.workspaceId = preserved.workspaceId;
317
+ if (preserved.configVersion !== void 0) {
318
+ config.configVersion = preserved.configVersion;
319
319
  }
320
320
  if (Object.keys(preserved.workspace).length > 0) {
321
321
  config.workspace = preserved.workspace;
@@ -323,15 +323,15 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
323
323
  if (Object.keys(preserved.issueLabels).length > 0) {
324
324
  config.issueLabels = preserved.issueLabels;
325
325
  }
326
- if (Object.keys(preserved.statusMapping).length > 0) {
327
- config.statusMapping = preserved.statusMapping;
328
- }
329
326
  if (Object.keys(preserved.detectedStack).length > 0) {
330
327
  config.detectedStack = preserved.detectedStack;
331
328
  }
332
329
  if (Object.keys(preserved.skills).length > 0) {
333
330
  config.skills = preserved.skills;
334
331
  }
332
+ if (preserved.topology !== void 0) {
333
+ config.topology = preserved.topology;
334
+ }
335
335
  if (preserved.designSystem !== null) {
336
336
  config.designSystem = preserved.designSystem;
337
337
  }
@@ -348,75 +348,32 @@ var init_config = __esm({
348
348
 
349
349
  // src/lib/skills.ts
350
350
  import { join as join4 } from "path";
351
- async function installOwnedSkills(templateDir, targetDir, tier) {
351
+ async function installOwnedSkills(templateDir, targetDir, _tier) {
352
352
  const skillsDir = join4(targetDir, ".claude", "skills");
353
353
  const templateSkillsDir = join4(templateDir, ".claude", "skills");
354
354
  await replaceDirectory(
355
- join4(templateSkillsDir, "flydocs-workflow"),
356
- join4(skillsDir, "flydocs-workflow")
355
+ join4(templateSkillsDir, OWNED_SKILL),
356
+ join4(skillsDir, OWNED_SKILL)
357
357
  );
358
- const activeMech = MECHANISM_SKILLS[tier];
359
- const inactiveMech = tier === "local" ? MECHANISM_SKILLS.cloud : MECHANISM_SKILLS.local;
360
- await replaceDirectory(
361
- join4(templateSkillsDir, activeMech),
362
- join4(skillsDir, activeMech)
363
- );
364
- const { rm: rm6 } = await import("fs/promises");
365
- const inactivePath = join4(skillsDir, inactiveMech);
366
- if (await pathExists(inactivePath)) {
367
- await rm6(inactivePath, { recursive: true, force: true });
368
- }
369
- for (const skill of CORE_SKILLS) {
370
- if (skill === "flydocs-workflow") continue;
371
- const src = join4(templateSkillsDir, skill);
372
- if (await pathExists(src)) {
373
- await replaceDirectory(src, join4(skillsDir, skill));
374
- }
375
- }
376
- if (tier === "cloud") {
377
- for (const skill of CLOUD_ONLY_SKILLS) {
378
- const src = join4(templateSkillsDir, skill);
379
- if (await pathExists(src)) {
380
- await replaceDirectory(src, join4(skillsDir, skill));
381
- }
382
- }
383
- }
384
358
  const readmeSrc = join4(templateSkillsDir, "README.md");
385
359
  if (await pathExists(readmeSrc)) {
386
360
  await copyFile(readmeSrc, join4(skillsDir, "README.md"));
387
361
  }
388
362
  }
389
- async function replaceOwnedSkills(templateDir, targetDir, tier) {
363
+ async function replaceOwnedSkills(templateDir, targetDir, _tier) {
390
364
  const skillsDir = join4(targetDir, ".claude", "skills");
391
365
  const templateSkillsDir = join4(templateDir, ".claude", "skills");
392
366
  const { rm: rm6 } = await import("fs/promises");
393
- for (const skill of CORE_SKILLS) {
394
- const src = join4(templateSkillsDir, skill);
395
- if (await pathExists(src)) {
396
- await replaceDirectory(src, join4(skillsDir, skill));
397
- }
398
- }
399
- for (const skill of CLOUD_ONLY_SKILLS) {
400
- const dest = join4(skillsDir, skill);
401
- if (tier === "cloud") {
402
- const src = join4(templateSkillsDir, skill);
403
- if (await pathExists(src)) {
404
- await replaceDirectory(src, dest);
405
- }
406
- } else if (await pathExists(dest)) {
407
- await rm6(dest, { recursive: true, force: true });
408
- }
409
- }
410
- const activeMech = MECHANISM_SKILLS[tier];
411
- const inactiveMech = tier === "local" ? MECHANISM_SKILLS.cloud : MECHANISM_SKILLS.local;
412
- const inactivePath = join4(skillsDir, inactiveMech);
413
- if (await pathExists(inactivePath)) {
414
- await rm6(inactivePath, { recursive: true, force: true });
415
- }
416
367
  await replaceDirectory(
417
- join4(templateSkillsDir, activeMech),
418
- join4(skillsDir, activeMech)
368
+ join4(templateSkillsDir, OWNED_SKILL),
369
+ join4(skillsDir, OWNED_SKILL)
419
370
  );
371
+ for (const legacy of LEGACY_SKILLS) {
372
+ const legacyPath = join4(skillsDir, legacy);
373
+ if (await pathExists(legacyPath)) {
374
+ await rm6(legacyPath, { recursive: true, force: true });
375
+ }
376
+ }
420
377
  const readmeSrc = join4(templateSkillsDir, "README.md");
421
378
  if (await pathExists(readmeSrc)) {
422
379
  await copyFile(readmeSrc, join4(skillsDir, "README.md"));
@@ -430,50 +387,34 @@ async function copyCursorRules(targetDir) {
430
387
  targetDir,
431
388
  ".claude",
432
389
  "skills",
433
- "flydocs-workflow",
390
+ OWNED_SKILL,
434
391
  "cursor-rule.mdc"
435
392
  );
436
393
  if (await pathExists(workflowRule)) {
437
394
  await copyFile(workflowRule, join4(rulesDir, "flydocs-workflow.mdc"));
438
395
  }
439
- for (const mech of ["flydocs-local", "flydocs-cloud"]) {
440
- const mechRule = join4(
441
- targetDir,
442
- ".claude",
443
- "skills",
444
- mech,
445
- "cursor-rule.mdc"
446
- );
447
- if (await pathExists(mechRule)) {
448
- await copyFile(mechRule, join4(rulesDir, "flydocs-mechanism.mdc"));
396
+ const { rm: rm6 } = await import("fs/promises");
397
+ for (const legacy of ["flydocs-mechanism.mdc", "flydocs-context7.mdc"]) {
398
+ const legacyRule = join4(rulesDir, legacy);
399
+ if (await pathExists(legacyRule)) {
400
+ await rm6(legacyRule, { force: true });
449
401
  }
450
402
  }
451
- const context7Rule = join4(
452
- targetDir,
453
- ".claude",
454
- "skills",
455
- "flydocs-context7",
456
- "cursor-rule.mdc"
457
- );
458
- if (await pathExists(context7Rule)) {
459
- await copyFile(context7Rule, join4(rulesDir, "flydocs-context7.mdc"));
460
- }
461
403
  }
462
- var CORE_SKILLS, CLOUD_ONLY_SKILLS, MECHANISM_SKILLS;
404
+ var OWNED_SKILL, LEGACY_SKILLS;
463
405
  var init_skills = __esm({
464
406
  "src/lib/skills.ts"() {
465
407
  "use strict";
466
408
  init_fs_ops();
467
- CORE_SKILLS = [
468
- "flydocs-workflow",
409
+ OWNED_SKILL = "flydocs-workflow";
410
+ LEGACY_SKILLS = [
411
+ "flydocs-cloud",
412
+ "flydocs-local",
469
413
  "flydocs-context-graph",
470
- "flydocs-context7"
414
+ "flydocs-context7",
415
+ "flydocs-figma",
416
+ "flydocs-estimates"
471
417
  ];
472
- CLOUD_ONLY_SKILLS = ["flydocs-figma", "flydocs-estimates"];
473
- MECHANISM_SKILLS = {
474
- local: "flydocs-local",
475
- cloud: "flydocs-cloud"
476
- };
477
418
  }
478
419
  });
479
420
 
@@ -485,6 +426,18 @@ import {
485
426
  writeFile as writeFile2
486
427
  } from "fs/promises";
487
428
  import { join as join5, dirname as dirname2 } from "path";
429
+ async function detectExistingConfigs(targetDir) {
430
+ const existing = [];
431
+ const backupDir = join5(targetDir, BACKUP_ORIGINALS_DIR);
432
+ for (const relativePath of RESTORABLE_FILES) {
433
+ const filePath = join5(targetDir, relativePath);
434
+ const backupPath = join5(backupDir, relativePath);
435
+ if (await pathExists(filePath) && !await pathExists(backupPath)) {
436
+ existing.push(relativePath);
437
+ }
438
+ }
439
+ return existing;
440
+ }
488
441
  async function backupOriginals(targetDir, files = RESTORABLE_FILES) {
489
442
  const backedUp = [];
490
443
  const backupDir = join5(targetDir, BACKUP_ORIGINALS_DIR);
@@ -703,7 +656,9 @@ import { join as join7 } from "path";
703
656
  async function runManifestGeneration(targetDir) {
704
657
  const scriptPath = join7(
705
658
  targetDir,
706
- ".flydocs",
659
+ ".claude",
660
+ "skills",
661
+ "flydocs-workflow",
707
662
  "scripts",
708
663
  "generate_manifest.py"
709
664
  );
@@ -723,7 +678,7 @@ async function runContextGraphBuild(targetDir) {
723
678
  targetDir,
724
679
  ".claude",
725
680
  "skills",
726
- "flydocs-context-graph",
681
+ "flydocs-workflow",
727
682
  "scripts",
728
683
  "graph_build.py"
729
684
  );
@@ -760,8 +715,8 @@ function flushFrontmatterValue(mode, lines) {
760
715
  return lines.join("\n").trim();
761
716
  }
762
717
  }
763
- function parseFrontmatter(text3) {
764
- const match = text3.match(/^---\s*\n([\s\S]*?)\n---/);
718
+ function parseFrontmatter(text4) {
719
+ const match = text4.match(/^---\s*\n([\s\S]*?)\n---/);
765
720
  if (!match) return null;
766
721
  const block = match[1];
767
722
  const result = {};
@@ -947,7 +902,8 @@ function searchCatalog(keyword) {
947
902
  return searchable.includes(lower);
948
903
  });
949
904
  }
950
- async function addSkill(targetDir, source) {
905
+ async function addSkill(targetDir, source, options) {
906
+ const quiet = options?.quiet ?? false;
951
907
  if (isPlatformSkill(source)) {
952
908
  printError(`Cannot install platform skill '${source}'.`);
953
909
  console.log(" Platform skills (flydocs-*) are managed by the installer.");
@@ -965,11 +921,13 @@ async function addSkill(targetDir, source) {
965
921
  console.log(` Remove first: flydocs skills remove ${skillName}`);
966
922
  return;
967
923
  }
968
- console.log();
969
- console.log(
970
- `${pc3.blue("->")} Installing ${pc3.cyan(skillName)} from ${repo}...`
971
- );
972
- console.log();
924
+ if (!quiet) {
925
+ console.log();
926
+ console.log(
927
+ `${pc3.blue("->")} Installing ${pc3.cyan(skillName)} from ${repo}...`
928
+ );
929
+ console.log();
930
+ }
973
931
  let success;
974
932
  try {
975
933
  success = await downloadSkillTree(repo, skillName, skillsDir);
@@ -1003,19 +961,23 @@ async function addSkill(targetDir, source) {
1003
961
  );
1004
962
  return;
1005
963
  }
1006
- if (!fm["triggers"]) {
964
+ if (!fm["triggers"] && !quiet) {
1007
965
  printWarning(
1008
966
  "SKILL.md has no triggers -- skill won't appear in manifest index."
1009
967
  );
1010
968
  }
1011
- printStatus("Downloaded skill files");
969
+ if (!quiet) {
970
+ printStatus("Downloaded skill files");
971
+ }
1012
972
  const cursorRuleSrc = join8(skillsDir, "cursor-rule.mdc");
1013
973
  if (await pathExists(cursorRuleSrc)) {
1014
974
  const cursorRulesDir = join8(targetDir, ".cursor", "rules");
1015
975
  await mkdir3(cursorRulesDir, { recursive: true });
1016
976
  const cursorRuleDest = join8(cursorRulesDir, `${skillName}.mdc`);
1017
977
  await copyFile(cursorRuleSrc, cursorRuleDest);
1018
- printStatus("Installed cursor rule");
978
+ if (!quiet) {
979
+ printStatus("Installed cursor rule");
980
+ }
1019
981
  }
1020
982
  const config = await readConfig(targetDir);
1021
983
  const installed = config.skills?.installed ?? [];
@@ -1027,12 +989,16 @@ async function addSkill(targetDir, source) {
1027
989
  }
1028
990
  config.skills.installed = installed;
1029
991
  await writeConfig(targetDir, config);
1030
- printStatus("Updated config.json");
992
+ if (!quiet) {
993
+ printStatus("Updated config.json");
994
+ }
1031
995
  }
1032
996
  await runManifestGeneration(targetDir);
1033
- console.log();
1034
- printStatus(`Installed ${pc3.cyan(skillName)}`);
1035
- console.log();
997
+ if (!quiet) {
998
+ console.log();
999
+ printStatus(`Installed ${pc3.cyan(skillName)}`);
1000
+ console.log();
1001
+ }
1036
1002
  }
1037
1003
  async function removeSkill(targetDir, name) {
1038
1004
  if (isPlatformSkill(name)) {
@@ -1187,7 +1153,8 @@ async function promptCommunitySkills(targetDir, stack, autoYes) {
1187
1153
  let successCount = 0;
1188
1154
  for (const skill of selected) {
1189
1155
  try {
1190
- await addSkill(targetDir, `${skill.repo}:${skill.name}`);
1156
+ await addSkill(targetDir, `${skill.repo}:${skill.name}`, { quiet: true });
1157
+ printStatus(`Installed ${pc4.cyan(skill.name)}`);
1191
1158
  successCount++;
1192
1159
  } catch {
1193
1160
  printError(`${skill.name} (failed)`);
@@ -1312,10 +1279,19 @@ async function scanDeprecated(targetDir) {
1312
1279
  found.push(dir);
1313
1280
  }
1314
1281
  }
1315
- for (const hook of DEPRECATED_HOOKS) {
1316
- const p = join10(targetDir, ".flydocs", "hooks", hook);
1317
- if (await pathExists(p)) {
1318
- found.push(`.flydocs/hooks/${hook}`);
1282
+ const foundFlydocsDirs = /* @__PURE__ */ new Set();
1283
+ for (const dir of DEPRECATED_FLYDOCS_DIRS) {
1284
+ if (await pathExists(join10(targetDir, dir))) {
1285
+ found.push(dir);
1286
+ foundFlydocsDirs.add(dir);
1287
+ }
1288
+ }
1289
+ if (!foundFlydocsDirs.has(".flydocs/hooks")) {
1290
+ for (const hook of DEPRECATED_HOOKS) {
1291
+ const p = join10(targetDir, ".flydocs", "hooks", hook);
1292
+ if (await pathExists(p)) {
1293
+ found.push(`.flydocs/hooks/${hook}`);
1294
+ }
1319
1295
  }
1320
1296
  }
1321
1297
  for (const rule of DEPRECATED_CURSOR_RULES) {
@@ -1400,7 +1376,7 @@ async function promptCleanup(targetDir, paths) {
1400
1376
  printStatus(`Deleted: ${p}`);
1401
1377
  }
1402
1378
  }
1403
- var DEPRECATED_DIRS, DEPRECATED_FILES, DEPRECATED_SKILLS, DEPRECATED_RULES_DIR, DEPRECATED_HOOKS, DEPRECATED_CURSOR_RULES, DEPRECATED_CURSOR_RULE_DIRS, DEPRECATED_COMMANDS;
1379
+ var DEPRECATED_DIRS, DEPRECATED_FILES, DEPRECATED_SKILLS, DEPRECATED_RULES_DIR, DEPRECATED_FLYDOCS_DIRS, DEPRECATED_HOOKS, DEPRECATED_CURSOR_RULES, DEPRECATED_CURSOR_RULE_DIRS, DEPRECATED_COMMANDS;
1404
1380
  var init_deprecated = __esm({
1405
1381
  "src/lib/deprecated.ts"() {
1406
1382
  "use strict";
@@ -1416,10 +1392,14 @@ var init_deprecated = __esm({
1416
1392
  "figma-mcp"
1417
1393
  ];
1418
1394
  DEPRECATED_RULES_DIR = [".flydocs/rules"];
1395
+ DEPRECATED_FLYDOCS_DIRS = [".flydocs/hooks", ".flydocs/scripts"];
1419
1396
  DEPRECATED_HOOKS = [
1420
1397
  "linear-auto-approve.py",
1421
1398
  "session-end.py",
1422
- "prefer-scripts.py"
1399
+ "prefer-scripts.py",
1400
+ "auto-approve.py",
1401
+ "post-edit.py",
1402
+ "prompt-submit.py"
1423
1403
  ];
1424
1404
  DEPRECATED_CURSOR_RULES = [
1425
1405
  "designer-agent.mdc",
@@ -1466,8 +1446,9 @@ async function ensureGitignore(targetDir) {
1466
1446
  async function migrateGitignore(targetDir) {
1467
1447
  const gitignorePath = join11(targetDir, ".gitignore");
1468
1448
  if (!await pathExists(gitignorePath)) return;
1469
- const content = await readFile6(gitignorePath, "utf-8");
1470
- if (!content.includes("flydocs/context/graph.json")) {
1449
+ let content = await readFile6(gitignorePath, "utf-8");
1450
+ for (const entry of FLYDOCS_GITIGNORE_ENTRIES) {
1451
+ if (content.includes(entry)) continue;
1471
1452
  if (content.includes("# FlyDocs")) {
1472
1453
  const lines = content.split("\n");
1473
1454
  const flyDocsIdx = lines.findIndex((l) => l.trim() === "# FlyDocs");
@@ -1476,37 +1457,64 @@ async function migrateGitignore(targetDir) {
1476
1457
  while (insertIdx < lines.length && lines[insertIdx].trim() !== "" && !lines[insertIdx].startsWith("#")) {
1477
1458
  insertIdx++;
1478
1459
  }
1479
- lines.splice(insertIdx, 0, "flydocs/context/graph.json");
1480
- await writeFile4(gitignorePath, lines.join("\n"), "utf-8");
1460
+ lines.splice(insertIdx, 0, entry);
1461
+ content = lines.join("\n");
1462
+ await writeFile4(gitignorePath, content, "utf-8");
1481
1463
  } else {
1482
- await appendFile(
1483
- gitignorePath,
1484
- "\nflydocs/context/graph.json\n",
1485
- "utf-8"
1486
- );
1464
+ content += `
1465
+ ${entry}
1466
+ `;
1467
+ await writeFile4(gitignorePath, content, "utf-8");
1487
1468
  }
1488
1469
  } else {
1489
- await appendFile(
1490
- gitignorePath,
1491
- "\nflydocs/context/graph.json\n",
1492
- "utf-8"
1493
- );
1470
+ content += `
1471
+ ${entry}
1472
+ `;
1473
+ await writeFile4(gitignorePath, content, "utf-8");
1494
1474
  }
1495
- printStatus("Added flydocs/context/graph.json to .gitignore");
1475
+ printStatus(`Added ${entry} to .gitignore`);
1496
1476
  }
1497
1477
  }
1498
- var FLYDOCS_GITIGNORE_ENTRIES, FULL_GITIGNORE_TEMPLATE;
1478
+ async function ensurePlatformIgnores(targetDir) {
1479
+ for (const filename of PLATFORM_IGNORE_FILES) {
1480
+ const filePath = join11(targetDir, filename);
1481
+ if (!await pathExists(filePath)) continue;
1482
+ const content = await readFile6(filePath, "utf-8");
1483
+ if (content.includes("# FlyDocs")) continue;
1484
+ const section = "\n# FlyDocs \u2014 exclude from builds and deploys\n" + FLYDOCS_DEPLOY_EXCLUSIONS.join("\n") + "\n";
1485
+ await appendFile(filePath, section, "utf-8");
1486
+ printStatus(`Added FlyDocs exclusions to ${filename}`);
1487
+ }
1488
+ }
1489
+ var PLATFORM_IGNORE_FILES, FLYDOCS_DEPLOY_EXCLUSIONS, FLYDOCS_GITIGNORE_ENTRIES, FULL_GITIGNORE_TEMPLATE;
1499
1490
  var init_gitignore = __esm({
1500
1491
  "src/lib/gitignore.ts"() {
1501
1492
  "use strict";
1502
1493
  init_fs_ops();
1503
1494
  init_ui();
1495
+ PLATFORM_IGNORE_FILES = [
1496
+ ".gcloudignore",
1497
+ ".dockerignore",
1498
+ ".vercelignore",
1499
+ ".npmignore"
1500
+ ];
1501
+ FLYDOCS_DEPLOY_EXCLUSIONS = [
1502
+ ".flydocs/",
1503
+ ".claude/",
1504
+ ".cursor/",
1505
+ "flydocs/",
1506
+ "AGENTS.md"
1507
+ ];
1504
1508
  FLYDOCS_GITIGNORE_ENTRIES = [
1505
1509
  ".env",
1506
1510
  ".env.local",
1507
1511
  ".flydocs/session.json",
1508
1512
  ".flydocs/logs/",
1509
1513
  ".flydocs/backup-*/",
1514
+ ".flydocs/me.json",
1515
+ ".flydocs/validation-cache.json",
1516
+ ".flydocs/integrity-cache.json",
1517
+ ".flydocs/session/",
1510
1518
  "flydocs/context/graph.json"
1511
1519
  ];
1512
1520
  FULL_GITIGNORE_TEMPLATE = `# Environment
@@ -1518,6 +1526,10 @@ var init_gitignore = __esm({
1518
1526
  .flydocs/session.json
1519
1527
  .flydocs/logs/
1520
1528
  .flydocs/backup-*/
1529
+ .flydocs/me.json
1530
+ .flydocs/validation-cache.json
1531
+ .flydocs/integrity-cache.json
1532
+ .flydocs/session/
1521
1533
  flydocs/context/graph.json
1522
1534
 
1523
1535
  # Dependencies
@@ -1840,6 +1852,143 @@ var init_telemetry = __esm({
1840
1852
  }
1841
1853
  });
1842
1854
 
1855
+ // src/lib/api-key.ts
1856
+ import { readFile as readFile10, writeFile as writeFile7, appendFile as appendFile2 } from "fs/promises";
1857
+ import { join as join14 } from "path";
1858
+ function detectKeyType(key) {
1859
+ if (key.startsWith("fdk_")) return "relay";
1860
+ if (key.startsWith("lin_api_")) return "direct";
1861
+ return "unknown";
1862
+ }
1863
+ async function validateRelayKey(apiKey) {
1864
+ const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
1865
+ const response = await fetch(`${baseUrl}/auth/validate`, {
1866
+ method: "POST",
1867
+ headers: {
1868
+ Authorization: `Bearer ${apiKey}`,
1869
+ "Content-Type": "application/json"
1870
+ },
1871
+ signal: AbortSignal.timeout(15e3)
1872
+ });
1873
+ if (!response.ok) return { valid: false };
1874
+ const data = await response.json();
1875
+ if (!data.valid) return { valid: false };
1876
+ return { valid: true, org: data.org ?? "your organization" };
1877
+ }
1878
+ async function fetchWorkspaces(apiKey) {
1879
+ const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
1880
+ const response = await fetch(`${baseUrl}/auth/workspaces`, {
1881
+ method: "GET",
1882
+ headers: {
1883
+ Authorization: `Bearer ${apiKey}`
1884
+ },
1885
+ signal: AbortSignal.timeout(15e3)
1886
+ });
1887
+ if (!response.ok) {
1888
+ throw new Error(`Failed to fetch workspaces: ${response.status}`);
1889
+ }
1890
+ const data = await response.json();
1891
+ return data;
1892
+ }
1893
+ async function fetchMe(apiKey) {
1894
+ const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
1895
+ const response = await fetch(`${baseUrl}/auth/me`, {
1896
+ method: "GET",
1897
+ headers: {
1898
+ Authorization: `Bearer ${apiKey}`
1899
+ },
1900
+ signal: AbortSignal.timeout(15e3)
1901
+ });
1902
+ if (!response.ok) {
1903
+ throw new Error(`Failed to fetch user identity: ${response.status}`);
1904
+ }
1905
+ const data = await response.json();
1906
+ return {
1907
+ displayName: data.displayName ?? null,
1908
+ email: data.email ?? null,
1909
+ providerId: data.providerId ?? null,
1910
+ provider: data.provider ?? null,
1911
+ providerIdentities: data.providerIdentities ?? [],
1912
+ preferences: data.preferences ?? {}
1913
+ };
1914
+ }
1915
+ async function validateLinearKey(apiKey) {
1916
+ const response = await fetch("https://api.linear.app/graphql", {
1917
+ method: "POST",
1918
+ headers: {
1919
+ Authorization: apiKey,
1920
+ "Content-Type": "application/json"
1921
+ },
1922
+ body: JSON.stringify({ query: "{ viewer { id name email } }" }),
1923
+ signal: AbortSignal.timeout(15e3)
1924
+ });
1925
+ if (!response.ok) return { valid: false };
1926
+ const data = await response.json();
1927
+ if (!data.data?.viewer) return { valid: false };
1928
+ return {
1929
+ valid: true,
1930
+ name: data.data.viewer.name,
1931
+ email: data.data.viewer.email
1932
+ };
1933
+ }
1934
+ async function storeEnvKey(targetDir, envVarName, value) {
1935
+ const envPath = join14(targetDir, ".env");
1936
+ const envLocalPath = join14(targetDir, ".env.local");
1937
+ const targetEnvPath = await pathExists(envLocalPath) ? envLocalPath : envPath;
1938
+ const pattern = new RegExp(`${envVarName}=.*`);
1939
+ if (await pathExists(targetEnvPath)) {
1940
+ const envContent = await readFile10(targetEnvPath, "utf-8");
1941
+ if (pattern.test(envContent)) {
1942
+ const updated = envContent.replace(pattern, `${envVarName}=${value}`);
1943
+ await writeFile7(targetEnvPath, updated, "utf-8");
1944
+ } else {
1945
+ await appendFile2(targetEnvPath, `
1946
+ ${envVarName}=${value}
1947
+ `);
1948
+ }
1949
+ } else {
1950
+ await writeFile7(targetEnvPath, `${envVarName}=${value}
1951
+ `, "utf-8");
1952
+ }
1953
+ return targetEnvPath === envLocalPath ? ".env.local" : ".env";
1954
+ }
1955
+ var init_api_key = __esm({
1956
+ "src/lib/api-key.ts"() {
1957
+ "use strict";
1958
+ init_fs_ops();
1959
+ }
1960
+ });
1961
+
1962
+ // src/lib/integrity.ts
1963
+ import { readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
1964
+ import { join as join15 } from "path";
1965
+ async function generateIntegrity(targetDir, version) {
1966
+ const manifestPath = join15(targetDir, ".flydocs", "manifest.json");
1967
+ if (!await pathExists(manifestPath)) return;
1968
+ const manifest = JSON.parse(await readFile11(manifestPath, "utf-8"));
1969
+ const ownership = manifest.ownership;
1970
+ if (!ownership) return;
1971
+ const ownedDirs = ownership.owned_directories?.paths ?? [];
1972
+ const ownedFiles = ownership.owned_files?.paths ?? [];
1973
+ const integrity = {
1974
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1975
+ version,
1976
+ ownedFiles,
1977
+ ownedDirectories: ownedDirs
1978
+ };
1979
+ await writeFile8(
1980
+ join15(targetDir, ".flydocs", "integrity.json"),
1981
+ JSON.stringify(integrity, null, 2) + "\n",
1982
+ "utf-8"
1983
+ );
1984
+ }
1985
+ var init_integrity = __esm({
1986
+ "src/lib/integrity.ts"() {
1987
+ "use strict";
1988
+ init_fs_ops();
1989
+ }
1990
+ });
1991
+
1843
1992
  // src/commands/install.ts
1844
1993
  var install_exports = {};
1845
1994
  __export(install_exports, {
@@ -1847,9 +1996,9 @@ __export(install_exports, {
1847
1996
  });
1848
1997
  import { defineCommand } from "citty";
1849
1998
  import { resolve as resolve2 } from "path";
1850
- import { join as join14 } from "path";
1851
- import { mkdir as mkdir7 } from "fs/promises";
1852
- import { confirm as confirm2, select, isCancel as isCancel3, cancel as cancel2 } from "@clack/prompts";
1999
+ import { join as join16 } from "path";
2000
+ import { mkdir as mkdir7, writeFile as writeFile9 } from "fs/promises";
2001
+ import { confirm as confirm2, select, text, isCancel as isCancel3, cancel as cancel2 } from "@clack/prompts";
1853
2002
  import pc6 from "picocolors";
1854
2003
  var install_default;
1855
2004
  var init_install = __esm({
@@ -1868,6 +2017,8 @@ var init_install = __esm({
1868
2017
  init_post_install();
1869
2018
  init_update_check();
1870
2019
  init_telemetry();
2020
+ init_api_key();
2021
+ init_integrity();
1871
2022
  install_default = defineCommand({
1872
2023
  meta: {
1873
2024
  name: "install",
@@ -1929,7 +2080,7 @@ var init_install = __esm({
1929
2080
  process.exit(1);
1930
2081
  }
1931
2082
  tier = args.tier;
1932
- } else if (await pathExists(join14(targetDir, ".flydocs", "config.json"))) {
2083
+ } else if (await pathExists(join16(targetDir, ".flydocs", "config.json"))) {
1933
2084
  try {
1934
2085
  const existing = await readConfig(targetDir);
1935
2086
  if (existing.tier) {
@@ -1951,7 +2102,7 @@ var init_install = __esm({
1951
2102
  {
1952
2103
  value: "cloud",
1953
2104
  label: "Cloud (managed)",
1954
- hint: "Sync with Linear \u2014 run flydocs connect after install"
2105
+ hint: "Sync with Linear, Jira, and more"
1955
2106
  }
1956
2107
  ]
1957
2108
  });
@@ -1978,48 +2129,198 @@ var init_install = __esm({
1978
2129
  }
1979
2130
  printInfo(`Tier: ${tier}`);
1980
2131
  await capture("install_tier_selected", { tier });
1981
- if (!await pathExists(join14(targetDir, ".git"))) {
2132
+ let selectedWorkspaceId = null;
2133
+ let selectedWorkspaceName = null;
2134
+ let apiKey = null;
2135
+ if (tier === "cloud") {
2136
+ console.log();
2137
+ console.log(` ${pc6.bold("Connect to FlyDocs Cloud")}`);
2138
+ console.log();
2139
+ console.log(
2140
+ ` ${pc6.dim("Get your API key from your FlyDocs dashboard (fdk_...)")}`
2141
+ );
2142
+ console.log();
2143
+ const keyInput = await text({
2144
+ message: "Enter your FlyDocs API key",
2145
+ placeholder: "fdk_...",
2146
+ validate(value) {
2147
+ if (!value.trim()) return "API key is required";
2148
+ const type = detectKeyType(value.trim());
2149
+ if (type !== "relay")
2150
+ return "Cloud tier requires a FlyDocs API key (fdk_...)";
2151
+ return void 0;
2152
+ }
2153
+ });
2154
+ if (isCancel3(keyInput)) {
2155
+ cancel2("Installation cancelled.");
2156
+ process.exit(0);
2157
+ }
2158
+ apiKey = keyInput.trim();
2159
+ printInfo("Validating API key...");
2160
+ try {
2161
+ const result = await validateRelayKey(apiKey);
2162
+ if (!result.valid) {
2163
+ printError("Invalid API key or relay API unreachable.");
2164
+ console.log(` Check your key and try again.`);
2165
+ process.exit(1);
2166
+ }
2167
+ printStatus(`Connected to ${pc6.bold(result.org)}`);
2168
+ } catch {
2169
+ printError(
2170
+ "Could not reach relay API. Check your network and try again."
2171
+ );
2172
+ process.exit(1);
2173
+ }
2174
+ const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
2175
+ printStatus(`API key stored in ${pc6.dim(envFile)}`);
2176
+ console.log();
2177
+ printInfo("Fetching workspaces...");
2178
+ try {
2179
+ const workspaces = await fetchWorkspaces(apiKey);
2180
+ if (workspaces.length === 0) {
2181
+ printError(
2182
+ "No workspaces found. Create a workspace in the FlyDocs dashboard first, then re-run install."
2183
+ );
2184
+ process.exit(1);
2185
+ }
2186
+ if (workspaces.length === 1) {
2187
+ selectedWorkspaceId = workspaces[0].id;
2188
+ selectedWorkspaceName = workspaces[0].name;
2189
+ printStatus(
2190
+ `Workspace: ${pc6.bold(selectedWorkspaceName)} (auto-selected)`
2191
+ );
2192
+ } else {
2193
+ const workspaceChoice = await select({
2194
+ message: "Select a workspace",
2195
+ options: workspaces.map((ws) => ({
2196
+ value: ws.id,
2197
+ label: `${ws.name} (${ws.provider.type})`,
2198
+ hint: ws.team.name
2199
+ }))
2200
+ });
2201
+ if (isCancel3(workspaceChoice)) {
2202
+ cancel2("Installation cancelled.");
2203
+ process.exit(0);
2204
+ }
2205
+ selectedWorkspaceId = workspaceChoice;
2206
+ const chosen = workspaces.find((ws) => ws.id === selectedWorkspaceId);
2207
+ selectedWorkspaceName = chosen?.name ?? "Unknown";
2208
+ printStatus(`Workspace: ${pc6.bold(selectedWorkspaceName)}`);
2209
+ }
2210
+ } catch {
2211
+ printError(
2212
+ "Could not fetch workspaces. Check your network and try again."
2213
+ );
2214
+ process.exit(1);
2215
+ }
2216
+ }
2217
+ let skipConfigOverwrite = false;
2218
+ if (!await pathExists(join16(targetDir, ".git"))) {
1982
2219
  printWarning("No git repository detected. Run git init when ready.");
1983
2220
  }
1984
2221
  await ensureDirectories(targetDir, tier);
1985
- const backedUp = await backupOriginals(targetDir);
1986
- if (backedUp.length > 0) {
1987
- printStatus(
1988
- `Backed up ${backedUp.length} existing config file(s) to .flydocs/backup-originals/`
2222
+ if (tier === "cloud" && apiKey) {
2223
+ try {
2224
+ const me = await fetchMe(apiKey);
2225
+ const meData = {
2226
+ displayName: me.displayName,
2227
+ email: me.email,
2228
+ providerId: me.providerId,
2229
+ provider: me.provider,
2230
+ providerIdentities: me.providerIdentities,
2231
+ preferences: me.preferences
2232
+ };
2233
+ const mePath = join16(targetDir, ".flydocs", "me.json");
2234
+ await writeFile9(
2235
+ mePath,
2236
+ JSON.stringify(meData, null, 2) + "\n",
2237
+ "utf-8"
2238
+ );
2239
+ if (me.displayName) {
2240
+ printStatus(`Identity: ${pc6.bold(me.displayName)}`);
2241
+ }
2242
+ } catch {
2243
+ printWarning(
2244
+ "Could not fetch user identity. Run /flydocs-setup to configure later."
2245
+ );
2246
+ }
2247
+ }
2248
+ const existingFiles = await detectExistingConfigs(targetDir);
2249
+ if (existingFiles.length > 0) {
2250
+ console.log();
2251
+ printWarning(
2252
+ `Found ${existingFiles.length} existing config file(s) that FlyDocs will overwrite:`
1989
2253
  );
1990
- for (const f of backedUp) {
1991
- printInfo(` ${f}`);
2254
+ for (const f of existingFiles) {
2255
+ console.log(` ${pc6.dim(f)}`);
2256
+ }
2257
+ console.log();
2258
+ if (!args.yes) {
2259
+ const overwriteChoice = await select({
2260
+ message: "How should FlyDocs handle these files?",
2261
+ options: [
2262
+ {
2263
+ value: "backup",
2264
+ label: "Overwrite (backed up)",
2265
+ hint: "Files are saved to .flydocs/backup-originals/ and restored on uninstall"
2266
+ },
2267
+ {
2268
+ value: "skip",
2269
+ label: "Skip overwriting",
2270
+ hint: "Keep your existing files \u2014 FlyDocs config may be incomplete"
2271
+ }
2272
+ ]
2273
+ });
2274
+ if (isCancel3(overwriteChoice)) {
2275
+ cancel2("Installation cancelled.");
2276
+ process.exit(0);
2277
+ }
2278
+ if (overwriteChoice === "skip") {
2279
+ printInfo(
2280
+ "Keeping existing files. Run /flydocs-setup to merge your config manually."
2281
+ );
2282
+ skipConfigOverwrite = true;
2283
+ }
2284
+ }
2285
+ if (!skipConfigOverwrite) {
2286
+ const backedUp = await backupOriginals(targetDir);
2287
+ if (backedUp.length > 0) {
2288
+ printStatus(
2289
+ `Backed up ${backedUp.length} file(s) to .flydocs/backup-originals/`
2290
+ );
2291
+ }
1992
2292
  }
1993
2293
  }
1994
2294
  console.log("Installing framework files...");
1995
2295
  await replaceDirectory(
1996
- join14(templateDir, ".flydocs", "templates"),
1997
- join14(targetDir, ".flydocs", "templates")
2296
+ join16(templateDir, ".flydocs", "templates"),
2297
+ join16(targetDir, ".flydocs", "templates")
1998
2298
  );
1999
2299
  await replaceDirectory(
2000
- join14(templateDir, ".flydocs", "hooks"),
2001
- join14(targetDir, ".flydocs", "hooks")
2002
- );
2003
- await replaceDirectory(
2004
- join14(templateDir, ".flydocs", "scripts"),
2005
- join14(targetDir, ".flydocs", "scripts")
2300
+ join16(templateDir, ".claude", "hooks"),
2301
+ join16(targetDir, ".claude", "hooks")
2006
2302
  );
2007
2303
  await copyFile(
2008
- join14(templateDir, ".flydocs", "version"),
2009
- join14(targetDir, ".flydocs", "version")
2304
+ join16(templateDir, ".flydocs", "version"),
2305
+ join16(targetDir, ".flydocs", "version")
2010
2306
  );
2011
- const manifestSrc = join14(templateDir, "manifest.json");
2307
+ const manifestSrc = join16(templateDir, "manifest.json");
2012
2308
  if (await pathExists(manifestSrc)) {
2013
- await copyFile(manifestSrc, join14(targetDir, ".flydocs", "manifest.json"));
2309
+ await copyFile(manifestSrc, join16(targetDir, ".flydocs", "manifest.json"));
2014
2310
  }
2015
- const changelogSrc = join14(templateDir, "CHANGELOG.md");
2311
+ const changelogSrc = join16(templateDir, "CHANGELOG.md");
2016
2312
  if (await pathExists(changelogSrc)) {
2017
- await copyFile(changelogSrc, join14(targetDir, ".flydocs", "CHANGELOG.md"));
2313
+ await copyFile(changelogSrc, join16(targetDir, ".flydocs", "CHANGELOG.md"));
2018
2314
  }
2019
- printStatus(".flydocs/templates, hooks, version, manifest, changelog");
2020
- const configPath = join14(targetDir, ".flydocs", "config.json");
2315
+ printStatus(
2316
+ ".flydocs/templates, .claude/hooks, version, manifest, changelog"
2317
+ );
2318
+ const configPath = join16(targetDir, ".flydocs", "config.json");
2021
2319
  if (!await pathExists(configPath)) {
2022
2320
  const config = await createFreshConfig(templateDir, version, tier);
2321
+ if (selectedWorkspaceId) {
2322
+ config.workspaceId = selectedWorkspaceId;
2323
+ }
2023
2324
  await writeConfig(targetDir, config);
2024
2325
  printStatus(`.flydocs/config.json (new, tier: ${tier})`);
2025
2326
  } else if (args.tier) {
@@ -2027,15 +2328,34 @@ var init_install = __esm({
2027
2328
  const existing = await readConfig(targetDir);
2028
2329
  existing.version = version;
2029
2330
  existing.tier = tier;
2331
+ if (selectedWorkspaceId) {
2332
+ existing.workspaceId = selectedWorkspaceId;
2333
+ }
2030
2334
  await writeConfig(targetDir, existing);
2031
2335
  printStatus(`.flydocs/config.json (tier updated: ${tier})`);
2032
2336
  } catch {
2033
2337
  const config = await createFreshConfig(templateDir, version, tier);
2338
+ if (selectedWorkspaceId) {
2339
+ config.workspaceId = selectedWorkspaceId;
2340
+ }
2034
2341
  await writeConfig(targetDir, config);
2035
2342
  printStatus(`.flydocs/config.json (recreated, tier: ${tier})`);
2036
2343
  }
2037
2344
  } else {
2038
- printWarning(".flydocs/config.json exists, preserving");
2345
+ if (selectedWorkspaceId) {
2346
+ try {
2347
+ const existing = await readConfig(targetDir);
2348
+ existing.workspaceId = selectedWorkspaceId;
2349
+ await writeConfig(targetDir, existing);
2350
+ printWarning(
2351
+ ".flydocs/config.json exists, preserving (workspace updated)"
2352
+ );
2353
+ } catch {
2354
+ printWarning(".flydocs/config.json exists, preserving");
2355
+ }
2356
+ } else {
2357
+ printWarning(".flydocs/config.json exists, preserving");
2358
+ }
2039
2359
  }
2040
2360
  console.log();
2041
2361
  console.log(`Installing skills (tier: ${tier})...`);
@@ -2071,20 +2391,20 @@ var init_install = __esm({
2071
2391
  }
2072
2392
  await capture("install_agents_chosen", { install_agents: installAgents });
2073
2393
  if (installAgents) {
2074
- const claudeAgentsSrc = join14(templateDir, ".claude", "agents");
2394
+ const claudeAgentsSrc = join16(templateDir, ".claude", "agents");
2075
2395
  if (await pathExists(claudeAgentsSrc)) {
2076
- await mkdir7(join14(targetDir, ".claude", "agents"), { recursive: true });
2396
+ await mkdir7(join16(targetDir, ".claude", "agents"), { recursive: true });
2077
2397
  await copyDirectoryContents(
2078
2398
  claudeAgentsSrc,
2079
- join14(targetDir, ".claude", "agents")
2399
+ join16(targetDir, ".claude", "agents")
2080
2400
  );
2081
2401
  }
2082
- const cursorAgentsSrc = join14(templateDir, ".cursor", "agents");
2402
+ const cursorAgentsSrc = join16(templateDir, ".cursor", "agents");
2083
2403
  if (await pathExists(cursorAgentsSrc)) {
2084
- await mkdir7(join14(targetDir, ".cursor", "agents"), { recursive: true });
2404
+ await mkdir7(join16(targetDir, ".cursor", "agents"), { recursive: true });
2085
2405
  await copyDirectoryContents(
2086
2406
  cursorAgentsSrc,
2087
- join14(targetDir, ".cursor", "agents")
2407
+ join16(targetDir, ".cursor", "agents")
2088
2408
  );
2089
2409
  }
2090
2410
  printStatus("Sub-agents installed (.claude/agents, .cursor/agents)");
@@ -2094,74 +2414,88 @@ var init_install = __esm({
2094
2414
  console.log();
2095
2415
  console.log("Installing commands and settings...");
2096
2416
  await copyDirectoryContents(
2097
- join14(templateDir, ".claude", "commands"),
2098
- join14(targetDir, ".claude", "commands")
2417
+ join16(templateDir, ".claude", "commands"),
2418
+ join16(targetDir, ".claude", "commands")
2099
2419
  );
2100
- await copyFile(
2101
- join14(templateDir, ".claude", "CLAUDE.md"),
2102
- join14(targetDir, ".claude", "CLAUDE.md")
2103
- );
2104
- await copyFile(
2105
- join14(templateDir, ".claude", "settings.json"),
2106
- join14(targetDir, ".claude", "settings.json")
2107
- );
2108
- printStatus(".claude/ (commands, CLAUDE.md, settings)");
2420
+ if (!skipConfigOverwrite) {
2421
+ await copyFile(
2422
+ join16(templateDir, ".claude", "CLAUDE.md"),
2423
+ join16(targetDir, ".claude", "CLAUDE.md")
2424
+ );
2425
+ await copyFile(
2426
+ join16(templateDir, ".claude", "settings.json"),
2427
+ join16(targetDir, ".claude", "settings.json")
2428
+ );
2429
+ printStatus(".claude/ (commands, CLAUDE.md, settings)");
2430
+ } else {
2431
+ printStatus(".claude/ (commands only \u2014 existing config preserved)");
2432
+ }
2109
2433
  await copyDirectoryContents(
2110
- join14(templateDir, ".claude", "commands"),
2111
- join14(targetDir, ".cursor", "commands")
2112
- );
2113
- await copyFile(
2114
- join14(templateDir, ".cursor", "hooks.json"),
2115
- join14(targetDir, ".cursor", "hooks.json")
2434
+ join16(templateDir, ".claude", "commands"),
2435
+ join16(targetDir, ".cursor", "commands")
2116
2436
  );
2117
- printStatus(".cursor/ (commands, hooks)");
2437
+ if (!skipConfigOverwrite) {
2438
+ await copyFile(
2439
+ join16(templateDir, ".cursor", "hooks.json"),
2440
+ join16(targetDir, ".cursor", "hooks.json")
2441
+ );
2442
+ printStatus(".cursor/ (commands, hooks)");
2443
+ } else {
2444
+ printStatus(".cursor/ (commands only \u2014 existing hooks preserved)");
2445
+ }
2118
2446
  await copyCursorRules(targetDir);
2119
2447
  printStatus(".cursor/rules/");
2120
- await copyFile(
2121
- join14(templateDir, "AGENTS.md"),
2122
- join14(targetDir, "AGENTS.md")
2123
- );
2124
- printStatus("AGENTS.md");
2448
+ if (!skipConfigOverwrite) {
2449
+ await copyFile(
2450
+ join16(templateDir, "AGENTS.md"),
2451
+ join16(targetDir, "AGENTS.md")
2452
+ );
2453
+ printStatus("AGENTS.md");
2454
+ } else {
2455
+ printInfo("AGENTS.md preserved (existing)");
2456
+ }
2125
2457
  await runManifestGeneration(targetDir);
2126
2458
  await runContextGraphBuild(targetDir);
2459
+ await generateIntegrity(targetDir, version);
2460
+ printStatus("Install integrity recorded");
2127
2461
  console.log();
2128
2462
  console.log("Installing project templates...");
2129
2463
  const userFiles = [
2130
2464
  {
2131
- src: join14(templateDir, "flydocs", "context", "project.md"),
2132
- dest: join14(targetDir, "flydocs", "context", "project.md"),
2465
+ src: join16(templateDir, "flydocs", "context", "project.md"),
2466
+ dest: join16(targetDir, "flydocs", "context", "project.md"),
2133
2467
  label: "flydocs/context/project.md"
2134
2468
  },
2135
2469
  {
2136
- src: join14(templateDir, "flydocs", "knowledge", "INDEX.md"),
2137
- dest: join14(targetDir, "flydocs", "knowledge", "INDEX.md"),
2470
+ src: join16(templateDir, "flydocs", "knowledge", "INDEX.md"),
2471
+ dest: join16(targetDir, "flydocs", "knowledge", "INDEX.md"),
2138
2472
  label: "flydocs/knowledge/INDEX.md"
2139
2473
  },
2140
2474
  {
2141
- src: join14(templateDir, "flydocs", "knowledge", "README.md"),
2142
- dest: join14(targetDir, "flydocs", "knowledge", "README.md"),
2475
+ src: join16(templateDir, "flydocs", "knowledge", "README.md"),
2476
+ dest: join16(targetDir, "flydocs", "knowledge", "README.md"),
2143
2477
  label: "flydocs/knowledge/README.md"
2144
2478
  },
2145
2479
  {
2146
- src: join14(
2480
+ src: join16(
2147
2481
  templateDir,
2148
2482
  "flydocs",
2149
2483
  "knowledge",
2150
2484
  "product",
2151
2485
  "personas.md"
2152
2486
  ),
2153
- dest: join14(targetDir, "flydocs", "knowledge", "product", "personas.md"),
2487
+ dest: join16(targetDir, "flydocs", "knowledge", "product", "personas.md"),
2154
2488
  label: "flydocs/knowledge/product/personas.md"
2155
2489
  },
2156
2490
  {
2157
- src: join14(
2491
+ src: join16(
2158
2492
  templateDir,
2159
2493
  "flydocs",
2160
2494
  "knowledge",
2161
2495
  "product",
2162
2496
  "user-flows.md"
2163
2497
  ),
2164
- dest: join14(
2498
+ dest: join16(
2165
2499
  targetDir,
2166
2500
  "flydocs",
2167
2501
  "knowledge",
@@ -2171,18 +2505,57 @@ var init_install = __esm({
2171
2505
  label: "flydocs/knowledge/product/user-flows.md"
2172
2506
  },
2173
2507
  {
2174
- src: join14(templateDir, "flydocs", "design-system", "README.md"),
2175
- dest: join14(targetDir, "flydocs", "design-system", "README.md"),
2508
+ src: join16(
2509
+ templateDir,
2510
+ "flydocs",
2511
+ "knowledge",
2512
+ "templates",
2513
+ "decision.md"
2514
+ ),
2515
+ dest: join16(
2516
+ targetDir,
2517
+ "flydocs",
2518
+ "knowledge",
2519
+ "templates",
2520
+ "decision.md"
2521
+ ),
2522
+ label: "flydocs/knowledge/templates/decision.md"
2523
+ },
2524
+ {
2525
+ src: join16(
2526
+ templateDir,
2527
+ "flydocs",
2528
+ "knowledge",
2529
+ "templates",
2530
+ "feature.md"
2531
+ ),
2532
+ dest: join16(
2533
+ targetDir,
2534
+ "flydocs",
2535
+ "knowledge",
2536
+ "templates",
2537
+ "feature.md"
2538
+ ),
2539
+ label: "flydocs/knowledge/templates/feature.md"
2540
+ },
2541
+ {
2542
+ src: join16(templateDir, "flydocs", "knowledge", "templates", "note.md"),
2543
+ dest: join16(targetDir, "flydocs", "knowledge", "templates", "note.md"),
2544
+ label: "flydocs/knowledge/templates/note.md"
2545
+ },
2546
+ {
2547
+ src: join16(templateDir, "flydocs", "design-system", "README.md"),
2548
+ dest: join16(targetDir, "flydocs", "design-system", "README.md"),
2176
2549
  label: "flydocs/design-system/README.md"
2177
2550
  },
2178
2551
  {
2179
- src: join14(
2552
+ src: join16(
2180
2553
  templateDir,
2181
2554
  "flydocs",
2182
2555
  "design-system",
2183
2556
  "component-patterns.md"
2184
2557
  ),
2185
- dest: join14(
2558
+ dest: join16(
2186
2559
  targetDir,
2187
2560
  "flydocs",
2188
2561
  "design-system",
@@ -2191,13 +2564,13 @@ var init_install = __esm({
2191
2564
  label: "flydocs/design-system/component-patterns.md"
2192
2565
  },
2193
2566
  {
2194
- src: join14(templateDir, "flydocs", "design-system", "token-mapping.md"),
2195
- dest: join14(targetDir, "flydocs", "design-system", "token-mapping.md"),
2567
+ src: join16(templateDir, "flydocs", "design-system", "token-mapping.md"),
2568
+ dest: join16(targetDir, "flydocs", "design-system", "token-mapping.md"),
2196
2569
  label: "flydocs/design-system/token-mapping.md"
2197
2570
  },
2198
2571
  {
2199
- src: join14(templateDir, "flydocs", "README.md"),
2200
- dest: join14(targetDir, "flydocs", "README.md"),
2572
+ src: join16(templateDir, "flydocs", "README.md"),
2573
+ dest: join16(targetDir, "flydocs", "README.md"),
2201
2574
  label: "flydocs/README.md"
2202
2575
  }
2203
2576
  ];
@@ -2211,12 +2584,13 @@ var init_install = __esm({
2211
2584
  }
2212
2585
  }
2213
2586
  }
2214
- const envExampleSrc = join14(templateDir, ".env.example");
2587
+ const envExampleSrc = join16(templateDir, ".env.example");
2215
2588
  if (await pathExists(envExampleSrc)) {
2216
- await copyFile(envExampleSrc, join14(targetDir, ".env.example"));
2589
+ await copyFile(envExampleSrc, join16(targetDir, ".env.example"));
2217
2590
  printStatus(".env.example");
2218
2591
  }
2219
2592
  await ensureGitignore(targetDir);
2593
+ await ensurePlatformIgnores(targetDir);
2220
2594
  console.log();
2221
2595
  console.log("Detecting project stack...");
2222
2596
  const stack = await detectStack(targetDir);
@@ -2253,9 +2627,8 @@ var init_install = __esm({
2253
2627
  `Version: ${version}`,
2254
2628
  "",
2255
2629
  "Next steps:",
2256
- " 1. Run flydocs connect to set up your API key",
2257
- " 2. Run /flydocs-setup to configure your workspace",
2258
- " 3. Start working with /start-session",
2630
+ " 1. Run /flydocs-setup to configure your workspace",
2631
+ " 2. Start working with /start-session",
2259
2632
  "",
2260
2633
  "Docs: https://www.flydocs.ai/docs"
2261
2634
  ];
@@ -2392,9 +2765,9 @@ __export(update_exports, {
2392
2765
  default: () => update_default
2393
2766
  });
2394
2767
  import { defineCommand as defineCommand2 } from "citty";
2395
- import { resolve as resolve3, join as join15 } from "path";
2396
- import { mkdir as mkdir8, cp as cp2, readFile as readFile10, readdir as readdir3, rm as rm4 } from "fs/promises";
2397
- import { select as select2, text, confirm as confirm3, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
2768
+ import { resolve as resolve3, join as join17 } from "path";
2769
+ import { mkdir as mkdir8, cp as cp2, readFile as readFile12, readdir as readdir4, rm as rm4 } from "fs/promises";
2770
+ import { select as select2, text as text2, confirm as confirm3, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
2398
2771
  import pc7 from "picocolors";
2399
2772
  var update_default;
2400
2773
  var init_update = __esm({
@@ -2413,6 +2786,7 @@ var init_update = __esm({
2413
2786
  init_post_install();
2414
2787
  init_update_check();
2415
2788
  init_telemetry();
2789
+ init_integrity();
2416
2790
  update_default = defineCommand2({
2417
2791
  meta: {
2418
2792
  name: "update",
@@ -2482,7 +2856,7 @@ var init_update = __esm({
2482
2856
  if (choice === "cwd") {
2483
2857
  targetDir = process.cwd();
2484
2858
  } else {
2485
- const enteredPath = await text({
2859
+ const enteredPath = await text2({
2486
2860
  message: "Enter project path:"
2487
2861
  });
2488
2862
  if (isCancel4(enteredPath)) {
@@ -2500,9 +2874,9 @@ var init_update = __esm({
2500
2874
  }
2501
2875
  targetDir = resolve3(targetDir);
2502
2876
  process.chdir(targetDir);
2503
- const hasVersion = await pathExists(join15(targetDir, ".flydocs", "version"));
2877
+ const hasVersion = await pathExists(join17(targetDir, ".flydocs", "version"));
2504
2878
  const hasConfig = await pathExists(
2505
- join15(targetDir, ".flydocs", "config.json")
2879
+ join17(targetDir, ".flydocs", "config.json")
2506
2880
  );
2507
2881
  if (!hasVersion && !hasConfig) {
2508
2882
  printError(`Not a FlyDocs project: ${targetDir}`);
@@ -2513,8 +2887,8 @@ var init_update = __esm({
2513
2887
  console.log();
2514
2888
  let currentVersion = "0.1.0";
2515
2889
  if (hasVersion) {
2516
- const vContent = await readFile10(
2517
- join15(targetDir, ".flydocs", "version"),
2890
+ const vContent = await readFile12(
2891
+ join17(targetDir, ".flydocs", "version"),
2518
2892
  "utf-8"
2519
2893
  );
2520
2894
  currentVersion = vContent.trim();
@@ -2548,7 +2922,7 @@ var init_update = __esm({
2548
2922
  });
2549
2923
  console.log(`Updating: v${currentVersion} \u2192 v${version}`);
2550
2924
  console.log();
2551
- const changelogPath = join15(templateDir, "CHANGELOG.md");
2925
+ const changelogPath = join17(templateDir, "CHANGELOG.md");
2552
2926
  const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
2553
2927
  if (whatsNew.length > 0) {
2554
2928
  console.log(pc7.cyan("What's new:"));
@@ -2560,36 +2934,37 @@ var init_update = __esm({
2560
2934
  }
2561
2935
  const now = /* @__PURE__ */ new Date();
2562
2936
  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")}`;
2563
- const backupDir = join15(targetDir, ".flydocs", `backup-${ts}`);
2937
+ const backupDir = join17(targetDir, ".flydocs", `backup-${ts}`);
2564
2938
  await mkdir8(backupDir, { recursive: true });
2565
2939
  if (hasConfig) {
2566
2940
  await cp2(
2567
- join15(targetDir, ".flydocs", "config.json"),
2568
- join15(backupDir, "config.json")
2941
+ join17(targetDir, ".flydocs", "config.json"),
2942
+ join17(backupDir, "config.json")
2569
2943
  );
2570
2944
  printStatus(`Config backed up to .flydocs/backup-${ts}/`);
2571
2945
  }
2572
2946
  try {
2573
- const flydocsDir = join15(targetDir, ".flydocs");
2574
- const entries = await readdir3(flydocsDir);
2947
+ const flydocsDir = join17(targetDir, ".flydocs");
2948
+ const entries = await readdir4(flydocsDir);
2575
2949
  const backups = entries.filter((e) => e.startsWith("backup-")).sort();
2576
2950
  if (backups.length > 3) {
2577
2951
  const toRemove = backups.slice(0, backups.length - 3);
2578
2952
  for (const old of toRemove) {
2579
- await rm4(join15(flydocsDir, old), { recursive: true, force: true });
2953
+ await rm4(join17(flydocsDir, old), { recursive: true, force: true });
2580
2954
  }
2581
2955
  }
2582
2956
  } catch {
2583
2957
  }
2584
2958
  let preserved = {
2585
- tier: "cloud",
2959
+ tier: "local",
2586
2960
  setupComplete: false,
2587
- providerTeamId: null,
2961
+ workspaceId: null,
2962
+ configVersion: void 0,
2588
2963
  workspace: {},
2589
2964
  issueLabels: {},
2590
- statusMapping: {},
2591
2965
  detectedStack: {},
2592
2966
  skills: {},
2967
+ topology: void 0,
2593
2968
  designSystem: null,
2594
2969
  aiLabor: {}
2595
2970
  };
@@ -2611,22 +2986,20 @@ var init_update = __esm({
2611
2986
  } else {
2612
2987
  effectiveTier = preserved.tier;
2613
2988
  }
2989
+ await ensureDirectories(targetDir, effectiveTier);
2614
2990
  console.log("Replacing framework directories...");
2615
2991
  await replaceDirectory(
2616
- join15(templateDir, ".flydocs", "templates"),
2617
- join15(targetDir, ".flydocs", "templates")
2992
+ join17(templateDir, ".flydocs", "templates"),
2993
+ join17(targetDir, ".flydocs", "templates")
2618
2994
  );
2995
+ printStatus(".flydocs/templates");
2619
2996
  await replaceDirectory(
2620
- join15(templateDir, ".flydocs", "hooks"),
2621
- join15(targetDir, ".flydocs", "hooks")
2997
+ join17(templateDir, ".claude", "hooks"),
2998
+ join17(targetDir, ".claude", "hooks")
2622
2999
  );
2623
- await replaceDirectory(
2624
- join15(templateDir, ".flydocs", "scripts"),
2625
- join15(targetDir, ".flydocs", "scripts")
2626
- );
2627
- printStatus(".flydocs/templates, hooks, scripts");
3000
+ printStatus(".claude/hooks");
2628
3001
  const hasExistingAgents = await pathExists(
2629
- join15(targetDir, ".claude", "agents")
3002
+ join17(targetDir, ".claude", "agents")
2630
3003
  );
2631
3004
  let installAgents;
2632
3005
  if (args.yes) {
@@ -2658,20 +3031,20 @@ var init_update = __esm({
2658
3031
  }
2659
3032
  }
2660
3033
  if (installAgents) {
2661
- const claudeAgentsSrc = join15(templateDir, ".claude", "agents");
3034
+ const claudeAgentsSrc = join17(templateDir, ".claude", "agents");
2662
3035
  if (await pathExists(claudeAgentsSrc)) {
2663
- await mkdir8(join15(targetDir, ".claude", "agents"), { recursive: true });
3036
+ await mkdir8(join17(targetDir, ".claude", "agents"), { recursive: true });
2664
3037
  await copyDirectoryContents(
2665
3038
  claudeAgentsSrc,
2666
- join15(targetDir, ".claude", "agents")
3039
+ join17(targetDir, ".claude", "agents")
2667
3040
  );
2668
3041
  }
2669
- const cursorAgentsSrc = join15(templateDir, ".cursor", "agents");
3042
+ const cursorAgentsSrc = join17(templateDir, ".cursor", "agents");
2670
3043
  if (await pathExists(cursorAgentsSrc)) {
2671
- await mkdir8(join15(targetDir, ".cursor", "agents"), { recursive: true });
3044
+ await mkdir8(join17(targetDir, ".cursor", "agents"), { recursive: true });
2672
3045
  await copyDirectoryContents(
2673
3046
  cursorAgentsSrc,
2674
- join15(targetDir, ".cursor", "agents")
3047
+ join17(targetDir, ".cursor", "agents")
2675
3048
  );
2676
3049
  }
2677
3050
  printStatus(
@@ -2685,46 +3058,62 @@ var init_update = __esm({
2685
3058
  console.log();
2686
3059
  console.log("Replacing framework files...");
2687
3060
  await copyFile(
2688
- join15(templateDir, ".claude", "CLAUDE.md"),
2689
- join15(targetDir, ".claude", "CLAUDE.md")
3061
+ join17(templateDir, ".claude", "CLAUDE.md"),
3062
+ join17(targetDir, ".claude", "CLAUDE.md")
2690
3063
  );
2691
3064
  await copyFile(
2692
- join15(templateDir, ".claude", "settings.json"),
2693
- join15(targetDir, ".claude", "settings.json")
3065
+ join17(templateDir, ".claude", "settings.json"),
3066
+ join17(targetDir, ".claude", "settings.json")
2694
3067
  );
2695
3068
  printStatus(".claude/CLAUDE.md, settings.json");
2696
3069
  await copyDirectoryContents(
2697
- join15(templateDir, ".claude", "commands"),
2698
- join15(targetDir, ".claude", "commands")
3070
+ join17(templateDir, ".claude", "commands"),
3071
+ join17(targetDir, ".claude", "commands")
2699
3072
  );
2700
3073
  await copyDirectoryContents(
2701
- join15(templateDir, ".claude", "commands"),
2702
- join15(targetDir, ".cursor", "commands")
3074
+ join17(templateDir, ".claude", "commands"),
3075
+ join17(targetDir, ".cursor", "commands")
2703
3076
  );
2704
3077
  printStatus(".claude/commands, .cursor/commands");
2705
- const skillsReadmeSrc = join15(templateDir, ".claude", "skills", "README.md");
3078
+ const skillsReadmeSrc = join17(templateDir, ".claude", "skills", "README.md");
2706
3079
  if (await pathExists(skillsReadmeSrc)) {
2707
3080
  await copyFile(
2708
3081
  skillsReadmeSrc,
2709
- join15(targetDir, ".claude", "skills", "README.md")
3082
+ join17(targetDir, ".claude", "skills", "README.md")
2710
3083
  );
2711
3084
  }
2712
3085
  printStatus(".claude/skills/README.md");
2713
3086
  await copyFile(
2714
- join15(templateDir, ".cursor", "hooks.json"),
2715
- join15(targetDir, ".cursor", "hooks.json")
3087
+ join17(templateDir, ".cursor", "hooks.json"),
3088
+ join17(targetDir, ".cursor", "hooks.json")
2716
3089
  );
2717
3090
  printStatus(".cursor/hooks.json");
2718
3091
  await copyFile(
2719
- join15(templateDir, "AGENTS.md"),
2720
- join15(targetDir, "AGENTS.md")
3092
+ join17(templateDir, "AGENTS.md"),
3093
+ join17(targetDir, "AGENTS.md")
2721
3094
  );
2722
3095
  printStatus("AGENTS.md");
2723
- const envExampleSrc = join15(templateDir, ".env.example");
3096
+ const envExampleSrc = join17(templateDir, ".env.example");
2724
3097
  if (await pathExists(envExampleSrc)) {
2725
- await copyFile(envExampleSrc, join15(targetDir, ".env.example"));
3098
+ await copyFile(envExampleSrc, join17(targetDir, ".env.example"));
2726
3099
  printStatus(".env.example");
2727
3100
  }
3101
+ const knowledgeTemplatesDir = join17(
3102
+ targetDir,
3103
+ "flydocs",
3104
+ "knowledge",
3105
+ "templates"
3106
+ );
3107
+ if (!await pathExists(knowledgeTemplatesDir)) {
3108
+ await mkdir8(knowledgeTemplatesDir, { recursive: true });
3109
+ }
3110
+ for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
3111
+ const src = join17(templateDir, "flydocs", "knowledge", "templates", tmpl);
3112
+ const dest = join17(knowledgeTemplatesDir, tmpl);
3113
+ if (await pathExists(src) && !await pathExists(dest)) {
3114
+ await copyFile(src, dest);
3115
+ }
3116
+ }
2728
3117
  await runManifestGeneration(targetDir);
2729
3118
  await runContextGraphBuild(targetDir);
2730
3119
  console.log();
@@ -2747,20 +3136,22 @@ var init_update = __esm({
2747
3136
  printWarning("Config merge failed \u2014 config.json preserved as-is");
2748
3137
  }
2749
3138
  await copyFile(
2750
- join15(templateDir, ".flydocs", "version"),
2751
- join15(targetDir, ".flydocs", "version")
3139
+ join17(templateDir, ".flydocs", "version"),
3140
+ join17(targetDir, ".flydocs", "version")
2752
3141
  );
2753
3142
  printStatus(`.flydocs/version \u2192 ${version}`);
2754
- const clSrc = join15(templateDir, "CHANGELOG.md");
3143
+ const clSrc = join17(templateDir, "CHANGELOG.md");
2755
3144
  if (await pathExists(clSrc)) {
2756
- await copyFile(clSrc, join15(targetDir, ".flydocs", "CHANGELOG.md"));
3145
+ await copyFile(clSrc, join17(targetDir, ".flydocs", "CHANGELOG.md"));
2757
3146
  printStatus(".flydocs/CHANGELOG.md");
2758
3147
  }
2759
- const mfSrc = join15(templateDir, "manifest.json");
3148
+ const mfSrc = join17(templateDir, "manifest.json");
2760
3149
  if (await pathExists(mfSrc)) {
2761
- await copyFile(mfSrc, join15(targetDir, ".flydocs", "manifest.json"));
3150
+ await copyFile(mfSrc, join17(targetDir, ".flydocs", "manifest.json"));
2762
3151
  printStatus(".flydocs/manifest.json");
2763
3152
  }
3153
+ await generateIntegrity(targetDir, version);
3154
+ printStatus("Install integrity recorded");
2764
3155
  console.log();
2765
3156
  console.log("Detecting project stack...");
2766
3157
  const stack = await detectStack(targetDir);
@@ -2777,6 +3168,7 @@ var init_update = __esm({
2777
3168
  printInfo("No framework detected in package.json");
2778
3169
  }
2779
3170
  await migrateGitignore(targetDir);
3171
+ await ensurePlatformIgnores(targetDir);
2780
3172
  console.log();
2781
3173
  console.log("Checking for deprecated files...");
2782
3174
  await handleLegacyContext(targetDir);
@@ -2818,21 +3210,21 @@ __export(uninstall_exports, {
2818
3210
  default: () => uninstall_default
2819
3211
  });
2820
3212
  import { defineCommand as defineCommand3 } from "citty";
2821
- import { resolve as resolve4, join as join16 } from "path";
2822
- import { readdir as readdir4, rm as rm5, rename as rename2 } from "fs/promises";
3213
+ import { resolve as resolve4, join as join18 } from "path";
3214
+ import { readdir as readdir5, rm as rm5, rename as rename2 } from "fs/promises";
2823
3215
  import { confirm as confirm4, select as select3, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
2824
3216
  import pc8 from "picocolors";
2825
3217
  async function removeOwnedSkills(targetDir) {
2826
- const skillsDir = join16(targetDir, ".claude", "skills");
3218
+ const skillsDir = join18(targetDir, ".claude", "skills");
2827
3219
  const removed = [];
2828
3220
  if (!await pathExists(skillsDir)) {
2829
3221
  return removed;
2830
3222
  }
2831
3223
  try {
2832
- const entries = await readdir4(skillsDir);
3224
+ const entries = await readdir5(skillsDir);
2833
3225
  for (const entry of entries) {
2834
3226
  if (entry.startsWith(OWNED_SKILL_PREFIX)) {
2835
- await rm5(join16(skillsDir, entry), { recursive: true, force: true });
3227
+ await rm5(join18(skillsDir, entry), { recursive: true, force: true });
2836
3228
  removed.push(`.claude/skills/${entry}`);
2837
3229
  }
2838
3230
  }
@@ -2841,16 +3233,16 @@ async function removeOwnedSkills(targetDir) {
2841
3233
  return removed;
2842
3234
  }
2843
3235
  async function removeOwnedCursorRules(targetDir) {
2844
- const rulesDir = join16(targetDir, ".cursor", "rules");
3236
+ const rulesDir = join18(targetDir, ".cursor", "rules");
2845
3237
  const removed = [];
2846
3238
  if (!await pathExists(rulesDir)) {
2847
3239
  return removed;
2848
3240
  }
2849
3241
  try {
2850
- const entries = await readdir4(rulesDir);
3242
+ const entries = await readdir5(rulesDir);
2851
3243
  for (const entry of entries) {
2852
3244
  if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
2853
- await rm5(join16(rulesDir, entry), { force: true });
3245
+ await rm5(join18(rulesDir, entry), { force: true });
2854
3246
  removed.push(`.cursor/rules/${entry}`);
2855
3247
  }
2856
3248
  }
@@ -2860,7 +3252,7 @@ async function removeOwnedCursorRules(targetDir) {
2860
3252
  }
2861
3253
  async function isEmptyDir(dirPath) {
2862
3254
  try {
2863
- const entries = await readdir4(dirPath);
3255
+ const entries = await readdir5(dirPath);
2864
3256
  return entries.length === 0;
2865
3257
  } catch {
2866
3258
  return false;
@@ -2869,7 +3261,7 @@ async function isEmptyDir(dirPath) {
2869
3261
  async function cleanupEmptyParents(targetDir, dirs) {
2870
3262
  const cleaned = [];
2871
3263
  for (const dir of dirs) {
2872
- const fullPath = join16(targetDir, dir);
3264
+ const fullPath = join18(targetDir, dir);
2873
3265
  if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
2874
3266
  await rm5(fullPath, { recursive: true, force: true });
2875
3267
  cleaned.push(dir);
@@ -2890,9 +3282,11 @@ var init_uninstall = __esm({
2890
3282
  [".claude/settings.json", "file"],
2891
3283
  [".claude/agents", "dir"],
2892
3284
  [".claude/commands", "dir"],
3285
+ [".claude/hooks", "dir"],
2893
3286
  [".claude/skills/README.md", "file"],
2894
3287
  [".cursor/hooks.json", "file"],
2895
3288
  [".cursor/agents", "dir"],
3289
+ [".cursor/commands", "dir"],
2896
3290
  [".flydocs", "dir"],
2897
3291
  ["AGENTS.md", "file"],
2898
3292
  [".env.example", "file"]
@@ -2946,8 +3340,8 @@ var init_uninstall = __esm({
2946
3340
  process.exit(1);
2947
3341
  }
2948
3342
  targetDir = resolve4(targetDir);
2949
- const hasFlydocs = await pathExists(join16(targetDir, ".flydocs"));
2950
- const hasAgentsMd = await pathExists(join16(targetDir, "AGENTS.md"));
3343
+ const hasFlydocs = await pathExists(join18(targetDir, ".flydocs"));
3344
+ const hasAgentsMd = await pathExists(join18(targetDir, "AGENTS.md"));
2951
3345
  if (!hasFlydocs && !hasAgentsMd) {
2952
3346
  printError(`Not a FlyDocs project: ${targetDir}`);
2953
3347
  printInfo("No .flydocs/ directory or AGENTS.md found.");
@@ -2959,7 +3353,7 @@ var init_uninstall = __esm({
2959
3353
  const removeAll = forceAll || args.all;
2960
3354
  const skipPrompts = forceAll || args.yes;
2961
3355
  let contentAction = "preserve";
2962
- const hasUserContent = await pathExists(join16(targetDir, "flydocs"));
3356
+ const hasUserContent = await pathExists(join18(targetDir, "flydocs"));
2963
3357
  if (hasUserContent) {
2964
3358
  if (removeAll) {
2965
3359
  contentAction = "remove";
@@ -3042,7 +3436,7 @@ var init_uninstall = __esm({
3042
3436
  const removedRules = await removeOwnedCursorRules(targetDir);
3043
3437
  result.removed.push(...removedRules);
3044
3438
  for (const [relativePath, type] of ALWAYS_REMOVED) {
3045
- const fullPath = join16(targetDir, relativePath);
3439
+ const fullPath = join18(targetDir, relativePath);
3046
3440
  if (!await pathExists(fullPath)) {
3047
3441
  result.skipped.push(relativePath);
3048
3442
  continue;
@@ -3060,9 +3454,9 @@ var init_uninstall = __esm({
3060
3454
  }
3061
3455
  }
3062
3456
  if (hasUserContent) {
3063
- const flydocsPath = join16(targetDir, "flydocs");
3457
+ const flydocsPath = join18(targetDir, "flydocs");
3064
3458
  if (contentAction === "archive") {
3065
- const archivePath = join16(targetDir, "flydocs-archive");
3459
+ const archivePath = join18(targetDir, "flydocs-archive");
3066
3460
  if (await pathExists(archivePath)) {
3067
3461
  await rm5(archivePath, { recursive: true, force: true });
3068
3462
  }
@@ -3301,70 +3695,9 @@ __export(connect_exports, {
3301
3695
  default: () => connect_default
3302
3696
  });
3303
3697
  import { defineCommand as defineCommand6 } from "citty";
3304
- import { text as text2, confirm as confirm5, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
3698
+ import { text as text3, confirm as confirm5, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
3305
3699
  import pc11 from "picocolors";
3306
- import { readFile as readFile11, writeFile as writeFile7, appendFile as appendFile2 } from "fs/promises";
3307
- import { join as join17 } from "path";
3308
- function detectKeyType(key) {
3309
- if (key.startsWith("fdk_")) return "relay";
3310
- if (key.startsWith("lin_api_")) return "direct";
3311
- return "unknown";
3312
- }
3313
- async function validateRelayKey(apiKey) {
3314
- const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
3315
- const response = await fetch(`${baseUrl}/auth/validate`, {
3316
- method: "POST",
3317
- headers: {
3318
- Authorization: `Bearer ${apiKey}`,
3319
- "Content-Type": "application/json"
3320
- },
3321
- signal: AbortSignal.timeout(15e3)
3322
- });
3323
- if (!response.ok) return { valid: false };
3324
- const data = await response.json();
3325
- if (!data.valid) return { valid: false };
3326
- return { valid: true, org: data.org ?? "your organization" };
3327
- }
3328
- async function validateLinearKey(apiKey) {
3329
- const response = await fetch("https://api.linear.app/graphql", {
3330
- method: "POST",
3331
- headers: {
3332
- Authorization: apiKey,
3333
- "Content-Type": "application/json"
3334
- },
3335
- body: JSON.stringify({ query: "{ viewer { id name email } }" }),
3336
- signal: AbortSignal.timeout(15e3)
3337
- });
3338
- if (!response.ok) return { valid: false };
3339
- const data = await response.json();
3340
- if (!data.data?.viewer) return { valid: false };
3341
- return {
3342
- valid: true,
3343
- name: data.data.viewer.name,
3344
- email: data.data.viewer.email
3345
- };
3346
- }
3347
- async function storeEnvKey(targetDir, envVarName, value) {
3348
- const envPath = join17(targetDir, ".env");
3349
- const envLocalPath = join17(targetDir, ".env.local");
3350
- const targetEnvPath = await pathExists(envLocalPath) ? envLocalPath : envPath;
3351
- const pattern = new RegExp(`${envVarName}=.*`);
3352
- if (await pathExists(targetEnvPath)) {
3353
- const envContent = await readFile11(targetEnvPath, "utf-8");
3354
- if (pattern.test(envContent)) {
3355
- const updated = envContent.replace(pattern, `${envVarName}=${value}`);
3356
- await writeFile7(targetEnvPath, updated, "utf-8");
3357
- } else {
3358
- await appendFile2(targetEnvPath, `
3359
- ${envVarName}=${value}
3360
- `);
3361
- }
3362
- } else {
3363
- await writeFile7(targetEnvPath, `${envVarName}=${value}
3364
- `, "utf-8");
3365
- }
3366
- return targetEnvPath === envLocalPath ? ".env.local" : ".env";
3367
- }
3700
+ import { join as join19 } from "path";
3368
3701
  var connect_default;
3369
3702
  var init_connect = __esm({
3370
3703
  "src/commands/connect.ts"() {
@@ -3373,6 +3706,7 @@ var init_connect = __esm({
3373
3706
  init_fs_ops();
3374
3707
  init_template();
3375
3708
  init_ui();
3709
+ init_api_key();
3376
3710
  connect_default = defineCommand6({
3377
3711
  meta: {
3378
3712
  name: "connect",
@@ -3393,12 +3727,12 @@ var init_connect = __esm({
3393
3727
  },
3394
3728
  key: {
3395
3729
  type: "string",
3396
- description: "API key (fdk_ for relay, lin_api_ for direct Linear)"
3730
+ description: "FlyDocs API key (fdk_...)"
3397
3731
  }
3398
3732
  },
3399
3733
  async run({ args }) {
3400
3734
  const targetDir = args.path ?? process.cwd();
3401
- const configPath = join17(targetDir, ".flydocs", "config.json");
3735
+ const configPath = join19(targetDir, ".flydocs", "config.json");
3402
3736
  if (!await pathExists(configPath)) {
3403
3737
  printError("Not a FlyDocs project (.flydocs/config.json not found).");
3404
3738
  console.log(
@@ -3422,22 +3756,19 @@ var init_connect = __esm({
3422
3756
  console.log(` ${pc11.bold("Connect to FlyDocs Cloud")}`);
3423
3757
  console.log();
3424
3758
  console.log(
3425
- ` ${pc11.dim("FlyDocs API key (fdk_...): Get from your FlyDocs dashboard")}`
3426
- );
3427
- console.log(
3428
- ` ${pc11.dim("Linear API key (lin_api_...): Get from Linear \u2192 Settings \u2192 API")}`
3759
+ ` ${pc11.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
3429
3760
  );
3430
3761
  console.log();
3431
3762
  let apiKey = args.key ?? "";
3432
3763
  if (!apiKey) {
3433
- const keyInput = await text2({
3764
+ const keyInput = await text3({
3434
3765
  message: "Enter your API key",
3435
- placeholder: "fdk_... or lin_api_...",
3766
+ placeholder: "fdk_...",
3436
3767
  validate(value) {
3437
3768
  if (!value.trim()) return "API key is required";
3438
3769
  const type = detectKeyType(value.trim());
3439
3770
  if (type === "unknown")
3440
- return "Key must start with fdk_ (FlyDocs) or lin_api_ (Linear)";
3771
+ return "Key must start with fdk_ (FlyDocs API key)";
3441
3772
  return void 0;
3442
3773
  }
3443
3774
  });
@@ -3449,7 +3780,9 @@ var init_connect = __esm({
3449
3780
  }
3450
3781
  const keyType = detectKeyType(apiKey);
3451
3782
  if (keyType === "unknown") {
3452
- printError("Unrecognized key format. Expected fdk_ or lin_api_ prefix.");
3783
+ printError(
3784
+ "Unrecognized key format. Expected fdk_ prefix (FlyDocs API key)."
3785
+ );
3453
3786
  process.exit(1);
3454
3787
  }
3455
3788
  printInfo("Validating API key...");
@@ -3491,32 +3824,15 @@ var init_connect = __esm({
3491
3824
  }
3492
3825
  const wasLocal = config.tier === "local";
3493
3826
  config.tier = "cloud";
3494
- config.provider = config.provider ?? { type: "linear", teamId: null };
3495
- config.provider.type = "linear";
3827
+ const configRecord = config;
3828
+ delete configRecord.statusMapping;
3829
+ delete configRecord.provider;
3496
3830
  await writeConfig(targetDir, config);
3497
3831
  printStatus("Config updated to cloud tier");
3498
3832
  if (wasLocal) {
3499
- try {
3500
- const templateDir = await resolveTemplatePath(
3501
- args["local-source"] || void 0
3502
- );
3503
- const templateSkillsDir = join17(templateDir, ".claude", "skills");
3504
- const skillsDir = join17(targetDir, ".claude", "skills");
3505
- await replaceDirectory(
3506
- join17(templateSkillsDir, "flydocs-cloud"),
3507
- join17(skillsDir, "flydocs-cloud")
3508
- );
3509
- const { rm: rm6 } = await import("fs/promises");
3510
- const localSkillDir = join17(skillsDir, "flydocs-local");
3511
- if (await pathExists(localSkillDir)) {
3512
- await rm6(localSkillDir, { recursive: true, force: true });
3513
- }
3514
- printStatus("Cloud mechanism skill installed");
3515
- } catch {
3516
- printInfo(
3517
- "Could not swap mechanism skills automatically. Run flydocs update to complete."
3518
- );
3519
- }
3833
+ printInfo(
3834
+ "Tier changed to cloud. Run flydocs update to refresh skill scripts."
3835
+ );
3520
3836
  }
3521
3837
  console.log();
3522
3838
  console.log(
@@ -3579,9 +3895,7 @@ var init_upgrade = __esm({
3579
3895
  ` ${pc12.green("\u2713")} You're already on the ${pc12.bold("cloud")} tier.`
3580
3896
  );
3581
3897
  console.log();
3582
- console.log(
3583
- ` Your issues sync with Linear via the cloud mechanism skill.`
3584
- );
3898
+ console.log(` Your issues sync with your provider via the relay API.`);
3585
3899
  console.log(
3586
3900
  ` Run ${pc12.cyan("flydocs connect")} to update your connection settings.`
3587
3901
  );
@@ -3593,9 +3907,7 @@ var init_upgrade = __esm({
3593
3907
  console.log(` You're currently on the ${pc12.yellow("local")} tier.`);
3594
3908
  console.log(` Upgrade to cloud for:`);
3595
3909
  console.log();
3596
- console.log(
3597
- ` ${pc12.cyan("\u2192")} Issue sync with Linear (Jira coming soon)`
3598
- );
3910
+ console.log(` ${pc12.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
3599
3911
  console.log(` ${pc12.cyan("\u2192")} Project milestones and cycle management`);
3600
3912
  console.log(` ${pc12.cyan("\u2192")} Team assignment and priority tracking`);
3601
3913
  console.log(` ${pc12.cyan("\u2192")} Project health updates and dashboards`);