@flydocs/cli 0.6.0-alpha.31 → 0.6.0-alpha.32
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 +965 -287
- package/package.json +1 -1
- package/template/.flydocs/config.json +1 -1
- package/template/.flydocs/version +1 -1
- 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.32";
|
|
19
19
|
CLI_NAME = "flydocs";
|
|
20
20
|
PACKAGE_NAME = "@flydocs/cli";
|
|
21
21
|
POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
|
|
@@ -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: mkdir15 } = await import("fs/promises");
|
|
444
444
|
const rulesDir = join4(targetDir, ".cursor", "rules");
|
|
445
|
-
await
|
|
445
|
+
await mkdir15(rulesDir, { recursive: true });
|
|
446
446
|
const workflowRule = join4(
|
|
447
447
|
targetDir,
|
|
448
448
|
".claude",
|
|
@@ -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,90 @@ var init_relay_client = __esm({
|
|
|
2989
3102
|
}
|
|
2990
3103
|
});
|
|
2991
3104
|
|
|
3105
|
+
// src/lib/artifacts.ts
|
|
3106
|
+
import { mkdir as mkdir9, writeFile as writeFile11 } from "fs/promises";
|
|
3107
|
+
import { join as join18, dirname as dirname3 } from "path";
|
|
3108
|
+
async function syncArtifacts(targetDir, apiKey, workspaceId, currentVersion) {
|
|
3109
|
+
const response = await fetchArtifacts(apiKey, currentVersion);
|
|
3110
|
+
if (response.artifacts.length === 0) {
|
|
3111
|
+
return { written: 0, version: currentVersion };
|
|
3112
|
+
}
|
|
3113
|
+
let written = 0;
|
|
3114
|
+
for (const artifact of response.artifacts) {
|
|
3115
|
+
await writeArtifact(targetDir, artifact);
|
|
3116
|
+
written++;
|
|
3117
|
+
}
|
|
3118
|
+
printStatus(
|
|
3119
|
+
`Synced ${written} artifact(s) (v${currentVersion} \u2192 v${response.version})`
|
|
3120
|
+
);
|
|
3121
|
+
return { written, version: response.version };
|
|
3122
|
+
}
|
|
3123
|
+
async function syncAgentConfigs(targetDir, apiKey, workspaceId) {
|
|
3124
|
+
const response = await fetchAgentConfigs(apiKey, workspaceId);
|
|
3125
|
+
let written = 0;
|
|
3126
|
+
for (const file of response.files) {
|
|
3127
|
+
const filePath = join18(targetDir, file.path);
|
|
3128
|
+
await mkdir9(dirname3(filePath), { recursive: true });
|
|
3129
|
+
await writeFile11(filePath, file.content, "utf-8");
|
|
3130
|
+
written++;
|
|
3131
|
+
}
|
|
3132
|
+
if (written > 0) {
|
|
3133
|
+
printStatus(`Wrote ${written} agent config(s) (CLAUDE.md, AGENTS.md)`);
|
|
3134
|
+
}
|
|
3135
|
+
return written;
|
|
3136
|
+
}
|
|
3137
|
+
async function writeArtifact(targetDir, artifact) {
|
|
3138
|
+
const filePath = join18(targetDir, artifact.path);
|
|
3139
|
+
await mkdir9(dirname3(filePath), { recursive: true });
|
|
3140
|
+
await writeFile11(filePath, artifact.content, "utf-8");
|
|
3141
|
+
}
|
|
3142
|
+
async function syncAllArtifacts(targetDir, apiKey, workspaceId, currentArtifactVersion) {
|
|
3143
|
+
const changes = [];
|
|
3144
|
+
let artifactVersion = currentArtifactVersion;
|
|
3145
|
+
try {
|
|
3146
|
+
const result = await syncArtifacts(
|
|
3147
|
+
targetDir,
|
|
3148
|
+
apiKey,
|
|
3149
|
+
workspaceId,
|
|
3150
|
+
currentArtifactVersion
|
|
3151
|
+
);
|
|
3152
|
+
if (result.written > 0) {
|
|
3153
|
+
changes.push(`Synced ${result.written} artifact(s)`);
|
|
3154
|
+
artifactVersion = result.version;
|
|
3155
|
+
}
|
|
3156
|
+
} catch (err) {
|
|
3157
|
+
if (err instanceof RelayError) {
|
|
3158
|
+
printWarning(
|
|
3159
|
+
`Could not fetch artifacts (${err.status}). Will retry on next sync.`
|
|
3160
|
+
);
|
|
3161
|
+
} else {
|
|
3162
|
+
printWarning("Artifact sync failed. Will retry on next sync.");
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
try {
|
|
3166
|
+
const configCount = await syncAgentConfigs(targetDir, apiKey, workspaceId);
|
|
3167
|
+
if (configCount > 0) {
|
|
3168
|
+
changes.push(`Wrote ${configCount} agent config(s)`);
|
|
3169
|
+
}
|
|
3170
|
+
} catch (err) {
|
|
3171
|
+
if (err instanceof RelayError) {
|
|
3172
|
+
printWarning(
|
|
3173
|
+
`Could not fetch agent configs (${err.status}). Will retry on next sync.`
|
|
3174
|
+
);
|
|
3175
|
+
} else {
|
|
3176
|
+
printWarning("Agent config sync failed. Will retry on next sync.");
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
return { artifactVersion, changes };
|
|
3180
|
+
}
|
|
3181
|
+
var init_artifacts = __esm({
|
|
3182
|
+
"src/lib/artifacts.ts"() {
|
|
3183
|
+
"use strict";
|
|
3184
|
+
init_ui();
|
|
3185
|
+
init_relay_client();
|
|
3186
|
+
}
|
|
3187
|
+
});
|
|
3188
|
+
|
|
2992
3189
|
// src/commands/cleanup.ts
|
|
2993
3190
|
var cleanup_exports = {};
|
|
2994
3191
|
__export(cleanup_exports, {
|
|
@@ -2998,11 +3195,11 @@ __export(cleanup_exports, {
|
|
|
2998
3195
|
});
|
|
2999
3196
|
import { defineCommand as defineCommand2 } from "citty";
|
|
3000
3197
|
import pc7 from "picocolors";
|
|
3001
|
-
import { join as
|
|
3002
|
-
import { readFile as readFile13, writeFile as
|
|
3198
|
+
import { join as join19 } from "path";
|
|
3199
|
+
import { readFile as readFile13, writeFile as writeFile12, rm as rm4 } from "fs/promises";
|
|
3003
3200
|
async function scanArtifacts(targetDir) {
|
|
3004
3201
|
const actions = [];
|
|
3005
|
-
const localMePath =
|
|
3202
|
+
const localMePath = join19(targetDir, ".flydocs", "me.json");
|
|
3006
3203
|
if (await pathExists(localMePath) && await pathExists(globalMePath())) {
|
|
3007
3204
|
actions.push({
|
|
3008
3205
|
description: "Remove .flydocs/me.json (migrated to ~/.flydocs/me.json)",
|
|
@@ -3010,7 +3207,7 @@ async function scanArtifacts(targetDir) {
|
|
|
3010
3207
|
type: "file"
|
|
3011
3208
|
});
|
|
3012
3209
|
}
|
|
3013
|
-
const validationCachePath =
|
|
3210
|
+
const validationCachePath = join19(
|
|
3014
3211
|
targetDir,
|
|
3015
3212
|
".flydocs",
|
|
3016
3213
|
"validation-cache.json"
|
|
@@ -3024,7 +3221,7 @@ async function scanArtifacts(targetDir) {
|
|
|
3024
3221
|
}
|
|
3025
3222
|
const hasGlobalCreds = await pathExists(credentialsPath());
|
|
3026
3223
|
for (const envFile of [".env", ".env.local"]) {
|
|
3027
|
-
const envPath =
|
|
3224
|
+
const envPath = join19(targetDir, envFile);
|
|
3028
3225
|
if (!await pathExists(envPath)) continue;
|
|
3029
3226
|
const content = await readFile13(envPath, "utf-8");
|
|
3030
3227
|
const lines = content.split("\n");
|
|
@@ -3049,7 +3246,7 @@ async function scanArtifacts(targetDir) {
|
|
|
3049
3246
|
try {
|
|
3050
3247
|
const config = await readAnyConfig(targetDir);
|
|
3051
3248
|
const rawContent = await readFile13(
|
|
3052
|
-
|
|
3249
|
+
join19(targetDir, ".flydocs", "config.json"),
|
|
3053
3250
|
"utf-8"
|
|
3054
3251
|
);
|
|
3055
3252
|
const raw = JSON.parse(rawContent);
|
|
@@ -3058,7 +3255,7 @@ async function scanArtifacts(targetDir) {
|
|
|
3058
3255
|
if (field in raw) {
|
|
3059
3256
|
actions.push({
|
|
3060
3257
|
description: `Remove ghost field "${field}" from config.json`,
|
|
3061
|
-
path:
|
|
3258
|
+
path: join19(targetDir, ".flydocs", "config.json"),
|
|
3062
3259
|
type: "field"
|
|
3063
3260
|
});
|
|
3064
3261
|
}
|
|
@@ -3077,7 +3274,7 @@ async function scanArtifacts(targetDir) {
|
|
|
3077
3274
|
if (field in raw) {
|
|
3078
3275
|
actions.push({
|
|
3079
3276
|
description: `Remove v1 field "${field}" from config.json (server-owned in v2)`,
|
|
3080
|
-
path:
|
|
3277
|
+
path: join19(targetDir, ".flydocs", "config.json"),
|
|
3081
3278
|
type: "field"
|
|
3082
3279
|
});
|
|
3083
3280
|
}
|
|
@@ -3112,11 +3309,11 @@ async function executeCleanup(targetDir, actions) {
|
|
|
3112
3309
|
if (!hasContent) {
|
|
3113
3310
|
await rm4(envPath, { force: true });
|
|
3114
3311
|
} else {
|
|
3115
|
-
await
|
|
3312
|
+
await writeFile12(envPath, filtered.join("\n"), "utf-8");
|
|
3116
3313
|
}
|
|
3117
3314
|
}
|
|
3118
3315
|
if (fieldRemovals.length > 0) {
|
|
3119
|
-
const configPath =
|
|
3316
|
+
const configPath = join19(targetDir, ".flydocs", "config.json");
|
|
3120
3317
|
const content = await readFile13(configPath, "utf-8");
|
|
3121
3318
|
const config = JSON.parse(content);
|
|
3122
3319
|
for (const action of fieldRemovals) {
|
|
@@ -3125,7 +3322,7 @@ async function executeCleanup(targetDir, actions) {
|
|
|
3125
3322
|
delete config[match[1]];
|
|
3126
3323
|
}
|
|
3127
3324
|
}
|
|
3128
|
-
await
|
|
3325
|
+
await writeFile12(
|
|
3129
3326
|
configPath,
|
|
3130
3327
|
JSON.stringify(config, null, 2) + "\n",
|
|
3131
3328
|
"utf-8"
|
|
@@ -3163,7 +3360,7 @@ var init_cleanup = __esm({
|
|
|
3163
3360
|
console.log(` ${pc7.bold("FlyDocs Cleanup")}`);
|
|
3164
3361
|
console.log();
|
|
3165
3362
|
const hasConfig = await pathExists(
|
|
3166
|
-
|
|
3363
|
+
join19(targetDir, ".flydocs", "config.json")
|
|
3167
3364
|
);
|
|
3168
3365
|
if (!hasConfig) {
|
|
3169
3366
|
printError("No .flydocs/config.json found. Run flydocs init first.");
|
|
@@ -3206,33 +3403,106 @@ var init_cleanup = __esm({
|
|
|
3206
3403
|
}
|
|
3207
3404
|
});
|
|
3208
3405
|
|
|
3406
|
+
// src/lib/ide-archive.ts
|
|
3407
|
+
import { readFile as readFile14, writeFile as writeFile13, mkdir as mkdir10, readdir as readdir4 } from "fs/promises";
|
|
3408
|
+
import { join as join20, basename } from "path";
|
|
3409
|
+
async function archiveIdeConfigs(targetDir) {
|
|
3410
|
+
const archived = [];
|
|
3411
|
+
for (const cfg of SINGLE_FILE_CONFIGS) {
|
|
3412
|
+
const absSource = join20(targetDir, cfg.source);
|
|
3413
|
+
if (!await pathExists(absSource)) continue;
|
|
3414
|
+
const content = await readFile14(absSource, "utf-8");
|
|
3415
|
+
const absDest = join20(targetDir, cfg.archive);
|
|
3416
|
+
await mkdir10(join20(absDest, ".."), { recursive: true });
|
|
3417
|
+
await writeFile13(absDest, content, "utf-8");
|
|
3418
|
+
archived.push({
|
|
3419
|
+
sourcePath: cfg.source,
|
|
3420
|
+
archivePath: cfg.archive,
|
|
3421
|
+
content
|
|
3422
|
+
});
|
|
3423
|
+
printStatus(`Archived ${cfg.source}`);
|
|
3424
|
+
}
|
|
3425
|
+
const cursorRulesDir = join20(targetDir, ".cursor", "rules");
|
|
3426
|
+
if (await pathExists(cursorRulesDir)) {
|
|
3427
|
+
let entries;
|
|
3428
|
+
try {
|
|
3429
|
+
entries = await readdir4(cursorRulesDir);
|
|
3430
|
+
} catch {
|
|
3431
|
+
entries = [];
|
|
3432
|
+
}
|
|
3433
|
+
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
3434
|
+
if (mdFiles.length > 0) {
|
|
3435
|
+
const archiveSubdir = join20(
|
|
3436
|
+
targetDir,
|
|
3437
|
+
"flydocs",
|
|
3438
|
+
"knowledge",
|
|
3439
|
+
"archived",
|
|
3440
|
+
"cursor-rules"
|
|
3441
|
+
);
|
|
3442
|
+
await mkdir10(archiveSubdir, { recursive: true });
|
|
3443
|
+
for (const file of mdFiles) {
|
|
3444
|
+
const absSource = join20(cursorRulesDir, file);
|
|
3445
|
+
const content = await readFile14(absSource, "utf-8");
|
|
3446
|
+
const archivePath = `flydocs/knowledge/archived/cursor-rules/${basename(file)}`;
|
|
3447
|
+
const absDest = join20(targetDir, archivePath);
|
|
3448
|
+
await writeFile13(absDest, content, "utf-8");
|
|
3449
|
+
archived.push({
|
|
3450
|
+
sourcePath: `.cursor/rules/${file}`,
|
|
3451
|
+
archivePath,
|
|
3452
|
+
content
|
|
3453
|
+
});
|
|
3454
|
+
printStatus(`Archived .cursor/rules/${file}`);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
return archived;
|
|
3459
|
+
}
|
|
3460
|
+
var SINGLE_FILE_CONFIGS;
|
|
3461
|
+
var init_ide_archive = __esm({
|
|
3462
|
+
"src/lib/ide-archive.ts"() {
|
|
3463
|
+
"use strict";
|
|
3464
|
+
init_fs_ops();
|
|
3465
|
+
init_ui();
|
|
3466
|
+
SINGLE_FILE_CONFIGS = [
|
|
3467
|
+
{
|
|
3468
|
+
source: ".cursorrules",
|
|
3469
|
+
archive: "flydocs/knowledge/archived/cursorrules.md"
|
|
3470
|
+
},
|
|
3471
|
+
{
|
|
3472
|
+
source: ".claude/CLAUDE.md",
|
|
3473
|
+
archive: "flydocs/knowledge/archived/claude-md.md"
|
|
3474
|
+
}
|
|
3475
|
+
];
|
|
3476
|
+
}
|
|
3477
|
+
});
|
|
3478
|
+
|
|
3209
3479
|
// src/lib/workspace.ts
|
|
3210
|
-
import { readFile as
|
|
3211
|
-
import { join as
|
|
3480
|
+
import { readFile as readFile15, writeFile as writeFile14, readdir as readdir5, stat as stat2 } from "fs/promises";
|
|
3481
|
+
import { join as join21, dirname as dirname4, relative, resolve as resolve3 } from "path";
|
|
3212
3482
|
async function readWorkspaceFile(dir) {
|
|
3213
|
-
const filePath =
|
|
3483
|
+
const filePath = join21(dir, WORKSPACE_FILENAME);
|
|
3214
3484
|
if (!await pathExists(filePath)) return null;
|
|
3215
|
-
const content = await
|
|
3485
|
+
const content = await readFile15(filePath, "utf-8");
|
|
3216
3486
|
const parsed = JSON.parse(content);
|
|
3217
3487
|
validateWorkspaceFile(parsed);
|
|
3218
3488
|
return parsed;
|
|
3219
3489
|
}
|
|
3220
3490
|
async function writeWorkspaceFile(dir, workspace) {
|
|
3221
|
-
const filePath =
|
|
3491
|
+
const filePath = join21(dir, WORKSPACE_FILENAME);
|
|
3222
3492
|
const content = JSON.stringify(workspace, null, 2) + "\n";
|
|
3223
|
-
await
|
|
3493
|
+
await writeFile14(filePath, content, "utf-8");
|
|
3224
3494
|
}
|
|
3225
3495
|
async function detectSiblingRepos(parentDir) {
|
|
3226
3496
|
const repos = {};
|
|
3227
|
-
const entries = await
|
|
3497
|
+
const entries = await readdir5(parentDir);
|
|
3228
3498
|
for (const entry of entries) {
|
|
3229
3499
|
if (entry.startsWith(".")) continue;
|
|
3230
|
-
const entryPath =
|
|
3500
|
+
const entryPath = join21(parentDir, entry);
|
|
3231
3501
|
const entryStat = await stat2(entryPath).catch(() => null);
|
|
3232
3502
|
if (!entryStat?.isDirectory()) continue;
|
|
3233
|
-
const hasGit = await pathExists(
|
|
3503
|
+
const hasGit = await pathExists(join21(entryPath, ".git"));
|
|
3234
3504
|
const hasFlydocs = await pathExists(
|
|
3235
|
-
|
|
3505
|
+
join21(entryPath, ".flydocs", "config.json")
|
|
3236
3506
|
);
|
|
3237
3507
|
if (hasGit || hasFlydocs) {
|
|
3238
3508
|
repos[entry] = { path: `./${entry}` };
|
|
@@ -3247,10 +3517,10 @@ async function readSiblingDescriptors(workspaceDir, repos) {
|
|
|
3247
3517
|
const descriptors = [];
|
|
3248
3518
|
for (const [name, entry] of Object.entries(repos)) {
|
|
3249
3519
|
const repoDir = resolve3(workspaceDir, entry.path);
|
|
3250
|
-
const serviceJsonPath =
|
|
3520
|
+
const serviceJsonPath = join21(repoDir, "flydocs", "context", "service.json");
|
|
3251
3521
|
if (!await pathExists(serviceJsonPath)) continue;
|
|
3252
3522
|
try {
|
|
3253
|
-
const content = await
|
|
3523
|
+
const content = await readFile15(serviceJsonPath, "utf-8");
|
|
3254
3524
|
const descriptor = JSON.parse(content);
|
|
3255
3525
|
descriptors.push({ name, path: entry.path, descriptor });
|
|
3256
3526
|
} catch {
|
|
@@ -3353,23 +3623,217 @@ var init_workspace = __esm({
|
|
|
3353
3623
|
}
|
|
3354
3624
|
});
|
|
3355
3625
|
|
|
3626
|
+
// src/commands/scan.ts
|
|
3627
|
+
var scan_exports = {};
|
|
3628
|
+
__export(scan_exports, {
|
|
3629
|
+
default: () => scan_default,
|
|
3630
|
+
runScan: () => runScan
|
|
3631
|
+
});
|
|
3632
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
3633
|
+
import { spinner } from "@clack/prompts";
|
|
3634
|
+
import pc8 from "picocolors";
|
|
3635
|
+
import { join as join22 } from "path";
|
|
3636
|
+
import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
|
|
3637
|
+
async function resolveRepoName(targetDir, repoArg) {
|
|
3638
|
+
if (repoArg) return repoArg;
|
|
3639
|
+
try {
|
|
3640
|
+
const config = await readAnyConfig(targetDir);
|
|
3641
|
+
if (!isConfigV2(config) && config.sourceRepo) {
|
|
3642
|
+
return config.sourceRepo;
|
|
3643
|
+
}
|
|
3644
|
+
} catch {
|
|
3645
|
+
}
|
|
3646
|
+
try {
|
|
3647
|
+
const { execFile: execFile2 } = await import("child_process");
|
|
3648
|
+
const { promisify: promisify2 } = await import("util");
|
|
3649
|
+
const execFileAsync2 = promisify2(execFile2);
|
|
3650
|
+
const { stdout } = await execFileAsync2(
|
|
3651
|
+
"git",
|
|
3652
|
+
["-C", targetDir, "remote", "get-url", "origin"],
|
|
3653
|
+
{ timeout: 5e3 }
|
|
3654
|
+
);
|
|
3655
|
+
const url = stdout.trim();
|
|
3656
|
+
const match = url.match(/[/:]([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
|
|
3657
|
+
if (match) return match[1];
|
|
3658
|
+
} catch {
|
|
3659
|
+
}
|
|
3660
|
+
return null;
|
|
3661
|
+
}
|
|
3662
|
+
async function writeScanResults(targetDir, response) {
|
|
3663
|
+
const written = [];
|
|
3664
|
+
const contextDir = join22(targetDir, "flydocs", "context");
|
|
3665
|
+
await mkdir11(contextDir, { recursive: true });
|
|
3666
|
+
if (response.projectMd) {
|
|
3667
|
+
const projectMdPath = join22(contextDir, "project.md");
|
|
3668
|
+
await writeFile15(projectMdPath, response.projectMd, "utf-8");
|
|
3669
|
+
written.push("flydocs/context/project.md");
|
|
3670
|
+
}
|
|
3671
|
+
if (response.serviceJson) {
|
|
3672
|
+
const serviceJsonPath = join22(contextDir, "service.json");
|
|
3673
|
+
await writeFile15(
|
|
3674
|
+
serviceJsonPath,
|
|
3675
|
+
JSON.stringify(response.serviceJson, null, 2) + "\n",
|
|
3676
|
+
"utf-8"
|
|
3677
|
+
);
|
|
3678
|
+
written.push("flydocs/context/service.json");
|
|
3679
|
+
}
|
|
3680
|
+
return written;
|
|
3681
|
+
}
|
|
3682
|
+
async function runScan(apiKey, workspaceId, targetDir, repoName, regenerate) {
|
|
3683
|
+
const s = spinner();
|
|
3684
|
+
s.start("Running AI scan (this may take 5-20 seconds)...");
|
|
3685
|
+
try {
|
|
3686
|
+
const response = await triggerScan(apiKey, repoName, {
|
|
3687
|
+
workspaceId,
|
|
3688
|
+
regenerate
|
|
3689
|
+
});
|
|
3690
|
+
if (!response.success) {
|
|
3691
|
+
s.stop("Scan returned unsuccessful", 1);
|
|
3692
|
+
return { success: false, written: [] };
|
|
3693
|
+
}
|
|
3694
|
+
s.stop("Scan complete");
|
|
3695
|
+
const written = await writeScanResults(targetDir, response);
|
|
3696
|
+
return { success: true, written };
|
|
3697
|
+
} catch (err) {
|
|
3698
|
+
if (err instanceof RelayError) {
|
|
3699
|
+
if (err.status === 403) {
|
|
3700
|
+
s.stop("Permission denied", 1);
|
|
3701
|
+
printError(
|
|
3702
|
+
"Admin role required. Only workspace admins can trigger AI scans."
|
|
3703
|
+
);
|
|
3704
|
+
} else {
|
|
3705
|
+
s.stop("Scan failed", 1);
|
|
3706
|
+
printError(`Server error: ${err.message}`);
|
|
3707
|
+
}
|
|
3708
|
+
} else {
|
|
3709
|
+
s.stop("Scan failed", 1);
|
|
3710
|
+
printError("Could not reach server. Check your network and try again.");
|
|
3711
|
+
}
|
|
3712
|
+
return { success: false, written: [] };
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
var scan_default;
|
|
3716
|
+
var init_scan = __esm({
|
|
3717
|
+
"src/commands/scan.ts"() {
|
|
3718
|
+
"use strict";
|
|
3719
|
+
init_ui();
|
|
3720
|
+
init_global_config();
|
|
3721
|
+
init_config();
|
|
3722
|
+
init_types();
|
|
3723
|
+
init_relay_client();
|
|
3724
|
+
scan_default = defineCommand3({
|
|
3725
|
+
meta: {
|
|
3726
|
+
name: "scan",
|
|
3727
|
+
description: "Trigger server-side AI scan to generate project context"
|
|
3728
|
+
},
|
|
3729
|
+
args: {
|
|
3730
|
+
force: {
|
|
3731
|
+
type: "boolean",
|
|
3732
|
+
description: "Regenerate context even if it already exists",
|
|
3733
|
+
default: false
|
|
3734
|
+
},
|
|
3735
|
+
path: {
|
|
3736
|
+
type: "string",
|
|
3737
|
+
description: "Path to project directory"
|
|
3738
|
+
},
|
|
3739
|
+
repo: {
|
|
3740
|
+
type: "string",
|
|
3741
|
+
description: "Override repo name (owner/repo format)"
|
|
3742
|
+
}
|
|
3743
|
+
},
|
|
3744
|
+
async run({ args }) {
|
|
3745
|
+
const targetDir = args.path ?? process.cwd();
|
|
3746
|
+
console.log();
|
|
3747
|
+
printInfo("FlyDocs AI Scan");
|
|
3748
|
+
console.log();
|
|
3749
|
+
const resolved = await resolveApiKey(void 0, targetDir);
|
|
3750
|
+
if (!resolved) {
|
|
3751
|
+
printError(
|
|
3752
|
+
"No API key found. Run `flydocs auth` or `flydocs init` first."
|
|
3753
|
+
);
|
|
3754
|
+
process.exit(1);
|
|
3755
|
+
}
|
|
3756
|
+
const apiKey = resolved.key;
|
|
3757
|
+
const cred = await readGlobalCredential();
|
|
3758
|
+
const workspaceId = cred?.workspaceId;
|
|
3759
|
+
if (!workspaceId) {
|
|
3760
|
+
printError(
|
|
3761
|
+
"No workspace ID found. Run `flydocs init` to set up your workspace."
|
|
3762
|
+
);
|
|
3763
|
+
process.exit(1);
|
|
3764
|
+
}
|
|
3765
|
+
printInfo("Checking permissions...");
|
|
3766
|
+
try {
|
|
3767
|
+
const configResponse = await fetchConfigV2(apiKey, { workspaceId });
|
|
3768
|
+
if (configResponse.identity?.role !== "admin") {
|
|
3769
|
+
printError(
|
|
3770
|
+
"Admin role required. Only workspace admins can trigger AI scans."
|
|
3771
|
+
);
|
|
3772
|
+
console.log(
|
|
3773
|
+
` ${pc8.dim("Your role: " + (configResponse.identity?.role ?? "unknown"))}`
|
|
3774
|
+
);
|
|
3775
|
+
console.log(
|
|
3776
|
+
` ${pc8.dim("Ask a workspace admin to run this command or promote your role.")}`
|
|
3777
|
+
);
|
|
3778
|
+
process.exit(1);
|
|
3779
|
+
}
|
|
3780
|
+
} catch (err) {
|
|
3781
|
+
if (err instanceof RelayError) {
|
|
3782
|
+
printError(`Could not verify permissions: ${err.message}`);
|
|
3783
|
+
} else {
|
|
3784
|
+
printError("Could not reach server. Check your network.");
|
|
3785
|
+
}
|
|
3786
|
+
process.exit(1);
|
|
3787
|
+
}
|
|
3788
|
+
const repoName = await resolveRepoName(targetDir, args.repo);
|
|
3789
|
+
if (!repoName) {
|
|
3790
|
+
printError(
|
|
3791
|
+
"Could not determine repo name. Use --repo owner/name to specify it."
|
|
3792
|
+
);
|
|
3793
|
+
process.exit(1);
|
|
3794
|
+
}
|
|
3795
|
+
printInfo(`Repo: ${pc8.bold(repoName)}`);
|
|
3796
|
+
const { success, written } = await runScan(
|
|
3797
|
+
apiKey,
|
|
3798
|
+
workspaceId,
|
|
3799
|
+
targetDir,
|
|
3800
|
+
repoName,
|
|
3801
|
+
args.force
|
|
3802
|
+
);
|
|
3803
|
+
if (success) {
|
|
3804
|
+
console.log();
|
|
3805
|
+
for (const file of written) {
|
|
3806
|
+
printStatus(`Wrote ${file}`);
|
|
3807
|
+
}
|
|
3808
|
+
if (written.length === 0) {
|
|
3809
|
+
printWarning("Scan completed but no context files were returned.");
|
|
3810
|
+
}
|
|
3811
|
+
console.log();
|
|
3812
|
+
} else {
|
|
3813
|
+
process.exit(1);
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
});
|
|
3817
|
+
}
|
|
3818
|
+
});
|
|
3819
|
+
|
|
3356
3820
|
// src/commands/init.ts
|
|
3357
3821
|
var init_exports = {};
|
|
3358
3822
|
__export(init_exports, {
|
|
3359
3823
|
default: () => init_default
|
|
3360
3824
|
});
|
|
3361
|
-
import { defineCommand as
|
|
3825
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
3362
3826
|
import { text as text2, confirm as confirm3, select as select2, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
|
|
3363
|
-
import
|
|
3364
|
-
import { join as
|
|
3365
|
-
import { mkdir as
|
|
3827
|
+
import pc9 from "picocolors";
|
|
3828
|
+
import { join as join23 } from "path";
|
|
3829
|
+
import { mkdir as mkdir12, writeFile as writeFile16 } from "fs/promises";
|
|
3366
3830
|
import { execFile } from "child_process";
|
|
3367
3831
|
import { promisify } from "util";
|
|
3368
3832
|
async function resolveAndValidateKey(keyArg, targetDir) {
|
|
3369
3833
|
let resolved = await resolveApiKey(keyArg, targetDir);
|
|
3370
3834
|
if (!resolved) {
|
|
3371
3835
|
console.log(
|
|
3372
|
-
` ${
|
|
3836
|
+
` ${pc9.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
3373
3837
|
);
|
|
3374
3838
|
console.log();
|
|
3375
3839
|
const keyInput = await text2({
|
|
@@ -3399,13 +3863,13 @@ async function resolveAndValidateKey(keyArg, targetDir) {
|
|
|
3399
3863
|
printError("Invalid API key. Check your key and try again.");
|
|
3400
3864
|
process.exit(1);
|
|
3401
3865
|
}
|
|
3402
|
-
printStatus(`Authenticated with ${
|
|
3866
|
+
printStatus(`Authenticated with ${pc9.bold(result.org)}`);
|
|
3403
3867
|
} catch {
|
|
3404
3868
|
printError(
|
|
3405
3869
|
"Could not reach FlyDocs API. Check your network and try again."
|
|
3406
3870
|
);
|
|
3407
3871
|
console.log(
|
|
3408
|
-
` ${
|
|
3872
|
+
` ${pc9.dim("flydocs init requires server access. For offline use, run flydocs install instead.")}`
|
|
3409
3873
|
);
|
|
3410
3874
|
process.exit(1);
|
|
3411
3875
|
}
|
|
@@ -3416,7 +3880,7 @@ async function resolveAndValidateKey(keyArg, targetDir) {
|
|
|
3416
3880
|
process.exit(1);
|
|
3417
3881
|
} else if (workspaces.length === 1) {
|
|
3418
3882
|
workspaceId = workspaces[0].id;
|
|
3419
|
-
printStatus(`Workspace: ${
|
|
3883
|
+
printStatus(`Workspace: ${pc9.bold(workspaces[0].name)}`);
|
|
3420
3884
|
} else {
|
|
3421
3885
|
const choice = await select2({
|
|
3422
3886
|
message: "Select workspace",
|
|
@@ -3442,7 +3906,7 @@ async function resolveAndValidateKey(keyArg, targetDir) {
|
|
|
3442
3906
|
}
|
|
3443
3907
|
async function pullServerConfig(apiKey, targetDir, workspaceId, forceIncludeContext = false) {
|
|
3444
3908
|
printInfo("Pulling config from server...");
|
|
3445
|
-
const isFirstInit = forceIncludeContext || !await pathExists(
|
|
3909
|
+
const isFirstInit = forceIncludeContext || !await pathExists(join23(targetDir, ".flydocs", "config.json"));
|
|
3446
3910
|
try {
|
|
3447
3911
|
const response = await fetchConfigV2(apiKey, {
|
|
3448
3912
|
includeContext: isFirstInit,
|
|
@@ -3458,7 +3922,7 @@ async function pullServerConfig(apiKey, targetDir, workspaceId, forceIncludeCont
|
|
|
3458
3922
|
printError(`Server error: ${err.message}`);
|
|
3459
3923
|
if (err.status === 401) {
|
|
3460
3924
|
console.log(
|
|
3461
|
-
` ${
|
|
3925
|
+
` ${pc9.dim("Your API key may be revoked. Run flydocs auth with a new key.")}`
|
|
3462
3926
|
);
|
|
3463
3927
|
}
|
|
3464
3928
|
} else {
|
|
@@ -3467,10 +3931,11 @@ async function pullServerConfig(apiKey, targetDir, workspaceId, forceIncludeCont
|
|
|
3467
3931
|
process.exit(1);
|
|
3468
3932
|
}
|
|
3469
3933
|
}
|
|
3470
|
-
async function initSingleRepo(targetDir, apiKey, serverResponse) {
|
|
3934
|
+
async function initSingleRepo(targetDir, apiKey, serverResponse, options = {}) {
|
|
3471
3935
|
const actions = [];
|
|
3472
3936
|
const skipped = [];
|
|
3473
|
-
|
|
3937
|
+
const { childRepoMode = false } = options;
|
|
3938
|
+
await mkdir12(join23(targetDir, ".flydocs"), { recursive: true });
|
|
3474
3939
|
const configWithHash = applyConfigHash(serverResponse.config);
|
|
3475
3940
|
await writeConfig(targetDir, configWithHash);
|
|
3476
3941
|
actions.push("Wrote .flydocs/config.json (from server)");
|
|
@@ -3483,18 +3948,18 @@ async function initSingleRepo(targetDir, apiKey, serverResponse) {
|
|
|
3483
3948
|
actions.push("Wrote ~/.flydocs/me.json");
|
|
3484
3949
|
}
|
|
3485
3950
|
if (serverResponse.context) {
|
|
3486
|
-
const contextDir =
|
|
3487
|
-
await
|
|
3488
|
-
const projectMdPath =
|
|
3951
|
+
const contextDir = join23(targetDir, "flydocs", "context");
|
|
3952
|
+
await mkdir12(contextDir, { recursive: true });
|
|
3953
|
+
const projectMdPath = join23(contextDir, "project.md");
|
|
3489
3954
|
if (!await pathExists(projectMdPath)) {
|
|
3490
|
-
await
|
|
3955
|
+
await writeFile16(projectMdPath, serverResponse.context.projectMd, "utf-8");
|
|
3491
3956
|
actions.push("Wrote flydocs/context/project.md");
|
|
3492
3957
|
} else {
|
|
3493
3958
|
skipped.push("flydocs/context/project.md (already exists)");
|
|
3494
3959
|
}
|
|
3495
|
-
const serviceJsonPath =
|
|
3960
|
+
const serviceJsonPath = join23(contextDir, "service.json");
|
|
3496
3961
|
if (serverResponse.context.serviceJson && !await pathExists(serviceJsonPath)) {
|
|
3497
|
-
await
|
|
3962
|
+
await writeFile16(
|
|
3498
3963
|
serviceJsonPath,
|
|
3499
3964
|
JSON.stringify(serverResponse.context.serviceJson, null, 2) + "\n",
|
|
3500
3965
|
"utf-8"
|
|
@@ -3504,6 +3969,27 @@ async function initSingleRepo(targetDir, apiKey, serverResponse) {
|
|
|
3504
3969
|
skipped.push("flydocs/context/service.json (already exists)");
|
|
3505
3970
|
}
|
|
3506
3971
|
}
|
|
3972
|
+
if (childRepoMode) {
|
|
3973
|
+
const cleaned = await cleanServerManagedFiles(targetDir);
|
|
3974
|
+
if (cleaned > 0) {
|
|
3975
|
+
actions.push(
|
|
3976
|
+
`Removed ${cleaned} server-managed file(s) (moved to workspace root)`
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
} else {
|
|
3980
|
+
const archivedConfigs = await archiveIdeConfigs(targetDir);
|
|
3981
|
+
if (archivedConfigs.length > 0) {
|
|
3982
|
+
actions.push(`Archived ${archivedConfigs.length} existing IDE config(s)`);
|
|
3983
|
+
}
|
|
3984
|
+
const { changes: artifactChanges } = await syncAllArtifacts(
|
|
3985
|
+
targetDir,
|
|
3986
|
+
apiKey,
|
|
3987
|
+
serverResponse.workspaceId,
|
|
3988
|
+
0
|
|
3989
|
+
// fresh init — fetch all artifacts
|
|
3990
|
+
);
|
|
3991
|
+
actions.push(...artifactChanges);
|
|
3992
|
+
}
|
|
3507
3993
|
await ensureGitignore(targetDir);
|
|
3508
3994
|
await ensurePlatformIgnores(targetDir);
|
|
3509
3995
|
actions.push("Updated ignore files");
|
|
@@ -3512,10 +3998,32 @@ async function initSingleRepo(targetDir, apiKey, serverResponse) {
|
|
|
3512
3998
|
await executeCleanup(targetDir, artifacts);
|
|
3513
3999
|
actions.push(`Cleaned ${artifacts.length} legacy artifact(s)`);
|
|
3514
4000
|
}
|
|
3515
|
-
return {
|
|
4001
|
+
return {
|
|
4002
|
+
actions,
|
|
4003
|
+
skipped
|
|
4004
|
+
};
|
|
4005
|
+
}
|
|
4006
|
+
async function cleanServerManagedFiles(targetDir) {
|
|
4007
|
+
const { rm: rm7 } = await import("fs/promises");
|
|
4008
|
+
let cleaned = 0;
|
|
4009
|
+
for (const dir of SERVER_MANAGED_DIRS) {
|
|
4010
|
+
const fullPath = join23(targetDir, dir);
|
|
4011
|
+
if (await pathExists(fullPath)) {
|
|
4012
|
+
await rm7(fullPath, { recursive: true, force: true });
|
|
4013
|
+
cleaned++;
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
for (const file of SERVER_MANAGED_FILES) {
|
|
4017
|
+
const fullPath = join23(targetDir, file);
|
|
4018
|
+
if (await pathExists(fullPath)) {
|
|
4019
|
+
await rm7(fullPath, { force: true });
|
|
4020
|
+
cleaned++;
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
return cleaned;
|
|
3516
4024
|
}
|
|
3517
4025
|
async function checkGitFreshness(repoDir) {
|
|
3518
|
-
const hasGit = await pathExists(
|
|
4026
|
+
const hasGit = await pathExists(join23(repoDir, ".git"));
|
|
3519
4027
|
if (!hasGit) return;
|
|
3520
4028
|
try {
|
|
3521
4029
|
await execFileAsync("git", ["-C", repoDir, "fetch", "--quiet"], {
|
|
@@ -3544,7 +4052,7 @@ async function checkGitFreshness(repoDir) {
|
|
|
3544
4052
|
}
|
|
3545
4053
|
}
|
|
3546
4054
|
async function isEmptyOrNonRepo(dir) {
|
|
3547
|
-
const hasGit = await pathExists(
|
|
4055
|
+
const hasGit = await pathExists(join23(dir, ".git"));
|
|
3548
4056
|
if (hasGit) return false;
|
|
3549
4057
|
const siblings = await detectSiblingRepos(dir);
|
|
3550
4058
|
if (Object.keys(siblings).length >= 2) return false;
|
|
@@ -3557,7 +4065,7 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
|
|
|
3557
4065
|
if (repos.length === 1) {
|
|
3558
4066
|
const repo = repos[0];
|
|
3559
4067
|
const shortName = repoShortName(repo.name);
|
|
3560
|
-
printInfo(`Workspace has 1 repo: ${
|
|
4068
|
+
printInfo(`Workspace has 1 repo: ${pc9.bold(shortName)}`);
|
|
3561
4069
|
const shouldClone = await confirm3({
|
|
3562
4070
|
message: `Clone ${shortName} into this directory and initialize?`
|
|
3563
4071
|
});
|
|
@@ -3595,7 +4103,7 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
|
|
|
3595
4103
|
const repoNames = repos.map((r) => repoShortName(r.name));
|
|
3596
4104
|
printInfo(`Workspace has ${repos.length} repos:`);
|
|
3597
4105
|
for (const name of repoNames) {
|
|
3598
|
-
console.log(` ${
|
|
4106
|
+
console.log(` ${pc9.cyan(name)}`);
|
|
3599
4107
|
}
|
|
3600
4108
|
console.log();
|
|
3601
4109
|
const shouldClone = await confirm3({
|
|
@@ -3614,13 +4122,13 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
|
|
|
3614
4122
|
for (let i = 0; i < repos.length; i++) {
|
|
3615
4123
|
const repo = repos[i];
|
|
3616
4124
|
const shortName = repoNames[i];
|
|
3617
|
-
const cloneDir =
|
|
4125
|
+
const cloneDir = join23(targetDir, shortName);
|
|
3618
4126
|
console.log();
|
|
3619
|
-
if (await pathExists(
|
|
3620
|
-
printInfo(`${
|
|
4127
|
+
if (await pathExists(join23(cloneDir, ".git"))) {
|
|
4128
|
+
printInfo(`${pc9.bold(shortName)} already cloned, initializing...`);
|
|
3621
4129
|
await checkGitFreshness(cloneDir);
|
|
3622
4130
|
} else {
|
|
3623
|
-
printInfo(`Cloning ${
|
|
4131
|
+
printInfo(`Cloning ${pc9.bold(shortName)}...`);
|
|
3624
4132
|
await execFileAsync("git", ["clone", repo.cloneUrl, cloneDir], {
|
|
3625
4133
|
timeout: 6e4
|
|
3626
4134
|
});
|
|
@@ -3659,12 +4167,12 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
|
|
|
3659
4167
|
);
|
|
3660
4168
|
await writeWorkspaceFile(targetDir, workspaceFile);
|
|
3661
4169
|
allActions.push(`.flydocs-workspace.json (${repos.length} repos)`);
|
|
3662
|
-
const contextDir =
|
|
3663
|
-
const workspaceMdPath =
|
|
4170
|
+
const contextDir = join23(targetDir, "flydocs", "context");
|
|
4171
|
+
const workspaceMdPath = join23(contextDir, "workspace.md");
|
|
3664
4172
|
if (!await pathExists(workspaceMdPath)) {
|
|
3665
|
-
await
|
|
4173
|
+
await mkdir12(contextDir, { recursive: true });
|
|
3666
4174
|
const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(targetDir, workspaceFile);
|
|
3667
|
-
await
|
|
4175
|
+
await writeFile16(workspaceMdPath, content, "utf-8");
|
|
3668
4176
|
const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
|
|
3669
4177
|
allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
|
|
3670
4178
|
}
|
|
@@ -3679,7 +4187,7 @@ async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
|
|
|
3679
4187
|
}
|
|
3680
4188
|
}
|
|
3681
4189
|
async function isParentDirectory(dir) {
|
|
3682
|
-
const hasGit = await pathExists(
|
|
4190
|
+
const hasGit = await pathExists(join23(dir, ".git"));
|
|
3683
4191
|
if (hasGit) return false;
|
|
3684
4192
|
const repos = await detectSiblingRepos(dir);
|
|
3685
4193
|
return Object.keys(repos).length >= 2;
|
|
@@ -3689,7 +4197,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
|
|
|
3689
4197
|
const repoNames = Object.keys(repos).sort();
|
|
3690
4198
|
printInfo(`Detected ${repoNames.length} repos:`);
|
|
3691
4199
|
for (const name of repoNames) {
|
|
3692
|
-
console.log(` ${
|
|
4200
|
+
console.log(` ${pc9.cyan(name)}`);
|
|
3693
4201
|
}
|
|
3694
4202
|
console.log();
|
|
3695
4203
|
const shouldContinue = await confirm3({
|
|
@@ -3699,7 +4207,7 @@ async function runMultiRepoInit(parentDir, keyArg) {
|
|
|
3699
4207
|
cancel3("Init cancelled.");
|
|
3700
4208
|
process.exit(0);
|
|
3701
4209
|
}
|
|
3702
|
-
const firstRepoDir =
|
|
4210
|
+
const firstRepoDir = join23(parentDir, repoNames[0]);
|
|
3703
4211
|
const { apiKey, workspaceId } = await resolveAndValidateKey(
|
|
3704
4212
|
keyArg,
|
|
3705
4213
|
firstRepoDir
|
|
@@ -3710,17 +4218,38 @@ async function runMultiRepoInit(parentDir, keyArg) {
|
|
|
3710
4218
|
const allSkipped = [];
|
|
3711
4219
|
let allWarnings = [];
|
|
3712
4220
|
let firstResponse;
|
|
4221
|
+
const workspaceResponse = await pullServerConfig(
|
|
4222
|
+
apiKey,
|
|
4223
|
+
firstRepoDir,
|
|
4224
|
+
workspaceId
|
|
4225
|
+
);
|
|
4226
|
+
firstResponse = workspaceResponse;
|
|
4227
|
+
const archivedConfigs = await archiveIdeConfigs(parentDir);
|
|
4228
|
+
if (archivedConfigs.length > 0) {
|
|
4229
|
+
allActions.push(
|
|
4230
|
+
`Archived ${archivedConfigs.length} existing IDE config(s) at workspace root`
|
|
4231
|
+
);
|
|
4232
|
+
}
|
|
4233
|
+
const { changes: artifactChanges } = await syncAllArtifacts(
|
|
4234
|
+
parentDir,
|
|
4235
|
+
apiKey,
|
|
4236
|
+
workspaceId,
|
|
4237
|
+
0
|
|
4238
|
+
);
|
|
4239
|
+
allActions.push(...artifactChanges);
|
|
4240
|
+
await ensureGitignore(parentDir);
|
|
4241
|
+
allActions.push("Updated workspace root .gitignore");
|
|
3713
4242
|
for (const repoName of repoNames) {
|
|
3714
|
-
const repoDir =
|
|
4243
|
+
const repoDir = join23(parentDir, repoName);
|
|
3715
4244
|
console.log();
|
|
3716
|
-
printInfo(`Initializing ${
|
|
4245
|
+
printInfo(`Initializing ${pc9.bold(repoName)}...`);
|
|
3717
4246
|
await checkGitFreshness(repoDir);
|
|
3718
4247
|
const serverResponse = await pullServerConfig(apiKey, repoDir, workspaceId);
|
|
3719
|
-
if (!firstResponse) firstResponse = serverResponse;
|
|
3720
4248
|
const { actions, skipped } = await initSingleRepo(
|
|
3721
4249
|
repoDir,
|
|
3722
4250
|
apiKey,
|
|
3723
|
-
serverResponse
|
|
4251
|
+
serverResponse,
|
|
4252
|
+
{ childRepoMode: true }
|
|
3724
4253
|
);
|
|
3725
4254
|
for (const action of actions) {
|
|
3726
4255
|
allActions.push(`[${repoName}] ${action}`);
|
|
@@ -3743,15 +4272,15 @@ async function runMultiRepoInit(parentDir, keyArg) {
|
|
|
3743
4272
|
await writeWorkspaceFile(parentDir, workspaceFile);
|
|
3744
4273
|
allActions.push(`.flydocs-workspace.json (${repoNames.length} repos)`);
|
|
3745
4274
|
}
|
|
3746
|
-
const contextDir =
|
|
3747
|
-
const workspaceMdPath =
|
|
4275
|
+
const contextDir = join23(parentDir, "flydocs", "context");
|
|
4276
|
+
const workspaceMdPath = join23(contextDir, "workspace.md");
|
|
3748
4277
|
if (await pathExists(workspaceMdPath)) {
|
|
3749
4278
|
allSkipped.push("flydocs/context/workspace.md (already exists)");
|
|
3750
4279
|
} else {
|
|
3751
|
-
await
|
|
4280
|
+
await mkdir12(contextDir, { recursive: true });
|
|
3752
4281
|
const workspaceJson = await readWorkspaceFile(parentDir) ?? buildWorkspaceFile(firstResponse.workspaceId, repos);
|
|
3753
4282
|
const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(parentDir, workspaceJson);
|
|
3754
|
-
await
|
|
4283
|
+
await writeFile16(workspaceMdPath, content, "utf-8");
|
|
3755
4284
|
const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
|
|
3756
4285
|
allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
|
|
3757
4286
|
}
|
|
@@ -3768,35 +4297,35 @@ function printInitReport(actions, skipped, warnings, hasContext) {
|
|
|
3768
4297
|
console.log();
|
|
3769
4298
|
const lines = [];
|
|
3770
4299
|
for (const action of actions) {
|
|
3771
|
-
lines.push(`${
|
|
4300
|
+
lines.push(`${pc9.green("+")} ${action}`);
|
|
3772
4301
|
}
|
|
3773
4302
|
for (const skip of skipped) {
|
|
3774
|
-
lines.push(`${
|
|
4303
|
+
lines.push(`${pc9.dim("-")} ${skip}`);
|
|
3775
4304
|
}
|
|
3776
4305
|
if (!hasContext) {
|
|
3777
4306
|
lines.push("");
|
|
3778
4307
|
lines.push(
|
|
3779
|
-
`${
|
|
4308
|
+
`${pc9.yellow("!")} No generated context yet. An admin can generate it from the portal.`
|
|
3780
4309
|
);
|
|
3781
4310
|
}
|
|
3782
4311
|
if (warnings.length > 0) {
|
|
3783
4312
|
lines.push("");
|
|
3784
4313
|
for (const warning of warnings) {
|
|
3785
|
-
lines.push(`${
|
|
4314
|
+
lines.push(`${pc9.yellow("!")} ${warning}`);
|
|
3786
4315
|
}
|
|
3787
4316
|
}
|
|
3788
4317
|
printCompletionBox("FlyDocs Initialized", lines);
|
|
3789
4318
|
console.log();
|
|
3790
4319
|
console.log(` Next steps:`);
|
|
3791
4320
|
console.log(
|
|
3792
|
-
` ${
|
|
4321
|
+
` ${pc9.cyan("flydocs sync")} Pull latest config and templates`
|
|
3793
4322
|
);
|
|
3794
4323
|
console.log(
|
|
3795
|
-
` ${
|
|
4324
|
+
` ${pc9.cyan("cursor .")} ${pc9.dim("or")} ${pc9.cyan("code .")} Open your IDE, then use ${pc9.bold("/start-session")}`
|
|
3796
4325
|
);
|
|
3797
4326
|
console.log();
|
|
3798
4327
|
}
|
|
3799
|
-
var execFileAsync, init_default;
|
|
4328
|
+
var SERVER_MANAGED_DIRS, SERVER_MANAGED_FILES, execFileAsync, init_default;
|
|
3800
4329
|
var init_init = __esm({
|
|
3801
4330
|
"src/commands/init.ts"() {
|
|
3802
4331
|
"use strict";
|
|
@@ -3808,10 +4337,26 @@ var init_init = __esm({
|
|
|
3808
4337
|
init_gitignore();
|
|
3809
4338
|
init_fs_ops();
|
|
3810
4339
|
init_relay_client();
|
|
4340
|
+
init_artifacts();
|
|
3811
4341
|
init_cleanup();
|
|
4342
|
+
init_ide_archive();
|
|
3812
4343
|
init_workspace();
|
|
4344
|
+
init_scan();
|
|
4345
|
+
SERVER_MANAGED_DIRS = [
|
|
4346
|
+
".claude/skills",
|
|
4347
|
+
".claude/commands",
|
|
4348
|
+
".claude/hooks",
|
|
4349
|
+
".claude/agents",
|
|
4350
|
+
".cursor/rules",
|
|
4351
|
+
".cursor/commands"
|
|
4352
|
+
];
|
|
4353
|
+
SERVER_MANAGED_FILES = [
|
|
4354
|
+
".claude/settings.json",
|
|
4355
|
+
".claude/CLAUDE.md",
|
|
4356
|
+
"AGENTS.md"
|
|
4357
|
+
];
|
|
3813
4358
|
execFileAsync = promisify(execFile);
|
|
3814
|
-
init_default =
|
|
4359
|
+
init_default = defineCommand4({
|
|
3815
4360
|
meta: {
|
|
3816
4361
|
name: "init",
|
|
3817
4362
|
description: "Initialize FlyDocs in this project (unified setup)"
|
|
@@ -3824,12 +4369,17 @@ var init_init = __esm({
|
|
|
3824
4369
|
path: {
|
|
3825
4370
|
type: "string",
|
|
3826
4371
|
description: "Path to project directory"
|
|
4372
|
+
},
|
|
4373
|
+
scan: {
|
|
4374
|
+
type: "boolean",
|
|
4375
|
+
description: "Run AI scan after init to generate project context",
|
|
4376
|
+
default: false
|
|
3827
4377
|
}
|
|
3828
4378
|
},
|
|
3829
4379
|
async run({ args }) {
|
|
3830
4380
|
const targetDir = args.path ?? process.cwd();
|
|
3831
4381
|
console.log();
|
|
3832
|
-
console.log(` ${
|
|
4382
|
+
console.log(` ${pc9.bold("FlyDocs Init")}`);
|
|
3833
4383
|
console.log();
|
|
3834
4384
|
if (await isParentDirectory(targetDir)) {
|
|
3835
4385
|
await runMultiRepoInit(targetDir, args.key);
|
|
@@ -3876,7 +4426,7 @@ var init_init = __esm({
|
|
|
3876
4426
|
actions.unshift("Stored credential globally (~/.flydocs/credentials)");
|
|
3877
4427
|
const topology = serverResponse.config.topology;
|
|
3878
4428
|
if (topology?.type === 4 && topology.label === "sibling-repos") {
|
|
3879
|
-
const parentDir =
|
|
4429
|
+
const parentDir = join23(targetDir, "..");
|
|
3880
4430
|
const existing = await readWorkspaceFile(parentDir);
|
|
3881
4431
|
if (existing) {
|
|
3882
4432
|
skipped.push(".flydocs-workspace.json (already exists)");
|
|
@@ -3900,6 +4450,98 @@ var init_init = __esm({
|
|
|
3900
4450
|
serverResponse.warnings,
|
|
3901
4451
|
!!serverResponse.context
|
|
3902
4452
|
);
|
|
4453
|
+
const shouldAutoScan = !args.scan && serverResponse.contextVersion === 0 && !serverResponse.context?.projectMd;
|
|
4454
|
+
if (shouldAutoScan) {
|
|
4455
|
+
const isAdmin = serverResponse.identity?.role === "admin";
|
|
4456
|
+
if (isAdmin) {
|
|
4457
|
+
console.log();
|
|
4458
|
+
printInfo(
|
|
4459
|
+
"No generated context found. AI scanning creates project.md and service.json."
|
|
4460
|
+
);
|
|
4461
|
+
const shouldScan = await confirm3({
|
|
4462
|
+
message: "Run AI scan now?"
|
|
4463
|
+
});
|
|
4464
|
+
if (!isCancel4(shouldScan) && shouldScan) {
|
|
4465
|
+
let repoName = null;
|
|
4466
|
+
try {
|
|
4467
|
+
const { stdout } = await execFileAsync(
|
|
4468
|
+
"git",
|
|
4469
|
+
["-C", targetDir, "remote", "get-url", "origin"],
|
|
4470
|
+
{ timeout: 5e3 }
|
|
4471
|
+
);
|
|
4472
|
+
const url = stdout.trim();
|
|
4473
|
+
const match = url.match(/[/:]([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
|
|
4474
|
+
if (match) repoName = match[1];
|
|
4475
|
+
} catch {
|
|
4476
|
+
}
|
|
4477
|
+
if (repoName) {
|
|
4478
|
+
const { success, written } = await runScan(
|
|
4479
|
+
apiKey,
|
|
4480
|
+
workspaceId,
|
|
4481
|
+
targetDir,
|
|
4482
|
+
repoName,
|
|
4483
|
+
false
|
|
4484
|
+
);
|
|
4485
|
+
if (success) {
|
|
4486
|
+
for (const file of written) {
|
|
4487
|
+
printStatus(`Wrote ${file}`);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
} else {
|
|
4491
|
+
printWarning(
|
|
4492
|
+
"Could not determine repo name from git remote. Run `flydocs scan --repo owner/name` manually."
|
|
4493
|
+
);
|
|
4494
|
+
}
|
|
4495
|
+
}
|
|
4496
|
+
} else {
|
|
4497
|
+
console.log();
|
|
4498
|
+
printInfo(
|
|
4499
|
+
"No generated context yet. Ask a workspace admin to generate it from the portal, then run `flydocs sync`."
|
|
4500
|
+
);
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
if (args.scan) {
|
|
4504
|
+
const isAdmin = serverResponse.identity?.role === "admin";
|
|
4505
|
+
if (!isAdmin) {
|
|
4506
|
+
printWarning(
|
|
4507
|
+
"Skipping scan: admin role required. Only workspace admins can trigger AI scans."
|
|
4508
|
+
);
|
|
4509
|
+
} else {
|
|
4510
|
+
let repoName = null;
|
|
4511
|
+
try {
|
|
4512
|
+
const { stdout } = await execFileAsync(
|
|
4513
|
+
"git",
|
|
4514
|
+
["-C", targetDir, "remote", "get-url", "origin"],
|
|
4515
|
+
{ timeout: 5e3 }
|
|
4516
|
+
);
|
|
4517
|
+
const url = stdout.trim();
|
|
4518
|
+
const match = url.match(/[/:]([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
|
|
4519
|
+
if (match) repoName = match[1];
|
|
4520
|
+
} catch {
|
|
4521
|
+
}
|
|
4522
|
+
if (!repoName) {
|
|
4523
|
+
printWarning(
|
|
4524
|
+
"Skipping scan: could not determine repo name from git remote."
|
|
4525
|
+
);
|
|
4526
|
+
} else {
|
|
4527
|
+
console.log();
|
|
4528
|
+
printInfo(`Running AI scan for ${pc9.bold(repoName)}...`);
|
|
4529
|
+
const { success, written } = await runScan(
|
|
4530
|
+
apiKey,
|
|
4531
|
+
workspaceId,
|
|
4532
|
+
targetDir,
|
|
4533
|
+
repoName,
|
|
4534
|
+
false
|
|
4535
|
+
// not a forced regeneration on init
|
|
4536
|
+
);
|
|
4537
|
+
if (success) {
|
|
4538
|
+
for (const file of written) {
|
|
4539
|
+
printStatus(`Wrote ${file}`);
|
|
4540
|
+
}
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
3903
4545
|
}
|
|
3904
4546
|
});
|
|
3905
4547
|
}
|
|
@@ -3910,11 +4552,11 @@ var update_exports = {};
|
|
|
3910
4552
|
__export(update_exports, {
|
|
3911
4553
|
default: () => update_default
|
|
3912
4554
|
});
|
|
3913
|
-
import { defineCommand as
|
|
3914
|
-
import { resolve as resolve4, join as
|
|
3915
|
-
import { mkdir as
|
|
4555
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
4556
|
+
import { resolve as resolve4, join as join24 } from "path";
|
|
4557
|
+
import { mkdir as mkdir13, cp as cp2, readFile as readFile16, readdir as readdir6, rm as rm5 } from "fs/promises";
|
|
3916
4558
|
import { select as select3, text as text3, confirm as confirm4, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
|
|
3917
|
-
import
|
|
4559
|
+
import pc10 from "picocolors";
|
|
3918
4560
|
var update_default;
|
|
3919
4561
|
var init_update = __esm({
|
|
3920
4562
|
"src/commands/update.ts"() {
|
|
@@ -3933,7 +4575,7 @@ var init_update = __esm({
|
|
|
3933
4575
|
init_update_check();
|
|
3934
4576
|
init_telemetry();
|
|
3935
4577
|
init_integrity();
|
|
3936
|
-
update_default =
|
|
4578
|
+
update_default = defineCommand5({
|
|
3937
4579
|
meta: {
|
|
3938
4580
|
name: "update",
|
|
3939
4581
|
description: "Update an existing FlyDocs installation"
|
|
@@ -4020,9 +4662,9 @@ var init_update = __esm({
|
|
|
4020
4662
|
}
|
|
4021
4663
|
targetDir = resolve4(targetDir);
|
|
4022
4664
|
process.chdir(targetDir);
|
|
4023
|
-
const hasVersion = await pathExists(
|
|
4665
|
+
const hasVersion = await pathExists(join24(targetDir, ".flydocs", "version"));
|
|
4024
4666
|
const hasConfig = await pathExists(
|
|
4025
|
-
|
|
4667
|
+
join24(targetDir, ".flydocs", "config.json")
|
|
4026
4668
|
);
|
|
4027
4669
|
if (!hasVersion && !hasConfig) {
|
|
4028
4670
|
printError(`Not a FlyDocs project: ${targetDir}`);
|
|
@@ -4033,8 +4675,8 @@ var init_update = __esm({
|
|
|
4033
4675
|
console.log();
|
|
4034
4676
|
let currentVersion = "0.1.0";
|
|
4035
4677
|
if (hasVersion) {
|
|
4036
|
-
const vContent = await
|
|
4037
|
-
|
|
4678
|
+
const vContent = await readFile16(
|
|
4679
|
+
join24(targetDir, ".flydocs", "version"),
|
|
4038
4680
|
"utf-8"
|
|
4039
4681
|
);
|
|
4040
4682
|
currentVersion = vContent.trim();
|
|
@@ -4068,10 +4710,10 @@ var init_update = __esm({
|
|
|
4068
4710
|
});
|
|
4069
4711
|
console.log(`Updating: v${currentVersion} \u2192 v${version}`);
|
|
4070
4712
|
console.log();
|
|
4071
|
-
const changelogPath =
|
|
4713
|
+
const changelogPath = join24(templateDir, "CHANGELOG.md");
|
|
4072
4714
|
const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
|
|
4073
4715
|
if (whatsNew.length > 0) {
|
|
4074
|
-
console.log(
|
|
4716
|
+
console.log(pc10.cyan("What's new:"));
|
|
4075
4717
|
console.log();
|
|
4076
4718
|
for (const entry of whatsNew) {
|
|
4077
4719
|
console.log(` ${entry}`);
|
|
@@ -4080,23 +4722,23 @@ var init_update = __esm({
|
|
|
4080
4722
|
}
|
|
4081
4723
|
const now = /* @__PURE__ */ new Date();
|
|
4082
4724
|
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
|
|
4725
|
+
const backupDir = join24(targetDir, ".flydocs", `backup-${ts}`);
|
|
4726
|
+
await mkdir13(backupDir, { recursive: true });
|
|
4085
4727
|
if (hasConfig) {
|
|
4086
4728
|
await cp2(
|
|
4087
|
-
|
|
4088
|
-
|
|
4729
|
+
join24(targetDir, ".flydocs", "config.json"),
|
|
4730
|
+
join24(backupDir, "config.json")
|
|
4089
4731
|
);
|
|
4090
4732
|
printStatus(`Config backed up to .flydocs/backup-${ts}/`);
|
|
4091
4733
|
}
|
|
4092
4734
|
try {
|
|
4093
|
-
const flydocsDir =
|
|
4094
|
-
const entries = await
|
|
4735
|
+
const flydocsDir = join24(targetDir, ".flydocs");
|
|
4736
|
+
const entries = await readdir6(flydocsDir);
|
|
4095
4737
|
const backups = entries.filter((e) => e.startsWith("backup-")).sort();
|
|
4096
4738
|
if (backups.length > 3) {
|
|
4097
4739
|
const toRemove = backups.slice(0, backups.length - 3);
|
|
4098
4740
|
for (const old of toRemove) {
|
|
4099
|
-
await rm5(
|
|
4741
|
+
await rm5(join24(flydocsDir, old), { recursive: true, force: true });
|
|
4100
4742
|
}
|
|
4101
4743
|
}
|
|
4102
4744
|
} catch {
|
|
@@ -4135,17 +4777,17 @@ var init_update = __esm({
|
|
|
4135
4777
|
await ensureDirectories(targetDir, effectiveTier);
|
|
4136
4778
|
console.log("Replacing framework directories...");
|
|
4137
4779
|
await replaceDirectory(
|
|
4138
|
-
|
|
4139
|
-
|
|
4780
|
+
join24(templateDir, ".flydocs", "templates"),
|
|
4781
|
+
join24(targetDir, ".flydocs", "templates")
|
|
4140
4782
|
);
|
|
4141
4783
|
printStatus(".flydocs/templates");
|
|
4142
4784
|
await replaceDirectory(
|
|
4143
|
-
|
|
4144
|
-
|
|
4785
|
+
join24(templateDir, ".claude", "hooks"),
|
|
4786
|
+
join24(targetDir, ".claude", "hooks")
|
|
4145
4787
|
);
|
|
4146
4788
|
printStatus(".claude/hooks");
|
|
4147
4789
|
const hasExistingAgents = await pathExists(
|
|
4148
|
-
|
|
4790
|
+
join24(targetDir, ".claude", "agents")
|
|
4149
4791
|
);
|
|
4150
4792
|
let installAgents;
|
|
4151
4793
|
if (args.yes) {
|
|
@@ -4154,7 +4796,7 @@ var init_update = __esm({
|
|
|
4154
4796
|
installAgents = true;
|
|
4155
4797
|
} else {
|
|
4156
4798
|
console.log();
|
|
4157
|
-
console.log(` ${
|
|
4799
|
+
console.log(` ${pc10.bold(pc10.yellow("Sub-Agents (Recommended)"))}`);
|
|
4158
4800
|
console.log();
|
|
4159
4801
|
console.log(
|
|
4160
4802
|
" Sub-agents are specialized roles (PM, implementation, review,"
|
|
@@ -4177,20 +4819,20 @@ var init_update = __esm({
|
|
|
4177
4819
|
}
|
|
4178
4820
|
}
|
|
4179
4821
|
if (installAgents) {
|
|
4180
|
-
const claudeAgentsSrc =
|
|
4822
|
+
const claudeAgentsSrc = join24(templateDir, ".claude", "agents");
|
|
4181
4823
|
if (await pathExists(claudeAgentsSrc)) {
|
|
4182
|
-
await
|
|
4824
|
+
await mkdir13(join24(targetDir, ".claude", "agents"), { recursive: true });
|
|
4183
4825
|
await copyDirectoryContents(
|
|
4184
4826
|
claudeAgentsSrc,
|
|
4185
|
-
|
|
4827
|
+
join24(targetDir, ".claude", "agents")
|
|
4186
4828
|
);
|
|
4187
4829
|
}
|
|
4188
|
-
const cursorAgentsSrc =
|
|
4830
|
+
const cursorAgentsSrc = join24(templateDir, ".cursor", "agents");
|
|
4189
4831
|
if (await pathExists(cursorAgentsSrc)) {
|
|
4190
|
-
await
|
|
4832
|
+
await mkdir13(join24(targetDir, ".cursor", "agents"), { recursive: true });
|
|
4191
4833
|
await copyDirectoryContents(
|
|
4192
4834
|
cursorAgentsSrc,
|
|
4193
|
-
|
|
4835
|
+
join24(targetDir, ".cursor", "agents")
|
|
4194
4836
|
);
|
|
4195
4837
|
}
|
|
4196
4838
|
printStatus(
|
|
@@ -4204,58 +4846,58 @@ var init_update = __esm({
|
|
|
4204
4846
|
console.log();
|
|
4205
4847
|
console.log("Replacing framework files...");
|
|
4206
4848
|
await copyFile(
|
|
4207
|
-
|
|
4208
|
-
|
|
4849
|
+
join24(templateDir, ".claude", "CLAUDE.md"),
|
|
4850
|
+
join24(targetDir, ".claude", "CLAUDE.md")
|
|
4209
4851
|
);
|
|
4210
4852
|
await copyFile(
|
|
4211
|
-
|
|
4212
|
-
|
|
4853
|
+
join24(templateDir, ".claude", "settings.json"),
|
|
4854
|
+
join24(targetDir, ".claude", "settings.json")
|
|
4213
4855
|
);
|
|
4214
4856
|
printStatus(".claude/CLAUDE.md, settings.json");
|
|
4215
4857
|
await copyDirectoryContents(
|
|
4216
|
-
|
|
4217
|
-
|
|
4858
|
+
join24(templateDir, ".claude", "commands"),
|
|
4859
|
+
join24(targetDir, ".claude", "commands")
|
|
4218
4860
|
);
|
|
4219
4861
|
await copyDirectoryContents(
|
|
4220
|
-
|
|
4221
|
-
|
|
4862
|
+
join24(templateDir, ".claude", "commands"),
|
|
4863
|
+
join24(targetDir, ".cursor", "commands")
|
|
4222
4864
|
);
|
|
4223
4865
|
printStatus(".claude/commands, .cursor/commands");
|
|
4224
|
-
const skillsReadmeSrc =
|
|
4866
|
+
const skillsReadmeSrc = join24(templateDir, ".claude", "skills", "README.md");
|
|
4225
4867
|
if (await pathExists(skillsReadmeSrc)) {
|
|
4226
4868
|
await copyFile(
|
|
4227
4869
|
skillsReadmeSrc,
|
|
4228
|
-
|
|
4870
|
+
join24(targetDir, ".claude", "skills", "README.md")
|
|
4229
4871
|
);
|
|
4230
4872
|
}
|
|
4231
4873
|
printStatus(".claude/skills/README.md");
|
|
4232
4874
|
await copyFile(
|
|
4233
|
-
|
|
4234
|
-
|
|
4875
|
+
join24(templateDir, ".cursor", "hooks.json"),
|
|
4876
|
+
join24(targetDir, ".cursor", "hooks.json")
|
|
4235
4877
|
);
|
|
4236
4878
|
printStatus(".cursor/hooks.json");
|
|
4237
4879
|
await copyFile(
|
|
4238
|
-
|
|
4239
|
-
|
|
4880
|
+
join24(templateDir, "AGENTS.md"),
|
|
4881
|
+
join24(targetDir, "AGENTS.md")
|
|
4240
4882
|
);
|
|
4241
4883
|
printStatus("AGENTS.md");
|
|
4242
|
-
const envExampleSrc =
|
|
4884
|
+
const envExampleSrc = join24(templateDir, ".env.example");
|
|
4243
4885
|
if (await pathExists(envExampleSrc)) {
|
|
4244
|
-
await copyFile(envExampleSrc,
|
|
4886
|
+
await copyFile(envExampleSrc, join24(targetDir, ".env.example"));
|
|
4245
4887
|
printStatus(".env.example");
|
|
4246
4888
|
}
|
|
4247
|
-
const knowledgeTemplatesDir =
|
|
4889
|
+
const knowledgeTemplatesDir = join24(
|
|
4248
4890
|
targetDir,
|
|
4249
4891
|
"flydocs",
|
|
4250
4892
|
"knowledge",
|
|
4251
4893
|
"templates"
|
|
4252
4894
|
);
|
|
4253
4895
|
if (!await pathExists(knowledgeTemplatesDir)) {
|
|
4254
|
-
await
|
|
4896
|
+
await mkdir13(knowledgeTemplatesDir, { recursive: true });
|
|
4255
4897
|
}
|
|
4256
4898
|
for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
|
|
4257
|
-
const src =
|
|
4258
|
-
const dest =
|
|
4899
|
+
const src = join24(templateDir, "flydocs", "knowledge", "templates", tmpl);
|
|
4900
|
+
const dest = join24(knowledgeTemplatesDir, tmpl);
|
|
4259
4901
|
if (await pathExists(src) && !await pathExists(dest)) {
|
|
4260
4902
|
await copyFile(src, dest);
|
|
4261
4903
|
}
|
|
@@ -4282,18 +4924,18 @@ var init_update = __esm({
|
|
|
4282
4924
|
printWarning("Config merge failed \u2014 config.json preserved as-is");
|
|
4283
4925
|
}
|
|
4284
4926
|
await copyFile(
|
|
4285
|
-
|
|
4286
|
-
|
|
4927
|
+
join24(templateDir, ".flydocs", "version"),
|
|
4928
|
+
join24(targetDir, ".flydocs", "version")
|
|
4287
4929
|
);
|
|
4288
4930
|
printStatus(`.flydocs/version \u2192 ${version}`);
|
|
4289
|
-
const clSrc =
|
|
4931
|
+
const clSrc = join24(templateDir, "CHANGELOG.md");
|
|
4290
4932
|
if (await pathExists(clSrc)) {
|
|
4291
|
-
await copyFile(clSrc,
|
|
4933
|
+
await copyFile(clSrc, join24(targetDir, ".flydocs", "CHANGELOG.md"));
|
|
4292
4934
|
printStatus(".flydocs/CHANGELOG.md");
|
|
4293
4935
|
}
|
|
4294
|
-
const mfSrc =
|
|
4936
|
+
const mfSrc = join24(templateDir, "manifest.json");
|
|
4295
4937
|
if (await pathExists(mfSrc)) {
|
|
4296
|
-
await copyFile(mfSrc,
|
|
4938
|
+
await copyFile(mfSrc, join24(targetDir, ".flydocs", "manifest.json"));
|
|
4297
4939
|
printStatus(".flydocs/manifest.json");
|
|
4298
4940
|
}
|
|
4299
4941
|
await generateIntegrity(targetDir, version);
|
|
@@ -4355,22 +4997,22 @@ var uninstall_exports = {};
|
|
|
4355
4997
|
__export(uninstall_exports, {
|
|
4356
4998
|
default: () => uninstall_default
|
|
4357
4999
|
});
|
|
4358
|
-
import { defineCommand as
|
|
4359
|
-
import { resolve as resolve5, join as
|
|
4360
|
-
import { readdir as
|
|
5000
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
5001
|
+
import { resolve as resolve5, join as join25 } from "path";
|
|
5002
|
+
import { readdir as readdir7, rm as rm6, rename as rename2 } from "fs/promises";
|
|
4361
5003
|
import { confirm as confirm5, select as select4, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
|
|
4362
|
-
import
|
|
5004
|
+
import pc11 from "picocolors";
|
|
4363
5005
|
async function removeOwnedSkills(targetDir) {
|
|
4364
|
-
const skillsDir =
|
|
5006
|
+
const skillsDir = join25(targetDir, ".claude", "skills");
|
|
4365
5007
|
const removed = [];
|
|
4366
5008
|
if (!await pathExists(skillsDir)) {
|
|
4367
5009
|
return removed;
|
|
4368
5010
|
}
|
|
4369
5011
|
try {
|
|
4370
|
-
const entries = await
|
|
5012
|
+
const entries = await readdir7(skillsDir);
|
|
4371
5013
|
for (const entry of entries) {
|
|
4372
5014
|
if (entry.startsWith(OWNED_SKILL_PREFIX)) {
|
|
4373
|
-
await rm6(
|
|
5015
|
+
await rm6(join25(skillsDir, entry), { recursive: true, force: true });
|
|
4374
5016
|
removed.push(`.claude/skills/${entry}`);
|
|
4375
5017
|
}
|
|
4376
5018
|
}
|
|
@@ -4379,16 +5021,16 @@ async function removeOwnedSkills(targetDir) {
|
|
|
4379
5021
|
return removed;
|
|
4380
5022
|
}
|
|
4381
5023
|
async function removeOwnedCursorRules(targetDir) {
|
|
4382
|
-
const rulesDir =
|
|
5024
|
+
const rulesDir = join25(targetDir, ".cursor", "rules");
|
|
4383
5025
|
const removed = [];
|
|
4384
5026
|
if (!await pathExists(rulesDir)) {
|
|
4385
5027
|
return removed;
|
|
4386
5028
|
}
|
|
4387
5029
|
try {
|
|
4388
|
-
const entries = await
|
|
5030
|
+
const entries = await readdir7(rulesDir);
|
|
4389
5031
|
for (const entry of entries) {
|
|
4390
5032
|
if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
|
|
4391
|
-
await rm6(
|
|
5033
|
+
await rm6(join25(rulesDir, entry), { force: true });
|
|
4392
5034
|
removed.push(`.cursor/rules/${entry}`);
|
|
4393
5035
|
}
|
|
4394
5036
|
}
|
|
@@ -4398,7 +5040,7 @@ async function removeOwnedCursorRules(targetDir) {
|
|
|
4398
5040
|
}
|
|
4399
5041
|
async function isEmptyDir(dirPath) {
|
|
4400
5042
|
try {
|
|
4401
|
-
const entries = await
|
|
5043
|
+
const entries = await readdir7(dirPath);
|
|
4402
5044
|
return entries.length === 0;
|
|
4403
5045
|
} catch {
|
|
4404
5046
|
return false;
|
|
@@ -4407,7 +5049,7 @@ async function isEmptyDir(dirPath) {
|
|
|
4407
5049
|
async function cleanupEmptyParents(targetDir, dirs) {
|
|
4408
5050
|
const cleaned = [];
|
|
4409
5051
|
for (const dir of dirs) {
|
|
4410
|
-
const fullPath =
|
|
5052
|
+
const fullPath = join25(targetDir, dir);
|
|
4411
5053
|
if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
|
|
4412
5054
|
await rm6(fullPath, { recursive: true, force: true });
|
|
4413
5055
|
cleaned.push(dir);
|
|
@@ -4439,7 +5081,7 @@ var init_uninstall = __esm({
|
|
|
4439
5081
|
];
|
|
4440
5082
|
OWNED_SKILL_PREFIX = "flydocs-";
|
|
4441
5083
|
OWNED_RULE_PREFIX = "flydocs-";
|
|
4442
|
-
uninstall_default =
|
|
5084
|
+
uninstall_default = defineCommand6({
|
|
4443
5085
|
meta: {
|
|
4444
5086
|
name: "uninstall",
|
|
4445
5087
|
description: "Remove FlyDocs from a project directory"
|
|
@@ -4486,8 +5128,8 @@ var init_uninstall = __esm({
|
|
|
4486
5128
|
process.exit(1);
|
|
4487
5129
|
}
|
|
4488
5130
|
targetDir = resolve5(targetDir);
|
|
4489
|
-
const hasFlydocs = await pathExists(
|
|
4490
|
-
const hasAgentsMd = await pathExists(
|
|
5131
|
+
const hasFlydocs = await pathExists(join25(targetDir, ".flydocs"));
|
|
5132
|
+
const hasAgentsMd = await pathExists(join25(targetDir, "AGENTS.md"));
|
|
4491
5133
|
if (!hasFlydocs && !hasAgentsMd) {
|
|
4492
5134
|
printError(`Not a FlyDocs project: ${targetDir}`);
|
|
4493
5135
|
printInfo("No .flydocs/ directory or AGENTS.md found.");
|
|
@@ -4499,7 +5141,7 @@ var init_uninstall = __esm({
|
|
|
4499
5141
|
const removeAll = forceAll || args.all;
|
|
4500
5142
|
const skipPrompts = forceAll || args.yes;
|
|
4501
5143
|
let contentAction = "preserve";
|
|
4502
|
-
const hasUserContent = await pathExists(
|
|
5144
|
+
const hasUserContent = await pathExists(join25(targetDir, "flydocs"));
|
|
4503
5145
|
if (hasUserContent) {
|
|
4504
5146
|
if (removeAll) {
|
|
4505
5147
|
contentAction = "remove";
|
|
@@ -4533,34 +5175,34 @@ var init_uninstall = __esm({
|
|
|
4533
5175
|
}
|
|
4534
5176
|
if (!skipPrompts) {
|
|
4535
5177
|
console.log();
|
|
4536
|
-
console.log(
|
|
5178
|
+
console.log(pc11.bold("The following will be removed:"));
|
|
4537
5179
|
console.log();
|
|
4538
5180
|
console.log(" Framework files:");
|
|
4539
5181
|
for (const [path] of ALWAYS_REMOVED) {
|
|
4540
|
-
console.log(` ${
|
|
5182
|
+
console.log(` ${pc11.dim(path)}`);
|
|
4541
5183
|
}
|
|
4542
|
-
console.log(` ${
|
|
4543
|
-
console.log(` ${
|
|
5184
|
+
console.log(` ${pc11.dim(".claude/skills/flydocs-*")}`);
|
|
5185
|
+
console.log(` ${pc11.dim(".cursor/rules/flydocs-*.mdc")}`);
|
|
4544
5186
|
if (hasUserContent) {
|
|
4545
5187
|
if (contentAction === "archive") {
|
|
4546
5188
|
console.log();
|
|
4547
5189
|
console.log(
|
|
4548
|
-
` User content: ${
|
|
5190
|
+
` User content: ${pc11.yellow("flydocs/ -> flydocs-archive/")}`
|
|
4549
5191
|
);
|
|
4550
5192
|
} else if (contentAction === "remove") {
|
|
4551
5193
|
console.log();
|
|
4552
|
-
console.log(` User content: ${
|
|
5194
|
+
console.log(` User content: ${pc11.red("flydocs/ (deleted)")}`);
|
|
4553
5195
|
} else {
|
|
4554
5196
|
console.log();
|
|
4555
|
-
console.log(` User content: ${
|
|
5197
|
+
console.log(` User content: ${pc11.green("flydocs/ (preserved)")}`);
|
|
4556
5198
|
}
|
|
4557
5199
|
}
|
|
4558
5200
|
console.log();
|
|
4559
|
-
console.log(
|
|
5201
|
+
console.log(pc11.bold("Preserved:"));
|
|
4560
5202
|
console.log(
|
|
4561
|
-
` ${
|
|
5203
|
+
` ${pc11.dim(".claude/skills/ (non-flydocs community skills)")}`
|
|
4562
5204
|
);
|
|
4563
|
-
console.log(` ${
|
|
5205
|
+
console.log(` ${pc11.dim(".env, .env.local")}`);
|
|
4564
5206
|
console.log();
|
|
4565
5207
|
const shouldContinue = await confirm5({
|
|
4566
5208
|
message: "Proceed with uninstall?"
|
|
@@ -4582,7 +5224,7 @@ var init_uninstall = __esm({
|
|
|
4582
5224
|
const removedRules = await removeOwnedCursorRules(targetDir);
|
|
4583
5225
|
result.removed.push(...removedRules);
|
|
4584
5226
|
for (const [relativePath, type] of ALWAYS_REMOVED) {
|
|
4585
|
-
const fullPath =
|
|
5227
|
+
const fullPath = join25(targetDir, relativePath);
|
|
4586
5228
|
if (!await pathExists(fullPath)) {
|
|
4587
5229
|
result.skipped.push(relativePath);
|
|
4588
5230
|
continue;
|
|
@@ -4600,9 +5242,9 @@ var init_uninstall = __esm({
|
|
|
4600
5242
|
}
|
|
4601
5243
|
}
|
|
4602
5244
|
if (hasUserContent) {
|
|
4603
|
-
const flydocsPath =
|
|
5245
|
+
const flydocsPath = join25(targetDir, "flydocs");
|
|
4604
5246
|
if (contentAction === "archive") {
|
|
4605
|
-
const archivePath =
|
|
5247
|
+
const archivePath = join25(targetDir, "flydocs-archive");
|
|
4606
5248
|
if (await pathExists(archivePath)) {
|
|
4607
5249
|
await rm6(archivePath, { recursive: true, force: true });
|
|
4608
5250
|
}
|
|
@@ -4628,17 +5270,17 @@ var init_uninstall = __esm({
|
|
|
4628
5270
|
result.restored = originals.map((f) => f.relativePath);
|
|
4629
5271
|
}
|
|
4630
5272
|
console.log();
|
|
4631
|
-
console.log(
|
|
5273
|
+
console.log(pc11.bold("Uninstall Summary"));
|
|
4632
5274
|
console.log();
|
|
4633
5275
|
if (result.removed.length > 0) {
|
|
4634
|
-
console.log(` ${
|
|
5276
|
+
console.log(` ${pc11.green("Removed")} (${result.removed.length}):`);
|
|
4635
5277
|
for (const item of result.removed) {
|
|
4636
5278
|
printStatus(item);
|
|
4637
5279
|
}
|
|
4638
5280
|
}
|
|
4639
5281
|
if (result.archived.length > 0) {
|
|
4640
5282
|
console.log();
|
|
4641
|
-
console.log(` ${
|
|
5283
|
+
console.log(` ${pc11.yellow("Archived")} (${result.archived.length}):`);
|
|
4642
5284
|
for (const item of result.archived) {
|
|
4643
5285
|
printInfo(item);
|
|
4644
5286
|
}
|
|
@@ -4646,7 +5288,7 @@ var init_uninstall = __esm({
|
|
|
4646
5288
|
if (result.restored.length > 0) {
|
|
4647
5289
|
console.log();
|
|
4648
5290
|
console.log(
|
|
4649
|
-
` ${
|
|
5291
|
+
` ${pc11.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
|
|
4650
5292
|
);
|
|
4651
5293
|
for (const item of result.restored) {
|
|
4652
5294
|
printInfo(item);
|
|
@@ -4655,16 +5297,16 @@ var init_uninstall = __esm({
|
|
|
4655
5297
|
if (result.skipped.length > 0) {
|
|
4656
5298
|
console.log();
|
|
4657
5299
|
console.log(
|
|
4658
|
-
` ${
|
|
5300
|
+
` ${pc11.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
|
|
4659
5301
|
);
|
|
4660
5302
|
for (const item of result.skipped) {
|
|
4661
|
-
console.log(` ${
|
|
5303
|
+
console.log(` ${pc11.dim(item)}`);
|
|
4662
5304
|
}
|
|
4663
5305
|
}
|
|
4664
5306
|
console.log();
|
|
4665
5307
|
printStatus("FlyDocs has been removed from this project.");
|
|
4666
5308
|
console.log();
|
|
4667
|
-
printInfo(`To reinstall: ${
|
|
5309
|
+
printInfo(`To reinstall: ${pc11.cyan("npx @flydocs/cli install --here")}`);
|
|
4668
5310
|
console.log();
|
|
4669
5311
|
}
|
|
4670
5312
|
});
|
|
@@ -4676,28 +5318,28 @@ var setup_exports = {};
|
|
|
4676
5318
|
__export(setup_exports, {
|
|
4677
5319
|
default: () => setup_default
|
|
4678
5320
|
});
|
|
4679
|
-
import { defineCommand as
|
|
4680
|
-
import
|
|
5321
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
5322
|
+
import pc12 from "picocolors";
|
|
4681
5323
|
var setup_default;
|
|
4682
5324
|
var init_setup = __esm({
|
|
4683
5325
|
"src/commands/setup.ts"() {
|
|
4684
5326
|
"use strict";
|
|
4685
|
-
setup_default =
|
|
5327
|
+
setup_default = defineCommand7({
|
|
4686
5328
|
meta: {
|
|
4687
5329
|
name: "setup",
|
|
4688
5330
|
description: "Configure FlyDocs settings for this project"
|
|
4689
5331
|
},
|
|
4690
5332
|
run() {
|
|
4691
5333
|
console.log();
|
|
4692
|
-
console.log(` ${
|
|
5334
|
+
console.log(` ${pc12.bold("FlyDocs Setup")}`);
|
|
4693
5335
|
console.log();
|
|
4694
5336
|
console.log(` Setup runs inside your IDE as an interactive AI command.`);
|
|
4695
5337
|
console.log();
|
|
4696
5338
|
console.log(
|
|
4697
|
-
` ${
|
|
5339
|
+
` ${pc12.cyan("Claude Code:")} Type ${pc12.bold("/flydocs-setup")} in chat`
|
|
4698
5340
|
);
|
|
4699
5341
|
console.log(
|
|
4700
|
-
` ${
|
|
5342
|
+
` ${pc12.cyan("Cursor:")} Type ${pc12.bold("/flydocs-setup")} in chat`
|
|
4701
5343
|
);
|
|
4702
5344
|
console.log();
|
|
4703
5345
|
console.log(` This configures your project context, detects your stack,`);
|
|
@@ -4713,14 +5355,14 @@ var skills_exports = {};
|
|
|
4713
5355
|
__export(skills_exports, {
|
|
4714
5356
|
default: () => skills_default
|
|
4715
5357
|
});
|
|
4716
|
-
import { defineCommand as
|
|
4717
|
-
import
|
|
5358
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
5359
|
+
import pc13 from "picocolors";
|
|
4718
5360
|
var list, search, add, remove, skills_default;
|
|
4719
5361
|
var init_skills2 = __esm({
|
|
4720
5362
|
"src/commands/skills.ts"() {
|
|
4721
5363
|
"use strict";
|
|
4722
5364
|
init_skill_manager();
|
|
4723
|
-
list =
|
|
5365
|
+
list = defineCommand8({
|
|
4724
5366
|
meta: {
|
|
4725
5367
|
name: "list",
|
|
4726
5368
|
description: "List installed skills"
|
|
@@ -4736,26 +5378,26 @@ var init_skills2 = __esm({
|
|
|
4736
5378
|
console.log(`${total} skill(s) installed:`);
|
|
4737
5379
|
if (result.platform.length > 0) {
|
|
4738
5380
|
console.log();
|
|
4739
|
-
console.log(
|
|
5381
|
+
console.log(pc13.bold("Platform"));
|
|
4740
5382
|
for (const skill of result.platform) {
|
|
4741
5383
|
console.log(
|
|
4742
|
-
` ${skill.name} ${
|
|
5384
|
+
` ${skill.name} ${pc13.dim(`(${skill.triggers} triggers)`)}`
|
|
4743
5385
|
);
|
|
4744
5386
|
}
|
|
4745
5387
|
}
|
|
4746
5388
|
if (result.community.length > 0) {
|
|
4747
5389
|
console.log();
|
|
4748
|
-
console.log(
|
|
5390
|
+
console.log(pc13.bold("Community"));
|
|
4749
5391
|
for (const skill of result.community) {
|
|
4750
5392
|
console.log(
|
|
4751
|
-
` ${skill.name} ${
|
|
5393
|
+
` ${skill.name} ${pc13.dim(`(${skill.triggers} triggers)`)}`
|
|
4752
5394
|
);
|
|
4753
5395
|
}
|
|
4754
5396
|
}
|
|
4755
5397
|
console.log();
|
|
4756
5398
|
}
|
|
4757
5399
|
});
|
|
4758
|
-
search =
|
|
5400
|
+
search = defineCommand8({
|
|
4759
5401
|
meta: {
|
|
4760
5402
|
name: "search",
|
|
4761
5403
|
description: "Search community skills"
|
|
@@ -4771,24 +5413,24 @@ var init_skills2 = __esm({
|
|
|
4771
5413
|
const results = await searchCatalog(args.keyword);
|
|
4772
5414
|
if (results.length === 0) {
|
|
4773
5415
|
console.log(`No skills found for "${args.keyword}".`);
|
|
4774
|
-
console.log(` Browse the catalog at: ${
|
|
5416
|
+
console.log(` Browse the catalog at: ${pc13.cyan("https://skills.sh/")}`);
|
|
4775
5417
|
return;
|
|
4776
5418
|
}
|
|
4777
5419
|
console.log();
|
|
4778
5420
|
console.log(`${results.length} skill(s) matching "${args.keyword}":`);
|
|
4779
5421
|
console.log();
|
|
4780
5422
|
for (const skill of results) {
|
|
4781
|
-
console.log(` ${
|
|
5423
|
+
console.log(` ${pc13.bold(skill.name)}`);
|
|
4782
5424
|
console.log(` ${skill.description}`);
|
|
4783
|
-
console.log(` ${
|
|
5425
|
+
console.log(` ${pc13.dim(skill.repo)}`);
|
|
4784
5426
|
if (skill.tags.length > 0) {
|
|
4785
|
-
console.log(` ${
|
|
5427
|
+
console.log(` ${pc13.dim(skill.tags.join(", "))}`);
|
|
4786
5428
|
}
|
|
4787
5429
|
console.log();
|
|
4788
5430
|
}
|
|
4789
5431
|
}
|
|
4790
5432
|
});
|
|
4791
|
-
add =
|
|
5433
|
+
add = defineCommand8({
|
|
4792
5434
|
meta: {
|
|
4793
5435
|
name: "add",
|
|
4794
5436
|
description: "Install a community skill"
|
|
@@ -4804,7 +5446,7 @@ var init_skills2 = __esm({
|
|
|
4804
5446
|
await addSkill(process.cwd(), args.source);
|
|
4805
5447
|
}
|
|
4806
5448
|
});
|
|
4807
|
-
remove =
|
|
5449
|
+
remove = defineCommand8({
|
|
4808
5450
|
meta: {
|
|
4809
5451
|
name: "remove",
|
|
4810
5452
|
description: "Remove an installed community skill"
|
|
@@ -4820,7 +5462,7 @@ var init_skills2 = __esm({
|
|
|
4820
5462
|
await removeSkill(process.cwd(), args.name);
|
|
4821
5463
|
}
|
|
4822
5464
|
});
|
|
4823
|
-
skills_default =
|
|
5465
|
+
skills_default = defineCommand8({
|
|
4824
5466
|
meta: {
|
|
4825
5467
|
name: "skills",
|
|
4826
5468
|
description: "Manage FlyDocs skills (list, search, add, remove)"
|
|
@@ -4840,10 +5482,10 @@ var connect_exports = {};
|
|
|
4840
5482
|
__export(connect_exports, {
|
|
4841
5483
|
default: () => connect_default
|
|
4842
5484
|
});
|
|
4843
|
-
import { defineCommand as
|
|
5485
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
4844
5486
|
import { text as text4, confirm as confirm6, isCancel as isCancel7, cancel as cancel6 } from "@clack/prompts";
|
|
4845
|
-
import
|
|
4846
|
-
import { join as
|
|
5487
|
+
import pc14 from "picocolors";
|
|
5488
|
+
import { join as join26 } from "path";
|
|
4847
5489
|
var connect_default;
|
|
4848
5490
|
var init_connect = __esm({
|
|
4849
5491
|
"src/commands/connect.ts"() {
|
|
@@ -4853,7 +5495,7 @@ var init_connect = __esm({
|
|
|
4853
5495
|
init_template();
|
|
4854
5496
|
init_ui();
|
|
4855
5497
|
init_api_key();
|
|
4856
|
-
connect_default =
|
|
5498
|
+
connect_default = defineCommand9({
|
|
4857
5499
|
meta: {
|
|
4858
5500
|
name: "connect",
|
|
4859
5501
|
description: "Connect FlyDocs to a cloud provider"
|
|
@@ -4878,11 +5520,11 @@ var init_connect = __esm({
|
|
|
4878
5520
|
},
|
|
4879
5521
|
async run({ args }) {
|
|
4880
5522
|
const targetDir = args.path ?? process.cwd();
|
|
4881
|
-
const configPath =
|
|
5523
|
+
const configPath = join26(targetDir, ".flydocs", "config.json");
|
|
4882
5524
|
if (!await pathExists(configPath)) {
|
|
4883
5525
|
printError("Not a FlyDocs project (.flydocs/config.json not found).");
|
|
4884
5526
|
console.log(
|
|
4885
|
-
` Run ${
|
|
5527
|
+
` Run ${pc14.cyan("flydocs")} first to install FlyDocs in this project.`
|
|
4886
5528
|
);
|
|
4887
5529
|
process.exit(1);
|
|
4888
5530
|
}
|
|
@@ -4899,10 +5541,10 @@ var init_connect = __esm({
|
|
|
4899
5541
|
}
|
|
4900
5542
|
}
|
|
4901
5543
|
console.log();
|
|
4902
|
-
console.log(` ${
|
|
5544
|
+
console.log(` ${pc14.bold("Connect to FlyDocs Cloud")}`);
|
|
4903
5545
|
console.log();
|
|
4904
5546
|
console.log(
|
|
4905
|
-
` ${
|
|
5547
|
+
` ${pc14.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
4906
5548
|
);
|
|
4907
5549
|
console.log();
|
|
4908
5550
|
let apiKey = args.key ?? "";
|
|
@@ -4940,7 +5582,7 @@ var init_connect = __esm({
|
|
|
4940
5582
|
console.log(` Check your key and try again.`);
|
|
4941
5583
|
process.exit(1);
|
|
4942
5584
|
}
|
|
4943
|
-
printStatus(`Connected to ${
|
|
5585
|
+
printStatus(`Connected to ${pc14.bold(result.org)}`);
|
|
4944
5586
|
} catch {
|
|
4945
5587
|
printError(
|
|
4946
5588
|
"Could not reach relay API. Check your network and try again."
|
|
@@ -4948,7 +5590,7 @@ var init_connect = __esm({
|
|
|
4948
5590
|
process.exit(1);
|
|
4949
5591
|
}
|
|
4950
5592
|
const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
|
|
4951
|
-
printStatus(`API key stored in ${
|
|
5593
|
+
printStatus(`API key stored in ${pc14.dim(envFile)}`);
|
|
4952
5594
|
} else {
|
|
4953
5595
|
try {
|
|
4954
5596
|
const result = await validateLinearKey(apiKey);
|
|
@@ -4958,7 +5600,7 @@ var init_connect = __esm({
|
|
|
4958
5600
|
process.exit(1);
|
|
4959
5601
|
}
|
|
4960
5602
|
printStatus(
|
|
4961
|
-
`Authenticated as ${
|
|
5603
|
+
`Authenticated as ${pc14.bold(result.name)} (${result.email})`
|
|
4962
5604
|
);
|
|
4963
5605
|
} catch {
|
|
4964
5606
|
printError("Invalid API key or network error.");
|
|
@@ -4966,7 +5608,7 @@ var init_connect = __esm({
|
|
|
4966
5608
|
process.exit(1);
|
|
4967
5609
|
}
|
|
4968
5610
|
const envFile = await storeEnvKey(targetDir, "LINEAR_API_KEY", apiKey);
|
|
4969
|
-
printStatus(`API key stored in ${
|
|
5611
|
+
printStatus(`API key stored in ${pc14.dim(envFile)}`);
|
|
4970
5612
|
}
|
|
4971
5613
|
const wasLocal = config.tier === "local";
|
|
4972
5614
|
config.tier = "cloud";
|
|
@@ -4982,14 +5624,14 @@ var init_connect = __esm({
|
|
|
4982
5624
|
}
|
|
4983
5625
|
console.log();
|
|
4984
5626
|
console.log(
|
|
4985
|
-
` ${
|
|
5627
|
+
` ${pc14.bold("Connected!")} Your project is now on the cloud tier.`
|
|
4986
5628
|
);
|
|
4987
5629
|
console.log();
|
|
4988
5630
|
console.log(` Next steps:`);
|
|
4989
5631
|
console.log(
|
|
4990
|
-
` 1. Run ${
|
|
5632
|
+
` 1. Run ${pc14.cyan("/flydocs-setup")} in your IDE to configure your project`
|
|
4991
5633
|
);
|
|
4992
|
-
console.log(` 2. Run ${
|
|
5634
|
+
console.log(` 2. Run ${pc14.cyan("/start-session")} to begin working`);
|
|
4993
5635
|
console.log();
|
|
4994
5636
|
}
|
|
4995
5637
|
});
|
|
@@ -5001,9 +5643,9 @@ var auth_exports = {};
|
|
|
5001
5643
|
__export(auth_exports, {
|
|
5002
5644
|
default: () => auth_default
|
|
5003
5645
|
});
|
|
5004
|
-
import { defineCommand as
|
|
5646
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
5005
5647
|
import { text as text5, confirm as confirm7, isCancel as isCancel8, cancel as cancel7 } from "@clack/prompts";
|
|
5006
|
-
import
|
|
5648
|
+
import pc15 from "picocolors";
|
|
5007
5649
|
var auth_default;
|
|
5008
5650
|
var init_auth = __esm({
|
|
5009
5651
|
"src/commands/auth.ts"() {
|
|
@@ -5011,7 +5653,7 @@ var init_auth = __esm({
|
|
|
5011
5653
|
init_ui();
|
|
5012
5654
|
init_api_key();
|
|
5013
5655
|
init_global_config();
|
|
5014
|
-
auth_default =
|
|
5656
|
+
auth_default = defineCommand10({
|
|
5015
5657
|
meta: {
|
|
5016
5658
|
name: "auth",
|
|
5017
5659
|
description: "Store API key globally (~/.flydocs/credentials)"
|
|
@@ -5025,13 +5667,13 @@ var init_auth = __esm({
|
|
|
5025
5667
|
},
|
|
5026
5668
|
async run({ args }) {
|
|
5027
5669
|
console.log();
|
|
5028
|
-
console.log(` ${
|
|
5670
|
+
console.log(` ${pc15.bold("FlyDocs Authentication")}`);
|
|
5029
5671
|
console.log();
|
|
5030
5672
|
let apiKey = args.key ?? "";
|
|
5031
5673
|
const existing = await readGlobalCredential();
|
|
5032
5674
|
if (existing?.apiKey && !apiKey) {
|
|
5033
5675
|
printInfo(
|
|
5034
|
-
`Existing key found: ${
|
|
5676
|
+
`Existing key found: ${pc15.dim(existing.apiKey.slice(0, 8) + "...")}`
|
|
5035
5677
|
);
|
|
5036
5678
|
const replace = await confirm7({
|
|
5037
5679
|
message: "Replace with a new key?"
|
|
@@ -5043,7 +5685,7 @@ var init_auth = __esm({
|
|
|
5043
5685
|
}
|
|
5044
5686
|
if (!apiKey) {
|
|
5045
5687
|
console.log(
|
|
5046
|
-
` ${
|
|
5688
|
+
` ${pc15.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
5047
5689
|
);
|
|
5048
5690
|
console.log();
|
|
5049
5691
|
const keyInput = await text5({
|
|
@@ -5079,7 +5721,7 @@ var init_auth = __esm({
|
|
|
5079
5721
|
printError("Invalid API key. Check your key and try again.");
|
|
5080
5722
|
process.exit(1);
|
|
5081
5723
|
}
|
|
5082
|
-
printStatus(`Authenticated with ${
|
|
5724
|
+
printStatus(`Authenticated with ${pc15.bold(result.org)}`);
|
|
5083
5725
|
} catch {
|
|
5084
5726
|
printError(
|
|
5085
5727
|
"Could not reach FlyDocs API. Check your network and try again."
|
|
@@ -5094,10 +5736,10 @@ var init_auth = __esm({
|
|
|
5094
5736
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5095
5737
|
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5096
5738
|
});
|
|
5097
|
-
printStatus(`Key stored at ${
|
|
5739
|
+
printStatus(`Key stored at ${pc15.dim(credentialsPath())}`);
|
|
5098
5740
|
await checkCredentialPermissions();
|
|
5099
5741
|
console.log();
|
|
5100
|
-
console.log(` ${
|
|
5742
|
+
console.log(` ${pc15.bold("Authenticated!")} Key stored globally.`);
|
|
5101
5743
|
console.log(` All FlyDocs projects on this machine will use this key.`);
|
|
5102
5744
|
console.log();
|
|
5103
5745
|
}
|
|
@@ -5110,10 +5752,21 @@ var sync_exports = {};
|
|
|
5110
5752
|
__export(sync_exports, {
|
|
5111
5753
|
default: () => sync_default
|
|
5112
5754
|
});
|
|
5113
|
-
import { defineCommand as
|
|
5114
|
-
import
|
|
5115
|
-
import { join as
|
|
5116
|
-
import { mkdir as
|
|
5755
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
5756
|
+
import pc16 from "picocolors";
|
|
5757
|
+
import { join as join27, resolve as resolve6 } from "path";
|
|
5758
|
+
import { mkdir as mkdir14, writeFile as writeFile17 } from "fs/promises";
|
|
5759
|
+
async function resolveArtifactWriteRoot(targetDir, config) {
|
|
5760
|
+
const topology = config.topology;
|
|
5761
|
+
if (topology && topology.type === 4 && topology.label === "sibling-repos") {
|
|
5762
|
+
const parentDir = join27(targetDir, "..");
|
|
5763
|
+
const workspaceFile = await readWorkspaceFile(parentDir);
|
|
5764
|
+
if (workspaceFile) {
|
|
5765
|
+
return resolve6(parentDir);
|
|
5766
|
+
}
|
|
5767
|
+
}
|
|
5768
|
+
return targetDir;
|
|
5769
|
+
}
|
|
5117
5770
|
var sync_default;
|
|
5118
5771
|
var init_sync = __esm({
|
|
5119
5772
|
"src/commands/sync.ts"() {
|
|
@@ -5124,8 +5777,11 @@ var init_sync = __esm({
|
|
|
5124
5777
|
init_config_integrity();
|
|
5125
5778
|
init_fs_ops();
|
|
5126
5779
|
init_types();
|
|
5780
|
+
init_gitignore();
|
|
5781
|
+
init_artifacts();
|
|
5782
|
+
init_workspace();
|
|
5127
5783
|
init_relay_client();
|
|
5128
|
-
sync_default =
|
|
5784
|
+
sync_default = defineCommand11({
|
|
5129
5785
|
meta: {
|
|
5130
5786
|
name: "sync",
|
|
5131
5787
|
description: "Pull latest config and templates from server"
|
|
@@ -5158,10 +5814,12 @@ var init_sync = __esm({
|
|
|
5158
5814
|
process.exit(1);
|
|
5159
5815
|
}
|
|
5160
5816
|
let currentTemplateVersion = 0;
|
|
5817
|
+
let currentArtifactVersion = 0;
|
|
5161
5818
|
try {
|
|
5162
5819
|
const currentConfig = await readAnyConfig(targetDir);
|
|
5163
5820
|
if (isConfigV2(currentConfig)) {
|
|
5164
5821
|
currentTemplateVersion = currentConfig.configVersion ?? 0;
|
|
5822
|
+
currentArtifactVersion = currentConfig.artifactVersion ?? 0;
|
|
5165
5823
|
}
|
|
5166
5824
|
} catch {
|
|
5167
5825
|
}
|
|
@@ -5176,14 +5834,14 @@ var init_sync = __esm({
|
|
|
5176
5834
|
} else {
|
|
5177
5835
|
printWarning("Server unreachable, using cached config.");
|
|
5178
5836
|
}
|
|
5179
|
-
console.log(` ${
|
|
5837
|
+
console.log(` ${pc16.dim("Config may be stale. Retry when connected.")}`);
|
|
5180
5838
|
return;
|
|
5181
5839
|
}
|
|
5182
5840
|
if (!serverResponse.valid) {
|
|
5183
5841
|
printWarning("Server returned invalid config. Keeping current config.");
|
|
5184
5842
|
return;
|
|
5185
5843
|
}
|
|
5186
|
-
await
|
|
5844
|
+
await mkdir14(join27(targetDir, ".flydocs"), { recursive: true });
|
|
5187
5845
|
const configWithHash = applyConfigHash(serverResponse.config);
|
|
5188
5846
|
await writeConfig(targetDir, configWithHash);
|
|
5189
5847
|
changes.push("Updated .flydocs/config.json");
|
|
@@ -5196,21 +5854,21 @@ var init_sync = __esm({
|
|
|
5196
5854
|
workspaceId
|
|
5197
5855
|
);
|
|
5198
5856
|
if (templatesResponse.templates.length > 0) {
|
|
5199
|
-
const templatesDir =
|
|
5857
|
+
const templatesDir = join27(
|
|
5200
5858
|
targetDir,
|
|
5201
5859
|
".claude",
|
|
5202
5860
|
"skills",
|
|
5203
5861
|
"flydocs-workflow",
|
|
5204
5862
|
"templates"
|
|
5205
5863
|
);
|
|
5206
|
-
await
|
|
5864
|
+
await mkdir14(templatesDir, { recursive: true });
|
|
5207
5865
|
for (const template of templatesResponse.templates) {
|
|
5208
5866
|
const filename = `${template.type}.md`;
|
|
5209
5867
|
const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
|
|
5210
|
-
const templateDir =
|
|
5211
|
-
await
|
|
5212
|
-
await
|
|
5213
|
-
|
|
5868
|
+
const templateDir = join27(templatesDir, subdir);
|
|
5869
|
+
await mkdir14(templateDir, { recursive: true });
|
|
5870
|
+
await writeFile17(
|
|
5871
|
+
join27(templateDir, filename),
|
|
5214
5872
|
template.content,
|
|
5215
5873
|
"utf-8"
|
|
5216
5874
|
);
|
|
@@ -5227,6 +5885,24 @@ var init_sync = __esm({
|
|
|
5227
5885
|
}
|
|
5228
5886
|
}
|
|
5229
5887
|
}
|
|
5888
|
+
const serverArtifactVersion = serverResponse.artifactVersion ?? 0;
|
|
5889
|
+
if (serverArtifactVersion > currentArtifactVersion) {
|
|
5890
|
+
const artifactWriteRoot = await resolveArtifactWriteRoot(
|
|
5891
|
+
targetDir,
|
|
5892
|
+
serverResponse.config
|
|
5893
|
+
);
|
|
5894
|
+
const { changes: artifactChanges } = await syncAllArtifacts(
|
|
5895
|
+
artifactWriteRoot,
|
|
5896
|
+
apiKey,
|
|
5897
|
+
workspaceId,
|
|
5898
|
+
currentArtifactVersion
|
|
5899
|
+
);
|
|
5900
|
+
changes.push(...artifactChanges);
|
|
5901
|
+
if (artifactWriteRoot !== targetDir) {
|
|
5902
|
+
changes.push(`Artifacts written to workspace root`);
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
await migrateGitignore(targetDir);
|
|
5230
5906
|
if (changes.length === 0) {
|
|
5231
5907
|
printStatus("Already up to date.");
|
|
5232
5908
|
} else {
|
|
@@ -5245,15 +5921,15 @@ var upgrade_exports = {};
|
|
|
5245
5921
|
__export(upgrade_exports, {
|
|
5246
5922
|
default: () => upgrade_default
|
|
5247
5923
|
});
|
|
5248
|
-
import { defineCommand as
|
|
5249
|
-
import
|
|
5924
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
5925
|
+
import pc17 from "picocolors";
|
|
5250
5926
|
var upgrade_default;
|
|
5251
5927
|
var init_upgrade = __esm({
|
|
5252
5928
|
"src/commands/upgrade.ts"() {
|
|
5253
5929
|
"use strict";
|
|
5254
5930
|
init_config();
|
|
5255
5931
|
init_fs_ops();
|
|
5256
|
-
upgrade_default =
|
|
5932
|
+
upgrade_default = defineCommand12({
|
|
5257
5933
|
meta: {
|
|
5258
5934
|
name: "upgrade",
|
|
5259
5935
|
description: "Learn about FlyDocs Cloud tier and upgrade from local"
|
|
@@ -5282,34 +5958,34 @@ var init_upgrade = __esm({
|
|
|
5282
5958
|
console.log();
|
|
5283
5959
|
if (currentTier === "cloud") {
|
|
5284
5960
|
console.log(
|
|
5285
|
-
` ${
|
|
5961
|
+
` ${pc17.green("\u2713")} You're already on the ${pc17.bold("cloud")} tier.`
|
|
5286
5962
|
);
|
|
5287
5963
|
console.log();
|
|
5288
5964
|
console.log(` Your issues sync with your provider via the relay API.`);
|
|
5289
5965
|
console.log(
|
|
5290
|
-
` Run ${
|
|
5966
|
+
` Run ${pc17.cyan("flydocs connect")} to update your connection settings.`
|
|
5291
5967
|
);
|
|
5292
5968
|
console.log();
|
|
5293
5969
|
return;
|
|
5294
5970
|
}
|
|
5295
|
-
console.log(` ${
|
|
5971
|
+
console.log(` ${pc17.bold("FlyDocs Cloud Tier")}`);
|
|
5296
5972
|
console.log();
|
|
5297
|
-
console.log(` You're currently on the ${
|
|
5973
|
+
console.log(` You're currently on the ${pc17.yellow("local")} tier.`);
|
|
5298
5974
|
console.log(` Upgrade to cloud for:`);
|
|
5299
5975
|
console.log();
|
|
5300
|
-
console.log(` ${
|
|
5301
|
-
console.log(` ${
|
|
5302
|
-
console.log(` ${
|
|
5303
|
-
console.log(` ${
|
|
5304
|
-
console.log(` ${
|
|
5976
|
+
console.log(` ${pc17.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
|
|
5977
|
+
console.log(` ${pc17.cyan("\u2192")} Project milestones and cycle management`);
|
|
5978
|
+
console.log(` ${pc17.cyan("\u2192")} Team assignment and priority tracking`);
|
|
5979
|
+
console.log(` ${pc17.cyan("\u2192")} Project health updates and dashboards`);
|
|
5980
|
+
console.log(` ${pc17.cyan("\u2192")} Cross-project issue linking`);
|
|
5305
5981
|
console.log();
|
|
5306
|
-
console.log(` ${
|
|
5982
|
+
console.log(` ${pc17.bold("How to upgrade:")}`);
|
|
5307
5983
|
console.log();
|
|
5308
|
-
console.log(` Option 1: Run ${
|
|
5984
|
+
console.log(` Option 1: Run ${pc17.cyan("/flydocs-upgrade")} in your IDE`);
|
|
5309
5985
|
console.log(` Guided migration with issue transfer`);
|
|
5310
5986
|
console.log();
|
|
5311
5987
|
console.log(
|
|
5312
|
-
` Option 2: Run ${
|
|
5988
|
+
` Option 2: Run ${pc17.cyan("flydocs connect")} from terminal`
|
|
5313
5989
|
);
|
|
5314
5990
|
console.log(` Quick tier swap (no issue migration)`);
|
|
5315
5991
|
console.log();
|
|
@@ -5323,23 +5999,23 @@ var self_update_exports = {};
|
|
|
5323
5999
|
__export(self_update_exports, {
|
|
5324
6000
|
default: () => self_update_default
|
|
5325
6001
|
});
|
|
5326
|
-
import { defineCommand as
|
|
6002
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
5327
6003
|
import { execSync } from "child_process";
|
|
5328
|
-
import
|
|
6004
|
+
import pc18 from "picocolors";
|
|
5329
6005
|
var self_update_default;
|
|
5330
6006
|
var init_self_update = __esm({
|
|
5331
6007
|
"src/commands/self-update.ts"() {
|
|
5332
6008
|
"use strict";
|
|
5333
6009
|
init_constants();
|
|
5334
6010
|
init_ui();
|
|
5335
|
-
self_update_default =
|
|
6011
|
+
self_update_default = defineCommand13({
|
|
5336
6012
|
meta: {
|
|
5337
6013
|
name: "self-update",
|
|
5338
6014
|
description: "Update FlyDocs CLI to the latest version"
|
|
5339
6015
|
},
|
|
5340
6016
|
async run() {
|
|
5341
6017
|
console.log();
|
|
5342
|
-
console.log(` Updating ${
|
|
6018
|
+
console.log(` Updating ${pc18.cyan(PACKAGE_NAME)}...`);
|
|
5343
6019
|
console.log();
|
|
5344
6020
|
try {
|
|
5345
6021
|
execSync(`npm install -g ${PACKAGE_NAME}@beta`, {
|
|
@@ -5364,15 +6040,15 @@ var telemetry_exports = {};
|
|
|
5364
6040
|
__export(telemetry_exports, {
|
|
5365
6041
|
default: () => telemetry_default
|
|
5366
6042
|
});
|
|
5367
|
-
import { defineCommand as
|
|
5368
|
-
import
|
|
6043
|
+
import { defineCommand as defineCommand14 } from "citty";
|
|
6044
|
+
import pc19 from "picocolors";
|
|
5369
6045
|
var enable, disable, status, telemetry_default;
|
|
5370
6046
|
var init_telemetry2 = __esm({
|
|
5371
6047
|
"src/commands/telemetry.ts"() {
|
|
5372
6048
|
"use strict";
|
|
5373
6049
|
init_telemetry();
|
|
5374
6050
|
init_ui();
|
|
5375
|
-
enable =
|
|
6051
|
+
enable = defineCommand14({
|
|
5376
6052
|
meta: {
|
|
5377
6053
|
name: "enable",
|
|
5378
6054
|
description: "Enable anonymous usage analytics"
|
|
@@ -5387,7 +6063,7 @@ var init_telemetry2 = __esm({
|
|
|
5387
6063
|
}
|
|
5388
6064
|
}
|
|
5389
6065
|
});
|
|
5390
|
-
disable =
|
|
6066
|
+
disable = defineCommand14({
|
|
5391
6067
|
meta: {
|
|
5392
6068
|
name: "disable",
|
|
5393
6069
|
description: "Disable anonymous usage analytics"
|
|
@@ -5405,7 +6081,7 @@ var init_telemetry2 = __esm({
|
|
|
5405
6081
|
}
|
|
5406
6082
|
}
|
|
5407
6083
|
});
|
|
5408
|
-
status =
|
|
6084
|
+
status = defineCommand14({
|
|
5409
6085
|
meta: {
|
|
5410
6086
|
name: "status",
|
|
5411
6087
|
description: "Show current telemetry status"
|
|
@@ -5413,37 +6089,37 @@ var init_telemetry2 = __esm({
|
|
|
5413
6089
|
async run() {
|
|
5414
6090
|
const info = await getStatus();
|
|
5415
6091
|
console.log();
|
|
5416
|
-
console.log(
|
|
6092
|
+
console.log(pc19.bold("Telemetry Status"));
|
|
5417
6093
|
console.log();
|
|
5418
6094
|
const effectivelyEnabled = info.enabled && !info.envOverride;
|
|
5419
6095
|
console.log(
|
|
5420
|
-
` Enabled: ${effectivelyEnabled ?
|
|
6096
|
+
` Enabled: ${effectivelyEnabled ? pc19.green("yes") : pc19.yellow("no")}`
|
|
5421
6097
|
);
|
|
5422
6098
|
if (info.envOverride) {
|
|
5423
6099
|
console.log(
|
|
5424
|
-
` Env override: ${
|
|
6100
|
+
` Env override: ${pc19.yellow("FLYDOCS_TELEMETRY=0 (disabled via env)")}`
|
|
5425
6101
|
);
|
|
5426
6102
|
}
|
|
5427
6103
|
if (info.anonymousId) {
|
|
5428
|
-
console.log(` Anonymous ID: ${
|
|
6104
|
+
console.log(` Anonymous ID: ${pc19.dim(info.anonymousId)}`);
|
|
5429
6105
|
} else {
|
|
5430
6106
|
console.log(
|
|
5431
|
-
` Anonymous ID: ${
|
|
6107
|
+
` Anonymous ID: ${pc19.dim("(not yet created \u2014 generated on first run)")}`
|
|
5432
6108
|
);
|
|
5433
6109
|
}
|
|
5434
6110
|
console.log();
|
|
5435
6111
|
console.log(
|
|
5436
|
-
|
|
6112
|
+
pc19.dim(
|
|
5437
6113
|
" FlyDocs collects anonymous usage analytics to improve the CLI."
|
|
5438
6114
|
)
|
|
5439
6115
|
);
|
|
5440
6116
|
console.log(
|
|
5441
|
-
|
|
6117
|
+
pc19.dim(" No personal data, file contents, or code is ever collected.")
|
|
5442
6118
|
);
|
|
5443
6119
|
console.log();
|
|
5444
6120
|
}
|
|
5445
6121
|
});
|
|
5446
|
-
telemetry_default =
|
|
6122
|
+
telemetry_default = defineCommand14({
|
|
5447
6123
|
meta: {
|
|
5448
6124
|
name: "telemetry",
|
|
5449
6125
|
description: "Manage anonymous usage analytics (enable, disable, status)"
|
|
@@ -5459,7 +6135,7 @@ var init_telemetry2 = __esm({
|
|
|
5459
6135
|
|
|
5460
6136
|
// src/cli.ts
|
|
5461
6137
|
init_constants();
|
|
5462
|
-
import { defineCommand as
|
|
6138
|
+
import { defineCommand as defineCommand15, runMain } from "citty";
|
|
5463
6139
|
var SUB_COMMANDS = /* @__PURE__ */ new Set([
|
|
5464
6140
|
"install",
|
|
5465
6141
|
"init",
|
|
@@ -5470,6 +6146,7 @@ var SUB_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
5470
6146
|
"connect",
|
|
5471
6147
|
"auth",
|
|
5472
6148
|
"sync",
|
|
6149
|
+
"scan",
|
|
5473
6150
|
"cleanup",
|
|
5474
6151
|
"upgrade",
|
|
5475
6152
|
"self-update",
|
|
@@ -5483,7 +6160,7 @@ var firstPositional = userArgs.find((a) => !a.startsWith("-"));
|
|
|
5483
6160
|
if (!hasMetaFlag && (!firstPositional || !SUB_COMMANDS.has(firstPositional))) {
|
|
5484
6161
|
process.argv.splice(2, 0, "install");
|
|
5485
6162
|
}
|
|
5486
|
-
var main =
|
|
6163
|
+
var main = defineCommand15({
|
|
5487
6164
|
meta: {
|
|
5488
6165
|
name: CLI_NAME,
|
|
5489
6166
|
version: CLI_VERSION,
|
|
@@ -5499,6 +6176,7 @@ var main = defineCommand14({
|
|
|
5499
6176
|
connect: () => Promise.resolve().then(() => (init_connect(), connect_exports)).then((m) => m.default),
|
|
5500
6177
|
auth: () => Promise.resolve().then(() => (init_auth(), auth_exports)).then((m) => m.default),
|
|
5501
6178
|
sync: () => Promise.resolve().then(() => (init_sync(), sync_exports)).then((m) => m.default),
|
|
6179
|
+
scan: () => Promise.resolve().then(() => (init_scan(), scan_exports)).then((m) => m.default),
|
|
5502
6180
|
cleanup: () => Promise.resolve().then(() => (init_cleanup(), cleanup_exports)).then((m) => m.default),
|
|
5503
6181
|
upgrade: () => Promise.resolve().then(() => (init_upgrade(), upgrade_exports)).then((m) => m.default),
|
|
5504
6182
|
"self-update": () => Promise.resolve().then(() => (init_self_update(), self_update_exports)).then((m) => m.default),
|