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