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

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 +678 -392
  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.20";
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,23 +1457,22 @@ 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
1478
  var FLYDOCS_GITIGNORE_ENTRIES, FULL_GITIGNORE_TEMPLATE;
@@ -1507,6 +1487,10 @@ var init_gitignore = __esm({
1507
1487
  ".flydocs/session.json",
1508
1488
  ".flydocs/logs/",
1509
1489
  ".flydocs/backup-*/",
1490
+ ".flydocs/me.json",
1491
+ ".flydocs/validation-cache.json",
1492
+ ".flydocs/integrity-cache.json",
1493
+ ".flydocs/session/",
1510
1494
  "flydocs/context/graph.json"
1511
1495
  ];
1512
1496
  FULL_GITIGNORE_TEMPLATE = `# Environment
@@ -1518,6 +1502,10 @@ var init_gitignore = __esm({
1518
1502
  .flydocs/session.json
1519
1503
  .flydocs/logs/
1520
1504
  .flydocs/backup-*/
1505
+ .flydocs/me.json
1506
+ .flydocs/validation-cache.json
1507
+ .flydocs/integrity-cache.json
1508
+ .flydocs/session/
1521
1509
  flydocs/context/graph.json
1522
1510
 
1523
1511
  # Dependencies
@@ -1840,6 +1828,143 @@ var init_telemetry = __esm({
1840
1828
  }
1841
1829
  });
1842
1830
 
1831
+ // src/lib/api-key.ts
1832
+ import { readFile as readFile10, writeFile as writeFile7, appendFile as appendFile2 } from "fs/promises";
1833
+ import { join as join14 } from "path";
1834
+ function detectKeyType(key) {
1835
+ if (key.startsWith("fdk_")) return "relay";
1836
+ if (key.startsWith("lin_api_")) return "direct";
1837
+ return "unknown";
1838
+ }
1839
+ async function validateRelayKey(apiKey) {
1840
+ const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
1841
+ const response = await fetch(`${baseUrl}/auth/validate`, {
1842
+ method: "POST",
1843
+ headers: {
1844
+ Authorization: `Bearer ${apiKey}`,
1845
+ "Content-Type": "application/json"
1846
+ },
1847
+ signal: AbortSignal.timeout(15e3)
1848
+ });
1849
+ if (!response.ok) return { valid: false };
1850
+ const data = await response.json();
1851
+ if (!data.valid) return { valid: false };
1852
+ return { valid: true, org: data.org ?? "your organization" };
1853
+ }
1854
+ async function fetchWorkspaces(apiKey) {
1855
+ const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
1856
+ const response = await fetch(`${baseUrl}/auth/workspaces`, {
1857
+ method: "GET",
1858
+ headers: {
1859
+ Authorization: `Bearer ${apiKey}`
1860
+ },
1861
+ signal: AbortSignal.timeout(15e3)
1862
+ });
1863
+ if (!response.ok) {
1864
+ throw new Error(`Failed to fetch workspaces: ${response.status}`);
1865
+ }
1866
+ const data = await response.json();
1867
+ return data;
1868
+ }
1869
+ async function fetchMe(apiKey) {
1870
+ const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
1871
+ const response = await fetch(`${baseUrl}/auth/me`, {
1872
+ method: "GET",
1873
+ headers: {
1874
+ Authorization: `Bearer ${apiKey}`
1875
+ },
1876
+ signal: AbortSignal.timeout(15e3)
1877
+ });
1878
+ if (!response.ok) {
1879
+ throw new Error(`Failed to fetch user identity: ${response.status}`);
1880
+ }
1881
+ const data = await response.json();
1882
+ return {
1883
+ displayName: data.displayName ?? null,
1884
+ email: data.email ?? null,
1885
+ providerId: data.providerId ?? null,
1886
+ provider: data.provider ?? null,
1887
+ providerIdentities: data.providerIdentities ?? [],
1888
+ preferences: data.preferences ?? {}
1889
+ };
1890
+ }
1891
+ async function validateLinearKey(apiKey) {
1892
+ const response = await fetch("https://api.linear.app/graphql", {
1893
+ method: "POST",
1894
+ headers: {
1895
+ Authorization: apiKey,
1896
+ "Content-Type": "application/json"
1897
+ },
1898
+ body: JSON.stringify({ query: "{ viewer { id name email } }" }),
1899
+ signal: AbortSignal.timeout(15e3)
1900
+ });
1901
+ if (!response.ok) return { valid: false };
1902
+ const data = await response.json();
1903
+ if (!data.data?.viewer) return { valid: false };
1904
+ return {
1905
+ valid: true,
1906
+ name: data.data.viewer.name,
1907
+ email: data.data.viewer.email
1908
+ };
1909
+ }
1910
+ async function storeEnvKey(targetDir, envVarName, value) {
1911
+ const envPath = join14(targetDir, ".env");
1912
+ const envLocalPath = join14(targetDir, ".env.local");
1913
+ const targetEnvPath = await pathExists(envLocalPath) ? envLocalPath : envPath;
1914
+ const pattern = new RegExp(`${envVarName}=.*`);
1915
+ if (await pathExists(targetEnvPath)) {
1916
+ const envContent = await readFile10(targetEnvPath, "utf-8");
1917
+ if (pattern.test(envContent)) {
1918
+ const updated = envContent.replace(pattern, `${envVarName}=${value}`);
1919
+ await writeFile7(targetEnvPath, updated, "utf-8");
1920
+ } else {
1921
+ await appendFile2(targetEnvPath, `
1922
+ ${envVarName}=${value}
1923
+ `);
1924
+ }
1925
+ } else {
1926
+ await writeFile7(targetEnvPath, `${envVarName}=${value}
1927
+ `, "utf-8");
1928
+ }
1929
+ return targetEnvPath === envLocalPath ? ".env.local" : ".env";
1930
+ }
1931
+ var init_api_key = __esm({
1932
+ "src/lib/api-key.ts"() {
1933
+ "use strict";
1934
+ init_fs_ops();
1935
+ }
1936
+ });
1937
+
1938
+ // src/lib/integrity.ts
1939
+ import { readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
1940
+ import { join as join15 } from "path";
1941
+ async function generateIntegrity(targetDir, version) {
1942
+ const manifestPath = join15(targetDir, ".flydocs", "manifest.json");
1943
+ if (!await pathExists(manifestPath)) return;
1944
+ const manifest = JSON.parse(await readFile11(manifestPath, "utf-8"));
1945
+ const ownership = manifest.ownership;
1946
+ if (!ownership) return;
1947
+ const ownedDirs = ownership.owned_directories?.paths ?? [];
1948
+ const ownedFiles = ownership.owned_files?.paths ?? [];
1949
+ const integrity = {
1950
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1951
+ version,
1952
+ ownedFiles,
1953
+ ownedDirectories: ownedDirs
1954
+ };
1955
+ await writeFile8(
1956
+ join15(targetDir, ".flydocs", "integrity.json"),
1957
+ JSON.stringify(integrity, null, 2) + "\n",
1958
+ "utf-8"
1959
+ );
1960
+ }
1961
+ var init_integrity = __esm({
1962
+ "src/lib/integrity.ts"() {
1963
+ "use strict";
1964
+ init_fs_ops();
1965
+ }
1966
+ });
1967
+
1843
1968
  // src/commands/install.ts
1844
1969
  var install_exports = {};
1845
1970
  __export(install_exports, {
@@ -1847,9 +1972,9 @@ __export(install_exports, {
1847
1972
  });
1848
1973
  import { defineCommand } from "citty";
1849
1974
  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";
1975
+ import { join as join16 } from "path";
1976
+ import { mkdir as mkdir7, writeFile as writeFile9 } from "fs/promises";
1977
+ import { confirm as confirm2, select, text, isCancel as isCancel3, cancel as cancel2 } from "@clack/prompts";
1853
1978
  import pc6 from "picocolors";
1854
1979
  var install_default;
1855
1980
  var init_install = __esm({
@@ -1868,6 +1993,8 @@ var init_install = __esm({
1868
1993
  init_post_install();
1869
1994
  init_update_check();
1870
1995
  init_telemetry();
1996
+ init_api_key();
1997
+ init_integrity();
1871
1998
  install_default = defineCommand({
1872
1999
  meta: {
1873
2000
  name: "install",
@@ -1929,7 +2056,7 @@ var init_install = __esm({
1929
2056
  process.exit(1);
1930
2057
  }
1931
2058
  tier = args.tier;
1932
- } else if (await pathExists(join14(targetDir, ".flydocs", "config.json"))) {
2059
+ } else if (await pathExists(join16(targetDir, ".flydocs", "config.json"))) {
1933
2060
  try {
1934
2061
  const existing = await readConfig(targetDir);
1935
2062
  if (existing.tier) {
@@ -1951,7 +2078,7 @@ var init_install = __esm({
1951
2078
  {
1952
2079
  value: "cloud",
1953
2080
  label: "Cloud (managed)",
1954
- hint: "Sync with Linear \u2014 run flydocs connect after install"
2081
+ hint: "Sync with Linear, Jira, and more"
1955
2082
  }
1956
2083
  ]
1957
2084
  });
@@ -1978,48 +2105,198 @@ var init_install = __esm({
1978
2105
  }
1979
2106
  printInfo(`Tier: ${tier}`);
1980
2107
  await capture("install_tier_selected", { tier });
1981
- if (!await pathExists(join14(targetDir, ".git"))) {
2108
+ let selectedWorkspaceId = null;
2109
+ let selectedWorkspaceName = null;
2110
+ let apiKey = null;
2111
+ if (tier === "cloud") {
2112
+ console.log();
2113
+ console.log(` ${pc6.bold("Connect to FlyDocs Cloud")}`);
2114
+ console.log();
2115
+ console.log(
2116
+ ` ${pc6.dim("Get your API key from your FlyDocs dashboard (fdk_...)")}`
2117
+ );
2118
+ console.log();
2119
+ const keyInput = await text({
2120
+ message: "Enter your FlyDocs API key",
2121
+ placeholder: "fdk_...",
2122
+ validate(value) {
2123
+ if (!value.trim()) return "API key is required";
2124
+ const type = detectKeyType(value.trim());
2125
+ if (type !== "relay")
2126
+ return "Cloud tier requires a FlyDocs API key (fdk_...)";
2127
+ return void 0;
2128
+ }
2129
+ });
2130
+ if (isCancel3(keyInput)) {
2131
+ cancel2("Installation cancelled.");
2132
+ process.exit(0);
2133
+ }
2134
+ apiKey = keyInput.trim();
2135
+ printInfo("Validating API key...");
2136
+ try {
2137
+ const result = await validateRelayKey(apiKey);
2138
+ if (!result.valid) {
2139
+ printError("Invalid API key or relay API unreachable.");
2140
+ console.log(` Check your key and try again.`);
2141
+ process.exit(1);
2142
+ }
2143
+ printStatus(`Connected to ${pc6.bold(result.org)}`);
2144
+ } catch {
2145
+ printError(
2146
+ "Could not reach relay API. Check your network and try again."
2147
+ );
2148
+ process.exit(1);
2149
+ }
2150
+ const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
2151
+ printStatus(`API key stored in ${pc6.dim(envFile)}`);
2152
+ console.log();
2153
+ printInfo("Fetching workspaces...");
2154
+ try {
2155
+ const workspaces = await fetchWorkspaces(apiKey);
2156
+ if (workspaces.length === 0) {
2157
+ printError(
2158
+ "No workspaces found. Create a workspace in the FlyDocs dashboard first, then re-run install."
2159
+ );
2160
+ process.exit(1);
2161
+ }
2162
+ if (workspaces.length === 1) {
2163
+ selectedWorkspaceId = workspaces[0].id;
2164
+ selectedWorkspaceName = workspaces[0].name;
2165
+ printStatus(
2166
+ `Workspace: ${pc6.bold(selectedWorkspaceName)} (auto-selected)`
2167
+ );
2168
+ } else {
2169
+ const workspaceChoice = await select({
2170
+ message: "Select a workspace",
2171
+ options: workspaces.map((ws) => ({
2172
+ value: ws.id,
2173
+ label: `${ws.name} (${ws.provider.type})`,
2174
+ hint: ws.team.name
2175
+ }))
2176
+ });
2177
+ if (isCancel3(workspaceChoice)) {
2178
+ cancel2("Installation cancelled.");
2179
+ process.exit(0);
2180
+ }
2181
+ selectedWorkspaceId = workspaceChoice;
2182
+ const chosen = workspaces.find((ws) => ws.id === selectedWorkspaceId);
2183
+ selectedWorkspaceName = chosen?.name ?? "Unknown";
2184
+ printStatus(`Workspace: ${pc6.bold(selectedWorkspaceName)}`);
2185
+ }
2186
+ } catch {
2187
+ printError(
2188
+ "Could not fetch workspaces. Check your network and try again."
2189
+ );
2190
+ process.exit(1);
2191
+ }
2192
+ }
2193
+ let skipConfigOverwrite = false;
2194
+ if (!await pathExists(join16(targetDir, ".git"))) {
1982
2195
  printWarning("No git repository detected. Run git init when ready.");
1983
2196
  }
1984
2197
  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/`
2198
+ if (tier === "cloud" && apiKey) {
2199
+ try {
2200
+ const me = await fetchMe(apiKey);
2201
+ const meData = {
2202
+ displayName: me.displayName,
2203
+ email: me.email,
2204
+ providerId: me.providerId,
2205
+ provider: me.provider,
2206
+ providerIdentities: me.providerIdentities,
2207
+ preferences: me.preferences
2208
+ };
2209
+ const mePath = join16(targetDir, ".flydocs", "me.json");
2210
+ await writeFile9(
2211
+ mePath,
2212
+ JSON.stringify(meData, null, 2) + "\n",
2213
+ "utf-8"
2214
+ );
2215
+ if (me.displayName) {
2216
+ printStatus(`Identity: ${pc6.bold(me.displayName)}`);
2217
+ }
2218
+ } catch {
2219
+ printWarning(
2220
+ "Could not fetch user identity. Run /flydocs-setup to configure later."
2221
+ );
2222
+ }
2223
+ }
2224
+ const existingFiles = await detectExistingConfigs(targetDir);
2225
+ if (existingFiles.length > 0) {
2226
+ console.log();
2227
+ printWarning(
2228
+ `Found ${existingFiles.length} existing config file(s) that FlyDocs will overwrite:`
1989
2229
  );
1990
- for (const f of backedUp) {
1991
- printInfo(` ${f}`);
2230
+ for (const f of existingFiles) {
2231
+ console.log(` ${pc6.dim(f)}`);
2232
+ }
2233
+ console.log();
2234
+ if (!args.yes) {
2235
+ const overwriteChoice = await select({
2236
+ message: "How should FlyDocs handle these files?",
2237
+ options: [
2238
+ {
2239
+ value: "backup",
2240
+ label: "Overwrite (backed up)",
2241
+ hint: "Files are saved to .flydocs/backup-originals/ and restored on uninstall"
2242
+ },
2243
+ {
2244
+ value: "skip",
2245
+ label: "Skip overwriting",
2246
+ hint: "Keep your existing files \u2014 FlyDocs config may be incomplete"
2247
+ }
2248
+ ]
2249
+ });
2250
+ if (isCancel3(overwriteChoice)) {
2251
+ cancel2("Installation cancelled.");
2252
+ process.exit(0);
2253
+ }
2254
+ if (overwriteChoice === "skip") {
2255
+ printInfo(
2256
+ "Keeping existing files. Run /flydocs-setup to merge your config manually."
2257
+ );
2258
+ skipConfigOverwrite = true;
2259
+ }
2260
+ }
2261
+ if (!skipConfigOverwrite) {
2262
+ const backedUp = await backupOriginals(targetDir);
2263
+ if (backedUp.length > 0) {
2264
+ printStatus(
2265
+ `Backed up ${backedUp.length} file(s) to .flydocs/backup-originals/`
2266
+ );
2267
+ }
1992
2268
  }
1993
2269
  }
1994
2270
  console.log("Installing framework files...");
1995
2271
  await replaceDirectory(
1996
- join14(templateDir, ".flydocs", "templates"),
1997
- join14(targetDir, ".flydocs", "templates")
1998
- );
1999
- await replaceDirectory(
2000
- join14(templateDir, ".flydocs", "hooks"),
2001
- join14(targetDir, ".flydocs", "hooks")
2272
+ join16(templateDir, ".flydocs", "templates"),
2273
+ join16(targetDir, ".flydocs", "templates")
2002
2274
  );
2003
2275
  await replaceDirectory(
2004
- join14(templateDir, ".flydocs", "scripts"),
2005
- join14(targetDir, ".flydocs", "scripts")
2276
+ join16(templateDir, ".claude", "hooks"),
2277
+ join16(targetDir, ".claude", "hooks")
2006
2278
  );
2007
2279
  await copyFile(
2008
- join14(templateDir, ".flydocs", "version"),
2009
- join14(targetDir, ".flydocs", "version")
2280
+ join16(templateDir, ".flydocs", "version"),
2281
+ join16(targetDir, ".flydocs", "version")
2010
2282
  );
2011
- const manifestSrc = join14(templateDir, "manifest.json");
2283
+ const manifestSrc = join16(templateDir, "manifest.json");
2012
2284
  if (await pathExists(manifestSrc)) {
2013
- await copyFile(manifestSrc, join14(targetDir, ".flydocs", "manifest.json"));
2285
+ await copyFile(manifestSrc, join16(targetDir, ".flydocs", "manifest.json"));
2014
2286
  }
2015
- const changelogSrc = join14(templateDir, "CHANGELOG.md");
2287
+ const changelogSrc = join16(templateDir, "CHANGELOG.md");
2016
2288
  if (await pathExists(changelogSrc)) {
2017
- await copyFile(changelogSrc, join14(targetDir, ".flydocs", "CHANGELOG.md"));
2289
+ await copyFile(changelogSrc, join16(targetDir, ".flydocs", "CHANGELOG.md"));
2018
2290
  }
2019
- printStatus(".flydocs/templates, hooks, version, manifest, changelog");
2020
- const configPath = join14(targetDir, ".flydocs", "config.json");
2291
+ printStatus(
2292
+ ".flydocs/templates, .claude/hooks, version, manifest, changelog"
2293
+ );
2294
+ const configPath = join16(targetDir, ".flydocs", "config.json");
2021
2295
  if (!await pathExists(configPath)) {
2022
2296
  const config = await createFreshConfig(templateDir, version, tier);
2297
+ if (selectedWorkspaceId) {
2298
+ config.workspaceId = selectedWorkspaceId;
2299
+ }
2023
2300
  await writeConfig(targetDir, config);
2024
2301
  printStatus(`.flydocs/config.json (new, tier: ${tier})`);
2025
2302
  } else if (args.tier) {
@@ -2027,15 +2304,34 @@ var init_install = __esm({
2027
2304
  const existing = await readConfig(targetDir);
2028
2305
  existing.version = version;
2029
2306
  existing.tier = tier;
2307
+ if (selectedWorkspaceId) {
2308
+ existing.workspaceId = selectedWorkspaceId;
2309
+ }
2030
2310
  await writeConfig(targetDir, existing);
2031
2311
  printStatus(`.flydocs/config.json (tier updated: ${tier})`);
2032
2312
  } catch {
2033
2313
  const config = await createFreshConfig(templateDir, version, tier);
2314
+ if (selectedWorkspaceId) {
2315
+ config.workspaceId = selectedWorkspaceId;
2316
+ }
2034
2317
  await writeConfig(targetDir, config);
2035
2318
  printStatus(`.flydocs/config.json (recreated, tier: ${tier})`);
2036
2319
  }
2037
2320
  } else {
2038
- printWarning(".flydocs/config.json exists, preserving");
2321
+ if (selectedWorkspaceId) {
2322
+ try {
2323
+ const existing = await readConfig(targetDir);
2324
+ existing.workspaceId = selectedWorkspaceId;
2325
+ await writeConfig(targetDir, existing);
2326
+ printWarning(
2327
+ ".flydocs/config.json exists, preserving (workspace updated)"
2328
+ );
2329
+ } catch {
2330
+ printWarning(".flydocs/config.json exists, preserving");
2331
+ }
2332
+ } else {
2333
+ printWarning(".flydocs/config.json exists, preserving");
2334
+ }
2039
2335
  }
2040
2336
  console.log();
2041
2337
  console.log(`Installing skills (tier: ${tier})...`);
@@ -2071,20 +2367,20 @@ var init_install = __esm({
2071
2367
  }
2072
2368
  await capture("install_agents_chosen", { install_agents: installAgents });
2073
2369
  if (installAgents) {
2074
- const claudeAgentsSrc = join14(templateDir, ".claude", "agents");
2370
+ const claudeAgentsSrc = join16(templateDir, ".claude", "agents");
2075
2371
  if (await pathExists(claudeAgentsSrc)) {
2076
- await mkdir7(join14(targetDir, ".claude", "agents"), { recursive: true });
2372
+ await mkdir7(join16(targetDir, ".claude", "agents"), { recursive: true });
2077
2373
  await copyDirectoryContents(
2078
2374
  claudeAgentsSrc,
2079
- join14(targetDir, ".claude", "agents")
2375
+ join16(targetDir, ".claude", "agents")
2080
2376
  );
2081
2377
  }
2082
- const cursorAgentsSrc = join14(templateDir, ".cursor", "agents");
2378
+ const cursorAgentsSrc = join16(templateDir, ".cursor", "agents");
2083
2379
  if (await pathExists(cursorAgentsSrc)) {
2084
- await mkdir7(join14(targetDir, ".cursor", "agents"), { recursive: true });
2380
+ await mkdir7(join16(targetDir, ".cursor", "agents"), { recursive: true });
2085
2381
  await copyDirectoryContents(
2086
2382
  cursorAgentsSrc,
2087
- join14(targetDir, ".cursor", "agents")
2383
+ join16(targetDir, ".cursor", "agents")
2088
2384
  );
2089
2385
  }
2090
2386
  printStatus("Sub-agents installed (.claude/agents, .cursor/agents)");
@@ -2094,74 +2390,88 @@ var init_install = __esm({
2094
2390
  console.log();
2095
2391
  console.log("Installing commands and settings...");
2096
2392
  await copyDirectoryContents(
2097
- join14(templateDir, ".claude", "commands"),
2098
- join14(targetDir, ".claude", "commands")
2099
- );
2100
- await copyFile(
2101
- join14(templateDir, ".claude", "CLAUDE.md"),
2102
- join14(targetDir, ".claude", "CLAUDE.md")
2393
+ join16(templateDir, ".claude", "commands"),
2394
+ join16(targetDir, ".claude", "commands")
2103
2395
  );
2104
- await copyFile(
2105
- join14(templateDir, ".claude", "settings.json"),
2106
- join14(targetDir, ".claude", "settings.json")
2107
- );
2108
- printStatus(".claude/ (commands, CLAUDE.md, settings)");
2396
+ if (!skipConfigOverwrite) {
2397
+ await copyFile(
2398
+ join16(templateDir, ".claude", "CLAUDE.md"),
2399
+ join16(targetDir, ".claude", "CLAUDE.md")
2400
+ );
2401
+ await copyFile(
2402
+ join16(templateDir, ".claude", "settings.json"),
2403
+ join16(targetDir, ".claude", "settings.json")
2404
+ );
2405
+ printStatus(".claude/ (commands, CLAUDE.md, settings)");
2406
+ } else {
2407
+ printStatus(".claude/ (commands only \u2014 existing config preserved)");
2408
+ }
2109
2409
  await copyDirectoryContents(
2110
- join14(templateDir, ".claude", "commands"),
2111
- join14(targetDir, ".cursor", "commands")
2410
+ join16(templateDir, ".claude", "commands"),
2411
+ join16(targetDir, ".cursor", "commands")
2112
2412
  );
2113
- await copyFile(
2114
- join14(templateDir, ".cursor", "hooks.json"),
2115
- join14(targetDir, ".cursor", "hooks.json")
2116
- );
2117
- printStatus(".cursor/ (commands, hooks)");
2413
+ if (!skipConfigOverwrite) {
2414
+ await copyFile(
2415
+ join16(templateDir, ".cursor", "hooks.json"),
2416
+ join16(targetDir, ".cursor", "hooks.json")
2417
+ );
2418
+ printStatus(".cursor/ (commands, hooks)");
2419
+ } else {
2420
+ printStatus(".cursor/ (commands only \u2014 existing hooks preserved)");
2421
+ }
2118
2422
  await copyCursorRules(targetDir);
2119
2423
  printStatus(".cursor/rules/");
2120
- await copyFile(
2121
- join14(templateDir, "AGENTS.md"),
2122
- join14(targetDir, "AGENTS.md")
2123
- );
2124
- printStatus("AGENTS.md");
2424
+ if (!skipConfigOverwrite) {
2425
+ await copyFile(
2426
+ join16(templateDir, "AGENTS.md"),
2427
+ join16(targetDir, "AGENTS.md")
2428
+ );
2429
+ printStatus("AGENTS.md");
2430
+ } else {
2431
+ printInfo("AGENTS.md preserved (existing)");
2432
+ }
2125
2433
  await runManifestGeneration(targetDir);
2126
2434
  await runContextGraphBuild(targetDir);
2435
+ await generateIntegrity(targetDir, version);
2436
+ printStatus("Install integrity recorded");
2127
2437
  console.log();
2128
2438
  console.log("Installing project templates...");
2129
2439
  const userFiles = [
2130
2440
  {
2131
- src: join14(templateDir, "flydocs", "context", "project.md"),
2132
- dest: join14(targetDir, "flydocs", "context", "project.md"),
2441
+ src: join16(templateDir, "flydocs", "context", "project.md"),
2442
+ dest: join16(targetDir, "flydocs", "context", "project.md"),
2133
2443
  label: "flydocs/context/project.md"
2134
2444
  },
2135
2445
  {
2136
- src: join14(templateDir, "flydocs", "knowledge", "INDEX.md"),
2137
- dest: join14(targetDir, "flydocs", "knowledge", "INDEX.md"),
2446
+ src: join16(templateDir, "flydocs", "knowledge", "INDEX.md"),
2447
+ dest: join16(targetDir, "flydocs", "knowledge", "INDEX.md"),
2138
2448
  label: "flydocs/knowledge/INDEX.md"
2139
2449
  },
2140
2450
  {
2141
- src: join14(templateDir, "flydocs", "knowledge", "README.md"),
2142
- dest: join14(targetDir, "flydocs", "knowledge", "README.md"),
2451
+ src: join16(templateDir, "flydocs", "knowledge", "README.md"),
2452
+ dest: join16(targetDir, "flydocs", "knowledge", "README.md"),
2143
2453
  label: "flydocs/knowledge/README.md"
2144
2454
  },
2145
2455
  {
2146
- src: join14(
2456
+ src: join16(
2147
2457
  templateDir,
2148
2458
  "flydocs",
2149
2459
  "knowledge",
2150
2460
  "product",
2151
2461
  "personas.md"
2152
2462
  ),
2153
- dest: join14(targetDir, "flydocs", "knowledge", "product", "personas.md"),
2463
+ dest: join16(targetDir, "flydocs", "knowledge", "product", "personas.md"),
2154
2464
  label: "flydocs/knowledge/product/personas.md"
2155
2465
  },
2156
2466
  {
2157
- src: join14(
2467
+ src: join16(
2158
2468
  templateDir,
2159
2469
  "flydocs",
2160
2470
  "knowledge",
2161
2471
  "product",
2162
2472
  "user-flows.md"
2163
2473
  ),
2164
- dest: join14(
2474
+ dest: join16(
2165
2475
  targetDir,
2166
2476
  "flydocs",
2167
2477
  "knowledge",
@@ -2171,18 +2481,57 @@ var init_install = __esm({
2171
2481
  label: "flydocs/knowledge/product/user-flows.md"
2172
2482
  },
2173
2483
  {
2174
- src: join14(templateDir, "flydocs", "design-system", "README.md"),
2175
- dest: join14(targetDir, "flydocs", "design-system", "README.md"),
2484
+ src: join16(
2485
+ templateDir,
2486
+ "flydocs",
2487
+ "knowledge",
2488
+ "templates",
2489
+ "decision.md"
2490
+ ),
2491
+ dest: join16(
2492
+ targetDir,
2493
+ "flydocs",
2494
+ "knowledge",
2495
+ "templates",
2496
+ "decision.md"
2497
+ ),
2498
+ label: "flydocs/knowledge/templates/decision.md"
2499
+ },
2500
+ {
2501
+ src: join16(
2502
+ templateDir,
2503
+ "flydocs",
2504
+ "knowledge",
2505
+ "templates",
2506
+ "feature.md"
2507
+ ),
2508
+ dest: join16(
2509
+ targetDir,
2510
+ "flydocs",
2511
+ "knowledge",
2512
+ "templates",
2513
+ "feature.md"
2514
+ ),
2515
+ label: "flydocs/knowledge/templates/feature.md"
2516
+ },
2517
+ {
2518
+ src: join16(templateDir, "flydocs", "knowledge", "templates", "note.md"),
2519
+ dest: join16(targetDir, "flydocs", "knowledge", "templates", "note.md"),
2520
+ label: "flydocs/knowledge/templates/note.md"
2521
+ },
2522
+ {
2523
+ src: join16(templateDir, "flydocs", "design-system", "README.md"),
2524
+ dest: join16(targetDir, "flydocs", "design-system", "README.md"),
2176
2525
  label: "flydocs/design-system/README.md"
2177
2526
  },
2178
2527
  {
2179
- src: join14(
2528
+ src: join16(
2180
2529
  templateDir,
2181
2530
  "flydocs",
2182
2531
  "design-system",
2183
2532
  "component-patterns.md"
2184
2533
  ),
2185
- dest: join14(
2534
+ dest: join16(
2186
2535
  targetDir,
2187
2536
  "flydocs",
2188
2537
  "design-system",
@@ -2191,13 +2540,13 @@ var init_install = __esm({
2191
2540
  label: "flydocs/design-system/component-patterns.md"
2192
2541
  },
2193
2542
  {
2194
- src: join14(templateDir, "flydocs", "design-system", "token-mapping.md"),
2195
- dest: join14(targetDir, "flydocs", "design-system", "token-mapping.md"),
2543
+ src: join16(templateDir, "flydocs", "design-system", "token-mapping.md"),
2544
+ dest: join16(targetDir, "flydocs", "design-system", "token-mapping.md"),
2196
2545
  label: "flydocs/design-system/token-mapping.md"
2197
2546
  },
2198
2547
  {
2199
- src: join14(templateDir, "flydocs", "README.md"),
2200
- dest: join14(targetDir, "flydocs", "README.md"),
2548
+ src: join16(templateDir, "flydocs", "README.md"),
2549
+ dest: join16(targetDir, "flydocs", "README.md"),
2201
2550
  label: "flydocs/README.md"
2202
2551
  }
2203
2552
  ];
@@ -2211,9 +2560,9 @@ var init_install = __esm({
2211
2560
  }
2212
2561
  }
2213
2562
  }
2214
- const envExampleSrc = join14(templateDir, ".env.example");
2563
+ const envExampleSrc = join16(templateDir, ".env.example");
2215
2564
  if (await pathExists(envExampleSrc)) {
2216
- await copyFile(envExampleSrc, join14(targetDir, ".env.example"));
2565
+ await copyFile(envExampleSrc, join16(targetDir, ".env.example"));
2217
2566
  printStatus(".env.example");
2218
2567
  }
2219
2568
  await ensureGitignore(targetDir);
@@ -2253,9 +2602,8 @@ var init_install = __esm({
2253
2602
  `Version: ${version}`,
2254
2603
  "",
2255
2604
  "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",
2605
+ " 1. Run /flydocs-setup to configure your workspace",
2606
+ " 2. Start working with /start-session",
2259
2607
  "",
2260
2608
  "Docs: https://www.flydocs.ai/docs"
2261
2609
  ];
@@ -2392,9 +2740,9 @@ __export(update_exports, {
2392
2740
  default: () => update_default
2393
2741
  });
2394
2742
  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";
2743
+ import { resolve as resolve3, join as join17 } from "path";
2744
+ import { mkdir as mkdir8, cp as cp2, readFile as readFile12, readdir as readdir4, rm as rm4 } from "fs/promises";
2745
+ import { select as select2, text as text2, confirm as confirm3, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
2398
2746
  import pc7 from "picocolors";
2399
2747
  var update_default;
2400
2748
  var init_update = __esm({
@@ -2413,6 +2761,7 @@ var init_update = __esm({
2413
2761
  init_post_install();
2414
2762
  init_update_check();
2415
2763
  init_telemetry();
2764
+ init_integrity();
2416
2765
  update_default = defineCommand2({
2417
2766
  meta: {
2418
2767
  name: "update",
@@ -2482,7 +2831,7 @@ var init_update = __esm({
2482
2831
  if (choice === "cwd") {
2483
2832
  targetDir = process.cwd();
2484
2833
  } else {
2485
- const enteredPath = await text({
2834
+ const enteredPath = await text2({
2486
2835
  message: "Enter project path:"
2487
2836
  });
2488
2837
  if (isCancel4(enteredPath)) {
@@ -2500,9 +2849,9 @@ var init_update = __esm({
2500
2849
  }
2501
2850
  targetDir = resolve3(targetDir);
2502
2851
  process.chdir(targetDir);
2503
- const hasVersion = await pathExists(join15(targetDir, ".flydocs", "version"));
2852
+ const hasVersion = await pathExists(join17(targetDir, ".flydocs", "version"));
2504
2853
  const hasConfig = await pathExists(
2505
- join15(targetDir, ".flydocs", "config.json")
2854
+ join17(targetDir, ".flydocs", "config.json")
2506
2855
  );
2507
2856
  if (!hasVersion && !hasConfig) {
2508
2857
  printError(`Not a FlyDocs project: ${targetDir}`);
@@ -2513,8 +2862,8 @@ var init_update = __esm({
2513
2862
  console.log();
2514
2863
  let currentVersion = "0.1.0";
2515
2864
  if (hasVersion) {
2516
- const vContent = await readFile10(
2517
- join15(targetDir, ".flydocs", "version"),
2865
+ const vContent = await readFile12(
2866
+ join17(targetDir, ".flydocs", "version"),
2518
2867
  "utf-8"
2519
2868
  );
2520
2869
  currentVersion = vContent.trim();
@@ -2548,7 +2897,7 @@ var init_update = __esm({
2548
2897
  });
2549
2898
  console.log(`Updating: v${currentVersion} \u2192 v${version}`);
2550
2899
  console.log();
2551
- const changelogPath = join15(templateDir, "CHANGELOG.md");
2900
+ const changelogPath = join17(templateDir, "CHANGELOG.md");
2552
2901
  const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
2553
2902
  if (whatsNew.length > 0) {
2554
2903
  console.log(pc7.cyan("What's new:"));
@@ -2560,36 +2909,37 @@ var init_update = __esm({
2560
2909
  }
2561
2910
  const now = /* @__PURE__ */ new Date();
2562
2911
  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}`);
2912
+ const backupDir = join17(targetDir, ".flydocs", `backup-${ts}`);
2564
2913
  await mkdir8(backupDir, { recursive: true });
2565
2914
  if (hasConfig) {
2566
2915
  await cp2(
2567
- join15(targetDir, ".flydocs", "config.json"),
2568
- join15(backupDir, "config.json")
2916
+ join17(targetDir, ".flydocs", "config.json"),
2917
+ join17(backupDir, "config.json")
2569
2918
  );
2570
2919
  printStatus(`Config backed up to .flydocs/backup-${ts}/`);
2571
2920
  }
2572
2921
  try {
2573
- const flydocsDir = join15(targetDir, ".flydocs");
2574
- const entries = await readdir3(flydocsDir);
2922
+ const flydocsDir = join17(targetDir, ".flydocs");
2923
+ const entries = await readdir4(flydocsDir);
2575
2924
  const backups = entries.filter((e) => e.startsWith("backup-")).sort();
2576
2925
  if (backups.length > 3) {
2577
2926
  const toRemove = backups.slice(0, backups.length - 3);
2578
2927
  for (const old of toRemove) {
2579
- await rm4(join15(flydocsDir, old), { recursive: true, force: true });
2928
+ await rm4(join17(flydocsDir, old), { recursive: true, force: true });
2580
2929
  }
2581
2930
  }
2582
2931
  } catch {
2583
2932
  }
2584
2933
  let preserved = {
2585
- tier: "cloud",
2934
+ tier: "local",
2586
2935
  setupComplete: false,
2587
- providerTeamId: null,
2936
+ workspaceId: null,
2937
+ configVersion: void 0,
2588
2938
  workspace: {},
2589
2939
  issueLabels: {},
2590
- statusMapping: {},
2591
2940
  detectedStack: {},
2592
2941
  skills: {},
2942
+ topology: void 0,
2593
2943
  designSystem: null,
2594
2944
  aiLabor: {}
2595
2945
  };
@@ -2611,22 +2961,20 @@ var init_update = __esm({
2611
2961
  } else {
2612
2962
  effectiveTier = preserved.tier;
2613
2963
  }
2964
+ await ensureDirectories(targetDir, effectiveTier);
2614
2965
  console.log("Replacing framework directories...");
2615
2966
  await replaceDirectory(
2616
- join15(templateDir, ".flydocs", "templates"),
2617
- join15(targetDir, ".flydocs", "templates")
2967
+ join17(templateDir, ".flydocs", "templates"),
2968
+ join17(targetDir, ".flydocs", "templates")
2618
2969
  );
2970
+ printStatus(".flydocs/templates");
2619
2971
  await replaceDirectory(
2620
- join15(templateDir, ".flydocs", "hooks"),
2621
- join15(targetDir, ".flydocs", "hooks")
2972
+ join17(templateDir, ".claude", "hooks"),
2973
+ join17(targetDir, ".claude", "hooks")
2622
2974
  );
2623
- await replaceDirectory(
2624
- join15(templateDir, ".flydocs", "scripts"),
2625
- join15(targetDir, ".flydocs", "scripts")
2626
- );
2627
- printStatus(".flydocs/templates, hooks, scripts");
2975
+ printStatus(".claude/hooks");
2628
2976
  const hasExistingAgents = await pathExists(
2629
- join15(targetDir, ".claude", "agents")
2977
+ join17(targetDir, ".claude", "agents")
2630
2978
  );
2631
2979
  let installAgents;
2632
2980
  if (args.yes) {
@@ -2658,20 +3006,20 @@ var init_update = __esm({
2658
3006
  }
2659
3007
  }
2660
3008
  if (installAgents) {
2661
- const claudeAgentsSrc = join15(templateDir, ".claude", "agents");
3009
+ const claudeAgentsSrc = join17(templateDir, ".claude", "agents");
2662
3010
  if (await pathExists(claudeAgentsSrc)) {
2663
- await mkdir8(join15(targetDir, ".claude", "agents"), { recursive: true });
3011
+ await mkdir8(join17(targetDir, ".claude", "agents"), { recursive: true });
2664
3012
  await copyDirectoryContents(
2665
3013
  claudeAgentsSrc,
2666
- join15(targetDir, ".claude", "agents")
3014
+ join17(targetDir, ".claude", "agents")
2667
3015
  );
2668
3016
  }
2669
- const cursorAgentsSrc = join15(templateDir, ".cursor", "agents");
3017
+ const cursorAgentsSrc = join17(templateDir, ".cursor", "agents");
2670
3018
  if (await pathExists(cursorAgentsSrc)) {
2671
- await mkdir8(join15(targetDir, ".cursor", "agents"), { recursive: true });
3019
+ await mkdir8(join17(targetDir, ".cursor", "agents"), { recursive: true });
2672
3020
  await copyDirectoryContents(
2673
3021
  cursorAgentsSrc,
2674
- join15(targetDir, ".cursor", "agents")
3022
+ join17(targetDir, ".cursor", "agents")
2675
3023
  );
2676
3024
  }
2677
3025
  printStatus(
@@ -2685,46 +3033,62 @@ var init_update = __esm({
2685
3033
  console.log();
2686
3034
  console.log("Replacing framework files...");
2687
3035
  await copyFile(
2688
- join15(templateDir, ".claude", "CLAUDE.md"),
2689
- join15(targetDir, ".claude", "CLAUDE.md")
3036
+ join17(templateDir, ".claude", "CLAUDE.md"),
3037
+ join17(targetDir, ".claude", "CLAUDE.md")
2690
3038
  );
2691
3039
  await copyFile(
2692
- join15(templateDir, ".claude", "settings.json"),
2693
- join15(targetDir, ".claude", "settings.json")
3040
+ join17(templateDir, ".claude", "settings.json"),
3041
+ join17(targetDir, ".claude", "settings.json")
2694
3042
  );
2695
3043
  printStatus(".claude/CLAUDE.md, settings.json");
2696
3044
  await copyDirectoryContents(
2697
- join15(templateDir, ".claude", "commands"),
2698
- join15(targetDir, ".claude", "commands")
3045
+ join17(templateDir, ".claude", "commands"),
3046
+ join17(targetDir, ".claude", "commands")
2699
3047
  );
2700
3048
  await copyDirectoryContents(
2701
- join15(templateDir, ".claude", "commands"),
2702
- join15(targetDir, ".cursor", "commands")
3049
+ join17(templateDir, ".claude", "commands"),
3050
+ join17(targetDir, ".cursor", "commands")
2703
3051
  );
2704
3052
  printStatus(".claude/commands, .cursor/commands");
2705
- const skillsReadmeSrc = join15(templateDir, ".claude", "skills", "README.md");
3053
+ const skillsReadmeSrc = join17(templateDir, ".claude", "skills", "README.md");
2706
3054
  if (await pathExists(skillsReadmeSrc)) {
2707
3055
  await copyFile(
2708
3056
  skillsReadmeSrc,
2709
- join15(targetDir, ".claude", "skills", "README.md")
3057
+ join17(targetDir, ".claude", "skills", "README.md")
2710
3058
  );
2711
3059
  }
2712
3060
  printStatus(".claude/skills/README.md");
2713
3061
  await copyFile(
2714
- join15(templateDir, ".cursor", "hooks.json"),
2715
- join15(targetDir, ".cursor", "hooks.json")
3062
+ join17(templateDir, ".cursor", "hooks.json"),
3063
+ join17(targetDir, ".cursor", "hooks.json")
2716
3064
  );
2717
3065
  printStatus(".cursor/hooks.json");
2718
3066
  await copyFile(
2719
- join15(templateDir, "AGENTS.md"),
2720
- join15(targetDir, "AGENTS.md")
3067
+ join17(templateDir, "AGENTS.md"),
3068
+ join17(targetDir, "AGENTS.md")
2721
3069
  );
2722
3070
  printStatus("AGENTS.md");
2723
- const envExampleSrc = join15(templateDir, ".env.example");
3071
+ const envExampleSrc = join17(templateDir, ".env.example");
2724
3072
  if (await pathExists(envExampleSrc)) {
2725
- await copyFile(envExampleSrc, join15(targetDir, ".env.example"));
3073
+ await copyFile(envExampleSrc, join17(targetDir, ".env.example"));
2726
3074
  printStatus(".env.example");
2727
3075
  }
3076
+ const knowledgeTemplatesDir = join17(
3077
+ targetDir,
3078
+ "flydocs",
3079
+ "knowledge",
3080
+ "templates"
3081
+ );
3082
+ if (!await pathExists(knowledgeTemplatesDir)) {
3083
+ await mkdir8(knowledgeTemplatesDir, { recursive: true });
3084
+ }
3085
+ for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
3086
+ const src = join17(templateDir, "flydocs", "knowledge", "templates", tmpl);
3087
+ const dest = join17(knowledgeTemplatesDir, tmpl);
3088
+ if (await pathExists(src) && !await pathExists(dest)) {
3089
+ await copyFile(src, dest);
3090
+ }
3091
+ }
2728
3092
  await runManifestGeneration(targetDir);
2729
3093
  await runContextGraphBuild(targetDir);
2730
3094
  console.log();
@@ -2747,20 +3111,22 @@ var init_update = __esm({
2747
3111
  printWarning("Config merge failed \u2014 config.json preserved as-is");
2748
3112
  }
2749
3113
  await copyFile(
2750
- join15(templateDir, ".flydocs", "version"),
2751
- join15(targetDir, ".flydocs", "version")
3114
+ join17(templateDir, ".flydocs", "version"),
3115
+ join17(targetDir, ".flydocs", "version")
2752
3116
  );
2753
3117
  printStatus(`.flydocs/version \u2192 ${version}`);
2754
- const clSrc = join15(templateDir, "CHANGELOG.md");
3118
+ const clSrc = join17(templateDir, "CHANGELOG.md");
2755
3119
  if (await pathExists(clSrc)) {
2756
- await copyFile(clSrc, join15(targetDir, ".flydocs", "CHANGELOG.md"));
3120
+ await copyFile(clSrc, join17(targetDir, ".flydocs", "CHANGELOG.md"));
2757
3121
  printStatus(".flydocs/CHANGELOG.md");
2758
3122
  }
2759
- const mfSrc = join15(templateDir, "manifest.json");
3123
+ const mfSrc = join17(templateDir, "manifest.json");
2760
3124
  if (await pathExists(mfSrc)) {
2761
- await copyFile(mfSrc, join15(targetDir, ".flydocs", "manifest.json"));
3125
+ await copyFile(mfSrc, join17(targetDir, ".flydocs", "manifest.json"));
2762
3126
  printStatus(".flydocs/manifest.json");
2763
3127
  }
3128
+ await generateIntegrity(targetDir, version);
3129
+ printStatus("Install integrity recorded");
2764
3130
  console.log();
2765
3131
  console.log("Detecting project stack...");
2766
3132
  const stack = await detectStack(targetDir);
@@ -2818,21 +3184,21 @@ __export(uninstall_exports, {
2818
3184
  default: () => uninstall_default
2819
3185
  });
2820
3186
  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";
3187
+ import { resolve as resolve4, join as join18 } from "path";
3188
+ import { readdir as readdir5, rm as rm5, rename as rename2 } from "fs/promises";
2823
3189
  import { confirm as confirm4, select as select3, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
2824
3190
  import pc8 from "picocolors";
2825
3191
  async function removeOwnedSkills(targetDir) {
2826
- const skillsDir = join16(targetDir, ".claude", "skills");
3192
+ const skillsDir = join18(targetDir, ".claude", "skills");
2827
3193
  const removed = [];
2828
3194
  if (!await pathExists(skillsDir)) {
2829
3195
  return removed;
2830
3196
  }
2831
3197
  try {
2832
- const entries = await readdir4(skillsDir);
3198
+ const entries = await readdir5(skillsDir);
2833
3199
  for (const entry of entries) {
2834
3200
  if (entry.startsWith(OWNED_SKILL_PREFIX)) {
2835
- await rm5(join16(skillsDir, entry), { recursive: true, force: true });
3201
+ await rm5(join18(skillsDir, entry), { recursive: true, force: true });
2836
3202
  removed.push(`.claude/skills/${entry}`);
2837
3203
  }
2838
3204
  }
@@ -2841,16 +3207,16 @@ async function removeOwnedSkills(targetDir) {
2841
3207
  return removed;
2842
3208
  }
2843
3209
  async function removeOwnedCursorRules(targetDir) {
2844
- const rulesDir = join16(targetDir, ".cursor", "rules");
3210
+ const rulesDir = join18(targetDir, ".cursor", "rules");
2845
3211
  const removed = [];
2846
3212
  if (!await pathExists(rulesDir)) {
2847
3213
  return removed;
2848
3214
  }
2849
3215
  try {
2850
- const entries = await readdir4(rulesDir);
3216
+ const entries = await readdir5(rulesDir);
2851
3217
  for (const entry of entries) {
2852
3218
  if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
2853
- await rm5(join16(rulesDir, entry), { force: true });
3219
+ await rm5(join18(rulesDir, entry), { force: true });
2854
3220
  removed.push(`.cursor/rules/${entry}`);
2855
3221
  }
2856
3222
  }
@@ -2860,7 +3226,7 @@ async function removeOwnedCursorRules(targetDir) {
2860
3226
  }
2861
3227
  async function isEmptyDir(dirPath) {
2862
3228
  try {
2863
- const entries = await readdir4(dirPath);
3229
+ const entries = await readdir5(dirPath);
2864
3230
  return entries.length === 0;
2865
3231
  } catch {
2866
3232
  return false;
@@ -2869,7 +3235,7 @@ async function isEmptyDir(dirPath) {
2869
3235
  async function cleanupEmptyParents(targetDir, dirs) {
2870
3236
  const cleaned = [];
2871
3237
  for (const dir of dirs) {
2872
- const fullPath = join16(targetDir, dir);
3238
+ const fullPath = join18(targetDir, dir);
2873
3239
  if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
2874
3240
  await rm5(fullPath, { recursive: true, force: true });
2875
3241
  cleaned.push(dir);
@@ -2890,9 +3256,11 @@ var init_uninstall = __esm({
2890
3256
  [".claude/settings.json", "file"],
2891
3257
  [".claude/agents", "dir"],
2892
3258
  [".claude/commands", "dir"],
3259
+ [".claude/hooks", "dir"],
2893
3260
  [".claude/skills/README.md", "file"],
2894
3261
  [".cursor/hooks.json", "file"],
2895
3262
  [".cursor/agents", "dir"],
3263
+ [".cursor/commands", "dir"],
2896
3264
  [".flydocs", "dir"],
2897
3265
  ["AGENTS.md", "file"],
2898
3266
  [".env.example", "file"]
@@ -2946,8 +3314,8 @@ var init_uninstall = __esm({
2946
3314
  process.exit(1);
2947
3315
  }
2948
3316
  targetDir = resolve4(targetDir);
2949
- const hasFlydocs = await pathExists(join16(targetDir, ".flydocs"));
2950
- const hasAgentsMd = await pathExists(join16(targetDir, "AGENTS.md"));
3317
+ const hasFlydocs = await pathExists(join18(targetDir, ".flydocs"));
3318
+ const hasAgentsMd = await pathExists(join18(targetDir, "AGENTS.md"));
2951
3319
  if (!hasFlydocs && !hasAgentsMd) {
2952
3320
  printError(`Not a FlyDocs project: ${targetDir}`);
2953
3321
  printInfo("No .flydocs/ directory or AGENTS.md found.");
@@ -2959,7 +3327,7 @@ var init_uninstall = __esm({
2959
3327
  const removeAll = forceAll || args.all;
2960
3328
  const skipPrompts = forceAll || args.yes;
2961
3329
  let contentAction = "preserve";
2962
- const hasUserContent = await pathExists(join16(targetDir, "flydocs"));
3330
+ const hasUserContent = await pathExists(join18(targetDir, "flydocs"));
2963
3331
  if (hasUserContent) {
2964
3332
  if (removeAll) {
2965
3333
  contentAction = "remove";
@@ -3042,7 +3410,7 @@ var init_uninstall = __esm({
3042
3410
  const removedRules = await removeOwnedCursorRules(targetDir);
3043
3411
  result.removed.push(...removedRules);
3044
3412
  for (const [relativePath, type] of ALWAYS_REMOVED) {
3045
- const fullPath = join16(targetDir, relativePath);
3413
+ const fullPath = join18(targetDir, relativePath);
3046
3414
  if (!await pathExists(fullPath)) {
3047
3415
  result.skipped.push(relativePath);
3048
3416
  continue;
@@ -3060,9 +3428,9 @@ var init_uninstall = __esm({
3060
3428
  }
3061
3429
  }
3062
3430
  if (hasUserContent) {
3063
- const flydocsPath = join16(targetDir, "flydocs");
3431
+ const flydocsPath = join18(targetDir, "flydocs");
3064
3432
  if (contentAction === "archive") {
3065
- const archivePath = join16(targetDir, "flydocs-archive");
3433
+ const archivePath = join18(targetDir, "flydocs-archive");
3066
3434
  if (await pathExists(archivePath)) {
3067
3435
  await rm5(archivePath, { recursive: true, force: true });
3068
3436
  }
@@ -3301,70 +3669,9 @@ __export(connect_exports, {
3301
3669
  default: () => connect_default
3302
3670
  });
3303
3671
  import { defineCommand as defineCommand6 } from "citty";
3304
- import { text as text2, confirm as confirm5, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
3672
+ import { text as text3, confirm as confirm5, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
3305
3673
  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
- }
3674
+ import { join as join19 } from "path";
3368
3675
  var connect_default;
3369
3676
  var init_connect = __esm({
3370
3677
  "src/commands/connect.ts"() {
@@ -3373,6 +3680,7 @@ var init_connect = __esm({
3373
3680
  init_fs_ops();
3374
3681
  init_template();
3375
3682
  init_ui();
3683
+ init_api_key();
3376
3684
  connect_default = defineCommand6({
3377
3685
  meta: {
3378
3686
  name: "connect",
@@ -3393,12 +3701,12 @@ var init_connect = __esm({
3393
3701
  },
3394
3702
  key: {
3395
3703
  type: "string",
3396
- description: "API key (fdk_ for relay, lin_api_ for direct Linear)"
3704
+ description: "FlyDocs API key (fdk_...)"
3397
3705
  }
3398
3706
  },
3399
3707
  async run({ args }) {
3400
3708
  const targetDir = args.path ?? process.cwd();
3401
- const configPath = join17(targetDir, ".flydocs", "config.json");
3709
+ const configPath = join19(targetDir, ".flydocs", "config.json");
3402
3710
  if (!await pathExists(configPath)) {
3403
3711
  printError("Not a FlyDocs project (.flydocs/config.json not found).");
3404
3712
  console.log(
@@ -3422,22 +3730,19 @@ var init_connect = __esm({
3422
3730
  console.log(` ${pc11.bold("Connect to FlyDocs Cloud")}`);
3423
3731
  console.log();
3424
3732
  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")}`
3733
+ ` ${pc11.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
3429
3734
  );
3430
3735
  console.log();
3431
3736
  let apiKey = args.key ?? "";
3432
3737
  if (!apiKey) {
3433
- const keyInput = await text2({
3738
+ const keyInput = await text3({
3434
3739
  message: "Enter your API key",
3435
- placeholder: "fdk_... or lin_api_...",
3740
+ placeholder: "fdk_...",
3436
3741
  validate(value) {
3437
3742
  if (!value.trim()) return "API key is required";
3438
3743
  const type = detectKeyType(value.trim());
3439
3744
  if (type === "unknown")
3440
- return "Key must start with fdk_ (FlyDocs) or lin_api_ (Linear)";
3745
+ return "Key must start with fdk_ (FlyDocs API key)";
3441
3746
  return void 0;
3442
3747
  }
3443
3748
  });
@@ -3449,7 +3754,9 @@ var init_connect = __esm({
3449
3754
  }
3450
3755
  const keyType = detectKeyType(apiKey);
3451
3756
  if (keyType === "unknown") {
3452
- printError("Unrecognized key format. Expected fdk_ or lin_api_ prefix.");
3757
+ printError(
3758
+ "Unrecognized key format. Expected fdk_ prefix (FlyDocs API key)."
3759
+ );
3453
3760
  process.exit(1);
3454
3761
  }
3455
3762
  printInfo("Validating API key...");
@@ -3491,32 +3798,15 @@ var init_connect = __esm({
3491
3798
  }
3492
3799
  const wasLocal = config.tier === "local";
3493
3800
  config.tier = "cloud";
3494
- config.provider = config.provider ?? { type: "linear", teamId: null };
3495
- config.provider.type = "linear";
3801
+ const configRecord = config;
3802
+ delete configRecord.statusMapping;
3803
+ delete configRecord.provider;
3496
3804
  await writeConfig(targetDir, config);
3497
3805
  printStatus("Config updated to cloud tier");
3498
3806
  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
- }
3807
+ printInfo(
3808
+ "Tier changed to cloud. Run flydocs update to refresh skill scripts."
3809
+ );
3520
3810
  }
3521
3811
  console.log();
3522
3812
  console.log(
@@ -3579,9 +3869,7 @@ var init_upgrade = __esm({
3579
3869
  ` ${pc12.green("\u2713")} You're already on the ${pc12.bold("cloud")} tier.`
3580
3870
  );
3581
3871
  console.log();
3582
- console.log(
3583
- ` Your issues sync with Linear via the cloud mechanism skill.`
3584
- );
3872
+ console.log(` Your issues sync with your provider via the relay API.`);
3585
3873
  console.log(
3586
3874
  ` Run ${pc12.cyan("flydocs connect")} to update your connection settings.`
3587
3875
  );
@@ -3593,9 +3881,7 @@ var init_upgrade = __esm({
3593
3881
  console.log(` You're currently on the ${pc12.yellow("local")} tier.`);
3594
3882
  console.log(` Upgrade to cloud for:`);
3595
3883
  console.log();
3596
- console.log(
3597
- ` ${pc12.cyan("\u2192")} Issue sync with Linear (Jira coming soon)`
3598
- );
3884
+ console.log(` ${pc12.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
3599
3885
  console.log(` ${pc12.cyan("\u2192")} Project milestones and cycle management`);
3600
3886
  console.log(` ${pc12.cyan("\u2192")} Team assignment and priority tracking`);
3601
3887
  console.log(` ${pc12.cyan("\u2192")} Project health updates and dashboards`);