@flydocs/cli 0.6.0-alpha.1 → 0.6.0-alpha.11

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 (50) hide show
  1. package/dist/cli.js +504 -254
  2. package/package.json +1 -1
  3. package/template/.claude/CLAUDE.md +11 -9
  4. package/template/.claude/commands/flydocs-setup.md +114 -17
  5. package/template/.claude/commands/flydocs-upgrade.md +27 -15
  6. package/template/.claude/commands/knowledge.md +61 -0
  7. package/template/.claude/skills/flydocs-cloud/SKILL.md +57 -32
  8. package/template/.claude/skills/flydocs-cloud/scripts/assign.py +10 -4
  9. package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +22 -2
  10. package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +39 -0
  11. package/template/.claude/skills/flydocs-cloud/scripts/delete_milestone.py +21 -0
  12. package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +9 -5
  13. package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +31 -0
  14. package/template/.claude/skills/flydocs-cloud/scripts/get_estimate_scale.py +23 -0
  15. package/template/.claude/skills/flydocs-cloud/scripts/list_providers.py +19 -0
  16. package/template/.claude/skills/flydocs-cloud/scripts/list_statuses.py +19 -0
  17. package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +1 -1
  18. package/template/.claude/skills/flydocs-cloud/scripts/refresh_labels.py +87 -0
  19. package/template/.claude/skills/flydocs-cloud/scripts/set_identity.py +38 -0
  20. package/template/.claude/skills/flydocs-cloud/scripts/set_preferences.py +49 -0
  21. package/template/.claude/skills/flydocs-cloud/scripts/set_provider.py +46 -0
  22. package/template/.claude/skills/flydocs-cloud/scripts/set_status_mapping.py +69 -0
  23. package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +5 -4
  24. package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +22 -4
  25. package/template/.claude/skills/flydocs-cloud/scripts/update_milestone.py +42 -0
  26. package/template/.claude/skills/flydocs-cloud/scripts/validate_setup.py +120 -0
  27. package/template/.claude/skills/flydocs-local/SKILL.md +1 -1
  28. package/template/.claude/skills/flydocs-local/scripts/assign.py +13 -4
  29. package/template/.claude/skills/flydocs-local/scripts/flydocs_api.py +5 -2
  30. package/template/.claude/skills/flydocs-workflow/SKILL.md +23 -18
  31. package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
  32. package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +105 -0
  33. package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
  34. package/template/.claude/skills/flydocs-workflow/session.md +24 -16
  35. package/template/.claude/skills/flydocs-workflow/stages/capture.md +8 -3
  36. package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
  37. package/template/.claude/skills/flydocs-workflow/stages/implement.md +28 -4
  38. package/template/.claude/skills/flydocs-workflow/stages/refine.md +20 -4
  39. package/template/.claude/skills/flydocs-workflow/stages/review.md +14 -2
  40. package/template/.flydocs/config.json +4 -18
  41. package/template/.flydocs/hooks/prompt-submit.py +27 -4
  42. package/template/.flydocs/version +1 -1
  43. package/template/AGENTS.md +8 -8
  44. package/template/CHANGELOG.md +39 -0
  45. package/template/flydocs/knowledge/INDEX.md +38 -53
  46. package/template/flydocs/knowledge/README.md +60 -9
  47. package/template/flydocs/knowledge/templates/decision.md +47 -0
  48. package/template/flydocs/knowledge/templates/feature.md +35 -0
  49. package/template/flydocs/knowledge/templates/note.md +25 -0
  50. package/template/manifest.json +8 -2
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.1";
18
+ CLI_VERSION = "0.6.0-alpha.11";
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,10 +293,9 @@ 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,
296
297
  workspace: config.workspace ?? {},
297
298
  issueLabels: config.issueLabels ?? {},
298
- statusMapping: config.statusMapping ?? {},
299
299
  detectedStack: config.detectedStack ?? {},
300
300
  skills: config.skills ?? {},
301
301
  designSystem: config.designSystem ?? null,
@@ -311,21 +311,13 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
311
311
  config.version = version;
312
312
  config.tier = tierFlag ?? preserved.tier;
313
313
  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;
319
- }
314
+ config.workspaceId = preserved.workspaceId;
320
315
  if (Object.keys(preserved.workspace).length > 0) {
321
316
  config.workspace = preserved.workspace;
322
317
  }
323
318
  if (Object.keys(preserved.issueLabels).length > 0) {
324
319
  config.issueLabels = preserved.issueLabels;
325
320
  }
326
- if (Object.keys(preserved.statusMapping).length > 0) {
327
- config.statusMapping = preserved.statusMapping;
328
- }
329
321
  if (Object.keys(preserved.detectedStack).length > 0) {
330
322
  config.detectedStack = preserved.detectedStack;
331
323
  }
@@ -485,6 +477,18 @@ import {
485
477
  writeFile as writeFile2
486
478
  } from "fs/promises";
487
479
  import { join as join5, dirname as dirname2 } from "path";
480
+ async function detectExistingConfigs(targetDir) {
481
+ const existing = [];
482
+ const backupDir = join5(targetDir, BACKUP_ORIGINALS_DIR);
483
+ for (const relativePath of RESTORABLE_FILES) {
484
+ const filePath = join5(targetDir, relativePath);
485
+ const backupPath = join5(backupDir, relativePath);
486
+ if (await pathExists(filePath) && !await pathExists(backupPath)) {
487
+ existing.push(relativePath);
488
+ }
489
+ }
490
+ return existing;
491
+ }
488
492
  async function backupOriginals(targetDir, files = RESTORABLE_FILES) {
489
493
  const backedUp = [];
490
494
  const backupDir = join5(targetDir, BACKUP_ORIGINALS_DIR);
@@ -760,8 +764,8 @@ function flushFrontmatterValue(mode, lines) {
760
764
  return lines.join("\n").trim();
761
765
  }
762
766
  }
763
- function parseFrontmatter(text3) {
764
- const match = text3.match(/^---\s*\n([\s\S]*?)\n---/);
767
+ function parseFrontmatter(text4) {
768
+ const match = text4.match(/^---\s*\n([\s\S]*?)\n---/);
765
769
  if (!match) return null;
766
770
  const block = match[1];
767
771
  const result = {};
@@ -947,7 +951,8 @@ function searchCatalog(keyword) {
947
951
  return searchable.includes(lower);
948
952
  });
949
953
  }
950
- async function addSkill(targetDir, source) {
954
+ async function addSkill(targetDir, source, options) {
955
+ const quiet = options?.quiet ?? false;
951
956
  if (isPlatformSkill(source)) {
952
957
  printError(`Cannot install platform skill '${source}'.`);
953
958
  console.log(" Platform skills (flydocs-*) are managed by the installer.");
@@ -965,11 +970,13 @@ async function addSkill(targetDir, source) {
965
970
  console.log(` Remove first: flydocs skills remove ${skillName}`);
966
971
  return;
967
972
  }
968
- console.log();
969
- console.log(
970
- `${pc3.blue("->")} Installing ${pc3.cyan(skillName)} from ${repo}...`
971
- );
972
- console.log();
973
+ if (!quiet) {
974
+ console.log();
975
+ console.log(
976
+ `${pc3.blue("->")} Installing ${pc3.cyan(skillName)} from ${repo}...`
977
+ );
978
+ console.log();
979
+ }
973
980
  let success;
974
981
  try {
975
982
  success = await downloadSkillTree(repo, skillName, skillsDir);
@@ -1003,19 +1010,23 @@ async function addSkill(targetDir, source) {
1003
1010
  );
1004
1011
  return;
1005
1012
  }
1006
- if (!fm["triggers"]) {
1013
+ if (!fm["triggers"] && !quiet) {
1007
1014
  printWarning(
1008
1015
  "SKILL.md has no triggers -- skill won't appear in manifest index."
1009
1016
  );
1010
1017
  }
1011
- printStatus("Downloaded skill files");
1018
+ if (!quiet) {
1019
+ printStatus("Downloaded skill files");
1020
+ }
1012
1021
  const cursorRuleSrc = join8(skillsDir, "cursor-rule.mdc");
1013
1022
  if (await pathExists(cursorRuleSrc)) {
1014
1023
  const cursorRulesDir = join8(targetDir, ".cursor", "rules");
1015
1024
  await mkdir3(cursorRulesDir, { recursive: true });
1016
1025
  const cursorRuleDest = join8(cursorRulesDir, `${skillName}.mdc`);
1017
1026
  await copyFile(cursorRuleSrc, cursorRuleDest);
1018
- printStatus("Installed cursor rule");
1027
+ if (!quiet) {
1028
+ printStatus("Installed cursor rule");
1029
+ }
1019
1030
  }
1020
1031
  const config = await readConfig(targetDir);
1021
1032
  const installed = config.skills?.installed ?? [];
@@ -1027,12 +1038,16 @@ async function addSkill(targetDir, source) {
1027
1038
  }
1028
1039
  config.skills.installed = installed;
1029
1040
  await writeConfig(targetDir, config);
1030
- printStatus("Updated config.json");
1041
+ if (!quiet) {
1042
+ printStatus("Updated config.json");
1043
+ }
1031
1044
  }
1032
1045
  await runManifestGeneration(targetDir);
1033
- console.log();
1034
- printStatus(`Installed ${pc3.cyan(skillName)}`);
1035
- console.log();
1046
+ if (!quiet) {
1047
+ console.log();
1048
+ printStatus(`Installed ${pc3.cyan(skillName)}`);
1049
+ console.log();
1050
+ }
1036
1051
  }
1037
1052
  async function removeSkill(targetDir, name) {
1038
1053
  if (isPlatformSkill(name)) {
@@ -1187,7 +1202,8 @@ async function promptCommunitySkills(targetDir, stack, autoYes) {
1187
1202
  let successCount = 0;
1188
1203
  for (const skill of selected) {
1189
1204
  try {
1190
- await addSkill(targetDir, `${skill.repo}:${skill.name}`);
1205
+ await addSkill(targetDir, `${skill.repo}:${skill.name}`, { quiet: true });
1206
+ printStatus(`Installed ${pc4.cyan(skill.name)}`);
1191
1207
  successCount++;
1192
1208
  } catch {
1193
1209
  printError(`${skill.name} (failed)`);
@@ -1840,6 +1856,91 @@ var init_telemetry = __esm({
1840
1856
  }
1841
1857
  });
1842
1858
 
1859
+ // src/lib/api-key.ts
1860
+ import { readFile as readFile10, writeFile as writeFile7, appendFile as appendFile2 } from "fs/promises";
1861
+ import { join as join14 } from "path";
1862
+ function detectKeyType(key) {
1863
+ if (key.startsWith("fdk_")) return "relay";
1864
+ if (key.startsWith("lin_api_")) return "direct";
1865
+ return "unknown";
1866
+ }
1867
+ async function validateRelayKey(apiKey) {
1868
+ const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
1869
+ const response = await fetch(`${baseUrl}/auth/validate`, {
1870
+ method: "POST",
1871
+ headers: {
1872
+ Authorization: `Bearer ${apiKey}`,
1873
+ "Content-Type": "application/json"
1874
+ },
1875
+ signal: AbortSignal.timeout(15e3)
1876
+ });
1877
+ if (!response.ok) return { valid: false };
1878
+ const data = await response.json();
1879
+ if (!data.valid) return { valid: false };
1880
+ return { valid: true, org: data.org ?? "your organization" };
1881
+ }
1882
+ async function fetchWorkspaces(apiKey) {
1883
+ const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
1884
+ const response = await fetch(`${baseUrl}/auth/workspaces`, {
1885
+ method: "GET",
1886
+ headers: {
1887
+ Authorization: `Bearer ${apiKey}`
1888
+ },
1889
+ signal: AbortSignal.timeout(15e3)
1890
+ });
1891
+ if (!response.ok) {
1892
+ throw new Error(`Failed to fetch workspaces: ${response.status}`);
1893
+ }
1894
+ const data = await response.json();
1895
+ return data;
1896
+ }
1897
+ async function validateLinearKey(apiKey) {
1898
+ const response = await fetch("https://api.linear.app/graphql", {
1899
+ method: "POST",
1900
+ headers: {
1901
+ Authorization: apiKey,
1902
+ "Content-Type": "application/json"
1903
+ },
1904
+ body: JSON.stringify({ query: "{ viewer { id name email } }" }),
1905
+ signal: AbortSignal.timeout(15e3)
1906
+ });
1907
+ if (!response.ok) return { valid: false };
1908
+ const data = await response.json();
1909
+ if (!data.data?.viewer) return { valid: false };
1910
+ return {
1911
+ valid: true,
1912
+ name: data.data.viewer.name,
1913
+ email: data.data.viewer.email
1914
+ };
1915
+ }
1916
+ async function storeEnvKey(targetDir, envVarName, value) {
1917
+ const envPath = join14(targetDir, ".env");
1918
+ const envLocalPath = join14(targetDir, ".env.local");
1919
+ const targetEnvPath = await pathExists(envLocalPath) ? envLocalPath : envPath;
1920
+ const pattern = new RegExp(`${envVarName}=.*`);
1921
+ if (await pathExists(targetEnvPath)) {
1922
+ const envContent = await readFile10(targetEnvPath, "utf-8");
1923
+ if (pattern.test(envContent)) {
1924
+ const updated = envContent.replace(pattern, `${envVarName}=${value}`);
1925
+ await writeFile7(targetEnvPath, updated, "utf-8");
1926
+ } else {
1927
+ await appendFile2(targetEnvPath, `
1928
+ ${envVarName}=${value}
1929
+ `);
1930
+ }
1931
+ } else {
1932
+ await writeFile7(targetEnvPath, `${envVarName}=${value}
1933
+ `, "utf-8");
1934
+ }
1935
+ return targetEnvPath === envLocalPath ? ".env.local" : ".env";
1936
+ }
1937
+ var init_api_key = __esm({
1938
+ "src/lib/api-key.ts"() {
1939
+ "use strict";
1940
+ init_fs_ops();
1941
+ }
1942
+ });
1943
+
1843
1944
  // src/commands/install.ts
1844
1945
  var install_exports = {};
1845
1946
  __export(install_exports, {
@@ -1847,9 +1948,9 @@ __export(install_exports, {
1847
1948
  });
1848
1949
  import { defineCommand } from "citty";
1849
1950
  import { resolve as resolve2 } from "path";
1850
- import { join as join14 } from "path";
1951
+ import { join as join15 } from "path";
1851
1952
  import { mkdir as mkdir7 } from "fs/promises";
1852
- import { confirm as confirm2, select, isCancel as isCancel3, cancel as cancel2 } from "@clack/prompts";
1953
+ import { confirm as confirm2, select, text, isCancel as isCancel3, cancel as cancel2 } from "@clack/prompts";
1853
1954
  import pc6 from "picocolors";
1854
1955
  var install_default;
1855
1956
  var init_install = __esm({
@@ -1868,6 +1969,7 @@ var init_install = __esm({
1868
1969
  init_post_install();
1869
1970
  init_update_check();
1870
1971
  init_telemetry();
1972
+ init_api_key();
1871
1973
  install_default = defineCommand({
1872
1974
  meta: {
1873
1975
  name: "install",
@@ -1929,7 +2031,7 @@ var init_install = __esm({
1929
2031
  process.exit(1);
1930
2032
  }
1931
2033
  tier = args.tier;
1932
- } else if (await pathExists(join14(targetDir, ".flydocs", "config.json"))) {
2034
+ } else if (await pathExists(join15(targetDir, ".flydocs", "config.json"))) {
1933
2035
  try {
1934
2036
  const existing = await readConfig(targetDir);
1935
2037
  if (existing.tier) {
@@ -1951,7 +2053,7 @@ var init_install = __esm({
1951
2053
  {
1952
2054
  value: "cloud",
1953
2055
  label: "Cloud (managed)",
1954
- hint: "Sync with Linear \u2014 run flydocs connect after install"
2056
+ hint: "Sync with Linear, Jira, and more"
1955
2057
  }
1956
2058
  ]
1957
2059
  });
@@ -1978,48 +2080,173 @@ var init_install = __esm({
1978
2080
  }
1979
2081
  printInfo(`Tier: ${tier}`);
1980
2082
  await capture("install_tier_selected", { tier });
1981
- if (!await pathExists(join14(targetDir, ".git"))) {
2083
+ let selectedWorkspaceId = null;
2084
+ let selectedWorkspaceName = null;
2085
+ if (tier === "cloud") {
2086
+ console.log();
2087
+ console.log(` ${pc6.bold("Connect to FlyDocs Cloud")}`);
2088
+ console.log();
2089
+ console.log(
2090
+ ` ${pc6.dim("Get your API key from your FlyDocs dashboard (fdk_...)")}`
2091
+ );
2092
+ console.log();
2093
+ const keyInput = await text({
2094
+ message: "Enter your FlyDocs API key",
2095
+ placeholder: "fdk_...",
2096
+ validate(value) {
2097
+ if (!value.trim()) return "API key is required";
2098
+ const type = detectKeyType(value.trim());
2099
+ if (type !== "relay")
2100
+ return "Cloud tier requires a FlyDocs API key (fdk_...)";
2101
+ return void 0;
2102
+ }
2103
+ });
2104
+ if (isCancel3(keyInput)) {
2105
+ cancel2("Installation cancelled.");
2106
+ process.exit(0);
2107
+ }
2108
+ const apiKey = keyInput.trim();
2109
+ printInfo("Validating API key...");
2110
+ try {
2111
+ const result = await validateRelayKey(apiKey);
2112
+ if (!result.valid) {
2113
+ printError("Invalid API key or relay API unreachable.");
2114
+ console.log(` Check your key and try again.`);
2115
+ process.exit(1);
2116
+ }
2117
+ printStatus(`Connected to ${pc6.bold(result.org)}`);
2118
+ } catch {
2119
+ printError(
2120
+ "Could not reach relay API. Check your network and try again."
2121
+ );
2122
+ process.exit(1);
2123
+ }
2124
+ const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
2125
+ printStatus(`API key stored in ${pc6.dim(envFile)}`);
2126
+ console.log();
2127
+ printInfo("Fetching workspaces...");
2128
+ try {
2129
+ const workspaces = await fetchWorkspaces(apiKey);
2130
+ if (workspaces.length === 0) {
2131
+ printError(
2132
+ "No workspaces found. Create a workspace in the FlyDocs dashboard first, then re-run install."
2133
+ );
2134
+ process.exit(1);
2135
+ }
2136
+ if (workspaces.length === 1) {
2137
+ selectedWorkspaceId = workspaces[0].id;
2138
+ selectedWorkspaceName = workspaces[0].name;
2139
+ printStatus(
2140
+ `Workspace: ${pc6.bold(selectedWorkspaceName)} (auto-selected)`
2141
+ );
2142
+ } else {
2143
+ const workspaceChoice = await select({
2144
+ message: "Select a workspace",
2145
+ options: workspaces.map((ws) => ({
2146
+ value: ws.id,
2147
+ label: `${ws.name} (${ws.provider.type})`,
2148
+ hint: ws.team.name
2149
+ }))
2150
+ });
2151
+ if (isCancel3(workspaceChoice)) {
2152
+ cancel2("Installation cancelled.");
2153
+ process.exit(0);
2154
+ }
2155
+ selectedWorkspaceId = workspaceChoice;
2156
+ const chosen = workspaces.find((ws) => ws.id === selectedWorkspaceId);
2157
+ selectedWorkspaceName = chosen?.name ?? "Unknown";
2158
+ printStatus(`Workspace: ${pc6.bold(selectedWorkspaceName)}`);
2159
+ }
2160
+ } catch {
2161
+ printError(
2162
+ "Could not fetch workspaces. Check your network and try again."
2163
+ );
2164
+ process.exit(1);
2165
+ }
2166
+ }
2167
+ let skipConfigOverwrite = false;
2168
+ if (!await pathExists(join15(targetDir, ".git"))) {
1982
2169
  printWarning("No git repository detected. Run git init when ready.");
1983
2170
  }
1984
2171
  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/`
2172
+ const existingFiles = await detectExistingConfigs(targetDir);
2173
+ if (existingFiles.length > 0) {
2174
+ console.log();
2175
+ printWarning(
2176
+ `Found ${existingFiles.length} existing config file(s) that FlyDocs will overwrite:`
1989
2177
  );
1990
- for (const f of backedUp) {
1991
- printInfo(` ${f}`);
2178
+ for (const f of existingFiles) {
2179
+ console.log(` ${pc6.dim(f)}`);
2180
+ }
2181
+ console.log();
2182
+ if (!args.yes) {
2183
+ const overwriteChoice = await select({
2184
+ message: "How should FlyDocs handle these files?",
2185
+ options: [
2186
+ {
2187
+ value: "backup",
2188
+ label: "Overwrite (backed up)",
2189
+ hint: "Files are saved to .flydocs/backup-originals/ and restored on uninstall"
2190
+ },
2191
+ {
2192
+ value: "skip",
2193
+ label: "Skip overwriting",
2194
+ hint: "Keep your existing files \u2014 FlyDocs config may be incomplete"
2195
+ }
2196
+ ]
2197
+ });
2198
+ if (isCancel3(overwriteChoice)) {
2199
+ cancel2("Installation cancelled.");
2200
+ process.exit(0);
2201
+ }
2202
+ if (overwriteChoice === "skip") {
2203
+ printInfo(
2204
+ "Keeping existing files. Run /flydocs-setup to merge your config manually."
2205
+ );
2206
+ skipConfigOverwrite = true;
2207
+ }
2208
+ }
2209
+ if (!skipConfigOverwrite) {
2210
+ const backedUp = await backupOriginals(targetDir);
2211
+ if (backedUp.length > 0) {
2212
+ printStatus(
2213
+ `Backed up ${backedUp.length} file(s) to .flydocs/backup-originals/`
2214
+ );
2215
+ }
1992
2216
  }
1993
2217
  }
1994
2218
  console.log("Installing framework files...");
1995
2219
  await replaceDirectory(
1996
- join14(templateDir, ".flydocs", "templates"),
1997
- join14(targetDir, ".flydocs", "templates")
2220
+ join15(templateDir, ".flydocs", "templates"),
2221
+ join15(targetDir, ".flydocs", "templates")
1998
2222
  );
1999
2223
  await replaceDirectory(
2000
- join14(templateDir, ".flydocs", "hooks"),
2001
- join14(targetDir, ".flydocs", "hooks")
2224
+ join15(templateDir, ".flydocs", "hooks"),
2225
+ join15(targetDir, ".flydocs", "hooks")
2002
2226
  );
2003
2227
  await replaceDirectory(
2004
- join14(templateDir, ".flydocs", "scripts"),
2005
- join14(targetDir, ".flydocs", "scripts")
2228
+ join15(templateDir, ".flydocs", "scripts"),
2229
+ join15(targetDir, ".flydocs", "scripts")
2006
2230
  );
2007
2231
  await copyFile(
2008
- join14(templateDir, ".flydocs", "version"),
2009
- join14(targetDir, ".flydocs", "version")
2232
+ join15(templateDir, ".flydocs", "version"),
2233
+ join15(targetDir, ".flydocs", "version")
2010
2234
  );
2011
- const manifestSrc = join14(templateDir, "manifest.json");
2235
+ const manifestSrc = join15(templateDir, "manifest.json");
2012
2236
  if (await pathExists(manifestSrc)) {
2013
- await copyFile(manifestSrc, join14(targetDir, ".flydocs", "manifest.json"));
2237
+ await copyFile(manifestSrc, join15(targetDir, ".flydocs", "manifest.json"));
2014
2238
  }
2015
- const changelogSrc = join14(templateDir, "CHANGELOG.md");
2239
+ const changelogSrc = join15(templateDir, "CHANGELOG.md");
2016
2240
  if (await pathExists(changelogSrc)) {
2017
- await copyFile(changelogSrc, join14(targetDir, ".flydocs", "CHANGELOG.md"));
2241
+ await copyFile(changelogSrc, join15(targetDir, ".flydocs", "CHANGELOG.md"));
2018
2242
  }
2019
2243
  printStatus(".flydocs/templates, hooks, version, manifest, changelog");
2020
- const configPath = join14(targetDir, ".flydocs", "config.json");
2244
+ const configPath = join15(targetDir, ".flydocs", "config.json");
2021
2245
  if (!await pathExists(configPath)) {
2022
2246
  const config = await createFreshConfig(templateDir, version, tier);
2247
+ if (selectedWorkspaceId) {
2248
+ config.workspaceId = selectedWorkspaceId;
2249
+ }
2023
2250
  await writeConfig(targetDir, config);
2024
2251
  printStatus(`.flydocs/config.json (new, tier: ${tier})`);
2025
2252
  } else if (args.tier) {
@@ -2027,15 +2254,34 @@ var init_install = __esm({
2027
2254
  const existing = await readConfig(targetDir);
2028
2255
  existing.version = version;
2029
2256
  existing.tier = tier;
2257
+ if (selectedWorkspaceId) {
2258
+ existing.workspaceId = selectedWorkspaceId;
2259
+ }
2030
2260
  await writeConfig(targetDir, existing);
2031
2261
  printStatus(`.flydocs/config.json (tier updated: ${tier})`);
2032
2262
  } catch {
2033
2263
  const config = await createFreshConfig(templateDir, version, tier);
2264
+ if (selectedWorkspaceId) {
2265
+ config.workspaceId = selectedWorkspaceId;
2266
+ }
2034
2267
  await writeConfig(targetDir, config);
2035
2268
  printStatus(`.flydocs/config.json (recreated, tier: ${tier})`);
2036
2269
  }
2037
2270
  } else {
2038
- printWarning(".flydocs/config.json exists, preserving");
2271
+ if (selectedWorkspaceId) {
2272
+ try {
2273
+ const existing = await readConfig(targetDir);
2274
+ existing.workspaceId = selectedWorkspaceId;
2275
+ await writeConfig(targetDir, existing);
2276
+ printWarning(
2277
+ ".flydocs/config.json exists, preserving (workspace updated)"
2278
+ );
2279
+ } catch {
2280
+ printWarning(".flydocs/config.json exists, preserving");
2281
+ }
2282
+ } else {
2283
+ printWarning(".flydocs/config.json exists, preserving");
2284
+ }
2039
2285
  }
2040
2286
  console.log();
2041
2287
  console.log(`Installing skills (tier: ${tier})...`);
@@ -2071,20 +2317,20 @@ var init_install = __esm({
2071
2317
  }
2072
2318
  await capture("install_agents_chosen", { install_agents: installAgents });
2073
2319
  if (installAgents) {
2074
- const claudeAgentsSrc = join14(templateDir, ".claude", "agents");
2320
+ const claudeAgentsSrc = join15(templateDir, ".claude", "agents");
2075
2321
  if (await pathExists(claudeAgentsSrc)) {
2076
- await mkdir7(join14(targetDir, ".claude", "agents"), { recursive: true });
2322
+ await mkdir7(join15(targetDir, ".claude", "agents"), { recursive: true });
2077
2323
  await copyDirectoryContents(
2078
2324
  claudeAgentsSrc,
2079
- join14(targetDir, ".claude", "agents")
2325
+ join15(targetDir, ".claude", "agents")
2080
2326
  );
2081
2327
  }
2082
- const cursorAgentsSrc = join14(templateDir, ".cursor", "agents");
2328
+ const cursorAgentsSrc = join15(templateDir, ".cursor", "agents");
2083
2329
  if (await pathExists(cursorAgentsSrc)) {
2084
- await mkdir7(join14(targetDir, ".cursor", "agents"), { recursive: true });
2330
+ await mkdir7(join15(targetDir, ".cursor", "agents"), { recursive: true });
2085
2331
  await copyDirectoryContents(
2086
2332
  cursorAgentsSrc,
2087
- join14(targetDir, ".cursor", "agents")
2333
+ join15(targetDir, ".cursor", "agents")
2088
2334
  );
2089
2335
  }
2090
2336
  printStatus("Sub-agents installed (.claude/agents, .cursor/agents)");
@@ -2094,74 +2340,86 @@ var init_install = __esm({
2094
2340
  console.log();
2095
2341
  console.log("Installing commands and settings...");
2096
2342
  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")
2103
- );
2104
- await copyFile(
2105
- join14(templateDir, ".claude", "settings.json"),
2106
- join14(targetDir, ".claude", "settings.json")
2343
+ join15(templateDir, ".claude", "commands"),
2344
+ join15(targetDir, ".claude", "commands")
2107
2345
  );
2108
- printStatus(".claude/ (commands, CLAUDE.md, settings)");
2346
+ if (!skipConfigOverwrite) {
2347
+ await copyFile(
2348
+ join15(templateDir, ".claude", "CLAUDE.md"),
2349
+ join15(targetDir, ".claude", "CLAUDE.md")
2350
+ );
2351
+ await copyFile(
2352
+ join15(templateDir, ".claude", "settings.json"),
2353
+ join15(targetDir, ".claude", "settings.json")
2354
+ );
2355
+ printStatus(".claude/ (commands, CLAUDE.md, settings)");
2356
+ } else {
2357
+ printStatus(".claude/ (commands only \u2014 existing config preserved)");
2358
+ }
2109
2359
  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")
2360
+ join15(templateDir, ".claude", "commands"),
2361
+ join15(targetDir, ".cursor", "commands")
2116
2362
  );
2117
- printStatus(".cursor/ (commands, hooks)");
2363
+ if (!skipConfigOverwrite) {
2364
+ await copyFile(
2365
+ join15(templateDir, ".cursor", "hooks.json"),
2366
+ join15(targetDir, ".cursor", "hooks.json")
2367
+ );
2368
+ printStatus(".cursor/ (commands, hooks)");
2369
+ } else {
2370
+ printStatus(".cursor/ (commands only \u2014 existing hooks preserved)");
2371
+ }
2118
2372
  await copyCursorRules(targetDir);
2119
2373
  printStatus(".cursor/rules/");
2120
- await copyFile(
2121
- join14(templateDir, "AGENTS.md"),
2122
- join14(targetDir, "AGENTS.md")
2123
- );
2124
- printStatus("AGENTS.md");
2374
+ if (!skipConfigOverwrite) {
2375
+ await copyFile(
2376
+ join15(templateDir, "AGENTS.md"),
2377
+ join15(targetDir, "AGENTS.md")
2378
+ );
2379
+ printStatus("AGENTS.md");
2380
+ } else {
2381
+ printInfo("AGENTS.md preserved (existing)");
2382
+ }
2125
2383
  await runManifestGeneration(targetDir);
2126
2384
  await runContextGraphBuild(targetDir);
2127
2385
  console.log();
2128
2386
  console.log("Installing project templates...");
2129
2387
  const userFiles = [
2130
2388
  {
2131
- src: join14(templateDir, "flydocs", "context", "project.md"),
2132
- dest: join14(targetDir, "flydocs", "context", "project.md"),
2389
+ src: join15(templateDir, "flydocs", "context", "project.md"),
2390
+ dest: join15(targetDir, "flydocs", "context", "project.md"),
2133
2391
  label: "flydocs/context/project.md"
2134
2392
  },
2135
2393
  {
2136
- src: join14(templateDir, "flydocs", "knowledge", "INDEX.md"),
2137
- dest: join14(targetDir, "flydocs", "knowledge", "INDEX.md"),
2394
+ src: join15(templateDir, "flydocs", "knowledge", "INDEX.md"),
2395
+ dest: join15(targetDir, "flydocs", "knowledge", "INDEX.md"),
2138
2396
  label: "flydocs/knowledge/INDEX.md"
2139
2397
  },
2140
2398
  {
2141
- src: join14(templateDir, "flydocs", "knowledge", "README.md"),
2142
- dest: join14(targetDir, "flydocs", "knowledge", "README.md"),
2399
+ src: join15(templateDir, "flydocs", "knowledge", "README.md"),
2400
+ dest: join15(targetDir, "flydocs", "knowledge", "README.md"),
2143
2401
  label: "flydocs/knowledge/README.md"
2144
2402
  },
2145
2403
  {
2146
- src: join14(
2404
+ src: join15(
2147
2405
  templateDir,
2148
2406
  "flydocs",
2149
2407
  "knowledge",
2150
2408
  "product",
2151
2409
  "personas.md"
2152
2410
  ),
2153
- dest: join14(targetDir, "flydocs", "knowledge", "product", "personas.md"),
2411
+ dest: join15(targetDir, "flydocs", "knowledge", "product", "personas.md"),
2154
2412
  label: "flydocs/knowledge/product/personas.md"
2155
2413
  },
2156
2414
  {
2157
- src: join14(
2415
+ src: join15(
2158
2416
  templateDir,
2159
2417
  "flydocs",
2160
2418
  "knowledge",
2161
2419
  "product",
2162
2420
  "user-flows.md"
2163
2421
  ),
2164
- dest: join14(
2422
+ dest: join15(
2165
2423
  targetDir,
2166
2424
  "flydocs",
2167
2425
  "knowledge",
@@ -2171,18 +2429,57 @@ var init_install = __esm({
2171
2429
  label: "flydocs/knowledge/product/user-flows.md"
2172
2430
  },
2173
2431
  {
2174
- src: join14(templateDir, "flydocs", "design-system", "README.md"),
2175
- dest: join14(targetDir, "flydocs", "design-system", "README.md"),
2432
+ src: join15(
2433
+ templateDir,
2434
+ "flydocs",
2435
+ "knowledge",
2436
+ "templates",
2437
+ "decision.md"
2438
+ ),
2439
+ dest: join15(
2440
+ targetDir,
2441
+ "flydocs",
2442
+ "knowledge",
2443
+ "templates",
2444
+ "decision.md"
2445
+ ),
2446
+ label: "flydocs/knowledge/templates/decision.md"
2447
+ },
2448
+ {
2449
+ src: join15(
2450
+ templateDir,
2451
+ "flydocs",
2452
+ "knowledge",
2453
+ "templates",
2454
+ "feature.md"
2455
+ ),
2456
+ dest: join15(
2457
+ targetDir,
2458
+ "flydocs",
2459
+ "knowledge",
2460
+ "templates",
2461
+ "feature.md"
2462
+ ),
2463
+ label: "flydocs/knowledge/templates/feature.md"
2464
+ },
2465
+ {
2466
+ src: join15(templateDir, "flydocs", "knowledge", "templates", "note.md"),
2467
+ dest: join15(targetDir, "flydocs", "knowledge", "templates", "note.md"),
2468
+ label: "flydocs/knowledge/templates/note.md"
2469
+ },
2470
+ {
2471
+ src: join15(templateDir, "flydocs", "design-system", "README.md"),
2472
+ dest: join15(targetDir, "flydocs", "design-system", "README.md"),
2176
2473
  label: "flydocs/design-system/README.md"
2177
2474
  },
2178
2475
  {
2179
- src: join14(
2476
+ src: join15(
2180
2477
  templateDir,
2181
2478
  "flydocs",
2182
2479
  "design-system",
2183
2480
  "component-patterns.md"
2184
2481
  ),
2185
- dest: join14(
2482
+ dest: join15(
2186
2483
  targetDir,
2187
2484
  "flydocs",
2188
2485
  "design-system",
@@ -2191,13 +2488,13 @@ var init_install = __esm({
2191
2488
  label: "flydocs/design-system/component-patterns.md"
2192
2489
  },
2193
2490
  {
2194
- src: join14(templateDir, "flydocs", "design-system", "token-mapping.md"),
2195
- dest: join14(targetDir, "flydocs", "design-system", "token-mapping.md"),
2491
+ src: join15(templateDir, "flydocs", "design-system", "token-mapping.md"),
2492
+ dest: join15(targetDir, "flydocs", "design-system", "token-mapping.md"),
2196
2493
  label: "flydocs/design-system/token-mapping.md"
2197
2494
  },
2198
2495
  {
2199
- src: join14(templateDir, "flydocs", "README.md"),
2200
- dest: join14(targetDir, "flydocs", "README.md"),
2496
+ src: join15(templateDir, "flydocs", "README.md"),
2497
+ dest: join15(targetDir, "flydocs", "README.md"),
2201
2498
  label: "flydocs/README.md"
2202
2499
  }
2203
2500
  ];
@@ -2211,9 +2508,9 @@ var init_install = __esm({
2211
2508
  }
2212
2509
  }
2213
2510
  }
2214
- const envExampleSrc = join14(templateDir, ".env.example");
2511
+ const envExampleSrc = join15(templateDir, ".env.example");
2215
2512
  if (await pathExists(envExampleSrc)) {
2216
- await copyFile(envExampleSrc, join14(targetDir, ".env.example"));
2513
+ await copyFile(envExampleSrc, join15(targetDir, ".env.example"));
2217
2514
  printStatus(".env.example");
2218
2515
  }
2219
2516
  await ensureGitignore(targetDir);
@@ -2253,9 +2550,8 @@ var init_install = __esm({
2253
2550
  `Version: ${version}`,
2254
2551
  "",
2255
2552
  "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",
2553
+ " 1. Run /flydocs-setup to configure your workspace",
2554
+ " 2. Start working with /start-session",
2259
2555
  "",
2260
2556
  "Docs: https://www.flydocs.ai/docs"
2261
2557
  ];
@@ -2392,9 +2688,9 @@ __export(update_exports, {
2392
2688
  default: () => update_default
2393
2689
  });
2394
2690
  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";
2691
+ import { resolve as resolve3, join as join16 } from "path";
2692
+ import { mkdir as mkdir8, cp as cp2, readFile as readFile11, readdir as readdir3, rm as rm4 } from "fs/promises";
2693
+ import { select as select2, text as text2, confirm as confirm3, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
2398
2694
  import pc7 from "picocolors";
2399
2695
  var update_default;
2400
2696
  var init_update = __esm({
@@ -2482,7 +2778,7 @@ var init_update = __esm({
2482
2778
  if (choice === "cwd") {
2483
2779
  targetDir = process.cwd();
2484
2780
  } else {
2485
- const enteredPath = await text({
2781
+ const enteredPath = await text2({
2486
2782
  message: "Enter project path:"
2487
2783
  });
2488
2784
  if (isCancel4(enteredPath)) {
@@ -2500,9 +2796,9 @@ var init_update = __esm({
2500
2796
  }
2501
2797
  targetDir = resolve3(targetDir);
2502
2798
  process.chdir(targetDir);
2503
- const hasVersion = await pathExists(join15(targetDir, ".flydocs", "version"));
2799
+ const hasVersion = await pathExists(join16(targetDir, ".flydocs", "version"));
2504
2800
  const hasConfig = await pathExists(
2505
- join15(targetDir, ".flydocs", "config.json")
2801
+ join16(targetDir, ".flydocs", "config.json")
2506
2802
  );
2507
2803
  if (!hasVersion && !hasConfig) {
2508
2804
  printError(`Not a FlyDocs project: ${targetDir}`);
@@ -2513,8 +2809,8 @@ var init_update = __esm({
2513
2809
  console.log();
2514
2810
  let currentVersion = "0.1.0";
2515
2811
  if (hasVersion) {
2516
- const vContent = await readFile10(
2517
- join15(targetDir, ".flydocs", "version"),
2812
+ const vContent = await readFile11(
2813
+ join16(targetDir, ".flydocs", "version"),
2518
2814
  "utf-8"
2519
2815
  );
2520
2816
  currentVersion = vContent.trim();
@@ -2548,7 +2844,7 @@ var init_update = __esm({
2548
2844
  });
2549
2845
  console.log(`Updating: v${currentVersion} \u2192 v${version}`);
2550
2846
  console.log();
2551
- const changelogPath = join15(templateDir, "CHANGELOG.md");
2847
+ const changelogPath = join16(templateDir, "CHANGELOG.md");
2552
2848
  const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
2553
2849
  if (whatsNew.length > 0) {
2554
2850
  console.log(pc7.cyan("What's new:"));
@@ -2560,23 +2856,23 @@ var init_update = __esm({
2560
2856
  }
2561
2857
  const now = /* @__PURE__ */ new Date();
2562
2858
  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}`);
2859
+ const backupDir = join16(targetDir, ".flydocs", `backup-${ts}`);
2564
2860
  await mkdir8(backupDir, { recursive: true });
2565
2861
  if (hasConfig) {
2566
2862
  await cp2(
2567
- join15(targetDir, ".flydocs", "config.json"),
2568
- join15(backupDir, "config.json")
2863
+ join16(targetDir, ".flydocs", "config.json"),
2864
+ join16(backupDir, "config.json")
2569
2865
  );
2570
2866
  printStatus(`Config backed up to .flydocs/backup-${ts}/`);
2571
2867
  }
2572
2868
  try {
2573
- const flydocsDir = join15(targetDir, ".flydocs");
2869
+ const flydocsDir = join16(targetDir, ".flydocs");
2574
2870
  const entries = await readdir3(flydocsDir);
2575
2871
  const backups = entries.filter((e) => e.startsWith("backup-")).sort();
2576
2872
  if (backups.length > 3) {
2577
2873
  const toRemove = backups.slice(0, backups.length - 3);
2578
2874
  for (const old of toRemove) {
2579
- await rm4(join15(flydocsDir, old), { recursive: true, force: true });
2875
+ await rm4(join16(flydocsDir, old), { recursive: true, force: true });
2580
2876
  }
2581
2877
  }
2582
2878
  } catch {
@@ -2584,10 +2880,9 @@ var init_update = __esm({
2584
2880
  let preserved = {
2585
2881
  tier: "cloud",
2586
2882
  setupComplete: false,
2587
- providerTeamId: null,
2883
+ workspaceId: null,
2588
2884
  workspace: {},
2589
2885
  issueLabels: {},
2590
- statusMapping: {},
2591
2886
  detectedStack: {},
2592
2887
  skills: {},
2593
2888
  designSystem: null,
@@ -2611,22 +2906,23 @@ var init_update = __esm({
2611
2906
  } else {
2612
2907
  effectiveTier = preserved.tier;
2613
2908
  }
2909
+ await ensureDirectories(targetDir, effectiveTier);
2614
2910
  console.log("Replacing framework directories...");
2615
2911
  await replaceDirectory(
2616
- join15(templateDir, ".flydocs", "templates"),
2617
- join15(targetDir, ".flydocs", "templates")
2912
+ join16(templateDir, ".flydocs", "templates"),
2913
+ join16(targetDir, ".flydocs", "templates")
2618
2914
  );
2619
2915
  await replaceDirectory(
2620
- join15(templateDir, ".flydocs", "hooks"),
2621
- join15(targetDir, ".flydocs", "hooks")
2916
+ join16(templateDir, ".flydocs", "hooks"),
2917
+ join16(targetDir, ".flydocs", "hooks")
2622
2918
  );
2623
2919
  await replaceDirectory(
2624
- join15(templateDir, ".flydocs", "scripts"),
2625
- join15(targetDir, ".flydocs", "scripts")
2920
+ join16(templateDir, ".flydocs", "scripts"),
2921
+ join16(targetDir, ".flydocs", "scripts")
2626
2922
  );
2627
2923
  printStatus(".flydocs/templates, hooks, scripts");
2628
2924
  const hasExistingAgents = await pathExists(
2629
- join15(targetDir, ".claude", "agents")
2925
+ join16(targetDir, ".claude", "agents")
2630
2926
  );
2631
2927
  let installAgents;
2632
2928
  if (args.yes) {
@@ -2658,20 +2954,20 @@ var init_update = __esm({
2658
2954
  }
2659
2955
  }
2660
2956
  if (installAgents) {
2661
- const claudeAgentsSrc = join15(templateDir, ".claude", "agents");
2957
+ const claudeAgentsSrc = join16(templateDir, ".claude", "agents");
2662
2958
  if (await pathExists(claudeAgentsSrc)) {
2663
- await mkdir8(join15(targetDir, ".claude", "agents"), { recursive: true });
2959
+ await mkdir8(join16(targetDir, ".claude", "agents"), { recursive: true });
2664
2960
  await copyDirectoryContents(
2665
2961
  claudeAgentsSrc,
2666
- join15(targetDir, ".claude", "agents")
2962
+ join16(targetDir, ".claude", "agents")
2667
2963
  );
2668
2964
  }
2669
- const cursorAgentsSrc = join15(templateDir, ".cursor", "agents");
2965
+ const cursorAgentsSrc = join16(templateDir, ".cursor", "agents");
2670
2966
  if (await pathExists(cursorAgentsSrc)) {
2671
- await mkdir8(join15(targetDir, ".cursor", "agents"), { recursive: true });
2967
+ await mkdir8(join16(targetDir, ".cursor", "agents"), { recursive: true });
2672
2968
  await copyDirectoryContents(
2673
2969
  cursorAgentsSrc,
2674
- join15(targetDir, ".cursor", "agents")
2970
+ join16(targetDir, ".cursor", "agents")
2675
2971
  );
2676
2972
  }
2677
2973
  printStatus(
@@ -2685,46 +2981,62 @@ var init_update = __esm({
2685
2981
  console.log();
2686
2982
  console.log("Replacing framework files...");
2687
2983
  await copyFile(
2688
- join15(templateDir, ".claude", "CLAUDE.md"),
2689
- join15(targetDir, ".claude", "CLAUDE.md")
2984
+ join16(templateDir, ".claude", "CLAUDE.md"),
2985
+ join16(targetDir, ".claude", "CLAUDE.md")
2690
2986
  );
2691
2987
  await copyFile(
2692
- join15(templateDir, ".claude", "settings.json"),
2693
- join15(targetDir, ".claude", "settings.json")
2988
+ join16(templateDir, ".claude", "settings.json"),
2989
+ join16(targetDir, ".claude", "settings.json")
2694
2990
  );
2695
2991
  printStatus(".claude/CLAUDE.md, settings.json");
2696
2992
  await copyDirectoryContents(
2697
- join15(templateDir, ".claude", "commands"),
2698
- join15(targetDir, ".claude", "commands")
2993
+ join16(templateDir, ".claude", "commands"),
2994
+ join16(targetDir, ".claude", "commands")
2699
2995
  );
2700
2996
  await copyDirectoryContents(
2701
- join15(templateDir, ".claude", "commands"),
2702
- join15(targetDir, ".cursor", "commands")
2997
+ join16(templateDir, ".claude", "commands"),
2998
+ join16(targetDir, ".cursor", "commands")
2703
2999
  );
2704
3000
  printStatus(".claude/commands, .cursor/commands");
2705
- const skillsReadmeSrc = join15(templateDir, ".claude", "skills", "README.md");
3001
+ const skillsReadmeSrc = join16(templateDir, ".claude", "skills", "README.md");
2706
3002
  if (await pathExists(skillsReadmeSrc)) {
2707
3003
  await copyFile(
2708
3004
  skillsReadmeSrc,
2709
- join15(targetDir, ".claude", "skills", "README.md")
3005
+ join16(targetDir, ".claude", "skills", "README.md")
2710
3006
  );
2711
3007
  }
2712
3008
  printStatus(".claude/skills/README.md");
2713
3009
  await copyFile(
2714
- join15(templateDir, ".cursor", "hooks.json"),
2715
- join15(targetDir, ".cursor", "hooks.json")
3010
+ join16(templateDir, ".cursor", "hooks.json"),
3011
+ join16(targetDir, ".cursor", "hooks.json")
2716
3012
  );
2717
3013
  printStatus(".cursor/hooks.json");
2718
3014
  await copyFile(
2719
- join15(templateDir, "AGENTS.md"),
2720
- join15(targetDir, "AGENTS.md")
3015
+ join16(templateDir, "AGENTS.md"),
3016
+ join16(targetDir, "AGENTS.md")
2721
3017
  );
2722
3018
  printStatus("AGENTS.md");
2723
- const envExampleSrc = join15(templateDir, ".env.example");
3019
+ const envExampleSrc = join16(templateDir, ".env.example");
2724
3020
  if (await pathExists(envExampleSrc)) {
2725
- await copyFile(envExampleSrc, join15(targetDir, ".env.example"));
3021
+ await copyFile(envExampleSrc, join16(targetDir, ".env.example"));
2726
3022
  printStatus(".env.example");
2727
3023
  }
3024
+ const knowledgeTemplatesDir = join16(
3025
+ targetDir,
3026
+ "flydocs",
3027
+ "knowledge",
3028
+ "templates"
3029
+ );
3030
+ if (!await pathExists(knowledgeTemplatesDir)) {
3031
+ await mkdir8(knowledgeTemplatesDir, { recursive: true });
3032
+ }
3033
+ for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
3034
+ const src = join16(templateDir, "flydocs", "knowledge", "templates", tmpl);
3035
+ const dest = join16(knowledgeTemplatesDir, tmpl);
3036
+ if (await pathExists(src) && !await pathExists(dest)) {
3037
+ await copyFile(src, dest);
3038
+ }
3039
+ }
2728
3040
  await runManifestGeneration(targetDir);
2729
3041
  await runContextGraphBuild(targetDir);
2730
3042
  console.log();
@@ -2747,18 +3059,18 @@ var init_update = __esm({
2747
3059
  printWarning("Config merge failed \u2014 config.json preserved as-is");
2748
3060
  }
2749
3061
  await copyFile(
2750
- join15(templateDir, ".flydocs", "version"),
2751
- join15(targetDir, ".flydocs", "version")
3062
+ join16(templateDir, ".flydocs", "version"),
3063
+ join16(targetDir, ".flydocs", "version")
2752
3064
  );
2753
3065
  printStatus(`.flydocs/version \u2192 ${version}`);
2754
- const clSrc = join15(templateDir, "CHANGELOG.md");
3066
+ const clSrc = join16(templateDir, "CHANGELOG.md");
2755
3067
  if (await pathExists(clSrc)) {
2756
- await copyFile(clSrc, join15(targetDir, ".flydocs", "CHANGELOG.md"));
3068
+ await copyFile(clSrc, join16(targetDir, ".flydocs", "CHANGELOG.md"));
2757
3069
  printStatus(".flydocs/CHANGELOG.md");
2758
3070
  }
2759
- const mfSrc = join15(templateDir, "manifest.json");
3071
+ const mfSrc = join16(templateDir, "manifest.json");
2760
3072
  if (await pathExists(mfSrc)) {
2761
- await copyFile(mfSrc, join15(targetDir, ".flydocs", "manifest.json"));
3073
+ await copyFile(mfSrc, join16(targetDir, ".flydocs", "manifest.json"));
2762
3074
  printStatus(".flydocs/manifest.json");
2763
3075
  }
2764
3076
  console.log();
@@ -2818,12 +3130,12 @@ __export(uninstall_exports, {
2818
3130
  default: () => uninstall_default
2819
3131
  });
2820
3132
  import { defineCommand as defineCommand3 } from "citty";
2821
- import { resolve as resolve4, join as join16 } from "path";
3133
+ import { resolve as resolve4, join as join17 } from "path";
2822
3134
  import { readdir as readdir4, rm as rm5, rename as rename2 } from "fs/promises";
2823
3135
  import { confirm as confirm4, select as select3, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
2824
3136
  import pc8 from "picocolors";
2825
3137
  async function removeOwnedSkills(targetDir) {
2826
- const skillsDir = join16(targetDir, ".claude", "skills");
3138
+ const skillsDir = join17(targetDir, ".claude", "skills");
2827
3139
  const removed = [];
2828
3140
  if (!await pathExists(skillsDir)) {
2829
3141
  return removed;
@@ -2832,7 +3144,7 @@ async function removeOwnedSkills(targetDir) {
2832
3144
  const entries = await readdir4(skillsDir);
2833
3145
  for (const entry of entries) {
2834
3146
  if (entry.startsWith(OWNED_SKILL_PREFIX)) {
2835
- await rm5(join16(skillsDir, entry), { recursive: true, force: true });
3147
+ await rm5(join17(skillsDir, entry), { recursive: true, force: true });
2836
3148
  removed.push(`.claude/skills/${entry}`);
2837
3149
  }
2838
3150
  }
@@ -2841,7 +3153,7 @@ async function removeOwnedSkills(targetDir) {
2841
3153
  return removed;
2842
3154
  }
2843
3155
  async function removeOwnedCursorRules(targetDir) {
2844
- const rulesDir = join16(targetDir, ".cursor", "rules");
3156
+ const rulesDir = join17(targetDir, ".cursor", "rules");
2845
3157
  const removed = [];
2846
3158
  if (!await pathExists(rulesDir)) {
2847
3159
  return removed;
@@ -2850,7 +3162,7 @@ async function removeOwnedCursorRules(targetDir) {
2850
3162
  const entries = await readdir4(rulesDir);
2851
3163
  for (const entry of entries) {
2852
3164
  if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
2853
- await rm5(join16(rulesDir, entry), { force: true });
3165
+ await rm5(join17(rulesDir, entry), { force: true });
2854
3166
  removed.push(`.cursor/rules/${entry}`);
2855
3167
  }
2856
3168
  }
@@ -2869,7 +3181,7 @@ async function isEmptyDir(dirPath) {
2869
3181
  async function cleanupEmptyParents(targetDir, dirs) {
2870
3182
  const cleaned = [];
2871
3183
  for (const dir of dirs) {
2872
- const fullPath = join16(targetDir, dir);
3184
+ const fullPath = join17(targetDir, dir);
2873
3185
  if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
2874
3186
  await rm5(fullPath, { recursive: true, force: true });
2875
3187
  cleaned.push(dir);
@@ -2946,8 +3258,8 @@ var init_uninstall = __esm({
2946
3258
  process.exit(1);
2947
3259
  }
2948
3260
  targetDir = resolve4(targetDir);
2949
- const hasFlydocs = await pathExists(join16(targetDir, ".flydocs"));
2950
- const hasAgentsMd = await pathExists(join16(targetDir, "AGENTS.md"));
3261
+ const hasFlydocs = await pathExists(join17(targetDir, ".flydocs"));
3262
+ const hasAgentsMd = await pathExists(join17(targetDir, "AGENTS.md"));
2951
3263
  if (!hasFlydocs && !hasAgentsMd) {
2952
3264
  printError(`Not a FlyDocs project: ${targetDir}`);
2953
3265
  printInfo("No .flydocs/ directory or AGENTS.md found.");
@@ -2959,7 +3271,7 @@ var init_uninstall = __esm({
2959
3271
  const removeAll = forceAll || args.all;
2960
3272
  const skipPrompts = forceAll || args.yes;
2961
3273
  let contentAction = "preserve";
2962
- const hasUserContent = await pathExists(join16(targetDir, "flydocs"));
3274
+ const hasUserContent = await pathExists(join17(targetDir, "flydocs"));
2963
3275
  if (hasUserContent) {
2964
3276
  if (removeAll) {
2965
3277
  contentAction = "remove";
@@ -3042,7 +3354,7 @@ var init_uninstall = __esm({
3042
3354
  const removedRules = await removeOwnedCursorRules(targetDir);
3043
3355
  result.removed.push(...removedRules);
3044
3356
  for (const [relativePath, type] of ALWAYS_REMOVED) {
3045
- const fullPath = join16(targetDir, relativePath);
3357
+ const fullPath = join17(targetDir, relativePath);
3046
3358
  if (!await pathExists(fullPath)) {
3047
3359
  result.skipped.push(relativePath);
3048
3360
  continue;
@@ -3060,9 +3372,9 @@ var init_uninstall = __esm({
3060
3372
  }
3061
3373
  }
3062
3374
  if (hasUserContent) {
3063
- const flydocsPath = join16(targetDir, "flydocs");
3375
+ const flydocsPath = join17(targetDir, "flydocs");
3064
3376
  if (contentAction === "archive") {
3065
- const archivePath = join16(targetDir, "flydocs-archive");
3377
+ const archivePath = join17(targetDir, "flydocs-archive");
3066
3378
  if (await pathExists(archivePath)) {
3067
3379
  await rm5(archivePath, { recursive: true, force: true });
3068
3380
  }
@@ -3301,70 +3613,9 @@ __export(connect_exports, {
3301
3613
  default: () => connect_default
3302
3614
  });
3303
3615
  import { defineCommand as defineCommand6 } from "citty";
3304
- import { text as text2, confirm as confirm5, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
3616
+ import { text as text3, confirm as confirm5, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
3305
3617
  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
- }
3618
+ import { join as join18 } from "path";
3368
3619
  var connect_default;
3369
3620
  var init_connect = __esm({
3370
3621
  "src/commands/connect.ts"() {
@@ -3373,6 +3624,7 @@ var init_connect = __esm({
3373
3624
  init_fs_ops();
3374
3625
  init_template();
3375
3626
  init_ui();
3627
+ init_api_key();
3376
3628
  connect_default = defineCommand6({
3377
3629
  meta: {
3378
3630
  name: "connect",
@@ -3393,12 +3645,12 @@ var init_connect = __esm({
3393
3645
  },
3394
3646
  key: {
3395
3647
  type: "string",
3396
- description: "API key (fdk_ for relay, lin_api_ for direct Linear)"
3648
+ description: "FlyDocs API key (fdk_...)"
3397
3649
  }
3398
3650
  },
3399
3651
  async run({ args }) {
3400
3652
  const targetDir = args.path ?? process.cwd();
3401
- const configPath = join17(targetDir, ".flydocs", "config.json");
3653
+ const configPath = join18(targetDir, ".flydocs", "config.json");
3402
3654
  if (!await pathExists(configPath)) {
3403
3655
  printError("Not a FlyDocs project (.flydocs/config.json not found).");
3404
3656
  console.log(
@@ -3422,22 +3674,19 @@ var init_connect = __esm({
3422
3674
  console.log(` ${pc11.bold("Connect to FlyDocs Cloud")}`);
3423
3675
  console.log();
3424
3676
  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")}`
3677
+ ` ${pc11.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
3429
3678
  );
3430
3679
  console.log();
3431
3680
  let apiKey = args.key ?? "";
3432
3681
  if (!apiKey) {
3433
- const keyInput = await text2({
3682
+ const keyInput = await text3({
3434
3683
  message: "Enter your API key",
3435
- placeholder: "fdk_... or lin_api_...",
3684
+ placeholder: "fdk_...",
3436
3685
  validate(value) {
3437
3686
  if (!value.trim()) return "API key is required";
3438
3687
  const type = detectKeyType(value.trim());
3439
3688
  if (type === "unknown")
3440
- return "Key must start with fdk_ (FlyDocs) or lin_api_ (Linear)";
3689
+ return "Key must start with fdk_ (FlyDocs API key)";
3441
3690
  return void 0;
3442
3691
  }
3443
3692
  });
@@ -3449,7 +3698,9 @@ var init_connect = __esm({
3449
3698
  }
3450
3699
  const keyType = detectKeyType(apiKey);
3451
3700
  if (keyType === "unknown") {
3452
- printError("Unrecognized key format. Expected fdk_ or lin_api_ prefix.");
3701
+ printError(
3702
+ "Unrecognized key format. Expected fdk_ prefix (FlyDocs API key)."
3703
+ );
3453
3704
  process.exit(1);
3454
3705
  }
3455
3706
  printInfo("Validating API key...");
@@ -3491,8 +3742,9 @@ var init_connect = __esm({
3491
3742
  }
3492
3743
  const wasLocal = config.tier === "local";
3493
3744
  config.tier = "cloud";
3494
- config.provider = config.provider ?? { type: "linear", teamId: null };
3495
- config.provider.type = "linear";
3745
+ const configRecord = config;
3746
+ delete configRecord.statusMapping;
3747
+ delete configRecord.provider;
3496
3748
  await writeConfig(targetDir, config);
3497
3749
  printStatus("Config updated to cloud tier");
3498
3750
  if (wasLocal) {
@@ -3500,14 +3752,14 @@ var init_connect = __esm({
3500
3752
  const templateDir = await resolveTemplatePath(
3501
3753
  args["local-source"] || void 0
3502
3754
  );
3503
- const templateSkillsDir = join17(templateDir, ".claude", "skills");
3504
- const skillsDir = join17(targetDir, ".claude", "skills");
3755
+ const templateSkillsDir = join18(templateDir, ".claude", "skills");
3756
+ const skillsDir = join18(targetDir, ".claude", "skills");
3505
3757
  await replaceDirectory(
3506
- join17(templateSkillsDir, "flydocs-cloud"),
3507
- join17(skillsDir, "flydocs-cloud")
3758
+ join18(templateSkillsDir, "flydocs-cloud"),
3759
+ join18(skillsDir, "flydocs-cloud")
3508
3760
  );
3509
3761
  const { rm: rm6 } = await import("fs/promises");
3510
- const localSkillDir = join17(skillsDir, "flydocs-local");
3762
+ const localSkillDir = join18(skillsDir, "flydocs-local");
3511
3763
  if (await pathExists(localSkillDir)) {
3512
3764
  await rm6(localSkillDir, { recursive: true, force: true });
3513
3765
  }
@@ -3580,7 +3832,7 @@ var init_upgrade = __esm({
3580
3832
  );
3581
3833
  console.log();
3582
3834
  console.log(
3583
- ` Your issues sync with Linear via the cloud mechanism skill.`
3835
+ ` Your issues sync with your provider via the cloud mechanism skill.`
3584
3836
  );
3585
3837
  console.log(
3586
3838
  ` Run ${pc12.cyan("flydocs connect")} to update your connection settings.`
@@ -3593,9 +3845,7 @@ var init_upgrade = __esm({
3593
3845
  console.log(` You're currently on the ${pc12.yellow("local")} tier.`);
3594
3846
  console.log(` Upgrade to cloud for:`);
3595
3847
  console.log();
3596
- console.log(
3597
- ` ${pc12.cyan("\u2192")} Issue sync with Linear (Jira coming soon)`
3598
- );
3848
+ console.log(` ${pc12.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
3599
3849
  console.log(` ${pc12.cyan("\u2192")} Project milestones and cycle management`);
3600
3850
  console.log(` ${pc12.cyan("\u2192")} Team assignment and priority tracking`);
3601
3851
  console.log(` ${pc12.cyan("\u2192")} Project health updates and dashboards`);