@flydocs/cli 0.6.0-alpha.3 → 0.6.0-alpha.30
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 +2054 -470
- package/package.json +1 -1
- package/template/.claude/CLAUDE.md +43 -48
- package/template/.claude/agents/implementation-agent.md +1 -1
- package/template/.claude/agents/pm-agent.md +1 -1
- package/template/.claude/commands/activate.md +1 -1
- package/template/.claude/commands/attach.md +1 -1
- package/template/.claude/commands/block.md +2 -2
- package/template/.claude/commands/capture.md +1 -1
- package/template/.claude/commands/close.md +1 -1
- package/template/.claude/commands/flydocs-setup.md +359 -72
- package/template/.claude/commands/flydocs-upgrade.md +26 -27
- package/template/.claude/commands/implement.md +1 -1
- package/template/.claude/commands/knowledge.md +61 -0
- package/template/.claude/commands/new-project.md +1 -1
- package/template/.claude/commands/onboard.md +275 -0
- package/template/.claude/commands/project-update.md +1 -1
- package/template/.claude/commands/refine.md +1 -1
- package/template/.claude/commands/review.md +1 -1
- package/template/.claude/commands/start-session.md +1 -1
- package/template/.claude/commands/status.md +1 -1
- package/template/.claude/commands/validate.md +1 -1
- package/template/.claude/commands/wrap-session.md +1 -1
- package/template/.claude/hooks/auto-approve.py +212 -0
- package/template/.claude/hooks/post-pr-check.py +108 -0
- package/template/.claude/hooks/post-transition-check.py +281 -0
- package/template/.claude/hooks/prompt-submit.py +554 -0
- package/template/.claude/hooks/session-start.py +262 -0
- package/template/.claude/hooks/stop-gate.py +162 -0
- package/template/.claude/settings.json +41 -4
- package/template/.claude/skills/README.md +23 -25
- package/template/.claude/skills/flydocs-workflow/SKILL.md +134 -42
- package/template/.claude/skills/flydocs-workflow/cursor-rule.mdc +9 -8
- package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
- package/template/.claude/skills/flydocs-workflow/reference/golden-rules.md +28 -17
- package/template/.claude/skills/flydocs-workflow/reference/graph-schema.md +116 -0
- package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +120 -0
- package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
- package/template/.claude/skills/flydocs-workflow/reference/service-descriptor-schema.md +260 -0
- package/template/.claude/skills/flydocs-workflow/reference/status-workflow.md +26 -26
- package/template/.claude/skills/flydocs-workflow/scripts/_local/__init__.py +0 -0
- package/template/.claude/skills/{flydocs-local/scripts/flydocs_api.py → flydocs-workflow/scripts/_local/file_store.py} +137 -47
- package/template/.claude/skills/flydocs-workflow/scripts/flydocs_api.py +724 -0
- package/template/{.flydocs → .claude/skills/flydocs-workflow}/scripts/generate_manifest.py +4 -4
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_build.py +132 -1
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_query.py +18 -5
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_session.py +1 -10
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_update.py +4 -4
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_utils.py +2 -1
- package/template/.claude/skills/flydocs-workflow/scripts/issues.py +738 -0
- package/template/.claude/skills/flydocs-workflow/scripts/projects.py +144 -0
- package/template/.claude/skills/flydocs-workflow/scripts/pull_services.py +128 -0
- package/template/.claude/skills/flydocs-workflow/scripts/push_service.py +132 -0
- package/template/.claude/skills/flydocs-workflow/scripts/session.py +54 -0
- package/template/.claude/skills/flydocs-workflow/scripts/test_enforcement.py +225 -0
- package/template/.claude/skills/flydocs-workflow/scripts/workspace.py +902 -0
- package/template/.claude/skills/flydocs-workflow/session.md +87 -29
- package/template/.claude/skills/flydocs-workflow/stages/activate.md +18 -7
- package/template/.claude/skills/flydocs-workflow/stages/capture.md +10 -5
- package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
- package/template/.claude/skills/flydocs-workflow/stages/implement.md +33 -9
- package/template/.claude/skills/flydocs-workflow/stages/refine.md +22 -6
- package/template/.claude/skills/flydocs-workflow/stages/review.md +16 -4
- package/template/.claude/skills/flydocs-workflow/stages/validate.md +3 -1
- package/template/.claude/skills/flydocs-workflow/templates/pr/default.md +33 -0
- package/template/.cursor/agents/implementation-agent.md +1 -1
- package/template/.cursor/agents/pm-agent.md +2 -2
- package/template/.cursor/hooks.json +10 -3
- package/template/.env.example +6 -6
- package/template/.flydocs/config.json +5 -18
- package/template/.flydocs/templates/README.md +13 -14
- package/template/.flydocs/templates/bug.md +17 -153
- package/template/.flydocs/templates/chore.md +10 -98
- package/template/.flydocs/templates/feature.md +12 -158
- package/template/.flydocs/templates/idea.md +11 -111
- package/template/.flydocs/templates/quick-capture.md +4 -8
- package/template/.flydocs/version +1 -1
- package/template/AGENTS.md +44 -32
- package/template/CHANGELOG.md +37 -0
- package/template/flydocs/README.md +1 -3
- package/template/flydocs/context/project.md +6 -3
- package/template/flydocs/design-system/README.md +3 -3
- package/template/flydocs/knowledge/INDEX.md +38 -53
- package/template/flydocs/knowledge/README.md +60 -9
- package/template/flydocs/knowledge/templates/decision.md +47 -0
- package/template/flydocs/knowledge/templates/feature.md +35 -0
- package/template/flydocs/knowledge/templates/note.md +25 -0
- package/template/manifest.json +24 -20
- package/template/.claude/skills/flydocs-cloud/SKILL.md +0 -113
- package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +0 -50
- package/template/.claude/skills/flydocs-cloud/scripts/assign.py +0 -22
- package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +0 -22
- package/template/.claude/skills/flydocs-cloud/scripts/comment.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +0 -66
- package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +0 -35
- package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +0 -33
- package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +0 -39
- package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +0 -210
- package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +0 -24
- package/template/.claude/skills/flydocs-cloud/scripts/link.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +0 -44
- package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +0 -19
- package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +0 -31
- package/template/.claude/skills/flydocs-cloud/scripts/list_providers.py +0 -19
- package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +0 -19
- package/template/.claude/skills/flydocs-cloud/scripts/priority.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +0 -45
- package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +0 -68
- package/template/.claude/skills/flydocs-cloud/scripts/set_provider.py +0 -46
- package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +0 -41
- package/template/.claude/skills/flydocs-cloud/scripts/transition.py +0 -26
- package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +0 -36
- package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +0 -82
- package/template/.claude/skills/flydocs-context-graph/SKILL.md +0 -87
- package/template/.claude/skills/flydocs-context-graph/schema.md +0 -78
- package/template/.claude/skills/flydocs-context-graph/scripts/graph_context.py +0 -338
- package/template/.claude/skills/flydocs-context7/SKILL.md +0 -105
- package/template/.claude/skills/flydocs-context7/cursor-rule.mdc +0 -49
- package/template/.claude/skills/flydocs-context7/scripts/context7.py +0 -293
- package/template/.claude/skills/flydocs-estimates/SKILL.md +0 -384
- package/template/.claude/skills/flydocs-figma/SKILL.md +0 -377
- package/template/.claude/skills/flydocs-figma/references/PROMPTING.md +0 -108
- package/template/.claude/skills/flydocs-figma/references/TROUBLESHOOTING.md +0 -112
- package/template/.claude/skills/flydocs-local/SKILL.md +0 -103
- package/template/.claude/skills/flydocs-local/cursor-rule.mdc +0 -43
- package/template/.claude/skills/flydocs-local/scripts/assign.py +0 -20
- package/template/.claude/skills/flydocs-local/scripts/comment.py +0 -27
- package/template/.claude/skills/flydocs-local/scripts/create_issue.py +0 -44
- package/template/.claude/skills/flydocs-local/scripts/estimate.py +0 -37
- package/template/.claude/skills/flydocs-local/scripts/get_issue.py +0 -20
- package/template/.claude/skills/flydocs-local/scripts/link.py +0 -41
- package/template/.claude/skills/flydocs-local/scripts/list_issues.py +0 -34
- package/template/.claude/skills/flydocs-local/scripts/priority.py +0 -37
- package/template/.claude/skills/flydocs-local/scripts/project_update.py +0 -67
- package/template/.claude/skills/flydocs-local/scripts/status_summary.py +0 -16
- package/template/.claude/skills/flydocs-local/scripts/transition.py +0 -24
- package/template/.claude/skills/flydocs-local/scripts/update_description.py +0 -35
- package/template/.claude/skills/flydocs-local/scripts/update_issue.py +0 -84
- package/template/.flydocs/hooks/auto-approve.py +0 -71
- package/template/.flydocs/hooks/prompt-submit.py +0 -277
- package/template/.flydocs/scripts/skill_manager.py +0 -541
- /package/template/{.flydocs → .claude}/hooks/post-edit.py +0 -0
- /package/template/.claude/skills/{flydocs-estimates/references → flydocs-workflow/reference}/provider-costs.md +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{bug.md → issues/bug.md} +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{chore.md → issues/chore.md} +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{feature.md → issues/feature.md} +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{idea.md → issues/idea.md} +0 -0
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ var CLI_VERSION, CLI_NAME, PACKAGE_NAME, POSTHOG_API_KEY;
|
|
|
15
15
|
var init_constants = __esm({
|
|
16
16
|
"src/lib/constants.ts"() {
|
|
17
17
|
"use strict";
|
|
18
|
-
CLI_VERSION = "0.6.0-alpha.
|
|
18
|
+
CLI_VERSION = "0.6.0-alpha.30";
|
|
19
19
|
CLI_NAME = "flydocs";
|
|
20
20
|
PACKAGE_NAME = "@flydocs/cli";
|
|
21
21
|
POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
|
|
@@ -50,6 +50,7 @@ async function ensureDirectories(targetDir, tier) {
|
|
|
50
50
|
"flydocs/knowledge/decisions",
|
|
51
51
|
"flydocs/knowledge/notes",
|
|
52
52
|
"flydocs/knowledge/product",
|
|
53
|
+
"flydocs/knowledge/templates",
|
|
53
54
|
"flydocs/design-system"
|
|
54
55
|
];
|
|
55
56
|
if (tier === "local") {
|
|
@@ -135,8 +136,8 @@ var init_template = __esm({
|
|
|
135
136
|
|
|
136
137
|
// src/lib/ui.ts
|
|
137
138
|
import pc2 from "picocolors";
|
|
138
|
-
function shadow(
|
|
139
|
-
return `\x1B[38;2;55;45;70m${
|
|
139
|
+
function shadow(text6) {
|
|
140
|
+
return `\x1B[38;2;55;45;70m${text6}\x1B[0m`;
|
|
140
141
|
}
|
|
141
142
|
function renderBannerBlock() {
|
|
142
143
|
const height = BANNER_ROWS.length;
|
|
@@ -267,12 +268,70 @@ var init_ui = __esm({
|
|
|
267
268
|
}
|
|
268
269
|
});
|
|
269
270
|
|
|
271
|
+
// src/lib/types.ts
|
|
272
|
+
function isConfigV2(config) {
|
|
273
|
+
return config.configFormat === 2;
|
|
274
|
+
}
|
|
275
|
+
var init_types = __esm({
|
|
276
|
+
"src/lib/types.ts"() {
|
|
277
|
+
"use strict";
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// src/lib/config-integrity.ts
|
|
282
|
+
import { createHash } from "crypto";
|
|
283
|
+
function computeConfigHash(config) {
|
|
284
|
+
const { configHash: _, ...rest } = config;
|
|
285
|
+
const serialized = JSON.stringify(rest, Object.keys(rest).sort());
|
|
286
|
+
return `sha256:${createHash("sha256").update(serialized, "utf-8").digest("hex")}`;
|
|
287
|
+
}
|
|
288
|
+
function validateConfigIntegrity(config) {
|
|
289
|
+
if (!config.configHash) return false;
|
|
290
|
+
const computed = computeConfigHash(config);
|
|
291
|
+
return computed === config.configHash;
|
|
292
|
+
}
|
|
293
|
+
function checkConfigIntegrity(config) {
|
|
294
|
+
if (!config.configHash) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
const valid = validateConfigIntegrity(config);
|
|
298
|
+
if (!valid) {
|
|
299
|
+
printWarning(
|
|
300
|
+
"Config modified locally \u2014 will restore from server on next sync."
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
return valid;
|
|
304
|
+
}
|
|
305
|
+
function applyConfigHash(config) {
|
|
306
|
+
const hash = computeConfigHash(config);
|
|
307
|
+
return { ...config, configHash: hash };
|
|
308
|
+
}
|
|
309
|
+
var init_config_integrity = __esm({
|
|
310
|
+
"src/lib/config-integrity.ts"() {
|
|
311
|
+
"use strict";
|
|
312
|
+
init_ui();
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
270
316
|
// src/lib/config.ts
|
|
271
317
|
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
272
318
|
import { join as join3 } from "path";
|
|
273
|
-
async function
|
|
319
|
+
async function readAnyConfig(dir) {
|
|
274
320
|
const content = await readFile2(join3(dir, ".flydocs", "config.json"), "utf-8");
|
|
275
|
-
|
|
321
|
+
const config = JSON.parse(content);
|
|
322
|
+
if (isConfigV2(config)) {
|
|
323
|
+
checkConfigIntegrity(config);
|
|
324
|
+
}
|
|
325
|
+
return config;
|
|
326
|
+
}
|
|
327
|
+
async function readConfig(dir) {
|
|
328
|
+
const config = await readAnyConfig(dir);
|
|
329
|
+
if (isConfigV2(config)) {
|
|
330
|
+
throw new Error(
|
|
331
|
+
"This command requires v1 config format. Run `flydocs sync` to use v2 commands."
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return config;
|
|
276
335
|
}
|
|
277
336
|
async function writeConfig(dir, config) {
|
|
278
337
|
const content = JSON.stringify(config, null, 2) + "\n";
|
|
@@ -292,12 +351,13 @@ function extractPreservedValues(config) {
|
|
|
292
351
|
return {
|
|
293
352
|
tier: config.tier,
|
|
294
353
|
setupComplete: config.setupComplete ?? false,
|
|
295
|
-
|
|
354
|
+
workspaceId: config.workspaceId ?? null,
|
|
355
|
+
configVersion: config.configVersion,
|
|
296
356
|
workspace: config.workspace ?? {},
|
|
297
357
|
issueLabels: config.issueLabels ?? {},
|
|
298
|
-
statusMapping: config.statusMapping ?? {},
|
|
299
358
|
detectedStack: config.detectedStack ?? {},
|
|
300
359
|
skills: config.skills ?? {},
|
|
360
|
+
topology: config.topology,
|
|
301
361
|
designSystem: config.designSystem ?? null,
|
|
302
362
|
aiLabor: config.aiLabor ?? {}
|
|
303
363
|
};
|
|
@@ -311,11 +371,9 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
|
|
|
311
371
|
config.version = version;
|
|
312
372
|
config.tier = tierFlag ?? preserved.tier;
|
|
313
373
|
config.setupComplete = preserved.setupComplete;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
config.provider.teamId = preserved.providerTeamId;
|
|
374
|
+
config.workspaceId = preserved.workspaceId;
|
|
375
|
+
if (preserved.configVersion !== void 0) {
|
|
376
|
+
config.configVersion = preserved.configVersion;
|
|
319
377
|
}
|
|
320
378
|
if (Object.keys(preserved.workspace).length > 0) {
|
|
321
379
|
config.workspace = preserved.workspace;
|
|
@@ -323,15 +381,15 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
|
|
|
323
381
|
if (Object.keys(preserved.issueLabels).length > 0) {
|
|
324
382
|
config.issueLabels = preserved.issueLabels;
|
|
325
383
|
}
|
|
326
|
-
if (Object.keys(preserved.statusMapping).length > 0) {
|
|
327
|
-
config.statusMapping = preserved.statusMapping;
|
|
328
|
-
}
|
|
329
384
|
if (Object.keys(preserved.detectedStack).length > 0) {
|
|
330
385
|
config.detectedStack = preserved.detectedStack;
|
|
331
386
|
}
|
|
332
387
|
if (Object.keys(preserved.skills).length > 0) {
|
|
333
388
|
config.skills = preserved.skills;
|
|
334
389
|
}
|
|
390
|
+
if (preserved.topology !== void 0) {
|
|
391
|
+
config.topology = preserved.topology;
|
|
392
|
+
}
|
|
335
393
|
if (preserved.designSystem !== null) {
|
|
336
394
|
config.designSystem = preserved.designSystem;
|
|
337
395
|
}
|
|
@@ -343,137 +401,80 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
|
|
|
343
401
|
var init_config = __esm({
|
|
344
402
|
"src/lib/config.ts"() {
|
|
345
403
|
"use strict";
|
|
404
|
+
init_types();
|
|
405
|
+
init_config_integrity();
|
|
346
406
|
}
|
|
347
407
|
});
|
|
348
408
|
|
|
349
409
|
// src/lib/skills.ts
|
|
350
410
|
import { join as join4 } from "path";
|
|
351
|
-
async function installOwnedSkills(templateDir, targetDir,
|
|
411
|
+
async function installOwnedSkills(templateDir, targetDir, _tier) {
|
|
352
412
|
const skillsDir = join4(targetDir, ".claude", "skills");
|
|
353
413
|
const templateSkillsDir = join4(templateDir, ".claude", "skills");
|
|
354
414
|
await replaceDirectory(
|
|
355
|
-
join4(templateSkillsDir,
|
|
356
|
-
join4(skillsDir,
|
|
357
|
-
);
|
|
358
|
-
const activeMech = MECHANISM_SKILLS[tier];
|
|
359
|
-
const inactiveMech = tier === "local" ? MECHANISM_SKILLS.cloud : MECHANISM_SKILLS.local;
|
|
360
|
-
await replaceDirectory(
|
|
361
|
-
join4(templateSkillsDir, activeMech),
|
|
362
|
-
join4(skillsDir, activeMech)
|
|
415
|
+
join4(templateSkillsDir, OWNED_SKILL),
|
|
416
|
+
join4(skillsDir, OWNED_SKILL)
|
|
363
417
|
);
|
|
364
|
-
const { rm: rm6 } = await import("fs/promises");
|
|
365
|
-
const inactivePath = join4(skillsDir, inactiveMech);
|
|
366
|
-
if (await pathExists(inactivePath)) {
|
|
367
|
-
await rm6(inactivePath, { recursive: true, force: true });
|
|
368
|
-
}
|
|
369
|
-
for (const skill of CORE_SKILLS) {
|
|
370
|
-
if (skill === "flydocs-workflow") continue;
|
|
371
|
-
const src = join4(templateSkillsDir, skill);
|
|
372
|
-
if (await pathExists(src)) {
|
|
373
|
-
await replaceDirectory(src, join4(skillsDir, skill));
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
if (tier === "cloud") {
|
|
377
|
-
for (const skill of CLOUD_ONLY_SKILLS) {
|
|
378
|
-
const src = join4(templateSkillsDir, skill);
|
|
379
|
-
if (await pathExists(src)) {
|
|
380
|
-
await replaceDirectory(src, join4(skillsDir, skill));
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
418
|
const readmeSrc = join4(templateSkillsDir, "README.md");
|
|
385
419
|
if (await pathExists(readmeSrc)) {
|
|
386
420
|
await copyFile(readmeSrc, join4(skillsDir, "README.md"));
|
|
387
421
|
}
|
|
388
422
|
}
|
|
389
|
-
async function replaceOwnedSkills(templateDir, targetDir,
|
|
423
|
+
async function replaceOwnedSkills(templateDir, targetDir, _tier) {
|
|
390
424
|
const skillsDir = join4(targetDir, ".claude", "skills");
|
|
391
425
|
const templateSkillsDir = join4(templateDir, ".claude", "skills");
|
|
392
|
-
const { rm:
|
|
393
|
-
for (const skill of CORE_SKILLS) {
|
|
394
|
-
const src = join4(templateSkillsDir, skill);
|
|
395
|
-
if (await pathExists(src)) {
|
|
396
|
-
await replaceDirectory(src, join4(skillsDir, skill));
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
for (const skill of CLOUD_ONLY_SKILLS) {
|
|
400
|
-
const dest = join4(skillsDir, skill);
|
|
401
|
-
if (tier === "cloud") {
|
|
402
|
-
const src = join4(templateSkillsDir, skill);
|
|
403
|
-
if (await pathExists(src)) {
|
|
404
|
-
await replaceDirectory(src, dest);
|
|
405
|
-
}
|
|
406
|
-
} else if (await pathExists(dest)) {
|
|
407
|
-
await rm6(dest, { recursive: true, force: true });
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
const activeMech = MECHANISM_SKILLS[tier];
|
|
411
|
-
const inactiveMech = tier === "local" ? MECHANISM_SKILLS.cloud : MECHANISM_SKILLS.local;
|
|
412
|
-
const inactivePath = join4(skillsDir, inactiveMech);
|
|
413
|
-
if (await pathExists(inactivePath)) {
|
|
414
|
-
await rm6(inactivePath, { recursive: true, force: true });
|
|
415
|
-
}
|
|
426
|
+
const { rm: rm7 } = await import("fs/promises");
|
|
416
427
|
await replaceDirectory(
|
|
417
|
-
join4(templateSkillsDir,
|
|
418
|
-
join4(skillsDir,
|
|
428
|
+
join4(templateSkillsDir, OWNED_SKILL),
|
|
429
|
+
join4(skillsDir, OWNED_SKILL)
|
|
419
430
|
);
|
|
431
|
+
for (const legacy of LEGACY_SKILLS) {
|
|
432
|
+
const legacyPath = join4(skillsDir, legacy);
|
|
433
|
+
if (await pathExists(legacyPath)) {
|
|
434
|
+
await rm7(legacyPath, { recursive: true, force: true });
|
|
435
|
+
}
|
|
436
|
+
}
|
|
420
437
|
const readmeSrc = join4(templateSkillsDir, "README.md");
|
|
421
438
|
if (await pathExists(readmeSrc)) {
|
|
422
439
|
await copyFile(readmeSrc, join4(skillsDir, "README.md"));
|
|
423
440
|
}
|
|
424
441
|
}
|
|
425
442
|
async function copyCursorRules(targetDir) {
|
|
426
|
-
const { mkdir:
|
|
443
|
+
const { mkdir: mkdir12 } = await import("fs/promises");
|
|
427
444
|
const rulesDir = join4(targetDir, ".cursor", "rules");
|
|
428
|
-
await
|
|
445
|
+
await mkdir12(rulesDir, { recursive: true });
|
|
429
446
|
const workflowRule = join4(
|
|
430
447
|
targetDir,
|
|
431
448
|
".claude",
|
|
432
449
|
"skills",
|
|
433
|
-
|
|
450
|
+
OWNED_SKILL,
|
|
434
451
|
"cursor-rule.mdc"
|
|
435
452
|
);
|
|
436
453
|
if (await pathExists(workflowRule)) {
|
|
437
454
|
await copyFile(workflowRule, join4(rulesDir, "flydocs-workflow.mdc"));
|
|
438
455
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
mech,
|
|
445
|
-
"cursor-rule.mdc"
|
|
446
|
-
);
|
|
447
|
-
if (await pathExists(mechRule)) {
|
|
448
|
-
await copyFile(mechRule, join4(rulesDir, "flydocs-mechanism.mdc"));
|
|
456
|
+
const { rm: rm7 } = await import("fs/promises");
|
|
457
|
+
for (const legacy of ["flydocs-mechanism.mdc", "flydocs-context7.mdc"]) {
|
|
458
|
+
const legacyRule = join4(rulesDir, legacy);
|
|
459
|
+
if (await pathExists(legacyRule)) {
|
|
460
|
+
await rm7(legacyRule, { force: true });
|
|
449
461
|
}
|
|
450
462
|
}
|
|
451
|
-
const context7Rule = join4(
|
|
452
|
-
targetDir,
|
|
453
|
-
".claude",
|
|
454
|
-
"skills",
|
|
455
|
-
"flydocs-context7",
|
|
456
|
-
"cursor-rule.mdc"
|
|
457
|
-
);
|
|
458
|
-
if (await pathExists(context7Rule)) {
|
|
459
|
-
await copyFile(context7Rule, join4(rulesDir, "flydocs-context7.mdc"));
|
|
460
|
-
}
|
|
461
463
|
}
|
|
462
|
-
var
|
|
464
|
+
var OWNED_SKILL, LEGACY_SKILLS;
|
|
463
465
|
var init_skills = __esm({
|
|
464
466
|
"src/lib/skills.ts"() {
|
|
465
467
|
"use strict";
|
|
466
468
|
init_fs_ops();
|
|
467
|
-
|
|
468
|
-
|
|
469
|
+
OWNED_SKILL = "flydocs-workflow";
|
|
470
|
+
LEGACY_SKILLS = [
|
|
471
|
+
"flydocs-cloud",
|
|
472
|
+
"flydocs-local",
|
|
469
473
|
"flydocs-context-graph",
|
|
470
|
-
"flydocs-context7"
|
|
474
|
+
"flydocs-context7",
|
|
475
|
+
"flydocs-figma",
|
|
476
|
+
"flydocs-estimates"
|
|
471
477
|
];
|
|
472
|
-
CLOUD_ONLY_SKILLS = ["flydocs-figma", "flydocs-estimates"];
|
|
473
|
-
MECHANISM_SKILLS = {
|
|
474
|
-
local: "flydocs-local",
|
|
475
|
-
cloud: "flydocs-cloud"
|
|
476
|
-
};
|
|
477
478
|
}
|
|
478
479
|
});
|
|
479
480
|
|
|
@@ -715,7 +716,9 @@ import { join as join7 } from "path";
|
|
|
715
716
|
async function runManifestGeneration(targetDir) {
|
|
716
717
|
const scriptPath = join7(
|
|
717
718
|
targetDir,
|
|
718
|
-
".
|
|
719
|
+
".claude",
|
|
720
|
+
"skills",
|
|
721
|
+
"flydocs-workflow",
|
|
719
722
|
"scripts",
|
|
720
723
|
"generate_manifest.py"
|
|
721
724
|
);
|
|
@@ -735,7 +738,7 @@ async function runContextGraphBuild(targetDir) {
|
|
|
735
738
|
targetDir,
|
|
736
739
|
".claude",
|
|
737
740
|
"skills",
|
|
738
|
-
"flydocs-
|
|
741
|
+
"flydocs-workflow",
|
|
739
742
|
"scripts",
|
|
740
743
|
"graph_build.py"
|
|
741
744
|
);
|
|
@@ -772,8 +775,8 @@ function flushFrontmatterValue(mode, lines) {
|
|
|
772
775
|
return lines.join("\n").trim();
|
|
773
776
|
}
|
|
774
777
|
}
|
|
775
|
-
function parseFrontmatter(
|
|
776
|
-
const match =
|
|
778
|
+
function parseFrontmatter(text6) {
|
|
779
|
+
const match = text6.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
777
780
|
if (!match) return null;
|
|
778
781
|
const block = match[1];
|
|
779
782
|
const result = {};
|
|
@@ -1336,10 +1339,19 @@ async function scanDeprecated(targetDir) {
|
|
|
1336
1339
|
found.push(dir);
|
|
1337
1340
|
}
|
|
1338
1341
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
if (await pathExists(
|
|
1342
|
-
found.push(
|
|
1342
|
+
const foundFlydocsDirs = /* @__PURE__ */ new Set();
|
|
1343
|
+
for (const dir of DEPRECATED_FLYDOCS_DIRS) {
|
|
1344
|
+
if (await pathExists(join10(targetDir, dir))) {
|
|
1345
|
+
found.push(dir);
|
|
1346
|
+
foundFlydocsDirs.add(dir);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
if (!foundFlydocsDirs.has(".flydocs/hooks")) {
|
|
1350
|
+
for (const hook of DEPRECATED_HOOKS) {
|
|
1351
|
+
const p = join10(targetDir, ".flydocs", "hooks", hook);
|
|
1352
|
+
if (await pathExists(p)) {
|
|
1353
|
+
found.push(`.flydocs/hooks/${hook}`);
|
|
1354
|
+
}
|
|
1343
1355
|
}
|
|
1344
1356
|
}
|
|
1345
1357
|
for (const rule of DEPRECATED_CURSOR_RULES) {
|
|
@@ -1424,7 +1436,7 @@ async function promptCleanup(targetDir, paths) {
|
|
|
1424
1436
|
printStatus(`Deleted: ${p}`);
|
|
1425
1437
|
}
|
|
1426
1438
|
}
|
|
1427
|
-
var DEPRECATED_DIRS, DEPRECATED_FILES, DEPRECATED_SKILLS, DEPRECATED_RULES_DIR, DEPRECATED_HOOKS, DEPRECATED_CURSOR_RULES, DEPRECATED_CURSOR_RULE_DIRS, DEPRECATED_COMMANDS;
|
|
1439
|
+
var DEPRECATED_DIRS, DEPRECATED_FILES, DEPRECATED_SKILLS, DEPRECATED_RULES_DIR, DEPRECATED_FLYDOCS_DIRS, DEPRECATED_HOOKS, DEPRECATED_CURSOR_RULES, DEPRECATED_CURSOR_RULE_DIRS, DEPRECATED_COMMANDS;
|
|
1428
1440
|
var init_deprecated = __esm({
|
|
1429
1441
|
"src/lib/deprecated.ts"() {
|
|
1430
1442
|
"use strict";
|
|
@@ -1440,10 +1452,14 @@ var init_deprecated = __esm({
|
|
|
1440
1452
|
"figma-mcp"
|
|
1441
1453
|
];
|
|
1442
1454
|
DEPRECATED_RULES_DIR = [".flydocs/rules"];
|
|
1455
|
+
DEPRECATED_FLYDOCS_DIRS = [".flydocs/hooks", ".flydocs/scripts"];
|
|
1443
1456
|
DEPRECATED_HOOKS = [
|
|
1444
1457
|
"linear-auto-approve.py",
|
|
1445
1458
|
"session-end.py",
|
|
1446
|
-
"prefer-scripts.py"
|
|
1459
|
+
"prefer-scripts.py",
|
|
1460
|
+
"auto-approve.py",
|
|
1461
|
+
"post-edit.py",
|
|
1462
|
+
"prompt-submit.py"
|
|
1447
1463
|
];
|
|
1448
1464
|
DEPRECATED_CURSOR_RULES = [
|
|
1449
1465
|
"designer-agent.mdc",
|
|
@@ -1490,8 +1506,9 @@ async function ensureGitignore(targetDir) {
|
|
|
1490
1506
|
async function migrateGitignore(targetDir) {
|
|
1491
1507
|
const gitignorePath = join11(targetDir, ".gitignore");
|
|
1492
1508
|
if (!await pathExists(gitignorePath)) return;
|
|
1493
|
-
|
|
1494
|
-
|
|
1509
|
+
let content = await readFile6(gitignorePath, "utf-8");
|
|
1510
|
+
for (const entry of FLYDOCS_GITIGNORE_ENTRIES) {
|
|
1511
|
+
if (content.includes(entry)) continue;
|
|
1495
1512
|
if (content.includes("# FlyDocs")) {
|
|
1496
1513
|
const lines = content.split("\n");
|
|
1497
1514
|
const flyDocsIdx = lines.findIndex((l) => l.trim() === "# FlyDocs");
|
|
@@ -1500,37 +1517,69 @@ async function migrateGitignore(targetDir) {
|
|
|
1500
1517
|
while (insertIdx < lines.length && lines[insertIdx].trim() !== "" && !lines[insertIdx].startsWith("#")) {
|
|
1501
1518
|
insertIdx++;
|
|
1502
1519
|
}
|
|
1503
|
-
lines.splice(insertIdx, 0,
|
|
1504
|
-
|
|
1520
|
+
lines.splice(insertIdx, 0, entry);
|
|
1521
|
+
content = lines.join("\n");
|
|
1522
|
+
await writeFile4(gitignorePath, content, "utf-8");
|
|
1505
1523
|
} else {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
);
|
|
1524
|
+
content += `
|
|
1525
|
+
${entry}
|
|
1526
|
+
`;
|
|
1527
|
+
await writeFile4(gitignorePath, content, "utf-8");
|
|
1511
1528
|
}
|
|
1512
1529
|
} else {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
);
|
|
1530
|
+
content += `
|
|
1531
|
+
${entry}
|
|
1532
|
+
`;
|
|
1533
|
+
await writeFile4(gitignorePath, content, "utf-8");
|
|
1518
1534
|
}
|
|
1519
|
-
printStatus(
|
|
1535
|
+
printStatus(`Added ${entry} to .gitignore`);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
async function ensurePlatformIgnores(targetDir) {
|
|
1539
|
+
for (const filename of PLATFORM_IGNORE_FILES) {
|
|
1540
|
+
const filePath = join11(targetDir, filename);
|
|
1541
|
+
if (!await pathExists(filePath)) continue;
|
|
1542
|
+
const content = await readFile6(filePath, "utf-8");
|
|
1543
|
+
if (content.includes("# FlyDocs")) continue;
|
|
1544
|
+
const section = "\n# FlyDocs \u2014 managed by flydocs-cli, do not edit this section\n" + FLYDOCS_DEPLOY_EXCLUSIONS.join("\n") + "\n";
|
|
1545
|
+
await appendFile(filePath, section, "utf-8");
|
|
1546
|
+
printStatus(`Added FlyDocs exclusions to ${filename}`);
|
|
1520
1547
|
}
|
|
1521
1548
|
}
|
|
1522
|
-
var FLYDOCS_GITIGNORE_ENTRIES, FULL_GITIGNORE_TEMPLATE;
|
|
1549
|
+
var PLATFORM_IGNORE_FILES, FLYDOCS_DEPLOY_EXCLUSIONS, FLYDOCS_GITIGNORE_ENTRIES, FULL_GITIGNORE_TEMPLATE;
|
|
1523
1550
|
var init_gitignore = __esm({
|
|
1524
1551
|
"src/lib/gitignore.ts"() {
|
|
1525
1552
|
"use strict";
|
|
1526
1553
|
init_fs_ops();
|
|
1527
1554
|
init_ui();
|
|
1555
|
+
PLATFORM_IGNORE_FILES = [
|
|
1556
|
+
".gcloudignore",
|
|
1557
|
+
".dockerignore",
|
|
1558
|
+
".vercelignore",
|
|
1559
|
+
".npmignore",
|
|
1560
|
+
".cfignore",
|
|
1561
|
+
".ebignore",
|
|
1562
|
+
".funcignore"
|
|
1563
|
+
];
|
|
1564
|
+
FLYDOCS_DEPLOY_EXCLUSIONS = [
|
|
1565
|
+
".flydocs/",
|
|
1566
|
+
".claude/",
|
|
1567
|
+
".cursor/",
|
|
1568
|
+
"flydocs/",
|
|
1569
|
+
".flydocs-workspace.json",
|
|
1570
|
+
"AGENTS.md",
|
|
1571
|
+
".cursorrules"
|
|
1572
|
+
];
|
|
1528
1573
|
FLYDOCS_GITIGNORE_ENTRIES = [
|
|
1529
1574
|
".env",
|
|
1530
1575
|
".env.local",
|
|
1531
1576
|
".flydocs/session.json",
|
|
1532
1577
|
".flydocs/logs/",
|
|
1533
1578
|
".flydocs/backup-*/",
|
|
1579
|
+
".flydocs/me.json",
|
|
1580
|
+
".flydocs/validation-cache.json",
|
|
1581
|
+
".flydocs/integrity-cache.json",
|
|
1582
|
+
".flydocs/session/",
|
|
1534
1583
|
"flydocs/context/graph.json"
|
|
1535
1584
|
];
|
|
1536
1585
|
FULL_GITIGNORE_TEMPLATE = `# Environment
|
|
@@ -1542,6 +1591,10 @@ var init_gitignore = __esm({
|
|
|
1542
1591
|
.flydocs/session.json
|
|
1543
1592
|
.flydocs/logs/
|
|
1544
1593
|
.flydocs/backup-*/
|
|
1594
|
+
.flydocs/me.json
|
|
1595
|
+
.flydocs/validation-cache.json
|
|
1596
|
+
.flydocs/integrity-cache.json
|
|
1597
|
+
.flydocs/session/
|
|
1545
1598
|
flydocs/context/graph.json
|
|
1546
1599
|
|
|
1547
1600
|
# Dependencies
|
|
@@ -1887,6 +1940,43 @@ async function validateRelayKey(apiKey) {
|
|
|
1887
1940
|
if (!data.valid) return { valid: false };
|
|
1888
1941
|
return { valid: true, org: data.org ?? "your organization" };
|
|
1889
1942
|
}
|
|
1943
|
+
async function fetchWorkspaces(apiKey) {
|
|
1944
|
+
const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
|
|
1945
|
+
const response = await fetch(`${baseUrl}/auth/workspaces`, {
|
|
1946
|
+
method: "GET",
|
|
1947
|
+
headers: {
|
|
1948
|
+
Authorization: `Bearer ${apiKey}`
|
|
1949
|
+
},
|
|
1950
|
+
signal: AbortSignal.timeout(15e3)
|
|
1951
|
+
});
|
|
1952
|
+
if (!response.ok) {
|
|
1953
|
+
throw new Error(`Failed to fetch workspaces: ${response.status}`);
|
|
1954
|
+
}
|
|
1955
|
+
const data = await response.json();
|
|
1956
|
+
return data;
|
|
1957
|
+
}
|
|
1958
|
+
async function fetchMe(apiKey) {
|
|
1959
|
+
const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
|
|
1960
|
+
const response = await fetch(`${baseUrl}/auth/me`, {
|
|
1961
|
+
method: "GET",
|
|
1962
|
+
headers: {
|
|
1963
|
+
Authorization: `Bearer ${apiKey}`
|
|
1964
|
+
},
|
|
1965
|
+
signal: AbortSignal.timeout(15e3)
|
|
1966
|
+
});
|
|
1967
|
+
if (!response.ok) {
|
|
1968
|
+
throw new Error(`Failed to fetch user identity: ${response.status}`);
|
|
1969
|
+
}
|
|
1970
|
+
const data = await response.json();
|
|
1971
|
+
return {
|
|
1972
|
+
displayName: data.displayName ?? null,
|
|
1973
|
+
email: data.email ?? null,
|
|
1974
|
+
providerId: data.providerId ?? null,
|
|
1975
|
+
provider: data.provider ?? null,
|
|
1976
|
+
providerIdentities: data.providerIdentities ?? [],
|
|
1977
|
+
preferences: data.preferences ?? {}
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1890
1980
|
async function validateLinearKey(apiKey) {
|
|
1891
1981
|
const response = await fetch("https://api.linear.app/graphql", {
|
|
1892
1982
|
method: "POST",
|
|
@@ -1934,6 +2024,36 @@ var init_api_key = __esm({
|
|
|
1934
2024
|
}
|
|
1935
2025
|
});
|
|
1936
2026
|
|
|
2027
|
+
// src/lib/integrity.ts
|
|
2028
|
+
import { readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
|
|
2029
|
+
import { join as join15 } from "path";
|
|
2030
|
+
async function generateIntegrity(targetDir, version) {
|
|
2031
|
+
const manifestPath = join15(targetDir, ".flydocs", "manifest.json");
|
|
2032
|
+
if (!await pathExists(manifestPath)) return;
|
|
2033
|
+
const manifest = JSON.parse(await readFile11(manifestPath, "utf-8"));
|
|
2034
|
+
const ownership = manifest.ownership;
|
|
2035
|
+
if (!ownership) return;
|
|
2036
|
+
const ownedDirs = ownership.owned_directories?.paths ?? [];
|
|
2037
|
+
const ownedFiles = ownership.owned_files?.paths ?? [];
|
|
2038
|
+
const integrity = {
|
|
2039
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2040
|
+
version,
|
|
2041
|
+
ownedFiles,
|
|
2042
|
+
ownedDirectories: ownedDirs
|
|
2043
|
+
};
|
|
2044
|
+
await writeFile8(
|
|
2045
|
+
join15(targetDir, ".flydocs", "integrity.json"),
|
|
2046
|
+
JSON.stringify(integrity, null, 2) + "\n",
|
|
2047
|
+
"utf-8"
|
|
2048
|
+
);
|
|
2049
|
+
}
|
|
2050
|
+
var init_integrity = __esm({
|
|
2051
|
+
"src/lib/integrity.ts"() {
|
|
2052
|
+
"use strict";
|
|
2053
|
+
init_fs_ops();
|
|
2054
|
+
}
|
|
2055
|
+
});
|
|
2056
|
+
|
|
1937
2057
|
// src/commands/install.ts
|
|
1938
2058
|
var install_exports = {};
|
|
1939
2059
|
__export(install_exports, {
|
|
@@ -1941,8 +2061,8 @@ __export(install_exports, {
|
|
|
1941
2061
|
});
|
|
1942
2062
|
import { defineCommand } from "citty";
|
|
1943
2063
|
import { resolve as resolve2 } from "path";
|
|
1944
|
-
import { join as
|
|
1945
|
-
import { mkdir as mkdir7 } from "fs/promises";
|
|
2064
|
+
import { join as join16 } from "path";
|
|
2065
|
+
import { mkdir as mkdir7, writeFile as writeFile9 } from "fs/promises";
|
|
1946
2066
|
import { confirm as confirm2, select, text, isCancel as isCancel3, cancel as cancel2 } from "@clack/prompts";
|
|
1947
2067
|
import pc6 from "picocolors";
|
|
1948
2068
|
var install_default;
|
|
@@ -1963,6 +2083,7 @@ var init_install = __esm({
|
|
|
1963
2083
|
init_update_check();
|
|
1964
2084
|
init_telemetry();
|
|
1965
2085
|
init_api_key();
|
|
2086
|
+
init_integrity();
|
|
1966
2087
|
install_default = defineCommand({
|
|
1967
2088
|
meta: {
|
|
1968
2089
|
name: "install",
|
|
@@ -2024,7 +2145,7 @@ var init_install = __esm({
|
|
|
2024
2145
|
process.exit(1);
|
|
2025
2146
|
}
|
|
2026
2147
|
tier = args.tier;
|
|
2027
|
-
} else if (await pathExists(
|
|
2148
|
+
} else if (await pathExists(join16(targetDir, ".flydocs", "config.json"))) {
|
|
2028
2149
|
try {
|
|
2029
2150
|
const existing = await readConfig(targetDir);
|
|
2030
2151
|
if (existing.tier) {
|
|
@@ -2046,7 +2167,7 @@ var init_install = __esm({
|
|
|
2046
2167
|
{
|
|
2047
2168
|
value: "cloud",
|
|
2048
2169
|
label: "Cloud (managed)",
|
|
2049
|
-
hint: "Sync with Linear
|
|
2170
|
+
hint: "Sync with Linear, Jira, and more"
|
|
2050
2171
|
}
|
|
2051
2172
|
]
|
|
2052
2173
|
});
|
|
@@ -2073,6 +2194,9 @@ var init_install = __esm({
|
|
|
2073
2194
|
}
|
|
2074
2195
|
printInfo(`Tier: ${tier}`);
|
|
2075
2196
|
await capture("install_tier_selected", { tier });
|
|
2197
|
+
let selectedWorkspaceId = null;
|
|
2198
|
+
let selectedWorkspaceName = null;
|
|
2199
|
+
let apiKey = null;
|
|
2076
2200
|
if (tier === "cloud") {
|
|
2077
2201
|
console.log();
|
|
2078
2202
|
console.log(` ${pc6.bold("Connect to FlyDocs Cloud")}`);
|
|
@@ -2096,7 +2220,7 @@ var init_install = __esm({
|
|
|
2096
2220
|
cancel2("Installation cancelled.");
|
|
2097
2221
|
process.exit(0);
|
|
2098
2222
|
}
|
|
2099
|
-
|
|
2223
|
+
apiKey = keyInput.trim();
|
|
2100
2224
|
printInfo("Validating API key...");
|
|
2101
2225
|
try {
|
|
2102
2226
|
const result = await validateRelayKey(apiKey);
|
|
@@ -2114,12 +2238,78 @@ var init_install = __esm({
|
|
|
2114
2238
|
}
|
|
2115
2239
|
const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
|
|
2116
2240
|
printStatus(`API key stored in ${pc6.dim(envFile)}`);
|
|
2241
|
+
console.log();
|
|
2242
|
+
printInfo("Fetching workspaces...");
|
|
2243
|
+
try {
|
|
2244
|
+
const workspaces = await fetchWorkspaces(apiKey);
|
|
2245
|
+
if (workspaces.length === 0) {
|
|
2246
|
+
printError(
|
|
2247
|
+
"No workspaces found. Create a workspace in the FlyDocs dashboard first, then re-run install."
|
|
2248
|
+
);
|
|
2249
|
+
process.exit(1);
|
|
2250
|
+
}
|
|
2251
|
+
if (workspaces.length === 1) {
|
|
2252
|
+
selectedWorkspaceId = workspaces[0].id;
|
|
2253
|
+
selectedWorkspaceName = workspaces[0].name;
|
|
2254
|
+
printStatus(
|
|
2255
|
+
`Workspace: ${pc6.bold(selectedWorkspaceName)} (auto-selected)`
|
|
2256
|
+
);
|
|
2257
|
+
} else {
|
|
2258
|
+
const workspaceChoice = await select({
|
|
2259
|
+
message: "Select a workspace",
|
|
2260
|
+
options: workspaces.map((ws) => ({
|
|
2261
|
+
value: ws.id,
|
|
2262
|
+
label: `${ws.name} (${ws.provider.type})`,
|
|
2263
|
+
hint: ws.team.name
|
|
2264
|
+
}))
|
|
2265
|
+
});
|
|
2266
|
+
if (isCancel3(workspaceChoice)) {
|
|
2267
|
+
cancel2("Installation cancelled.");
|
|
2268
|
+
process.exit(0);
|
|
2269
|
+
}
|
|
2270
|
+
selectedWorkspaceId = workspaceChoice;
|
|
2271
|
+
const chosen = workspaces.find((ws) => ws.id === selectedWorkspaceId);
|
|
2272
|
+
selectedWorkspaceName = chosen?.name ?? "Unknown";
|
|
2273
|
+
printStatus(`Workspace: ${pc6.bold(selectedWorkspaceName)}`);
|
|
2274
|
+
}
|
|
2275
|
+
} catch {
|
|
2276
|
+
printError(
|
|
2277
|
+
"Could not fetch workspaces. Check your network and try again."
|
|
2278
|
+
);
|
|
2279
|
+
process.exit(1);
|
|
2280
|
+
}
|
|
2117
2281
|
}
|
|
2118
2282
|
let skipConfigOverwrite = false;
|
|
2119
|
-
if (!await pathExists(
|
|
2283
|
+
if (!await pathExists(join16(targetDir, ".git"))) {
|
|
2120
2284
|
printWarning("No git repository detected. Run git init when ready.");
|
|
2121
2285
|
}
|
|
2122
2286
|
await ensureDirectories(targetDir, tier);
|
|
2287
|
+
if (tier === "cloud" && apiKey) {
|
|
2288
|
+
try {
|
|
2289
|
+
const me = await fetchMe(apiKey);
|
|
2290
|
+
const meData = {
|
|
2291
|
+
displayName: me.displayName,
|
|
2292
|
+
email: me.email,
|
|
2293
|
+
providerId: me.providerId,
|
|
2294
|
+
provider: me.provider,
|
|
2295
|
+
providerIdentities: me.providerIdentities,
|
|
2296
|
+
preferences: me.preferences
|
|
2297
|
+
};
|
|
2298
|
+
const mePath = join16(targetDir, ".flydocs", "me.json");
|
|
2299
|
+
await writeFile9(
|
|
2300
|
+
mePath,
|
|
2301
|
+
JSON.stringify(meData, null, 2) + "\n",
|
|
2302
|
+
"utf-8"
|
|
2303
|
+
);
|
|
2304
|
+
if (me.displayName) {
|
|
2305
|
+
printStatus(`Identity: ${pc6.bold(me.displayName)}`);
|
|
2306
|
+
}
|
|
2307
|
+
} catch {
|
|
2308
|
+
printWarning(
|
|
2309
|
+
"Could not fetch user identity. Run /flydocs-setup to configure later."
|
|
2310
|
+
);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2123
2313
|
const existingFiles = await detectExistingConfigs(targetDir);
|
|
2124
2314
|
if (existingFiles.length > 0) {
|
|
2125
2315
|
console.log();
|
|
@@ -2168,33 +2358,34 @@ var init_install = __esm({
|
|
|
2168
2358
|
}
|
|
2169
2359
|
console.log("Installing framework files...");
|
|
2170
2360
|
await replaceDirectory(
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
);
|
|
2174
|
-
await replaceDirectory(
|
|
2175
|
-
join15(templateDir, ".flydocs", "hooks"),
|
|
2176
|
-
join15(targetDir, ".flydocs", "hooks")
|
|
2361
|
+
join16(templateDir, ".flydocs", "templates"),
|
|
2362
|
+
join16(targetDir, ".flydocs", "templates")
|
|
2177
2363
|
);
|
|
2178
2364
|
await replaceDirectory(
|
|
2179
|
-
|
|
2180
|
-
|
|
2365
|
+
join16(templateDir, ".claude", "hooks"),
|
|
2366
|
+
join16(targetDir, ".claude", "hooks")
|
|
2181
2367
|
);
|
|
2182
2368
|
await copyFile(
|
|
2183
|
-
|
|
2184
|
-
|
|
2369
|
+
join16(templateDir, ".flydocs", "version"),
|
|
2370
|
+
join16(targetDir, ".flydocs", "version")
|
|
2185
2371
|
);
|
|
2186
|
-
const manifestSrc =
|
|
2372
|
+
const manifestSrc = join16(templateDir, "manifest.json");
|
|
2187
2373
|
if (await pathExists(manifestSrc)) {
|
|
2188
|
-
await copyFile(manifestSrc,
|
|
2374
|
+
await copyFile(manifestSrc, join16(targetDir, ".flydocs", "manifest.json"));
|
|
2189
2375
|
}
|
|
2190
|
-
const changelogSrc =
|
|
2376
|
+
const changelogSrc = join16(templateDir, "CHANGELOG.md");
|
|
2191
2377
|
if (await pathExists(changelogSrc)) {
|
|
2192
|
-
await copyFile(changelogSrc,
|
|
2378
|
+
await copyFile(changelogSrc, join16(targetDir, ".flydocs", "CHANGELOG.md"));
|
|
2193
2379
|
}
|
|
2194
|
-
printStatus(
|
|
2195
|
-
|
|
2380
|
+
printStatus(
|
|
2381
|
+
".flydocs/templates, .claude/hooks, version, manifest, changelog"
|
|
2382
|
+
);
|
|
2383
|
+
const configPath = join16(targetDir, ".flydocs", "config.json");
|
|
2196
2384
|
if (!await pathExists(configPath)) {
|
|
2197
2385
|
const config = await createFreshConfig(templateDir, version, tier);
|
|
2386
|
+
if (selectedWorkspaceId) {
|
|
2387
|
+
config.workspaceId = selectedWorkspaceId;
|
|
2388
|
+
}
|
|
2198
2389
|
await writeConfig(targetDir, config);
|
|
2199
2390
|
printStatus(`.flydocs/config.json (new, tier: ${tier})`);
|
|
2200
2391
|
} else if (args.tier) {
|
|
@@ -2202,15 +2393,34 @@ var init_install = __esm({
|
|
|
2202
2393
|
const existing = await readConfig(targetDir);
|
|
2203
2394
|
existing.version = version;
|
|
2204
2395
|
existing.tier = tier;
|
|
2396
|
+
if (selectedWorkspaceId) {
|
|
2397
|
+
existing.workspaceId = selectedWorkspaceId;
|
|
2398
|
+
}
|
|
2205
2399
|
await writeConfig(targetDir, existing);
|
|
2206
2400
|
printStatus(`.flydocs/config.json (tier updated: ${tier})`);
|
|
2207
2401
|
} catch {
|
|
2208
2402
|
const config = await createFreshConfig(templateDir, version, tier);
|
|
2403
|
+
if (selectedWorkspaceId) {
|
|
2404
|
+
config.workspaceId = selectedWorkspaceId;
|
|
2405
|
+
}
|
|
2209
2406
|
await writeConfig(targetDir, config);
|
|
2210
2407
|
printStatus(`.flydocs/config.json (recreated, tier: ${tier})`);
|
|
2211
2408
|
}
|
|
2212
2409
|
} else {
|
|
2213
|
-
|
|
2410
|
+
if (selectedWorkspaceId) {
|
|
2411
|
+
try {
|
|
2412
|
+
const existing = await readConfig(targetDir);
|
|
2413
|
+
existing.workspaceId = selectedWorkspaceId;
|
|
2414
|
+
await writeConfig(targetDir, existing);
|
|
2415
|
+
printWarning(
|
|
2416
|
+
".flydocs/config.json exists, preserving (workspace updated)"
|
|
2417
|
+
);
|
|
2418
|
+
} catch {
|
|
2419
|
+
printWarning(".flydocs/config.json exists, preserving");
|
|
2420
|
+
}
|
|
2421
|
+
} else {
|
|
2422
|
+
printWarning(".flydocs/config.json exists, preserving");
|
|
2423
|
+
}
|
|
2214
2424
|
}
|
|
2215
2425
|
console.log();
|
|
2216
2426
|
console.log(`Installing skills (tier: ${tier})...`);
|
|
@@ -2246,20 +2456,20 @@ var init_install = __esm({
|
|
|
2246
2456
|
}
|
|
2247
2457
|
await capture("install_agents_chosen", { install_agents: installAgents });
|
|
2248
2458
|
if (installAgents) {
|
|
2249
|
-
const claudeAgentsSrc =
|
|
2459
|
+
const claudeAgentsSrc = join16(templateDir, ".claude", "agents");
|
|
2250
2460
|
if (await pathExists(claudeAgentsSrc)) {
|
|
2251
|
-
await mkdir7(
|
|
2461
|
+
await mkdir7(join16(targetDir, ".claude", "agents"), { recursive: true });
|
|
2252
2462
|
await copyDirectoryContents(
|
|
2253
2463
|
claudeAgentsSrc,
|
|
2254
|
-
|
|
2464
|
+
join16(targetDir, ".claude", "agents")
|
|
2255
2465
|
);
|
|
2256
2466
|
}
|
|
2257
|
-
const cursorAgentsSrc =
|
|
2467
|
+
const cursorAgentsSrc = join16(templateDir, ".cursor", "agents");
|
|
2258
2468
|
if (await pathExists(cursorAgentsSrc)) {
|
|
2259
|
-
await mkdir7(
|
|
2469
|
+
await mkdir7(join16(targetDir, ".cursor", "agents"), { recursive: true });
|
|
2260
2470
|
await copyDirectoryContents(
|
|
2261
2471
|
cursorAgentsSrc,
|
|
2262
|
-
|
|
2472
|
+
join16(targetDir, ".cursor", "agents")
|
|
2263
2473
|
);
|
|
2264
2474
|
}
|
|
2265
2475
|
printStatus("Sub-agents installed (.claude/agents, .cursor/agents)");
|
|
@@ -2269,30 +2479,30 @@ var init_install = __esm({
|
|
|
2269
2479
|
console.log();
|
|
2270
2480
|
console.log("Installing commands and settings...");
|
|
2271
2481
|
await copyDirectoryContents(
|
|
2272
|
-
|
|
2273
|
-
|
|
2482
|
+
join16(templateDir, ".claude", "commands"),
|
|
2483
|
+
join16(targetDir, ".claude", "commands")
|
|
2274
2484
|
);
|
|
2275
2485
|
if (!skipConfigOverwrite) {
|
|
2276
2486
|
await copyFile(
|
|
2277
|
-
|
|
2278
|
-
|
|
2487
|
+
join16(templateDir, ".claude", "CLAUDE.md"),
|
|
2488
|
+
join16(targetDir, ".claude", "CLAUDE.md")
|
|
2279
2489
|
);
|
|
2280
2490
|
await copyFile(
|
|
2281
|
-
|
|
2282
|
-
|
|
2491
|
+
join16(templateDir, ".claude", "settings.json"),
|
|
2492
|
+
join16(targetDir, ".claude", "settings.json")
|
|
2283
2493
|
);
|
|
2284
2494
|
printStatus(".claude/ (commands, CLAUDE.md, settings)");
|
|
2285
2495
|
} else {
|
|
2286
2496
|
printStatus(".claude/ (commands only \u2014 existing config preserved)");
|
|
2287
2497
|
}
|
|
2288
2498
|
await copyDirectoryContents(
|
|
2289
|
-
|
|
2290
|
-
|
|
2499
|
+
join16(templateDir, ".claude", "commands"),
|
|
2500
|
+
join16(targetDir, ".cursor", "commands")
|
|
2291
2501
|
);
|
|
2292
2502
|
if (!skipConfigOverwrite) {
|
|
2293
2503
|
await copyFile(
|
|
2294
|
-
|
|
2295
|
-
|
|
2504
|
+
join16(templateDir, ".cursor", "hooks.json"),
|
|
2505
|
+
join16(targetDir, ".cursor", "hooks.json")
|
|
2296
2506
|
);
|
|
2297
2507
|
printStatus(".cursor/ (commands, hooks)");
|
|
2298
2508
|
} else {
|
|
@@ -2302,8 +2512,8 @@ var init_install = __esm({
|
|
|
2302
2512
|
printStatus(".cursor/rules/");
|
|
2303
2513
|
if (!skipConfigOverwrite) {
|
|
2304
2514
|
await copyFile(
|
|
2305
|
-
|
|
2306
|
-
|
|
2515
|
+
join16(templateDir, "AGENTS.md"),
|
|
2516
|
+
join16(targetDir, "AGENTS.md")
|
|
2307
2517
|
);
|
|
2308
2518
|
printStatus("AGENTS.md");
|
|
2309
2519
|
} else {
|
|
@@ -2311,44 +2521,46 @@ var init_install = __esm({
|
|
|
2311
2521
|
}
|
|
2312
2522
|
await runManifestGeneration(targetDir);
|
|
2313
2523
|
await runContextGraphBuild(targetDir);
|
|
2524
|
+
await generateIntegrity(targetDir, version);
|
|
2525
|
+
printStatus("Install integrity recorded");
|
|
2314
2526
|
console.log();
|
|
2315
2527
|
console.log("Installing project templates...");
|
|
2316
2528
|
const userFiles = [
|
|
2317
2529
|
{
|
|
2318
|
-
src:
|
|
2319
|
-
dest:
|
|
2530
|
+
src: join16(templateDir, "flydocs", "context", "project.md"),
|
|
2531
|
+
dest: join16(targetDir, "flydocs", "context", "project.md"),
|
|
2320
2532
|
label: "flydocs/context/project.md"
|
|
2321
2533
|
},
|
|
2322
2534
|
{
|
|
2323
|
-
src:
|
|
2324
|
-
dest:
|
|
2535
|
+
src: join16(templateDir, "flydocs", "knowledge", "INDEX.md"),
|
|
2536
|
+
dest: join16(targetDir, "flydocs", "knowledge", "INDEX.md"),
|
|
2325
2537
|
label: "flydocs/knowledge/INDEX.md"
|
|
2326
2538
|
},
|
|
2327
2539
|
{
|
|
2328
|
-
src:
|
|
2329
|
-
dest:
|
|
2540
|
+
src: join16(templateDir, "flydocs", "knowledge", "README.md"),
|
|
2541
|
+
dest: join16(targetDir, "flydocs", "knowledge", "README.md"),
|
|
2330
2542
|
label: "flydocs/knowledge/README.md"
|
|
2331
2543
|
},
|
|
2332
2544
|
{
|
|
2333
|
-
src:
|
|
2545
|
+
src: join16(
|
|
2334
2546
|
templateDir,
|
|
2335
2547
|
"flydocs",
|
|
2336
2548
|
"knowledge",
|
|
2337
2549
|
"product",
|
|
2338
2550
|
"personas.md"
|
|
2339
2551
|
),
|
|
2340
|
-
dest:
|
|
2552
|
+
dest: join16(targetDir, "flydocs", "knowledge", "product", "personas.md"),
|
|
2341
2553
|
label: "flydocs/knowledge/product/personas.md"
|
|
2342
2554
|
},
|
|
2343
2555
|
{
|
|
2344
|
-
src:
|
|
2556
|
+
src: join16(
|
|
2345
2557
|
templateDir,
|
|
2346
2558
|
"flydocs",
|
|
2347
2559
|
"knowledge",
|
|
2348
2560
|
"product",
|
|
2349
2561
|
"user-flows.md"
|
|
2350
2562
|
),
|
|
2351
|
-
dest:
|
|
2563
|
+
dest: join16(
|
|
2352
2564
|
targetDir,
|
|
2353
2565
|
"flydocs",
|
|
2354
2566
|
"knowledge",
|
|
@@ -2358,18 +2570,57 @@ var init_install = __esm({
|
|
|
2358
2570
|
label: "flydocs/knowledge/product/user-flows.md"
|
|
2359
2571
|
},
|
|
2360
2572
|
{
|
|
2361
|
-
src:
|
|
2362
|
-
|
|
2573
|
+
src: join16(
|
|
2574
|
+
templateDir,
|
|
2575
|
+
"flydocs",
|
|
2576
|
+
"knowledge",
|
|
2577
|
+
"templates",
|
|
2578
|
+
"decision.md"
|
|
2579
|
+
),
|
|
2580
|
+
dest: join16(
|
|
2581
|
+
targetDir,
|
|
2582
|
+
"flydocs",
|
|
2583
|
+
"knowledge",
|
|
2584
|
+
"templates",
|
|
2585
|
+
"decision.md"
|
|
2586
|
+
),
|
|
2587
|
+
label: "flydocs/knowledge/templates/decision.md"
|
|
2588
|
+
},
|
|
2589
|
+
{
|
|
2590
|
+
src: join16(
|
|
2591
|
+
templateDir,
|
|
2592
|
+
"flydocs",
|
|
2593
|
+
"knowledge",
|
|
2594
|
+
"templates",
|
|
2595
|
+
"feature.md"
|
|
2596
|
+
),
|
|
2597
|
+
dest: join16(
|
|
2598
|
+
targetDir,
|
|
2599
|
+
"flydocs",
|
|
2600
|
+
"knowledge",
|
|
2601
|
+
"templates",
|
|
2602
|
+
"feature.md"
|
|
2603
|
+
),
|
|
2604
|
+
label: "flydocs/knowledge/templates/feature.md"
|
|
2605
|
+
},
|
|
2606
|
+
{
|
|
2607
|
+
src: join16(templateDir, "flydocs", "knowledge", "templates", "note.md"),
|
|
2608
|
+
dest: join16(targetDir, "flydocs", "knowledge", "templates", "note.md"),
|
|
2609
|
+
label: "flydocs/knowledge/templates/note.md"
|
|
2610
|
+
},
|
|
2611
|
+
{
|
|
2612
|
+
src: join16(templateDir, "flydocs", "design-system", "README.md"),
|
|
2613
|
+
dest: join16(targetDir, "flydocs", "design-system", "README.md"),
|
|
2363
2614
|
label: "flydocs/design-system/README.md"
|
|
2364
2615
|
},
|
|
2365
2616
|
{
|
|
2366
|
-
src:
|
|
2617
|
+
src: join16(
|
|
2367
2618
|
templateDir,
|
|
2368
2619
|
"flydocs",
|
|
2369
2620
|
"design-system",
|
|
2370
2621
|
"component-patterns.md"
|
|
2371
2622
|
),
|
|
2372
|
-
dest:
|
|
2623
|
+
dest: join16(
|
|
2373
2624
|
targetDir,
|
|
2374
2625
|
"flydocs",
|
|
2375
2626
|
"design-system",
|
|
@@ -2378,13 +2629,13 @@ var init_install = __esm({
|
|
|
2378
2629
|
label: "flydocs/design-system/component-patterns.md"
|
|
2379
2630
|
},
|
|
2380
2631
|
{
|
|
2381
|
-
src:
|
|
2382
|
-
dest:
|
|
2632
|
+
src: join16(templateDir, "flydocs", "design-system", "token-mapping.md"),
|
|
2633
|
+
dest: join16(targetDir, "flydocs", "design-system", "token-mapping.md"),
|
|
2383
2634
|
label: "flydocs/design-system/token-mapping.md"
|
|
2384
2635
|
},
|
|
2385
2636
|
{
|
|
2386
|
-
src:
|
|
2387
|
-
dest:
|
|
2637
|
+
src: join16(templateDir, "flydocs", "README.md"),
|
|
2638
|
+
dest: join16(targetDir, "flydocs", "README.md"),
|
|
2388
2639
|
label: "flydocs/README.md"
|
|
2389
2640
|
}
|
|
2390
2641
|
];
|
|
@@ -2398,12 +2649,13 @@ var init_install = __esm({
|
|
|
2398
2649
|
}
|
|
2399
2650
|
}
|
|
2400
2651
|
}
|
|
2401
|
-
const envExampleSrc =
|
|
2652
|
+
const envExampleSrc = join16(templateDir, ".env.example");
|
|
2402
2653
|
if (await pathExists(envExampleSrc)) {
|
|
2403
|
-
await copyFile(envExampleSrc,
|
|
2654
|
+
await copyFile(envExampleSrc, join16(targetDir, ".env.example"));
|
|
2404
2655
|
printStatus(".env.example");
|
|
2405
2656
|
}
|
|
2406
2657
|
await ensureGitignore(targetDir);
|
|
2658
|
+
await ensurePlatformIgnores(targetDir);
|
|
2407
2659
|
console.log();
|
|
2408
2660
|
console.log("Detecting project stack...");
|
|
2409
2661
|
const stack = await detectStack(targetDir);
|
|
@@ -2572,60 +2824,1142 @@ var init_install = __esm({
|
|
|
2572
2824
|
}
|
|
2573
2825
|
});
|
|
2574
2826
|
|
|
2575
|
-
// src/
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2827
|
+
// src/lib/global-config.ts
|
|
2828
|
+
import { readFile as readFile12, writeFile as writeFile10, mkdir as mkdir8, stat, chmod } from "fs/promises";
|
|
2829
|
+
import { join as join17 } from "path";
|
|
2830
|
+
import { homedir as homedir3 } from "os";
|
|
2831
|
+
function globalDir() {
|
|
2832
|
+
return join17(homedir3(), ".flydocs");
|
|
2833
|
+
}
|
|
2834
|
+
function credentialsPath() {
|
|
2835
|
+
return join17(globalDir(), "credentials");
|
|
2836
|
+
}
|
|
2837
|
+
function globalMePath() {
|
|
2838
|
+
return join17(globalDir(), "me.json");
|
|
2839
|
+
}
|
|
2840
|
+
async function ensureGlobalDir() {
|
|
2841
|
+
const dir = globalDir();
|
|
2842
|
+
if (!await pathExists(dir)) {
|
|
2843
|
+
await mkdir8(dir, { recursive: true, mode: 448 });
|
|
2844
|
+
}
|
|
2845
|
+
return dir;
|
|
2846
|
+
}
|
|
2847
|
+
async function readGlobalCredential() {
|
|
2848
|
+
const path = credentialsPath();
|
|
2849
|
+
if (!await pathExists(path)) return null;
|
|
2850
|
+
try {
|
|
2851
|
+
const content = await readFile12(path, "utf-8");
|
|
2852
|
+
return JSON.parse(content);
|
|
2853
|
+
} catch {
|
|
2854
|
+
return null;
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
async function writeGlobalCredential(credential) {
|
|
2858
|
+
await ensureGlobalDir();
|
|
2859
|
+
const path = credentialsPath();
|
|
2860
|
+
await writeFile10(path, JSON.stringify(credential, null, 2) + "\n", {
|
|
2861
|
+
encoding: "utf-8",
|
|
2862
|
+
mode: 384
|
|
2863
|
+
});
|
|
2864
|
+
await chmod(path, 384);
|
|
2865
|
+
}
|
|
2866
|
+
async function checkCredentialPermissions() {
|
|
2867
|
+
const path = credentialsPath();
|
|
2868
|
+
if (!await pathExists(path)) return;
|
|
2869
|
+
try {
|
|
2870
|
+
const stats = await stat(path);
|
|
2871
|
+
const mode = stats.mode & 511;
|
|
2872
|
+
if (mode !== 384) {
|
|
2873
|
+
printWarning(
|
|
2874
|
+
`Credential file permissions are too open (${mode.toString(8)}). Expected 0600. Run: chmod 600 ${path}`
|
|
2875
|
+
);
|
|
2876
|
+
}
|
|
2877
|
+
} catch {
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
async function writeGlobalIdentity(identity) {
|
|
2881
|
+
await ensureGlobalDir();
|
|
2882
|
+
const path = globalMePath();
|
|
2883
|
+
await writeFile10(path, JSON.stringify(identity, null, 2) + "\n", "utf-8");
|
|
2884
|
+
}
|
|
2885
|
+
async function resolveApiKey(explicitKey, projectDir) {
|
|
2886
|
+
if (explicitKey) {
|
|
2887
|
+
return { key: explicitKey, source: "--key flag" };
|
|
2888
|
+
}
|
|
2889
|
+
const envKey = process.env.FLYDOCS_API_KEY;
|
|
2890
|
+
if (envKey) {
|
|
2891
|
+
return { key: envKey, source: "FLYDOCS_API_KEY env var" };
|
|
2892
|
+
}
|
|
2893
|
+
const globalCred = await readGlobalCredential();
|
|
2894
|
+
if (globalCred?.apiKey) {
|
|
2895
|
+
return { key: globalCred.apiKey, source: "~/.flydocs/credentials" };
|
|
2896
|
+
}
|
|
2897
|
+
if (projectDir) {
|
|
2898
|
+
const envLocalKey = await readEnvKey(projectDir, "FLYDOCS_API_KEY");
|
|
2899
|
+
if (envLocalKey) {
|
|
2900
|
+
return { key: envLocalKey, source: ".env.local (legacy)" };
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
return null;
|
|
2904
|
+
}
|
|
2905
|
+
async function readEnvKey(dir, varName) {
|
|
2906
|
+
for (const filename of [".env.local", ".env"]) {
|
|
2907
|
+
const filePath = join17(dir, filename);
|
|
2908
|
+
if (!await pathExists(filePath)) continue;
|
|
2909
|
+
try {
|
|
2910
|
+
const content = await readFile12(filePath, "utf-8");
|
|
2911
|
+
const match = content.match(new RegExp(`^${varName}=(.+)$`, "m"));
|
|
2912
|
+
if (match) return match[1].trim();
|
|
2913
|
+
} catch {
|
|
2914
|
+
continue;
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
return null;
|
|
2918
|
+
}
|
|
2919
|
+
var init_global_config = __esm({
|
|
2920
|
+
"src/lib/global-config.ts"() {
|
|
2588
2921
|
"use strict";
|
|
2589
|
-
init_template();
|
|
2590
|
-
init_version();
|
|
2591
2922
|
init_fs_ops();
|
|
2592
2923
|
init_ui();
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2924
|
+
}
|
|
2925
|
+
});
|
|
2926
|
+
|
|
2927
|
+
// src/lib/relay-client.ts
|
|
2928
|
+
function resolveRelayUrl() {
|
|
2929
|
+
return process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? DEFAULT_RELAY_URL;
|
|
2930
|
+
}
|
|
2931
|
+
function headers(apiKey, workspaceId) {
|
|
2932
|
+
const h = {
|
|
2933
|
+
Authorization: `Bearer ${apiKey}`,
|
|
2934
|
+
"Content-Type": "application/json",
|
|
2935
|
+
Accept: "application/json"
|
|
2936
|
+
};
|
|
2937
|
+
if (workspaceId) h["X-Workspace"] = workspaceId;
|
|
2938
|
+
return h;
|
|
2939
|
+
}
|
|
2940
|
+
async function fetchConfigV2(apiKey, options = {}) {
|
|
2941
|
+
const baseUrl = resolveRelayUrl();
|
|
2942
|
+
const params = new URLSearchParams({ format: "2" });
|
|
2943
|
+
if (options.includeContext) params.set("includeContext", "true");
|
|
2944
|
+
const response = await fetch(`${baseUrl}/config/generate?${params}`, {
|
|
2945
|
+
method: "GET",
|
|
2946
|
+
headers: headers(apiKey, options.workspaceId),
|
|
2947
|
+
signal: AbortSignal.timeout(3e4)
|
|
2948
|
+
});
|
|
2949
|
+
if (!response.ok) {
|
|
2950
|
+
const body = await response.text().catch(() => "");
|
|
2951
|
+
throw new RelayError(
|
|
2952
|
+
`config/generate failed (${response.status})`,
|
|
2953
|
+
response.status,
|
|
2954
|
+
body
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2957
|
+
return await response.json();
|
|
2958
|
+
}
|
|
2959
|
+
async function fetchTemplates(apiKey, sinceVersion, workspaceId) {
|
|
2960
|
+
const baseUrl = resolveRelayUrl();
|
|
2961
|
+
const response = await fetch(`${baseUrl}/templates?since=${sinceVersion}`, {
|
|
2962
|
+
method: "GET",
|
|
2963
|
+
headers: headers(apiKey, workspaceId),
|
|
2964
|
+
signal: AbortSignal.timeout(15e3)
|
|
2965
|
+
});
|
|
2966
|
+
if (!response.ok) {
|
|
2967
|
+
const body = await response.text().catch(() => "");
|
|
2968
|
+
throw new RelayError(
|
|
2969
|
+
`templates fetch failed (${response.status})`,
|
|
2970
|
+
response.status,
|
|
2971
|
+
body
|
|
2972
|
+
);
|
|
2973
|
+
}
|
|
2974
|
+
return await response.json();
|
|
2975
|
+
}
|
|
2976
|
+
var DEFAULT_RELAY_URL, RelayError;
|
|
2977
|
+
var init_relay_client = __esm({
|
|
2978
|
+
"src/lib/relay-client.ts"() {
|
|
2979
|
+
"use strict";
|
|
2980
|
+
DEFAULT_RELAY_URL = "https://app.flydocs.ai/api/relay";
|
|
2981
|
+
RelayError = class extends Error {
|
|
2982
|
+
constructor(message, status2, body) {
|
|
2983
|
+
super(message);
|
|
2984
|
+
this.status = status2;
|
|
2985
|
+
this.body = body;
|
|
2986
|
+
this.name = "RelayError";
|
|
2987
|
+
}
|
|
2988
|
+
};
|
|
2989
|
+
}
|
|
2990
|
+
});
|
|
2991
|
+
|
|
2992
|
+
// src/commands/cleanup.ts
|
|
2993
|
+
var cleanup_exports = {};
|
|
2994
|
+
__export(cleanup_exports, {
|
|
2995
|
+
default: () => cleanup_default,
|
|
2996
|
+
executeCleanup: () => executeCleanup,
|
|
2997
|
+
scanArtifacts: () => scanArtifacts
|
|
2998
|
+
});
|
|
2999
|
+
import { defineCommand as defineCommand2 } from "citty";
|
|
3000
|
+
import pc7 from "picocolors";
|
|
3001
|
+
import { join as join18 } from "path";
|
|
3002
|
+
import { readFile as readFile13, writeFile as writeFile11, rm as rm4 } from "fs/promises";
|
|
3003
|
+
async function scanArtifacts(targetDir) {
|
|
3004
|
+
const actions = [];
|
|
3005
|
+
const localMePath = join18(targetDir, ".flydocs", "me.json");
|
|
3006
|
+
if (await pathExists(localMePath) && await pathExists(globalMePath())) {
|
|
3007
|
+
actions.push({
|
|
3008
|
+
description: "Remove .flydocs/me.json (migrated to ~/.flydocs/me.json)",
|
|
3009
|
+
path: localMePath,
|
|
3010
|
+
type: "file"
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
const validationCachePath = join18(
|
|
3014
|
+
targetDir,
|
|
3015
|
+
".flydocs",
|
|
3016
|
+
"validation-cache.json"
|
|
3017
|
+
);
|
|
3018
|
+
if (await pathExists(validationCachePath)) {
|
|
3019
|
+
actions.push({
|
|
3020
|
+
description: "Remove .flydocs/validation-cache.json (replaced by configVersion)",
|
|
3021
|
+
path: validationCachePath,
|
|
3022
|
+
type: "file"
|
|
3023
|
+
});
|
|
3024
|
+
}
|
|
3025
|
+
const hasGlobalCreds = await pathExists(credentialsPath());
|
|
3026
|
+
for (const envFile of [".env", ".env.local"]) {
|
|
3027
|
+
const envPath = join18(targetDir, envFile);
|
|
3028
|
+
if (!await pathExists(envPath)) continue;
|
|
3029
|
+
const content = await readFile13(envPath, "utf-8");
|
|
3030
|
+
const lines = content.split("\n");
|
|
3031
|
+
for (const line of lines) {
|
|
3032
|
+
const trimmed = line.trim();
|
|
3033
|
+
if (hasGlobalCreds && trimmed.startsWith("FLYDOCS_API_KEY=")) {
|
|
3034
|
+
actions.push({
|
|
3035
|
+
description: `Remove FLYDOCS_API_KEY from ${envFile} (migrated to ~/.flydocs/credentials)`,
|
|
3036
|
+
path: envPath,
|
|
3037
|
+
type: "line"
|
|
3038
|
+
});
|
|
3039
|
+
}
|
|
3040
|
+
if (trimmed.startsWith("FLYDOCS_RELAY_URL=")) {
|
|
3041
|
+
actions.push({
|
|
3042
|
+
description: `Remove FLYDOCS_RELAY_URL from ${envFile}`,
|
|
3043
|
+
path: envPath,
|
|
3044
|
+
type: "line"
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
try {
|
|
3050
|
+
const config = await readAnyConfig(targetDir);
|
|
3051
|
+
const rawContent = await readFile13(
|
|
3052
|
+
join18(targetDir, ".flydocs", "config.json"),
|
|
3053
|
+
"utf-8"
|
|
3054
|
+
);
|
|
3055
|
+
const raw = JSON.parse(rawContent);
|
|
3056
|
+
const ghostFields = ["provider", "statusMapping"];
|
|
3057
|
+
for (const field of ghostFields) {
|
|
3058
|
+
if (field in raw) {
|
|
3059
|
+
actions.push({
|
|
3060
|
+
description: `Remove ghost field "${field}" from config.json`,
|
|
3061
|
+
path: join18(targetDir, ".flydocs", "config.json"),
|
|
3062
|
+
type: "field"
|
|
3063
|
+
});
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
if (isConfigV2(config)) {
|
|
3067
|
+
const v1OnlyFields = [
|
|
3068
|
+
"workspaceId",
|
|
3069
|
+
"issueLabels",
|
|
3070
|
+
"workspace",
|
|
3071
|
+
"onboardComplete",
|
|
3072
|
+
"sourceRepo",
|
|
3073
|
+
"designSystem",
|
|
3074
|
+
"aiLabor"
|
|
3075
|
+
];
|
|
3076
|
+
for (const field of v1OnlyFields) {
|
|
3077
|
+
if (field in raw) {
|
|
3078
|
+
actions.push({
|
|
3079
|
+
description: `Remove v1 field "${field}" from config.json (server-owned in v2)`,
|
|
3080
|
+
path: join18(targetDir, ".flydocs", "config.json"),
|
|
3081
|
+
type: "field"
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
} catch {
|
|
3087
|
+
}
|
|
3088
|
+
return actions;
|
|
3089
|
+
}
|
|
3090
|
+
async function executeCleanup(targetDir, actions) {
|
|
3091
|
+
const fileRemovals = actions.filter((a) => a.type === "file");
|
|
3092
|
+
const lineRemovals = actions.filter((a) => a.type === "line");
|
|
3093
|
+
const fieldRemovals = actions.filter((a) => a.type === "field");
|
|
3094
|
+
for (const action of fileRemovals) {
|
|
3095
|
+
await rm4(action.path, { force: true });
|
|
3096
|
+
}
|
|
3097
|
+
const envFiles = /* @__PURE__ */ new Map();
|
|
3098
|
+
for (const action of lineRemovals) {
|
|
3099
|
+
if (!envFiles.has(action.path)) {
|
|
3100
|
+
const content = await readFile13(action.path, "utf-8");
|
|
3101
|
+
envFiles.set(action.path, content.split("\n"));
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
for (const [envPath, lines] of envFiles) {
|
|
3105
|
+
const filtered = lines.filter((line) => {
|
|
3106
|
+
const trimmed = line.trim();
|
|
3107
|
+
return !trimmed.startsWith("FLYDOCS_API_KEY=") && !trimmed.startsWith("FLYDOCS_RELAY_URL=");
|
|
3108
|
+
});
|
|
3109
|
+
const hasContent = filtered.some(
|
|
3110
|
+
(l) => l.trim().length > 0 && !l.trim().startsWith("#")
|
|
3111
|
+
);
|
|
3112
|
+
if (!hasContent) {
|
|
3113
|
+
await rm4(envPath, { force: true });
|
|
3114
|
+
} else {
|
|
3115
|
+
await writeFile11(envPath, filtered.join("\n"), "utf-8");
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
if (fieldRemovals.length > 0) {
|
|
3119
|
+
const configPath = join18(targetDir, ".flydocs", "config.json");
|
|
3120
|
+
const content = await readFile13(configPath, "utf-8");
|
|
3121
|
+
const config = JSON.parse(content);
|
|
3122
|
+
for (const action of fieldRemovals) {
|
|
3123
|
+
const match = action.description.match(/"(\w+)"/);
|
|
3124
|
+
if (match) {
|
|
3125
|
+
delete config[match[1]];
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
await writeFile11(
|
|
3129
|
+
configPath,
|
|
3130
|
+
JSON.stringify(config, null, 2) + "\n",
|
|
3131
|
+
"utf-8"
|
|
3132
|
+
);
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
var cleanup_default;
|
|
3136
|
+
var init_cleanup = __esm({
|
|
3137
|
+
"src/commands/cleanup.ts"() {
|
|
3138
|
+
"use strict";
|
|
3139
|
+
init_ui();
|
|
3140
|
+
init_fs_ops();
|
|
3141
|
+
init_config();
|
|
3142
|
+
init_types();
|
|
3143
|
+
init_global_config();
|
|
3144
|
+
cleanup_default = defineCommand2({
|
|
3145
|
+
meta: {
|
|
3146
|
+
name: "cleanup",
|
|
3147
|
+
description: "Remove legacy v1 artifacts after migration (dry-run by default)"
|
|
3148
|
+
},
|
|
3149
|
+
args: {
|
|
3150
|
+
confirm: {
|
|
3151
|
+
type: "boolean",
|
|
3152
|
+
description: "Actually remove artifacts (default: dry-run only)",
|
|
3153
|
+
default: false
|
|
3154
|
+
},
|
|
3155
|
+
path: {
|
|
3156
|
+
type: "string",
|
|
3157
|
+
description: "Path to project directory"
|
|
3158
|
+
}
|
|
3159
|
+
},
|
|
3160
|
+
async run({ args }) {
|
|
3161
|
+
const targetDir = args.path ?? process.cwd();
|
|
3162
|
+
console.log();
|
|
3163
|
+
console.log(` ${pc7.bold("FlyDocs Cleanup")}`);
|
|
3164
|
+
console.log();
|
|
3165
|
+
const hasConfig = await pathExists(
|
|
3166
|
+
join18(targetDir, ".flydocs", "config.json")
|
|
3167
|
+
);
|
|
3168
|
+
if (!hasConfig) {
|
|
3169
|
+
printError("No .flydocs/config.json found. Run flydocs init first.");
|
|
3170
|
+
process.exit(1);
|
|
3171
|
+
}
|
|
3172
|
+
const hasGlobalCreds = await pathExists(credentialsPath());
|
|
3173
|
+
if (!hasGlobalCreds) {
|
|
3174
|
+
printWarning(
|
|
3175
|
+
"No global credentials found. Run flydocs init to set up credentials before cleanup."
|
|
3176
|
+
);
|
|
3177
|
+
}
|
|
3178
|
+
const actions = await scanArtifacts(targetDir);
|
|
3179
|
+
if (actions.length === 0) {
|
|
3180
|
+
printStatus("No legacy artifacts found. Already clean.");
|
|
3181
|
+
console.log();
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
3184
|
+
const mode = args.confirm ? "Removing" : "Would remove";
|
|
3185
|
+
printInfo(`${mode} ${actions.length} legacy artifact(s):`);
|
|
3186
|
+
console.log();
|
|
3187
|
+
for (const action of actions) {
|
|
3188
|
+
const icon = args.confirm ? pc7.red("-") : pc7.yellow("?");
|
|
3189
|
+
console.log(` ${icon} ${action.description}`);
|
|
3190
|
+
}
|
|
3191
|
+
console.log();
|
|
3192
|
+
if (!args.confirm) {
|
|
3193
|
+
printInfo(
|
|
3194
|
+
`Dry-run complete. Run ${pc7.cyan("flydocs cleanup --confirm")} to remove.`
|
|
3195
|
+
);
|
|
3196
|
+
console.log();
|
|
3197
|
+
return;
|
|
3198
|
+
}
|
|
3199
|
+
await executeCleanup(targetDir, actions);
|
|
3200
|
+
printCompletionBox("Cleanup Complete", [
|
|
3201
|
+
`${pc7.green("+")} Removed ${actions.length} legacy artifact(s)`
|
|
3202
|
+
]);
|
|
3203
|
+
console.log();
|
|
3204
|
+
}
|
|
3205
|
+
});
|
|
3206
|
+
}
|
|
3207
|
+
});
|
|
3208
|
+
|
|
3209
|
+
// src/lib/workspace.ts
|
|
3210
|
+
import { readFile as readFile14, writeFile as writeFile12, readdir as readdir4, stat as stat2 } from "fs/promises";
|
|
3211
|
+
import { join as join19, dirname as dirname3, relative, resolve as resolve3 } from "path";
|
|
3212
|
+
async function readWorkspaceFile(dir) {
|
|
3213
|
+
const filePath = join19(dir, WORKSPACE_FILENAME);
|
|
3214
|
+
if (!await pathExists(filePath)) return null;
|
|
3215
|
+
const content = await readFile14(filePath, "utf-8");
|
|
3216
|
+
const parsed = JSON.parse(content);
|
|
3217
|
+
validateWorkspaceFile(parsed);
|
|
3218
|
+
return parsed;
|
|
3219
|
+
}
|
|
3220
|
+
async function writeWorkspaceFile(dir, workspace) {
|
|
3221
|
+
const filePath = join19(dir, WORKSPACE_FILENAME);
|
|
3222
|
+
const content = JSON.stringify(workspace, null, 2) + "\n";
|
|
3223
|
+
await writeFile12(filePath, content, "utf-8");
|
|
3224
|
+
}
|
|
3225
|
+
async function detectSiblingRepos(parentDir) {
|
|
3226
|
+
const repos = {};
|
|
3227
|
+
const entries = await readdir4(parentDir);
|
|
3228
|
+
for (const entry of entries) {
|
|
3229
|
+
if (entry.startsWith(".")) continue;
|
|
3230
|
+
const entryPath = join19(parentDir, entry);
|
|
3231
|
+
const entryStat = await stat2(entryPath).catch(() => null);
|
|
3232
|
+
if (!entryStat?.isDirectory()) continue;
|
|
3233
|
+
const hasGit = await pathExists(join19(entryPath, ".git"));
|
|
3234
|
+
const hasFlydocs = await pathExists(
|
|
3235
|
+
join19(entryPath, ".flydocs", "config.json")
|
|
3236
|
+
);
|
|
3237
|
+
if (hasGit || hasFlydocs) {
|
|
3238
|
+
repos[entry] = { path: `./${entry}` };
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
return repos;
|
|
3242
|
+
}
|
|
3243
|
+
function buildWorkspaceFile(workspaceId, repos) {
|
|
3244
|
+
return { workspaceId, repos };
|
|
3245
|
+
}
|
|
3246
|
+
async function readSiblingDescriptors(workspaceDir, repos) {
|
|
3247
|
+
const descriptors = [];
|
|
3248
|
+
for (const [name, entry] of Object.entries(repos)) {
|
|
3249
|
+
const repoDir = resolve3(workspaceDir, entry.path);
|
|
3250
|
+
const serviceJsonPath = join19(repoDir, "flydocs", "context", "service.json");
|
|
3251
|
+
if (!await pathExists(serviceJsonPath)) continue;
|
|
3252
|
+
try {
|
|
3253
|
+
const content = await readFile14(serviceJsonPath, "utf-8");
|
|
3254
|
+
const descriptor = JSON.parse(content);
|
|
3255
|
+
descriptors.push({ name, path: entry.path, descriptor });
|
|
3256
|
+
} catch {
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
return descriptors;
|
|
3260
|
+
}
|
|
3261
|
+
async function generateWorkspaceMd(workspaceDir, workspace, productName) {
|
|
3262
|
+
const descriptors = await readSiblingDescriptors(
|
|
3263
|
+
workspaceDir,
|
|
3264
|
+
workspace.repos
|
|
3265
|
+
);
|
|
3266
|
+
const repoCount = Object.keys(workspace.repos).length;
|
|
3267
|
+
const lines = [];
|
|
3268
|
+
const title = productName ?? "Workspace";
|
|
3269
|
+
lines.push(`# Product: ${title}`);
|
|
3270
|
+
lines.push("");
|
|
3271
|
+
lines.push("## Overview");
|
|
3272
|
+
lines.push("");
|
|
3273
|
+
if (descriptors.length > 0) {
|
|
3274
|
+
const purposes = descriptors.map((d) => d.descriptor.purpose).filter(Boolean);
|
|
3275
|
+
if (purposes.length > 0) {
|
|
3276
|
+
lines.push(purposes.join(". ") + ".");
|
|
3277
|
+
} else {
|
|
3278
|
+
lines.push(`Multi-repo workspace with ${repoCount} repositories.`);
|
|
3279
|
+
}
|
|
3280
|
+
} else {
|
|
3281
|
+
lines.push(`Multi-repo workspace with ${repoCount} repositories.`);
|
|
3282
|
+
}
|
|
3283
|
+
lines.push("");
|
|
3284
|
+
lines.push("## Architecture");
|
|
3285
|
+
lines.push("");
|
|
3286
|
+
lines.push(`${repoCount}-repo sibling topology.`);
|
|
3287
|
+
lines.push("");
|
|
3288
|
+
lines.push("## Repos");
|
|
3289
|
+
lines.push("");
|
|
3290
|
+
for (const [name, entry] of Object.entries(workspace.repos)) {
|
|
3291
|
+
const desc = descriptors.find((d) => d.name === name);
|
|
3292
|
+
const purpose = desc?.descriptor.purpose ?? "";
|
|
3293
|
+
const stack = desc?.descriptor.stack?.join(", ") ?? "";
|
|
3294
|
+
const detail = [purpose, stack].filter(Boolean).join(". ");
|
|
3295
|
+
const projectMdLink = `${entry.path}/flydocs/context/project.md`;
|
|
3296
|
+
lines.push(
|
|
3297
|
+
`- **${name}** \u2014 ${detail || "No description"}. [Details](${projectMdLink})`
|
|
3298
|
+
);
|
|
3299
|
+
}
|
|
3300
|
+
lines.push("");
|
|
3301
|
+
const edges = [];
|
|
3302
|
+
for (const desc of descriptors) {
|
|
3303
|
+
for (const dep of desc.descriptor.dependencies ?? []) {
|
|
3304
|
+
const target = dep.service;
|
|
3305
|
+
const iface = dep.interface;
|
|
3306
|
+
edges.push(`- ${desc.name} \u2192 ${target} (${iface})`);
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
lines.push("## Cross-Repo Dependencies");
|
|
3310
|
+
lines.push("");
|
|
3311
|
+
if (edges.length > 0) {
|
|
3312
|
+
lines.push(...edges);
|
|
3313
|
+
} else {
|
|
3314
|
+
lines.push("No cross-repo dependencies detected.");
|
|
3315
|
+
}
|
|
3316
|
+
lines.push("");
|
|
3317
|
+
return lines.join("\n");
|
|
3318
|
+
}
|
|
3319
|
+
function validateWorkspaceFile(value) {
|
|
3320
|
+
if (typeof value !== "object" || value === null) {
|
|
3321
|
+
throw new Error("Invalid workspace file: expected an object");
|
|
3322
|
+
}
|
|
3323
|
+
const obj = value;
|
|
3324
|
+
if (typeof obj.workspaceId !== "string" || obj.workspaceId.length === 0) {
|
|
3325
|
+
throw new Error(
|
|
3326
|
+
"Invalid workspace file: workspaceId must be a non-empty string"
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
3329
|
+
if (typeof obj.repos !== "object" || obj.repos === null) {
|
|
3330
|
+
throw new Error("Invalid workspace file: repos must be an object");
|
|
3331
|
+
}
|
|
3332
|
+
const repos = obj.repos;
|
|
3333
|
+
for (const [name, entry] of Object.entries(repos)) {
|
|
3334
|
+
if (typeof entry !== "object" || entry === null) {
|
|
3335
|
+
throw new Error(
|
|
3336
|
+
`Invalid workspace file: repos.${name} must be an object`
|
|
3337
|
+
);
|
|
3338
|
+
}
|
|
3339
|
+
const repoEntry = entry;
|
|
3340
|
+
if (typeof repoEntry.path !== "string" || repoEntry.path.length === 0) {
|
|
3341
|
+
throw new Error(
|
|
3342
|
+
`Invalid workspace file: repos.${name}.path must be a non-empty string`
|
|
3343
|
+
);
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
var WORKSPACE_FILENAME;
|
|
3348
|
+
var init_workspace = __esm({
|
|
3349
|
+
"src/lib/workspace.ts"() {
|
|
3350
|
+
"use strict";
|
|
3351
|
+
init_fs_ops();
|
|
3352
|
+
WORKSPACE_FILENAME = ".flydocs-workspace.json";
|
|
3353
|
+
}
|
|
3354
|
+
});
|
|
3355
|
+
|
|
3356
|
+
// src/commands/init.ts
|
|
3357
|
+
var init_exports = {};
|
|
3358
|
+
__export(init_exports, {
|
|
3359
|
+
default: () => init_default
|
|
3360
|
+
});
|
|
3361
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
3362
|
+
import { text as text2, confirm as confirm3, select as select2, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
|
|
3363
|
+
import pc8 from "picocolors";
|
|
3364
|
+
import { join as join20 } from "path";
|
|
3365
|
+
import { mkdir as mkdir9, writeFile as writeFile13 } from "fs/promises";
|
|
3366
|
+
import { execFile } from "child_process";
|
|
3367
|
+
import { promisify } from "util";
|
|
3368
|
+
async function resolveAndValidateKey(keyArg, targetDir) {
|
|
3369
|
+
let resolved = await resolveApiKey(keyArg, targetDir);
|
|
3370
|
+
if (!resolved) {
|
|
3371
|
+
console.log(
|
|
3372
|
+
` ${pc8.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
3373
|
+
);
|
|
3374
|
+
console.log();
|
|
3375
|
+
const keyInput = await text2({
|
|
3376
|
+
message: "Enter your API key",
|
|
3377
|
+
placeholder: "fdk_...",
|
|
3378
|
+
validate(value) {
|
|
3379
|
+
if (!value.trim()) return "API key is required";
|
|
3380
|
+
if (detectKeyType(value.trim()) !== "relay") {
|
|
3381
|
+
return "Key must start with fdk_ (FlyDocs API key)";
|
|
3382
|
+
}
|
|
3383
|
+
return void 0;
|
|
3384
|
+
}
|
|
3385
|
+
});
|
|
3386
|
+
if (isCancel4(keyInput)) {
|
|
3387
|
+
cancel3("Init cancelled.");
|
|
3388
|
+
process.exit(0);
|
|
3389
|
+
}
|
|
3390
|
+
resolved = { key: keyInput.trim(), source: "prompt" };
|
|
3391
|
+
} else {
|
|
3392
|
+
printInfo(`Using key from ${resolved.source}`);
|
|
3393
|
+
}
|
|
3394
|
+
const apiKey = resolved.key;
|
|
3395
|
+
printInfo("Validating API key...");
|
|
3396
|
+
try {
|
|
3397
|
+
const result = await validateRelayKey(apiKey);
|
|
3398
|
+
if (!result.valid) {
|
|
3399
|
+
printError("Invalid API key. Check your key and try again.");
|
|
3400
|
+
process.exit(1);
|
|
3401
|
+
}
|
|
3402
|
+
printStatus(`Authenticated with ${pc8.bold(result.org)}`);
|
|
3403
|
+
} catch {
|
|
3404
|
+
printError(
|
|
3405
|
+
"Could not reach FlyDocs API. Check your network and try again."
|
|
3406
|
+
);
|
|
3407
|
+
console.log(
|
|
3408
|
+
` ${pc8.dim("flydocs init requires server access. For offline use, run flydocs install instead.")}`
|
|
3409
|
+
);
|
|
3410
|
+
process.exit(1);
|
|
3411
|
+
}
|
|
3412
|
+
const workspaces = await fetchWorkspaces(apiKey);
|
|
3413
|
+
let workspaceId;
|
|
3414
|
+
if (workspaces.length === 0) {
|
|
3415
|
+
printError("No workspaces found. Create one at app.flydocs.ai first.");
|
|
3416
|
+
process.exit(1);
|
|
3417
|
+
} else if (workspaces.length === 1) {
|
|
3418
|
+
workspaceId = workspaces[0].id;
|
|
3419
|
+
printStatus(`Workspace: ${pc8.bold(workspaces[0].name)}`);
|
|
3420
|
+
} else {
|
|
3421
|
+
const choice = await select2({
|
|
3422
|
+
message: "Select workspace",
|
|
3423
|
+
options: workspaces.map((ws) => ({
|
|
3424
|
+
value: ws.id,
|
|
3425
|
+
label: ws.name
|
|
3426
|
+
}))
|
|
3427
|
+
});
|
|
3428
|
+
if (isCancel4(choice)) {
|
|
3429
|
+
cancel3("Init cancelled.");
|
|
3430
|
+
process.exit(0);
|
|
3431
|
+
}
|
|
3432
|
+
workspaceId = choice;
|
|
3433
|
+
}
|
|
3434
|
+
await writeGlobalCredential({
|
|
3435
|
+
apiKey,
|
|
3436
|
+
workspaceId,
|
|
3437
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3438
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
3439
|
+
});
|
|
3440
|
+
await checkCredentialPermissions();
|
|
3441
|
+
return { apiKey, workspaceId };
|
|
3442
|
+
}
|
|
3443
|
+
async function pullServerConfig(apiKey, targetDir, workspaceId, forceIncludeContext = false) {
|
|
3444
|
+
printInfo("Pulling config from server...");
|
|
3445
|
+
const isFirstInit = forceIncludeContext || !await pathExists(join20(targetDir, ".flydocs", "config.json"));
|
|
3446
|
+
try {
|
|
3447
|
+
const response = await fetchConfigV2(apiKey, {
|
|
3448
|
+
includeContext: isFirstInit,
|
|
3449
|
+
workspaceId
|
|
3450
|
+
});
|
|
3451
|
+
if (!response.valid) {
|
|
3452
|
+
printError("Server returned invalid config. Contact support.");
|
|
3453
|
+
process.exit(1);
|
|
3454
|
+
}
|
|
3455
|
+
return response;
|
|
3456
|
+
} catch (err) {
|
|
3457
|
+
if (err instanceof RelayError) {
|
|
3458
|
+
printError(`Server error: ${err.message}`);
|
|
3459
|
+
if (err.status === 401) {
|
|
3460
|
+
console.log(
|
|
3461
|
+
` ${pc8.dim("Your API key may be revoked. Run flydocs auth with a new key.")}`
|
|
3462
|
+
);
|
|
3463
|
+
}
|
|
3464
|
+
} else {
|
|
3465
|
+
printError("Could not reach server. Check your network.");
|
|
3466
|
+
}
|
|
3467
|
+
process.exit(1);
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
async function initSingleRepo(targetDir, apiKey, serverResponse) {
|
|
3471
|
+
const actions = [];
|
|
3472
|
+
const skipped = [];
|
|
3473
|
+
await mkdir9(join20(targetDir, ".flydocs"), { recursive: true });
|
|
3474
|
+
const configWithHash = applyConfigHash(serverResponse.config);
|
|
3475
|
+
await writeConfig(targetDir, configWithHash);
|
|
3476
|
+
actions.push("Wrote .flydocs/config.json (from server)");
|
|
3477
|
+
if (serverResponse.identity) {
|
|
3478
|
+
await writeGlobalIdentity({
|
|
3479
|
+
name: serverResponse.identity.name,
|
|
3480
|
+
email: serverResponse.identity.email,
|
|
3481
|
+
providerIdentities: serverResponse.identity.providerIdentities
|
|
3482
|
+
});
|
|
3483
|
+
actions.push("Wrote ~/.flydocs/me.json");
|
|
3484
|
+
}
|
|
3485
|
+
if (serverResponse.context) {
|
|
3486
|
+
const contextDir = join20(targetDir, "flydocs", "context");
|
|
3487
|
+
await mkdir9(contextDir, { recursive: true });
|
|
3488
|
+
const projectMdPath = join20(contextDir, "project.md");
|
|
3489
|
+
if (!await pathExists(projectMdPath)) {
|
|
3490
|
+
await writeFile13(projectMdPath, serverResponse.context.projectMd, "utf-8");
|
|
3491
|
+
actions.push("Wrote flydocs/context/project.md");
|
|
3492
|
+
} else {
|
|
3493
|
+
skipped.push("flydocs/context/project.md (already exists)");
|
|
3494
|
+
}
|
|
3495
|
+
const serviceJsonPath = join20(contextDir, "service.json");
|
|
3496
|
+
if (serverResponse.context.serviceJson && !await pathExists(serviceJsonPath)) {
|
|
3497
|
+
await writeFile13(
|
|
3498
|
+
serviceJsonPath,
|
|
3499
|
+
JSON.stringify(serverResponse.context.serviceJson, null, 2) + "\n",
|
|
3500
|
+
"utf-8"
|
|
3501
|
+
);
|
|
3502
|
+
actions.push("Wrote flydocs/context/service.json");
|
|
3503
|
+
} else if (await pathExists(serviceJsonPath)) {
|
|
3504
|
+
skipped.push("flydocs/context/service.json (already exists)");
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
await ensureGitignore(targetDir);
|
|
3508
|
+
await ensurePlatformIgnores(targetDir);
|
|
3509
|
+
actions.push("Updated ignore files");
|
|
3510
|
+
const artifacts = await scanArtifacts(targetDir);
|
|
3511
|
+
if (artifacts.length > 0) {
|
|
3512
|
+
await executeCleanup(targetDir, artifacts);
|
|
3513
|
+
actions.push(`Cleaned ${artifacts.length} legacy artifact(s)`);
|
|
3514
|
+
}
|
|
3515
|
+
return { actions, skipped };
|
|
3516
|
+
}
|
|
3517
|
+
async function checkGitFreshness(repoDir) {
|
|
3518
|
+
const hasGit = await pathExists(join20(repoDir, ".git"));
|
|
3519
|
+
if (!hasGit) return;
|
|
3520
|
+
try {
|
|
3521
|
+
await execFileAsync("git", ["-C", repoDir, "fetch", "--quiet"], {
|
|
3522
|
+
timeout: 15e3
|
|
3523
|
+
});
|
|
3524
|
+
const { stdout } = await execFileAsync(
|
|
3525
|
+
"git",
|
|
3526
|
+
["-C", repoDir, "rev-list", "--count", "HEAD..@{upstream}"],
|
|
3527
|
+
{ timeout: 5e3 }
|
|
3528
|
+
);
|
|
3529
|
+
const behind = parseInt(stdout.trim(), 10);
|
|
3530
|
+
if (behind <= 0) return;
|
|
3531
|
+
printWarning(`Repo is ${behind} commit(s) behind remote.`);
|
|
3532
|
+
const shouldPull = await confirm3({
|
|
3533
|
+
message: "Pull latest before init?"
|
|
3534
|
+
});
|
|
3535
|
+
if (isCancel4(shouldPull) || !shouldPull) {
|
|
3536
|
+
printInfo("Continuing without pulling.");
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
await execFileAsync("git", ["-C", repoDir, "pull", "--ff-only"], {
|
|
3540
|
+
timeout: 3e4
|
|
3541
|
+
});
|
|
3542
|
+
printStatus("Pulled latest from remote.");
|
|
3543
|
+
} catch {
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
async function isEmptyOrNonRepo(dir) {
|
|
3547
|
+
const hasGit = await pathExists(join20(dir, ".git"));
|
|
3548
|
+
if (hasGit) return false;
|
|
3549
|
+
const siblings = await detectSiblingRepos(dir);
|
|
3550
|
+
if (Object.keys(siblings).length >= 2) return false;
|
|
3551
|
+
return true;
|
|
3552
|
+
}
|
|
3553
|
+
function repoShortName(fullName) {
|
|
3554
|
+
return fullName.includes("/") ? fullName.split("/").pop() : fullName;
|
|
3555
|
+
}
|
|
3556
|
+
async function runCloneAndInit(targetDir, keyArg, repos, apiKey, workspaceId) {
|
|
3557
|
+
if (repos.length === 1) {
|
|
3558
|
+
const repo = repos[0];
|
|
3559
|
+
const shortName = repoShortName(repo.name);
|
|
3560
|
+
printInfo(`Workspace has 1 repo: ${pc8.bold(shortName)}`);
|
|
3561
|
+
const shouldClone = await confirm3({
|
|
3562
|
+
message: `Clone ${shortName} and initialize?`
|
|
3563
|
+
});
|
|
3564
|
+
if (isCancel4(shouldClone) || !shouldClone) {
|
|
3565
|
+
cancel3("Init cancelled.");
|
|
3566
|
+
process.exit(0);
|
|
3567
|
+
}
|
|
3568
|
+
const cloneDir = join20(targetDir, shortName);
|
|
3569
|
+
printInfo(`Cloning ${shortName}...`);
|
|
3570
|
+
await execFileAsync("git", ["clone", repo.cloneUrl, cloneDir], {
|
|
3571
|
+
timeout: 6e4
|
|
3572
|
+
});
|
|
3573
|
+
printStatus(`Cloned ${shortName}`);
|
|
3574
|
+
const serverResponse = await pullServerConfig(
|
|
3575
|
+
apiKey,
|
|
3576
|
+
cloneDir,
|
|
3577
|
+
workspaceId,
|
|
3578
|
+
true
|
|
3579
|
+
// always include context on fresh clone
|
|
3580
|
+
);
|
|
3581
|
+
const { actions, skipped } = await initSingleRepo(
|
|
3582
|
+
cloneDir,
|
|
3583
|
+
apiKey,
|
|
3584
|
+
serverResponse
|
|
3585
|
+
);
|
|
3586
|
+
actions.unshift("Stored credential globally (~/.flydocs/credentials)");
|
|
3587
|
+
actions.unshift(`Cloned ${shortName}`);
|
|
3588
|
+
printInitReport(
|
|
3589
|
+
actions,
|
|
3590
|
+
skipped,
|
|
3591
|
+
serverResponse.warnings,
|
|
3592
|
+
!!serverResponse.context
|
|
3593
|
+
);
|
|
3594
|
+
} else {
|
|
3595
|
+
const repoNames = repos.map((r) => repoShortName(r.name));
|
|
3596
|
+
printInfo(`Workspace has ${repos.length} repos:`);
|
|
3597
|
+
for (const name of repoNames) {
|
|
3598
|
+
console.log(` ${pc8.cyan(name)}`);
|
|
3599
|
+
}
|
|
3600
|
+
console.log();
|
|
3601
|
+
const shouldClone = await confirm3({
|
|
3602
|
+
message: `Clone and initialize all ${repos.length} repos?`
|
|
3603
|
+
});
|
|
3604
|
+
if (isCancel4(shouldClone) || !shouldClone) {
|
|
3605
|
+
cancel3("Init cancelled.");
|
|
3606
|
+
process.exit(0);
|
|
3607
|
+
}
|
|
3608
|
+
const allActions = [
|
|
3609
|
+
"Stored credential globally (~/.flydocs/credentials)"
|
|
3610
|
+
];
|
|
3611
|
+
const allSkipped = [];
|
|
3612
|
+
let allWarnings = [];
|
|
3613
|
+
let firstResponse;
|
|
3614
|
+
for (let i = 0; i < repos.length; i++) {
|
|
3615
|
+
const repo = repos[i];
|
|
3616
|
+
const shortName = repoNames[i];
|
|
3617
|
+
const cloneDir = join20(targetDir, shortName);
|
|
3618
|
+
console.log();
|
|
3619
|
+
if (await pathExists(join20(cloneDir, ".git"))) {
|
|
3620
|
+
printInfo(`${pc8.bold(shortName)} already cloned, initializing...`);
|
|
3621
|
+
await checkGitFreshness(cloneDir);
|
|
3622
|
+
} else {
|
|
3623
|
+
printInfo(`Cloning ${pc8.bold(shortName)}...`);
|
|
3624
|
+
await execFileAsync("git", ["clone", repo.cloneUrl, cloneDir], {
|
|
3625
|
+
timeout: 6e4
|
|
3626
|
+
});
|
|
3627
|
+
allActions.push(`Cloned ${shortName}`);
|
|
3628
|
+
}
|
|
3629
|
+
const serverResponse = await pullServerConfig(
|
|
3630
|
+
apiKey,
|
|
3631
|
+
cloneDir,
|
|
3632
|
+
workspaceId,
|
|
3633
|
+
true
|
|
3634
|
+
// always include context on fresh clone
|
|
3635
|
+
);
|
|
3636
|
+
if (!firstResponse) firstResponse = serverResponse;
|
|
3637
|
+
const { actions, skipped } = await initSingleRepo(
|
|
3638
|
+
cloneDir,
|
|
3639
|
+
apiKey,
|
|
3640
|
+
serverResponse
|
|
3641
|
+
);
|
|
3642
|
+
for (const action of actions) {
|
|
3643
|
+
allActions.push(`[${shortName}] ${action}`);
|
|
3644
|
+
}
|
|
3645
|
+
for (const skip of skipped) {
|
|
3646
|
+
allSkipped.push(`[${shortName}] ${skip}`);
|
|
3647
|
+
}
|
|
3648
|
+
allWarnings = [...allWarnings, ...serverResponse.warnings];
|
|
3649
|
+
printStatus(`${shortName} initialized`);
|
|
3650
|
+
}
|
|
3651
|
+
if (firstResponse) {
|
|
3652
|
+
const repoEntries = {};
|
|
3653
|
+
for (const name of repoNames) {
|
|
3654
|
+
repoEntries[name] = { path: `./${name}` };
|
|
3655
|
+
}
|
|
3656
|
+
const workspaceFile = buildWorkspaceFile(
|
|
3657
|
+
firstResponse.workspaceId,
|
|
3658
|
+
repoEntries
|
|
3659
|
+
);
|
|
3660
|
+
await writeWorkspaceFile(targetDir, workspaceFile);
|
|
3661
|
+
allActions.push(`.flydocs-workspace.json (${repos.length} repos)`);
|
|
3662
|
+
const contextDir = join20(targetDir, "flydocs", "context");
|
|
3663
|
+
const workspaceMdPath = join20(contextDir, "workspace.md");
|
|
3664
|
+
if (!await pathExists(workspaceMdPath)) {
|
|
3665
|
+
await mkdir9(contextDir, { recursive: true });
|
|
3666
|
+
const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(targetDir, workspaceFile);
|
|
3667
|
+
await writeFile13(workspaceMdPath, content, "utf-8");
|
|
3668
|
+
const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
|
|
3669
|
+
allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
console.log();
|
|
3673
|
+
printInitReport(
|
|
3674
|
+
allActions,
|
|
3675
|
+
allSkipped,
|
|
3676
|
+
allWarnings,
|
|
3677
|
+
!!firstResponse?.context
|
|
3678
|
+
);
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
async function isParentDirectory(dir) {
|
|
3682
|
+
const hasGit = await pathExists(join20(dir, ".git"));
|
|
3683
|
+
if (hasGit) return false;
|
|
3684
|
+
const repos = await detectSiblingRepos(dir);
|
|
3685
|
+
return Object.keys(repos).length >= 2;
|
|
3686
|
+
}
|
|
3687
|
+
async function runMultiRepoInit(parentDir, keyArg) {
|
|
3688
|
+
const repos = await detectSiblingRepos(parentDir);
|
|
3689
|
+
const repoNames = Object.keys(repos).sort();
|
|
3690
|
+
printInfo(`Detected ${repoNames.length} repos:`);
|
|
3691
|
+
for (const name of repoNames) {
|
|
3692
|
+
console.log(` ${pc8.cyan(name)}`);
|
|
3693
|
+
}
|
|
3694
|
+
console.log();
|
|
3695
|
+
const shouldContinue = await confirm3({
|
|
3696
|
+
message: `Initialize all ${repoNames.length} repos?`
|
|
3697
|
+
});
|
|
3698
|
+
if (isCancel4(shouldContinue) || !shouldContinue) {
|
|
3699
|
+
cancel3("Init cancelled.");
|
|
3700
|
+
process.exit(0);
|
|
3701
|
+
}
|
|
3702
|
+
const firstRepoDir = join20(parentDir, repoNames[0]);
|
|
3703
|
+
const { apiKey, workspaceId } = await resolveAndValidateKey(
|
|
3704
|
+
keyArg,
|
|
3705
|
+
firstRepoDir
|
|
3706
|
+
);
|
|
3707
|
+
const allActions = [
|
|
3708
|
+
"Stored credential globally (~/.flydocs/credentials)"
|
|
3709
|
+
];
|
|
3710
|
+
const allSkipped = [];
|
|
3711
|
+
let allWarnings = [];
|
|
3712
|
+
let firstResponse;
|
|
3713
|
+
for (const repoName of repoNames) {
|
|
3714
|
+
const repoDir = join20(parentDir, repoName);
|
|
3715
|
+
console.log();
|
|
3716
|
+
printInfo(`Initializing ${pc8.bold(repoName)}...`);
|
|
3717
|
+
await checkGitFreshness(repoDir);
|
|
3718
|
+
const serverResponse = await pullServerConfig(apiKey, repoDir, workspaceId);
|
|
3719
|
+
if (!firstResponse) firstResponse = serverResponse;
|
|
3720
|
+
const { actions, skipped } = await initSingleRepo(
|
|
3721
|
+
repoDir,
|
|
3722
|
+
apiKey,
|
|
3723
|
+
serverResponse
|
|
3724
|
+
);
|
|
3725
|
+
for (const action of actions) {
|
|
3726
|
+
allActions.push(`[${repoName}] ${action}`);
|
|
3727
|
+
}
|
|
3728
|
+
for (const skip of skipped) {
|
|
3729
|
+
allSkipped.push(`[${repoName}] ${skip}`);
|
|
3730
|
+
}
|
|
3731
|
+
allWarnings = [...allWarnings, ...serverResponse.warnings];
|
|
3732
|
+
printStatus(`${repoName} initialized`);
|
|
3733
|
+
}
|
|
3734
|
+
if (firstResponse) {
|
|
3735
|
+
const existing = await readWorkspaceFile(parentDir);
|
|
3736
|
+
if (existing) {
|
|
3737
|
+
allSkipped.push(".flydocs-workspace.json (already exists)");
|
|
3738
|
+
} else {
|
|
3739
|
+
const workspaceFile = buildWorkspaceFile(
|
|
3740
|
+
firstResponse.workspaceId,
|
|
3741
|
+
repos
|
|
3742
|
+
);
|
|
3743
|
+
await writeWorkspaceFile(parentDir, workspaceFile);
|
|
3744
|
+
allActions.push(`.flydocs-workspace.json (${repoNames.length} repos)`);
|
|
3745
|
+
}
|
|
3746
|
+
const contextDir = join20(parentDir, "flydocs", "context");
|
|
3747
|
+
const workspaceMdPath = join20(contextDir, "workspace.md");
|
|
3748
|
+
if (await pathExists(workspaceMdPath)) {
|
|
3749
|
+
allSkipped.push("flydocs/context/workspace.md (already exists)");
|
|
3750
|
+
} else {
|
|
3751
|
+
await mkdir9(contextDir, { recursive: true });
|
|
3752
|
+
const workspaceJson = await readWorkspaceFile(parentDir) ?? buildWorkspaceFile(firstResponse.workspaceId, repos);
|
|
3753
|
+
const content = firstResponse.context?.workspaceMd ?? await generateWorkspaceMd(parentDir, workspaceJson);
|
|
3754
|
+
await writeFile13(workspaceMdPath, content, "utf-8");
|
|
3755
|
+
const source = firstResponse.context?.workspaceMd ? "from server" : "generated locally";
|
|
3756
|
+
allActions.push(`Wrote flydocs/context/workspace.md (${source})`);
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
console.log();
|
|
3760
|
+
printInitReport(
|
|
3761
|
+
allActions,
|
|
3762
|
+
allSkipped,
|
|
3763
|
+
allWarnings,
|
|
3764
|
+
!!firstResponse?.context
|
|
3765
|
+
);
|
|
3766
|
+
}
|
|
3767
|
+
function printInitReport(actions, skipped, warnings, hasContext) {
|
|
3768
|
+
console.log();
|
|
3769
|
+
const lines = [];
|
|
3770
|
+
for (const action of actions) {
|
|
3771
|
+
lines.push(`${pc8.green("+")} ${action}`);
|
|
3772
|
+
}
|
|
3773
|
+
for (const skip of skipped) {
|
|
3774
|
+
lines.push(`${pc8.dim("-")} ${skip}`);
|
|
3775
|
+
}
|
|
3776
|
+
if (!hasContext) {
|
|
3777
|
+
lines.push("");
|
|
3778
|
+
lines.push(
|
|
3779
|
+
`${pc8.yellow("!")} No generated context yet. An admin can generate it from the portal.`
|
|
3780
|
+
);
|
|
3781
|
+
}
|
|
3782
|
+
if (warnings.length > 0) {
|
|
3783
|
+
lines.push("");
|
|
3784
|
+
for (const warning of warnings) {
|
|
3785
|
+
lines.push(`${pc8.yellow("!")} ${warning}`);
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
printCompletionBox("FlyDocs Initialized", lines);
|
|
3789
|
+
console.log();
|
|
3790
|
+
console.log(` Next steps:`);
|
|
3791
|
+
console.log(
|
|
3792
|
+
` ${pc8.cyan("flydocs sync")} Pull latest config and templates`
|
|
3793
|
+
);
|
|
3794
|
+
console.log(
|
|
3795
|
+
` ${pc8.cyan("cursor .")} ${pc8.dim("or")} ${pc8.cyan("code .")} Open your IDE, then use ${pc8.bold("/start-session")}`
|
|
3796
|
+
);
|
|
3797
|
+
console.log();
|
|
3798
|
+
}
|
|
3799
|
+
var execFileAsync, init_default;
|
|
3800
|
+
var init_init = __esm({
|
|
3801
|
+
"src/commands/init.ts"() {
|
|
3802
|
+
"use strict";
|
|
3803
|
+
init_ui();
|
|
3804
|
+
init_api_key();
|
|
3805
|
+
init_global_config();
|
|
3806
|
+
init_config();
|
|
3807
|
+
init_config_integrity();
|
|
3808
|
+
init_gitignore();
|
|
3809
|
+
init_fs_ops();
|
|
3810
|
+
init_relay_client();
|
|
3811
|
+
init_cleanup();
|
|
3812
|
+
init_workspace();
|
|
3813
|
+
execFileAsync = promisify(execFile);
|
|
3814
|
+
init_default = defineCommand3({
|
|
3815
|
+
meta: {
|
|
3816
|
+
name: "init",
|
|
3817
|
+
description: "Initialize FlyDocs in this project (unified setup)"
|
|
3818
|
+
},
|
|
3819
|
+
args: {
|
|
3820
|
+
key: {
|
|
3821
|
+
type: "string",
|
|
3822
|
+
description: "FlyDocs API key (fdk_...)"
|
|
3823
|
+
},
|
|
3824
|
+
path: {
|
|
3825
|
+
type: "string",
|
|
3826
|
+
description: "Path to project directory"
|
|
3827
|
+
}
|
|
3828
|
+
},
|
|
3829
|
+
async run({ args }) {
|
|
3830
|
+
const targetDir = args.path ?? process.cwd();
|
|
3831
|
+
console.log();
|
|
3832
|
+
console.log(` ${pc8.bold("FlyDocs Init")}`);
|
|
3833
|
+
console.log();
|
|
3834
|
+
if (await isParentDirectory(targetDir)) {
|
|
3835
|
+
await runMultiRepoInit(targetDir, args.key);
|
|
3836
|
+
return;
|
|
3837
|
+
}
|
|
3838
|
+
if (await isEmptyOrNonRepo(targetDir)) {
|
|
3839
|
+
const { apiKey: apiKey2, workspaceId: workspaceId2 } = await resolveAndValidateKey(
|
|
3840
|
+
args.key,
|
|
3841
|
+
targetDir
|
|
3842
|
+
);
|
|
3843
|
+
const preflightResponse = await pullServerConfig(
|
|
3844
|
+
apiKey2,
|
|
3845
|
+
targetDir,
|
|
3846
|
+
workspaceId2
|
|
3847
|
+
);
|
|
3848
|
+
if (preflightResponse.repos && preflightResponse.repos.length > 0) {
|
|
3849
|
+
await runCloneAndInit(
|
|
3850
|
+
targetDir,
|
|
3851
|
+
args.key,
|
|
3852
|
+
preflightResponse.repos,
|
|
3853
|
+
apiKey2,
|
|
3854
|
+
workspaceId2
|
|
3855
|
+
);
|
|
3856
|
+
return;
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
await checkGitFreshness(targetDir);
|
|
3860
|
+
const { apiKey, workspaceId } = await resolveAndValidateKey(
|
|
3861
|
+
args.key,
|
|
3862
|
+
targetDir
|
|
3863
|
+
);
|
|
3864
|
+
const serverResponse = await pullServerConfig(
|
|
3865
|
+
apiKey,
|
|
3866
|
+
targetDir,
|
|
3867
|
+
workspaceId,
|
|
3868
|
+
true
|
|
3869
|
+
// init always requests context
|
|
3870
|
+
);
|
|
3871
|
+
const { actions, skipped } = await initSingleRepo(
|
|
3872
|
+
targetDir,
|
|
3873
|
+
apiKey,
|
|
3874
|
+
serverResponse
|
|
3875
|
+
);
|
|
3876
|
+
actions.unshift("Stored credential globally (~/.flydocs/credentials)");
|
|
3877
|
+
const topology = serverResponse.config.topology;
|
|
3878
|
+
if (topology?.type === 4 && topology.label === "sibling-repos") {
|
|
3879
|
+
const parentDir = join20(targetDir, "..");
|
|
3880
|
+
const existing = await readWorkspaceFile(parentDir);
|
|
3881
|
+
if (existing) {
|
|
3882
|
+
skipped.push(".flydocs-workspace.json (already exists)");
|
|
3883
|
+
} else {
|
|
3884
|
+
const repos = await detectSiblingRepos(parentDir);
|
|
3885
|
+
if (Object.keys(repos).length > 0) {
|
|
3886
|
+
const workspaceFile = buildWorkspaceFile(
|
|
3887
|
+
serverResponse.workspaceId,
|
|
3888
|
+
repos
|
|
3889
|
+
);
|
|
3890
|
+
await writeWorkspaceFile(parentDir, workspaceFile);
|
|
3891
|
+
actions.push(
|
|
3892
|
+
`.flydocs-workspace.json (${Object.keys(repos).length} repos detected)`
|
|
3893
|
+
);
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
printInitReport(
|
|
3898
|
+
actions,
|
|
3899
|
+
skipped,
|
|
3900
|
+
serverResponse.warnings,
|
|
3901
|
+
!!serverResponse.context
|
|
3902
|
+
);
|
|
3903
|
+
}
|
|
3904
|
+
});
|
|
3905
|
+
}
|
|
3906
|
+
});
|
|
3907
|
+
|
|
3908
|
+
// src/commands/update.ts
|
|
3909
|
+
var update_exports = {};
|
|
3910
|
+
__export(update_exports, {
|
|
3911
|
+
default: () => update_default
|
|
3912
|
+
});
|
|
3913
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
3914
|
+
import { resolve as resolve4, join as join21 } from "path";
|
|
3915
|
+
import { mkdir as mkdir10, cp as cp2, readFile as readFile15, readdir as readdir5, rm as rm5 } from "fs/promises";
|
|
3916
|
+
import { select as select3, text as text3, confirm as confirm4, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
|
|
3917
|
+
import pc9 from "picocolors";
|
|
3918
|
+
var update_default;
|
|
3919
|
+
var init_update = __esm({
|
|
3920
|
+
"src/commands/update.ts"() {
|
|
3921
|
+
"use strict";
|
|
3922
|
+
init_template();
|
|
3923
|
+
init_version();
|
|
3924
|
+
init_fs_ops();
|
|
3925
|
+
init_ui();
|
|
3926
|
+
init_config();
|
|
3927
|
+
init_skills();
|
|
3928
|
+
init_stack();
|
|
3929
|
+
init_community_skills();
|
|
3930
|
+
init_deprecated();
|
|
3931
|
+
init_gitignore();
|
|
3932
|
+
init_post_install();
|
|
3933
|
+
init_update_check();
|
|
3934
|
+
init_telemetry();
|
|
3935
|
+
init_integrity();
|
|
3936
|
+
update_default = defineCommand4({
|
|
3937
|
+
meta: {
|
|
3938
|
+
name: "update",
|
|
3939
|
+
description: "Update an existing FlyDocs installation"
|
|
3940
|
+
},
|
|
3941
|
+
args: {
|
|
3942
|
+
path: {
|
|
3943
|
+
type: "string",
|
|
3944
|
+
description: "Update the specified project directory"
|
|
3945
|
+
},
|
|
3946
|
+
here: {
|
|
3947
|
+
type: "boolean",
|
|
3948
|
+
description: "Update in the current directory",
|
|
3949
|
+
default: false
|
|
3950
|
+
},
|
|
3951
|
+
tier: {
|
|
3952
|
+
type: "string",
|
|
3953
|
+
description: "Change tier: 'local' (free) or 'cloud' (managed)"
|
|
3954
|
+
},
|
|
3955
|
+
force: {
|
|
3956
|
+
type: "boolean",
|
|
3957
|
+
description: "Force update even if versions match",
|
|
3958
|
+
default: false
|
|
3959
|
+
},
|
|
3960
|
+
"local-source": {
|
|
3961
|
+
type: "boolean",
|
|
3962
|
+
description: "Force use of local template/ directory",
|
|
2629
3963
|
default: false
|
|
2630
3964
|
},
|
|
2631
3965
|
yes: {
|
|
@@ -2644,11 +3978,11 @@ var init_update = __esm({
|
|
|
2644
3978
|
await capture("update_started", { template_version: version });
|
|
2645
3979
|
let targetDir;
|
|
2646
3980
|
if (args.path) {
|
|
2647
|
-
targetDir =
|
|
3981
|
+
targetDir = resolve4(args.path.replace(/^~/, process.env.HOME ?? "~"));
|
|
2648
3982
|
} else if (args.here) {
|
|
2649
3983
|
targetDir = process.cwd();
|
|
2650
3984
|
} else {
|
|
2651
|
-
const choice = await
|
|
3985
|
+
const choice = await select3({
|
|
2652
3986
|
message: "Which project would you like to update?",
|
|
2653
3987
|
options: [
|
|
2654
3988
|
{
|
|
@@ -2661,21 +3995,21 @@ var init_update = __esm({
|
|
|
2661
3995
|
}
|
|
2662
3996
|
]
|
|
2663
3997
|
});
|
|
2664
|
-
if (
|
|
2665
|
-
|
|
3998
|
+
if (isCancel5(choice)) {
|
|
3999
|
+
cancel4("Update cancelled.");
|
|
2666
4000
|
process.exit(0);
|
|
2667
4001
|
}
|
|
2668
4002
|
if (choice === "cwd") {
|
|
2669
4003
|
targetDir = process.cwd();
|
|
2670
4004
|
} else {
|
|
2671
|
-
const enteredPath = await
|
|
4005
|
+
const enteredPath = await text3({
|
|
2672
4006
|
message: "Enter project path:"
|
|
2673
4007
|
});
|
|
2674
|
-
if (
|
|
2675
|
-
|
|
4008
|
+
if (isCancel5(enteredPath)) {
|
|
4009
|
+
cancel4("Update cancelled.");
|
|
2676
4010
|
process.exit(0);
|
|
2677
4011
|
}
|
|
2678
|
-
targetDir =
|
|
4012
|
+
targetDir = resolve4(
|
|
2679
4013
|
enteredPath.replace(/^~/, process.env.HOME ?? "~")
|
|
2680
4014
|
);
|
|
2681
4015
|
}
|
|
@@ -2684,11 +4018,11 @@ var init_update = __esm({
|
|
|
2684
4018
|
printError(`Directory does not exist: ${targetDir}`);
|
|
2685
4019
|
process.exit(1);
|
|
2686
4020
|
}
|
|
2687
|
-
targetDir =
|
|
4021
|
+
targetDir = resolve4(targetDir);
|
|
2688
4022
|
process.chdir(targetDir);
|
|
2689
|
-
const hasVersion = await pathExists(
|
|
4023
|
+
const hasVersion = await pathExists(join21(targetDir, ".flydocs", "version"));
|
|
2690
4024
|
const hasConfig = await pathExists(
|
|
2691
|
-
|
|
4025
|
+
join21(targetDir, ".flydocs", "config.json")
|
|
2692
4026
|
);
|
|
2693
4027
|
if (!hasVersion && !hasConfig) {
|
|
2694
4028
|
printError(`Not a FlyDocs project: ${targetDir}`);
|
|
@@ -2699,8 +4033,8 @@ var init_update = __esm({
|
|
|
2699
4033
|
console.log();
|
|
2700
4034
|
let currentVersion = "0.1.0";
|
|
2701
4035
|
if (hasVersion) {
|
|
2702
|
-
const vContent = await
|
|
2703
|
-
|
|
4036
|
+
const vContent = await readFile15(
|
|
4037
|
+
join21(targetDir, ".flydocs", "version"),
|
|
2704
4038
|
"utf-8"
|
|
2705
4039
|
);
|
|
2706
4040
|
currentVersion = vContent.trim();
|
|
@@ -2719,10 +4053,10 @@ var init_update = __esm({
|
|
|
2719
4053
|
printWarning(
|
|
2720
4054
|
`Project version (${currentVersion}) is newer than installer (${version})`
|
|
2721
4055
|
);
|
|
2722
|
-
const shouldContinue = await
|
|
4056
|
+
const shouldContinue = await confirm4({
|
|
2723
4057
|
message: "Continue anyway?"
|
|
2724
4058
|
});
|
|
2725
|
-
if (
|
|
4059
|
+
if (isCancel5(shouldContinue) || !shouldContinue) {
|
|
2726
4060
|
process.exit(0);
|
|
2727
4061
|
}
|
|
2728
4062
|
}
|
|
@@ -2734,10 +4068,10 @@ var init_update = __esm({
|
|
|
2734
4068
|
});
|
|
2735
4069
|
console.log(`Updating: v${currentVersion} \u2192 v${version}`);
|
|
2736
4070
|
console.log();
|
|
2737
|
-
const changelogPath =
|
|
4071
|
+
const changelogPath = join21(templateDir, "CHANGELOG.md");
|
|
2738
4072
|
const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
|
|
2739
4073
|
if (whatsNew.length > 0) {
|
|
2740
|
-
console.log(
|
|
4074
|
+
console.log(pc9.cyan("What's new:"));
|
|
2741
4075
|
console.log();
|
|
2742
4076
|
for (const entry of whatsNew) {
|
|
2743
4077
|
console.log(` ${entry}`);
|
|
@@ -2746,36 +4080,37 @@ var init_update = __esm({
|
|
|
2746
4080
|
}
|
|
2747
4081
|
const now = /* @__PURE__ */ new Date();
|
|
2748
4082
|
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")}`;
|
|
2749
|
-
const backupDir =
|
|
2750
|
-
await
|
|
4083
|
+
const backupDir = join21(targetDir, ".flydocs", `backup-${ts}`);
|
|
4084
|
+
await mkdir10(backupDir, { recursive: true });
|
|
2751
4085
|
if (hasConfig) {
|
|
2752
4086
|
await cp2(
|
|
2753
|
-
|
|
2754
|
-
|
|
4087
|
+
join21(targetDir, ".flydocs", "config.json"),
|
|
4088
|
+
join21(backupDir, "config.json")
|
|
2755
4089
|
);
|
|
2756
4090
|
printStatus(`Config backed up to .flydocs/backup-${ts}/`);
|
|
2757
4091
|
}
|
|
2758
4092
|
try {
|
|
2759
|
-
const flydocsDir =
|
|
2760
|
-
const entries = await
|
|
4093
|
+
const flydocsDir = join21(targetDir, ".flydocs");
|
|
4094
|
+
const entries = await readdir5(flydocsDir);
|
|
2761
4095
|
const backups = entries.filter((e) => e.startsWith("backup-")).sort();
|
|
2762
4096
|
if (backups.length > 3) {
|
|
2763
4097
|
const toRemove = backups.slice(0, backups.length - 3);
|
|
2764
4098
|
for (const old of toRemove) {
|
|
2765
|
-
await
|
|
4099
|
+
await rm5(join21(flydocsDir, old), { recursive: true, force: true });
|
|
2766
4100
|
}
|
|
2767
4101
|
}
|
|
2768
4102
|
} catch {
|
|
2769
4103
|
}
|
|
2770
4104
|
let preserved = {
|
|
2771
|
-
tier: "
|
|
4105
|
+
tier: "local",
|
|
2772
4106
|
setupComplete: false,
|
|
2773
|
-
|
|
4107
|
+
workspaceId: null,
|
|
4108
|
+
configVersion: void 0,
|
|
2774
4109
|
workspace: {},
|
|
2775
4110
|
issueLabels: {},
|
|
2776
|
-
statusMapping: {},
|
|
2777
4111
|
detectedStack: {},
|
|
2778
4112
|
skills: {},
|
|
4113
|
+
topology: void 0,
|
|
2779
4114
|
designSystem: null,
|
|
2780
4115
|
aiLabor: {}
|
|
2781
4116
|
};
|
|
@@ -2797,22 +4132,20 @@ var init_update = __esm({
|
|
|
2797
4132
|
} else {
|
|
2798
4133
|
effectiveTier = preserved.tier;
|
|
2799
4134
|
}
|
|
4135
|
+
await ensureDirectories(targetDir, effectiveTier);
|
|
2800
4136
|
console.log("Replacing framework directories...");
|
|
2801
4137
|
await replaceDirectory(
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
);
|
|
2805
|
-
await replaceDirectory(
|
|
2806
|
-
join16(templateDir, ".flydocs", "hooks"),
|
|
2807
|
-
join16(targetDir, ".flydocs", "hooks")
|
|
4138
|
+
join21(templateDir, ".flydocs", "templates"),
|
|
4139
|
+
join21(targetDir, ".flydocs", "templates")
|
|
2808
4140
|
);
|
|
4141
|
+
printStatus(".flydocs/templates");
|
|
2809
4142
|
await replaceDirectory(
|
|
2810
|
-
|
|
2811
|
-
|
|
4143
|
+
join21(templateDir, ".claude", "hooks"),
|
|
4144
|
+
join21(targetDir, ".claude", "hooks")
|
|
2812
4145
|
);
|
|
2813
|
-
printStatus(".
|
|
4146
|
+
printStatus(".claude/hooks");
|
|
2814
4147
|
const hasExistingAgents = await pathExists(
|
|
2815
|
-
|
|
4148
|
+
join21(targetDir, ".claude", "agents")
|
|
2816
4149
|
);
|
|
2817
4150
|
let installAgents;
|
|
2818
4151
|
if (args.yes) {
|
|
@@ -2821,7 +4154,7 @@ var init_update = __esm({
|
|
|
2821
4154
|
installAgents = true;
|
|
2822
4155
|
} else {
|
|
2823
4156
|
console.log();
|
|
2824
|
-
console.log(` ${
|
|
4157
|
+
console.log(` ${pc9.bold(pc9.yellow("Sub-Agents (Recommended)"))}`);
|
|
2825
4158
|
console.log();
|
|
2826
4159
|
console.log(
|
|
2827
4160
|
" Sub-agents are specialized roles (PM, implementation, review,"
|
|
@@ -2833,31 +4166,31 @@ var init_update = __esm({
|
|
|
2833
4166
|
" structure work but are not required \u2014 everything works without them."
|
|
2834
4167
|
);
|
|
2835
4168
|
console.log();
|
|
2836
|
-
const agentConfirm = await
|
|
4169
|
+
const agentConfirm = await confirm4({
|
|
2837
4170
|
message: "Install sub-agents?",
|
|
2838
4171
|
initialValue: true
|
|
2839
4172
|
});
|
|
2840
|
-
if (
|
|
4173
|
+
if (isCancel5(agentConfirm)) {
|
|
2841
4174
|
installAgents = false;
|
|
2842
4175
|
} else {
|
|
2843
4176
|
installAgents = agentConfirm;
|
|
2844
4177
|
}
|
|
2845
4178
|
}
|
|
2846
4179
|
if (installAgents) {
|
|
2847
|
-
const claudeAgentsSrc =
|
|
4180
|
+
const claudeAgentsSrc = join21(templateDir, ".claude", "agents");
|
|
2848
4181
|
if (await pathExists(claudeAgentsSrc)) {
|
|
2849
|
-
await
|
|
4182
|
+
await mkdir10(join21(targetDir, ".claude", "agents"), { recursive: true });
|
|
2850
4183
|
await copyDirectoryContents(
|
|
2851
4184
|
claudeAgentsSrc,
|
|
2852
|
-
|
|
4185
|
+
join21(targetDir, ".claude", "agents")
|
|
2853
4186
|
);
|
|
2854
4187
|
}
|
|
2855
|
-
const cursorAgentsSrc =
|
|
4188
|
+
const cursorAgentsSrc = join21(templateDir, ".cursor", "agents");
|
|
2856
4189
|
if (await pathExists(cursorAgentsSrc)) {
|
|
2857
|
-
await
|
|
4190
|
+
await mkdir10(join21(targetDir, ".cursor", "agents"), { recursive: true });
|
|
2858
4191
|
await copyDirectoryContents(
|
|
2859
4192
|
cursorAgentsSrc,
|
|
2860
|
-
|
|
4193
|
+
join21(targetDir, ".cursor", "agents")
|
|
2861
4194
|
);
|
|
2862
4195
|
}
|
|
2863
4196
|
printStatus(
|
|
@@ -2871,46 +4204,62 @@ var init_update = __esm({
|
|
|
2871
4204
|
console.log();
|
|
2872
4205
|
console.log("Replacing framework files...");
|
|
2873
4206
|
await copyFile(
|
|
2874
|
-
|
|
2875
|
-
|
|
4207
|
+
join21(templateDir, ".claude", "CLAUDE.md"),
|
|
4208
|
+
join21(targetDir, ".claude", "CLAUDE.md")
|
|
2876
4209
|
);
|
|
2877
4210
|
await copyFile(
|
|
2878
|
-
|
|
2879
|
-
|
|
4211
|
+
join21(templateDir, ".claude", "settings.json"),
|
|
4212
|
+
join21(targetDir, ".claude", "settings.json")
|
|
2880
4213
|
);
|
|
2881
4214
|
printStatus(".claude/CLAUDE.md, settings.json");
|
|
2882
4215
|
await copyDirectoryContents(
|
|
2883
|
-
|
|
2884
|
-
|
|
4216
|
+
join21(templateDir, ".claude", "commands"),
|
|
4217
|
+
join21(targetDir, ".claude", "commands")
|
|
2885
4218
|
);
|
|
2886
4219
|
await copyDirectoryContents(
|
|
2887
|
-
|
|
2888
|
-
|
|
4220
|
+
join21(templateDir, ".claude", "commands"),
|
|
4221
|
+
join21(targetDir, ".cursor", "commands")
|
|
2889
4222
|
);
|
|
2890
4223
|
printStatus(".claude/commands, .cursor/commands");
|
|
2891
|
-
const skillsReadmeSrc =
|
|
4224
|
+
const skillsReadmeSrc = join21(templateDir, ".claude", "skills", "README.md");
|
|
2892
4225
|
if (await pathExists(skillsReadmeSrc)) {
|
|
2893
4226
|
await copyFile(
|
|
2894
4227
|
skillsReadmeSrc,
|
|
2895
|
-
|
|
4228
|
+
join21(targetDir, ".claude", "skills", "README.md")
|
|
2896
4229
|
);
|
|
2897
4230
|
}
|
|
2898
4231
|
printStatus(".claude/skills/README.md");
|
|
2899
4232
|
await copyFile(
|
|
2900
|
-
|
|
2901
|
-
|
|
4233
|
+
join21(templateDir, ".cursor", "hooks.json"),
|
|
4234
|
+
join21(targetDir, ".cursor", "hooks.json")
|
|
2902
4235
|
);
|
|
2903
4236
|
printStatus(".cursor/hooks.json");
|
|
2904
4237
|
await copyFile(
|
|
2905
|
-
|
|
2906
|
-
|
|
4238
|
+
join21(templateDir, "AGENTS.md"),
|
|
4239
|
+
join21(targetDir, "AGENTS.md")
|
|
2907
4240
|
);
|
|
2908
4241
|
printStatus("AGENTS.md");
|
|
2909
|
-
const envExampleSrc =
|
|
4242
|
+
const envExampleSrc = join21(templateDir, ".env.example");
|
|
2910
4243
|
if (await pathExists(envExampleSrc)) {
|
|
2911
|
-
await copyFile(envExampleSrc,
|
|
4244
|
+
await copyFile(envExampleSrc, join21(targetDir, ".env.example"));
|
|
2912
4245
|
printStatus(".env.example");
|
|
2913
4246
|
}
|
|
4247
|
+
const knowledgeTemplatesDir = join21(
|
|
4248
|
+
targetDir,
|
|
4249
|
+
"flydocs",
|
|
4250
|
+
"knowledge",
|
|
4251
|
+
"templates"
|
|
4252
|
+
);
|
|
4253
|
+
if (!await pathExists(knowledgeTemplatesDir)) {
|
|
4254
|
+
await mkdir10(knowledgeTemplatesDir, { recursive: true });
|
|
4255
|
+
}
|
|
4256
|
+
for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
|
|
4257
|
+
const src = join21(templateDir, "flydocs", "knowledge", "templates", tmpl);
|
|
4258
|
+
const dest = join21(knowledgeTemplatesDir, tmpl);
|
|
4259
|
+
if (await pathExists(src) && !await pathExists(dest)) {
|
|
4260
|
+
await copyFile(src, dest);
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
2914
4263
|
await runManifestGeneration(targetDir);
|
|
2915
4264
|
await runContextGraphBuild(targetDir);
|
|
2916
4265
|
console.log();
|
|
@@ -2933,20 +4282,22 @@ var init_update = __esm({
|
|
|
2933
4282
|
printWarning("Config merge failed \u2014 config.json preserved as-is");
|
|
2934
4283
|
}
|
|
2935
4284
|
await copyFile(
|
|
2936
|
-
|
|
2937
|
-
|
|
4285
|
+
join21(templateDir, ".flydocs", "version"),
|
|
4286
|
+
join21(targetDir, ".flydocs", "version")
|
|
2938
4287
|
);
|
|
2939
4288
|
printStatus(`.flydocs/version \u2192 ${version}`);
|
|
2940
|
-
const clSrc =
|
|
4289
|
+
const clSrc = join21(templateDir, "CHANGELOG.md");
|
|
2941
4290
|
if (await pathExists(clSrc)) {
|
|
2942
|
-
await copyFile(clSrc,
|
|
4291
|
+
await copyFile(clSrc, join21(targetDir, ".flydocs", "CHANGELOG.md"));
|
|
2943
4292
|
printStatus(".flydocs/CHANGELOG.md");
|
|
2944
4293
|
}
|
|
2945
|
-
const mfSrc =
|
|
4294
|
+
const mfSrc = join21(templateDir, "manifest.json");
|
|
2946
4295
|
if (await pathExists(mfSrc)) {
|
|
2947
|
-
await copyFile(mfSrc,
|
|
4296
|
+
await copyFile(mfSrc, join21(targetDir, ".flydocs", "manifest.json"));
|
|
2948
4297
|
printStatus(".flydocs/manifest.json");
|
|
2949
4298
|
}
|
|
4299
|
+
await generateIntegrity(targetDir, version);
|
|
4300
|
+
printStatus("Install integrity recorded");
|
|
2950
4301
|
console.log();
|
|
2951
4302
|
console.log("Detecting project stack...");
|
|
2952
4303
|
const stack = await detectStack(targetDir);
|
|
@@ -2963,6 +4314,7 @@ var init_update = __esm({
|
|
|
2963
4314
|
printInfo("No framework detected in package.json");
|
|
2964
4315
|
}
|
|
2965
4316
|
await migrateGitignore(targetDir);
|
|
4317
|
+
await ensurePlatformIgnores(targetDir);
|
|
2966
4318
|
console.log();
|
|
2967
4319
|
console.log("Checking for deprecated files...");
|
|
2968
4320
|
await handleLegacyContext(targetDir);
|
|
@@ -3003,22 +4355,22 @@ var uninstall_exports = {};
|
|
|
3003
4355
|
__export(uninstall_exports, {
|
|
3004
4356
|
default: () => uninstall_default
|
|
3005
4357
|
});
|
|
3006
|
-
import { defineCommand as
|
|
3007
|
-
import { resolve as
|
|
3008
|
-
import { readdir as
|
|
3009
|
-
import { confirm as
|
|
3010
|
-
import
|
|
4358
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
4359
|
+
import { resolve as resolve5, join as join22 } from "path";
|
|
4360
|
+
import { readdir as readdir6, rm as rm6, rename as rename2 } from "fs/promises";
|
|
4361
|
+
import { confirm as confirm5, select as select4, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
|
|
4362
|
+
import pc10 from "picocolors";
|
|
3011
4363
|
async function removeOwnedSkills(targetDir) {
|
|
3012
|
-
const skillsDir =
|
|
4364
|
+
const skillsDir = join22(targetDir, ".claude", "skills");
|
|
3013
4365
|
const removed = [];
|
|
3014
4366
|
if (!await pathExists(skillsDir)) {
|
|
3015
4367
|
return removed;
|
|
3016
4368
|
}
|
|
3017
4369
|
try {
|
|
3018
|
-
const entries = await
|
|
4370
|
+
const entries = await readdir6(skillsDir);
|
|
3019
4371
|
for (const entry of entries) {
|
|
3020
4372
|
if (entry.startsWith(OWNED_SKILL_PREFIX)) {
|
|
3021
|
-
await
|
|
4373
|
+
await rm6(join22(skillsDir, entry), { recursive: true, force: true });
|
|
3022
4374
|
removed.push(`.claude/skills/${entry}`);
|
|
3023
4375
|
}
|
|
3024
4376
|
}
|
|
@@ -3027,16 +4379,16 @@ async function removeOwnedSkills(targetDir) {
|
|
|
3027
4379
|
return removed;
|
|
3028
4380
|
}
|
|
3029
4381
|
async function removeOwnedCursorRules(targetDir) {
|
|
3030
|
-
const rulesDir =
|
|
4382
|
+
const rulesDir = join22(targetDir, ".cursor", "rules");
|
|
3031
4383
|
const removed = [];
|
|
3032
4384
|
if (!await pathExists(rulesDir)) {
|
|
3033
4385
|
return removed;
|
|
3034
4386
|
}
|
|
3035
4387
|
try {
|
|
3036
|
-
const entries = await
|
|
4388
|
+
const entries = await readdir6(rulesDir);
|
|
3037
4389
|
for (const entry of entries) {
|
|
3038
4390
|
if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
|
|
3039
|
-
await
|
|
4391
|
+
await rm6(join22(rulesDir, entry), { force: true });
|
|
3040
4392
|
removed.push(`.cursor/rules/${entry}`);
|
|
3041
4393
|
}
|
|
3042
4394
|
}
|
|
@@ -3046,7 +4398,7 @@ async function removeOwnedCursorRules(targetDir) {
|
|
|
3046
4398
|
}
|
|
3047
4399
|
async function isEmptyDir(dirPath) {
|
|
3048
4400
|
try {
|
|
3049
|
-
const entries = await
|
|
4401
|
+
const entries = await readdir6(dirPath);
|
|
3050
4402
|
return entries.length === 0;
|
|
3051
4403
|
} catch {
|
|
3052
4404
|
return false;
|
|
@@ -3055,9 +4407,9 @@ async function isEmptyDir(dirPath) {
|
|
|
3055
4407
|
async function cleanupEmptyParents(targetDir, dirs) {
|
|
3056
4408
|
const cleaned = [];
|
|
3057
4409
|
for (const dir of dirs) {
|
|
3058
|
-
const fullPath =
|
|
4410
|
+
const fullPath = join22(targetDir, dir);
|
|
3059
4411
|
if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
|
|
3060
|
-
await
|
|
4412
|
+
await rm6(fullPath, { recursive: true, force: true });
|
|
3061
4413
|
cleaned.push(dir);
|
|
3062
4414
|
}
|
|
3063
4415
|
}
|
|
@@ -3076,16 +4428,18 @@ var init_uninstall = __esm({
|
|
|
3076
4428
|
[".claude/settings.json", "file"],
|
|
3077
4429
|
[".claude/agents", "dir"],
|
|
3078
4430
|
[".claude/commands", "dir"],
|
|
4431
|
+
[".claude/hooks", "dir"],
|
|
3079
4432
|
[".claude/skills/README.md", "file"],
|
|
3080
4433
|
[".cursor/hooks.json", "file"],
|
|
3081
4434
|
[".cursor/agents", "dir"],
|
|
4435
|
+
[".cursor/commands", "dir"],
|
|
3082
4436
|
[".flydocs", "dir"],
|
|
3083
4437
|
["AGENTS.md", "file"],
|
|
3084
4438
|
[".env.example", "file"]
|
|
3085
4439
|
];
|
|
3086
4440
|
OWNED_SKILL_PREFIX = "flydocs-";
|
|
3087
4441
|
OWNED_RULE_PREFIX = "flydocs-";
|
|
3088
|
-
uninstall_default =
|
|
4442
|
+
uninstall_default = defineCommand5({
|
|
3089
4443
|
meta: {
|
|
3090
4444
|
name: "uninstall",
|
|
3091
4445
|
description: "Remove FlyDocs from a project directory"
|
|
@@ -3121,7 +4475,7 @@ var init_uninstall = __esm({
|
|
|
3121
4475
|
printBanner(CLI_VERSION);
|
|
3122
4476
|
let targetDir;
|
|
3123
4477
|
if (args.path) {
|
|
3124
|
-
targetDir =
|
|
4478
|
+
targetDir = resolve5(args.path.replace(/^~/, process.env.HOME ?? "~"));
|
|
3125
4479
|
} else if (args.here) {
|
|
3126
4480
|
targetDir = process.cwd();
|
|
3127
4481
|
} else {
|
|
@@ -3131,9 +4485,9 @@ var init_uninstall = __esm({
|
|
|
3131
4485
|
printError(`Directory does not exist: ${targetDir}`);
|
|
3132
4486
|
process.exit(1);
|
|
3133
4487
|
}
|
|
3134
|
-
targetDir =
|
|
3135
|
-
const hasFlydocs = await pathExists(
|
|
3136
|
-
const hasAgentsMd = await pathExists(
|
|
4488
|
+
targetDir = resolve5(targetDir);
|
|
4489
|
+
const hasFlydocs = await pathExists(join22(targetDir, ".flydocs"));
|
|
4490
|
+
const hasAgentsMd = await pathExists(join22(targetDir, "AGENTS.md"));
|
|
3137
4491
|
if (!hasFlydocs && !hasAgentsMd) {
|
|
3138
4492
|
printError(`Not a FlyDocs project: ${targetDir}`);
|
|
3139
4493
|
printInfo("No .flydocs/ directory or AGENTS.md found.");
|
|
@@ -3145,12 +4499,12 @@ var init_uninstall = __esm({
|
|
|
3145
4499
|
const removeAll = forceAll || args.all;
|
|
3146
4500
|
const skipPrompts = forceAll || args.yes;
|
|
3147
4501
|
let contentAction = "preserve";
|
|
3148
|
-
const hasUserContent = await pathExists(
|
|
4502
|
+
const hasUserContent = await pathExists(join22(targetDir, "flydocs"));
|
|
3149
4503
|
if (hasUserContent) {
|
|
3150
4504
|
if (removeAll) {
|
|
3151
4505
|
contentAction = "remove";
|
|
3152
4506
|
} else if (!skipPrompts) {
|
|
3153
|
-
const choice = await
|
|
4507
|
+
const choice = await select4({
|
|
3154
4508
|
message: "What should happen to your flydocs/ content (project docs, knowledge base)?",
|
|
3155
4509
|
options: [
|
|
3156
4510
|
{
|
|
@@ -3170,8 +4524,8 @@ var init_uninstall = __esm({
|
|
|
3170
4524
|
}
|
|
3171
4525
|
]
|
|
3172
4526
|
});
|
|
3173
|
-
if (
|
|
3174
|
-
|
|
4527
|
+
if (isCancel6(choice)) {
|
|
4528
|
+
cancel5("Uninstall cancelled.");
|
|
3175
4529
|
process.exit(0);
|
|
3176
4530
|
}
|
|
3177
4531
|
contentAction = choice;
|
|
@@ -3179,40 +4533,40 @@ var init_uninstall = __esm({
|
|
|
3179
4533
|
}
|
|
3180
4534
|
if (!skipPrompts) {
|
|
3181
4535
|
console.log();
|
|
3182
|
-
console.log(
|
|
4536
|
+
console.log(pc10.bold("The following will be removed:"));
|
|
3183
4537
|
console.log();
|
|
3184
4538
|
console.log(" Framework files:");
|
|
3185
4539
|
for (const [path] of ALWAYS_REMOVED) {
|
|
3186
|
-
console.log(` ${
|
|
4540
|
+
console.log(` ${pc10.dim(path)}`);
|
|
3187
4541
|
}
|
|
3188
|
-
console.log(` ${
|
|
3189
|
-
console.log(` ${
|
|
4542
|
+
console.log(` ${pc10.dim(".claude/skills/flydocs-*")}`);
|
|
4543
|
+
console.log(` ${pc10.dim(".cursor/rules/flydocs-*.mdc")}`);
|
|
3190
4544
|
if (hasUserContent) {
|
|
3191
4545
|
if (contentAction === "archive") {
|
|
3192
4546
|
console.log();
|
|
3193
4547
|
console.log(
|
|
3194
|
-
` User content: ${
|
|
4548
|
+
` User content: ${pc10.yellow("flydocs/ -> flydocs-archive/")}`
|
|
3195
4549
|
);
|
|
3196
4550
|
} else if (contentAction === "remove") {
|
|
3197
4551
|
console.log();
|
|
3198
|
-
console.log(` User content: ${
|
|
4552
|
+
console.log(` User content: ${pc10.red("flydocs/ (deleted)")}`);
|
|
3199
4553
|
} else {
|
|
3200
4554
|
console.log();
|
|
3201
|
-
console.log(` User content: ${
|
|
4555
|
+
console.log(` User content: ${pc10.green("flydocs/ (preserved)")}`);
|
|
3202
4556
|
}
|
|
3203
4557
|
}
|
|
3204
4558
|
console.log();
|
|
3205
|
-
console.log(
|
|
4559
|
+
console.log(pc10.bold("Preserved:"));
|
|
3206
4560
|
console.log(
|
|
3207
|
-
` ${
|
|
4561
|
+
` ${pc10.dim(".claude/skills/ (non-flydocs community skills)")}`
|
|
3208
4562
|
);
|
|
3209
|
-
console.log(` ${
|
|
4563
|
+
console.log(` ${pc10.dim(".env, .env.local")}`);
|
|
3210
4564
|
console.log();
|
|
3211
|
-
const shouldContinue = await
|
|
4565
|
+
const shouldContinue = await confirm5({
|
|
3212
4566
|
message: "Proceed with uninstall?"
|
|
3213
4567
|
});
|
|
3214
|
-
if (
|
|
3215
|
-
|
|
4568
|
+
if (isCancel6(shouldContinue) || !shouldContinue) {
|
|
4569
|
+
cancel5("Uninstall cancelled.");
|
|
3216
4570
|
process.exit(0);
|
|
3217
4571
|
}
|
|
3218
4572
|
}
|
|
@@ -3228,16 +4582,16 @@ var init_uninstall = __esm({
|
|
|
3228
4582
|
const removedRules = await removeOwnedCursorRules(targetDir);
|
|
3229
4583
|
result.removed.push(...removedRules);
|
|
3230
4584
|
for (const [relativePath, type] of ALWAYS_REMOVED) {
|
|
3231
|
-
const fullPath =
|
|
4585
|
+
const fullPath = join22(targetDir, relativePath);
|
|
3232
4586
|
if (!await pathExists(fullPath)) {
|
|
3233
4587
|
result.skipped.push(relativePath);
|
|
3234
4588
|
continue;
|
|
3235
4589
|
}
|
|
3236
4590
|
try {
|
|
3237
4591
|
if (type === "dir") {
|
|
3238
|
-
await
|
|
4592
|
+
await rm6(fullPath, { recursive: true, force: true });
|
|
3239
4593
|
} else {
|
|
3240
|
-
await
|
|
4594
|
+
await rm6(fullPath, { force: true });
|
|
3241
4595
|
}
|
|
3242
4596
|
result.removed.push(relativePath);
|
|
3243
4597
|
} catch {
|
|
@@ -3246,16 +4600,16 @@ var init_uninstall = __esm({
|
|
|
3246
4600
|
}
|
|
3247
4601
|
}
|
|
3248
4602
|
if (hasUserContent) {
|
|
3249
|
-
const flydocsPath =
|
|
4603
|
+
const flydocsPath = join22(targetDir, "flydocs");
|
|
3250
4604
|
if (contentAction === "archive") {
|
|
3251
|
-
const archivePath =
|
|
4605
|
+
const archivePath = join22(targetDir, "flydocs-archive");
|
|
3252
4606
|
if (await pathExists(archivePath)) {
|
|
3253
|
-
await
|
|
4607
|
+
await rm6(archivePath, { recursive: true, force: true });
|
|
3254
4608
|
}
|
|
3255
4609
|
await rename2(flydocsPath, archivePath);
|
|
3256
4610
|
result.archived.push("flydocs/ -> flydocs-archive/");
|
|
3257
4611
|
} else if (contentAction === "remove") {
|
|
3258
|
-
await
|
|
4612
|
+
await rm6(flydocsPath, { recursive: true, force: true });
|
|
3259
4613
|
result.removed.push("flydocs/");
|
|
3260
4614
|
}
|
|
3261
4615
|
}
|
|
@@ -3274,17 +4628,17 @@ var init_uninstall = __esm({
|
|
|
3274
4628
|
result.restored = originals.map((f) => f.relativePath);
|
|
3275
4629
|
}
|
|
3276
4630
|
console.log();
|
|
3277
|
-
console.log(
|
|
4631
|
+
console.log(pc10.bold("Uninstall Summary"));
|
|
3278
4632
|
console.log();
|
|
3279
4633
|
if (result.removed.length > 0) {
|
|
3280
|
-
console.log(` ${
|
|
4634
|
+
console.log(` ${pc10.green("Removed")} (${result.removed.length}):`);
|
|
3281
4635
|
for (const item of result.removed) {
|
|
3282
4636
|
printStatus(item);
|
|
3283
4637
|
}
|
|
3284
4638
|
}
|
|
3285
4639
|
if (result.archived.length > 0) {
|
|
3286
4640
|
console.log();
|
|
3287
|
-
console.log(` ${
|
|
4641
|
+
console.log(` ${pc10.yellow("Archived")} (${result.archived.length}):`);
|
|
3288
4642
|
for (const item of result.archived) {
|
|
3289
4643
|
printInfo(item);
|
|
3290
4644
|
}
|
|
@@ -3292,7 +4646,7 @@ var init_uninstall = __esm({
|
|
|
3292
4646
|
if (result.restored.length > 0) {
|
|
3293
4647
|
console.log();
|
|
3294
4648
|
console.log(
|
|
3295
|
-
` ${
|
|
4649
|
+
` ${pc10.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
|
|
3296
4650
|
);
|
|
3297
4651
|
for (const item of result.restored) {
|
|
3298
4652
|
printInfo(item);
|
|
@@ -3301,16 +4655,16 @@ var init_uninstall = __esm({
|
|
|
3301
4655
|
if (result.skipped.length > 0) {
|
|
3302
4656
|
console.log();
|
|
3303
4657
|
console.log(
|
|
3304
|
-
` ${
|
|
4658
|
+
` ${pc10.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
|
|
3305
4659
|
);
|
|
3306
4660
|
for (const item of result.skipped) {
|
|
3307
|
-
console.log(` ${
|
|
4661
|
+
console.log(` ${pc10.dim(item)}`);
|
|
3308
4662
|
}
|
|
3309
4663
|
}
|
|
3310
4664
|
console.log();
|
|
3311
4665
|
printStatus("FlyDocs has been removed from this project.");
|
|
3312
4666
|
console.log();
|
|
3313
|
-
printInfo(`To reinstall: ${
|
|
4667
|
+
printInfo(`To reinstall: ${pc10.cyan("npx @flydocs/cli install --here")}`);
|
|
3314
4668
|
console.log();
|
|
3315
4669
|
}
|
|
3316
4670
|
});
|
|
@@ -3322,28 +4676,28 @@ var setup_exports = {};
|
|
|
3322
4676
|
__export(setup_exports, {
|
|
3323
4677
|
default: () => setup_default
|
|
3324
4678
|
});
|
|
3325
|
-
import { defineCommand as
|
|
3326
|
-
import
|
|
4679
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
4680
|
+
import pc11 from "picocolors";
|
|
3327
4681
|
var setup_default;
|
|
3328
4682
|
var init_setup = __esm({
|
|
3329
4683
|
"src/commands/setup.ts"() {
|
|
3330
4684
|
"use strict";
|
|
3331
|
-
setup_default =
|
|
4685
|
+
setup_default = defineCommand6({
|
|
3332
4686
|
meta: {
|
|
3333
4687
|
name: "setup",
|
|
3334
4688
|
description: "Configure FlyDocs settings for this project"
|
|
3335
4689
|
},
|
|
3336
4690
|
run() {
|
|
3337
4691
|
console.log();
|
|
3338
|
-
console.log(` ${
|
|
4692
|
+
console.log(` ${pc11.bold("FlyDocs Setup")}`);
|
|
3339
4693
|
console.log();
|
|
3340
4694
|
console.log(` Setup runs inside your IDE as an interactive AI command.`);
|
|
3341
4695
|
console.log();
|
|
3342
4696
|
console.log(
|
|
3343
|
-
` ${
|
|
4697
|
+
` ${pc11.cyan("Claude Code:")} Type ${pc11.bold("/flydocs-setup")} in chat`
|
|
3344
4698
|
);
|
|
3345
4699
|
console.log(
|
|
3346
|
-
` ${
|
|
4700
|
+
` ${pc11.cyan("Cursor:")} Type ${pc11.bold("/flydocs-setup")} in chat`
|
|
3347
4701
|
);
|
|
3348
4702
|
console.log();
|
|
3349
4703
|
console.log(` This configures your project context, detects your stack,`);
|
|
@@ -3359,14 +4713,14 @@ var skills_exports = {};
|
|
|
3359
4713
|
__export(skills_exports, {
|
|
3360
4714
|
default: () => skills_default
|
|
3361
4715
|
});
|
|
3362
|
-
import { defineCommand as
|
|
3363
|
-
import
|
|
4716
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
4717
|
+
import pc12 from "picocolors";
|
|
3364
4718
|
var list, search, add, remove, skills_default;
|
|
3365
4719
|
var init_skills2 = __esm({
|
|
3366
4720
|
"src/commands/skills.ts"() {
|
|
3367
4721
|
"use strict";
|
|
3368
4722
|
init_skill_manager();
|
|
3369
|
-
list =
|
|
4723
|
+
list = defineCommand7({
|
|
3370
4724
|
meta: {
|
|
3371
4725
|
name: "list",
|
|
3372
4726
|
description: "List installed skills"
|
|
@@ -3382,26 +4736,26 @@ var init_skills2 = __esm({
|
|
|
3382
4736
|
console.log(`${total} skill(s) installed:`);
|
|
3383
4737
|
if (result.platform.length > 0) {
|
|
3384
4738
|
console.log();
|
|
3385
|
-
console.log(
|
|
4739
|
+
console.log(pc12.bold("Platform"));
|
|
3386
4740
|
for (const skill of result.platform) {
|
|
3387
4741
|
console.log(
|
|
3388
|
-
` ${skill.name} ${
|
|
4742
|
+
` ${skill.name} ${pc12.dim(`(${skill.triggers} triggers)`)}`
|
|
3389
4743
|
);
|
|
3390
4744
|
}
|
|
3391
4745
|
}
|
|
3392
4746
|
if (result.community.length > 0) {
|
|
3393
4747
|
console.log();
|
|
3394
|
-
console.log(
|
|
4748
|
+
console.log(pc12.bold("Community"));
|
|
3395
4749
|
for (const skill of result.community) {
|
|
3396
4750
|
console.log(
|
|
3397
|
-
` ${skill.name} ${
|
|
4751
|
+
` ${skill.name} ${pc12.dim(`(${skill.triggers} triggers)`)}`
|
|
3398
4752
|
);
|
|
3399
4753
|
}
|
|
3400
4754
|
}
|
|
3401
4755
|
console.log();
|
|
3402
4756
|
}
|
|
3403
4757
|
});
|
|
3404
|
-
search =
|
|
4758
|
+
search = defineCommand7({
|
|
3405
4759
|
meta: {
|
|
3406
4760
|
name: "search",
|
|
3407
4761
|
description: "Search community skills"
|
|
@@ -3417,24 +4771,24 @@ var init_skills2 = __esm({
|
|
|
3417
4771
|
const results = await searchCatalog(args.keyword);
|
|
3418
4772
|
if (results.length === 0) {
|
|
3419
4773
|
console.log(`No skills found for "${args.keyword}".`);
|
|
3420
|
-
console.log(` Browse the catalog at: ${
|
|
4774
|
+
console.log(` Browse the catalog at: ${pc12.cyan("https://skills.sh/")}`);
|
|
3421
4775
|
return;
|
|
3422
4776
|
}
|
|
3423
4777
|
console.log();
|
|
3424
4778
|
console.log(`${results.length} skill(s) matching "${args.keyword}":`);
|
|
3425
4779
|
console.log();
|
|
3426
4780
|
for (const skill of results) {
|
|
3427
|
-
console.log(` ${
|
|
4781
|
+
console.log(` ${pc12.bold(skill.name)}`);
|
|
3428
4782
|
console.log(` ${skill.description}`);
|
|
3429
|
-
console.log(` ${
|
|
4783
|
+
console.log(` ${pc12.dim(skill.repo)}`);
|
|
3430
4784
|
if (skill.tags.length > 0) {
|
|
3431
|
-
console.log(` ${
|
|
4785
|
+
console.log(` ${pc12.dim(skill.tags.join(", "))}`);
|
|
3432
4786
|
}
|
|
3433
4787
|
console.log();
|
|
3434
4788
|
}
|
|
3435
4789
|
}
|
|
3436
4790
|
});
|
|
3437
|
-
add =
|
|
4791
|
+
add = defineCommand7({
|
|
3438
4792
|
meta: {
|
|
3439
4793
|
name: "add",
|
|
3440
4794
|
description: "Install a community skill"
|
|
@@ -3450,7 +4804,7 @@ var init_skills2 = __esm({
|
|
|
3450
4804
|
await addSkill(process.cwd(), args.source);
|
|
3451
4805
|
}
|
|
3452
4806
|
});
|
|
3453
|
-
remove =
|
|
4807
|
+
remove = defineCommand7({
|
|
3454
4808
|
meta: {
|
|
3455
4809
|
name: "remove",
|
|
3456
4810
|
description: "Remove an installed community skill"
|
|
@@ -3466,7 +4820,7 @@ var init_skills2 = __esm({
|
|
|
3466
4820
|
await removeSkill(process.cwd(), args.name);
|
|
3467
4821
|
}
|
|
3468
4822
|
});
|
|
3469
|
-
skills_default =
|
|
4823
|
+
skills_default = defineCommand7({
|
|
3470
4824
|
meta: {
|
|
3471
4825
|
name: "skills",
|
|
3472
4826
|
description: "Manage FlyDocs skills (list, search, add, remove)"
|
|
@@ -3486,10 +4840,10 @@ var connect_exports = {};
|
|
|
3486
4840
|
__export(connect_exports, {
|
|
3487
4841
|
default: () => connect_default
|
|
3488
4842
|
});
|
|
3489
|
-
import { defineCommand as
|
|
3490
|
-
import { text as
|
|
3491
|
-
import
|
|
3492
|
-
import { join as
|
|
4843
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
4844
|
+
import { text as text4, confirm as confirm6, isCancel as isCancel7, cancel as cancel6 } from "@clack/prompts";
|
|
4845
|
+
import pc13 from "picocolors";
|
|
4846
|
+
import { join as join23 } from "path";
|
|
3493
4847
|
var connect_default;
|
|
3494
4848
|
var init_connect = __esm({
|
|
3495
4849
|
"src/commands/connect.ts"() {
|
|
@@ -3499,7 +4853,7 @@ var init_connect = __esm({
|
|
|
3499
4853
|
init_template();
|
|
3500
4854
|
init_ui();
|
|
3501
4855
|
init_api_key();
|
|
3502
|
-
connect_default =
|
|
4856
|
+
connect_default = defineCommand8({
|
|
3503
4857
|
meta: {
|
|
3504
4858
|
name: "connect",
|
|
3505
4859
|
description: "Connect FlyDocs to a cloud provider"
|
|
@@ -3519,16 +4873,16 @@ var init_connect = __esm({
|
|
|
3519
4873
|
},
|
|
3520
4874
|
key: {
|
|
3521
4875
|
type: "string",
|
|
3522
|
-
description: "API key (fdk_
|
|
4876
|
+
description: "FlyDocs API key (fdk_...)"
|
|
3523
4877
|
}
|
|
3524
4878
|
},
|
|
3525
4879
|
async run({ args }) {
|
|
3526
4880
|
const targetDir = args.path ?? process.cwd();
|
|
3527
|
-
const configPath =
|
|
4881
|
+
const configPath = join23(targetDir, ".flydocs", "config.json");
|
|
3528
4882
|
if (!await pathExists(configPath)) {
|
|
3529
4883
|
printError("Not a FlyDocs project (.flydocs/config.json not found).");
|
|
3530
4884
|
console.log(
|
|
3531
|
-
` Run ${
|
|
4885
|
+
` Run ${pc13.cyan("flydocs")} first to install FlyDocs in this project.`
|
|
3532
4886
|
);
|
|
3533
4887
|
process.exit(1);
|
|
3534
4888
|
}
|
|
@@ -3536,46 +4890,45 @@ var init_connect = __esm({
|
|
|
3536
4890
|
if (config.tier === "cloud") {
|
|
3537
4891
|
printInfo("This project is already connected to the cloud tier.");
|
|
3538
4892
|
console.log();
|
|
3539
|
-
const reconnect = await
|
|
4893
|
+
const reconnect = await confirm6({
|
|
3540
4894
|
message: "Want to update your API key?"
|
|
3541
4895
|
});
|
|
3542
|
-
if (
|
|
4896
|
+
if (isCancel7(reconnect) || !reconnect) {
|
|
3543
4897
|
console.log(` No changes made.`);
|
|
3544
4898
|
return;
|
|
3545
4899
|
}
|
|
3546
4900
|
}
|
|
3547
4901
|
console.log();
|
|
3548
|
-
console.log(` ${
|
|
4902
|
+
console.log(` ${pc13.bold("Connect to FlyDocs Cloud")}`);
|
|
3549
4903
|
console.log();
|
|
3550
4904
|
console.log(
|
|
3551
|
-
` ${
|
|
3552
|
-
);
|
|
3553
|
-
console.log(
|
|
3554
|
-
` ${pc11.dim("Linear API key (lin_api_...): Get from Linear \u2192 Settings \u2192 API")}`
|
|
4905
|
+
` ${pc13.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
3555
4906
|
);
|
|
3556
4907
|
console.log();
|
|
3557
4908
|
let apiKey = args.key ?? "";
|
|
3558
4909
|
if (!apiKey) {
|
|
3559
|
-
const keyInput = await
|
|
4910
|
+
const keyInput = await text4({
|
|
3560
4911
|
message: "Enter your API key",
|
|
3561
|
-
placeholder: "fdk_...
|
|
4912
|
+
placeholder: "fdk_...",
|
|
3562
4913
|
validate(value) {
|
|
3563
4914
|
if (!value.trim()) return "API key is required";
|
|
3564
4915
|
const type = detectKeyType(value.trim());
|
|
3565
4916
|
if (type === "unknown")
|
|
3566
|
-
return "Key must start with fdk_ (FlyDocs
|
|
4917
|
+
return "Key must start with fdk_ (FlyDocs API key)";
|
|
3567
4918
|
return void 0;
|
|
3568
4919
|
}
|
|
3569
4920
|
});
|
|
3570
|
-
if (
|
|
3571
|
-
|
|
4921
|
+
if (isCancel7(keyInput)) {
|
|
4922
|
+
cancel6("Connection cancelled.");
|
|
3572
4923
|
process.exit(0);
|
|
3573
4924
|
}
|
|
3574
4925
|
apiKey = keyInput.trim();
|
|
3575
4926
|
}
|
|
3576
4927
|
const keyType = detectKeyType(apiKey);
|
|
3577
4928
|
if (keyType === "unknown") {
|
|
3578
|
-
printError(
|
|
4929
|
+
printError(
|
|
4930
|
+
"Unrecognized key format. Expected fdk_ prefix (FlyDocs API key)."
|
|
4931
|
+
);
|
|
3579
4932
|
process.exit(1);
|
|
3580
4933
|
}
|
|
3581
4934
|
printInfo("Validating API key...");
|
|
@@ -3587,7 +4940,7 @@ var init_connect = __esm({
|
|
|
3587
4940
|
console.log(` Check your key and try again.`);
|
|
3588
4941
|
process.exit(1);
|
|
3589
4942
|
}
|
|
3590
|
-
printStatus(`Connected to ${
|
|
4943
|
+
printStatus(`Connected to ${pc13.bold(result.org)}`);
|
|
3591
4944
|
} catch {
|
|
3592
4945
|
printError(
|
|
3593
4946
|
"Could not reach relay API. Check your network and try again."
|
|
@@ -3595,7 +4948,7 @@ var init_connect = __esm({
|
|
|
3595
4948
|
process.exit(1);
|
|
3596
4949
|
}
|
|
3597
4950
|
const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
|
|
3598
|
-
printStatus(`API key stored in ${
|
|
4951
|
+
printStatus(`API key stored in ${pc13.dim(envFile)}`);
|
|
3599
4952
|
} else {
|
|
3600
4953
|
try {
|
|
3601
4954
|
const result = await validateLinearKey(apiKey);
|
|
@@ -3605,7 +4958,7 @@ var init_connect = __esm({
|
|
|
3605
4958
|
process.exit(1);
|
|
3606
4959
|
}
|
|
3607
4960
|
printStatus(
|
|
3608
|
-
`Authenticated as ${
|
|
4961
|
+
`Authenticated as ${pc13.bold(result.name)} (${result.email})`
|
|
3609
4962
|
);
|
|
3610
4963
|
} catch {
|
|
3611
4964
|
printError("Invalid API key or network error.");
|
|
@@ -3613,47 +4966,274 @@ var init_connect = __esm({
|
|
|
3613
4966
|
process.exit(1);
|
|
3614
4967
|
}
|
|
3615
4968
|
const envFile = await storeEnvKey(targetDir, "LINEAR_API_KEY", apiKey);
|
|
3616
|
-
printStatus(`API key stored in ${
|
|
4969
|
+
printStatus(`API key stored in ${pc13.dim(envFile)}`);
|
|
3617
4970
|
}
|
|
3618
4971
|
const wasLocal = config.tier === "local";
|
|
3619
4972
|
config.tier = "cloud";
|
|
3620
|
-
|
|
3621
|
-
|
|
4973
|
+
const configRecord = config;
|
|
4974
|
+
delete configRecord.statusMapping;
|
|
4975
|
+
delete configRecord.provider;
|
|
3622
4976
|
await writeConfig(targetDir, config);
|
|
3623
4977
|
printStatus("Config updated to cloud tier");
|
|
3624
4978
|
if (wasLocal) {
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
);
|
|
3629
|
-
const templateSkillsDir = join18(templateDir, ".claude", "skills");
|
|
3630
|
-
const skillsDir = join18(targetDir, ".claude", "skills");
|
|
3631
|
-
await replaceDirectory(
|
|
3632
|
-
join18(templateSkillsDir, "flydocs-cloud"),
|
|
3633
|
-
join18(skillsDir, "flydocs-cloud")
|
|
3634
|
-
);
|
|
3635
|
-
const { rm: rm6 } = await import("fs/promises");
|
|
3636
|
-
const localSkillDir = join18(skillsDir, "flydocs-local");
|
|
3637
|
-
if (await pathExists(localSkillDir)) {
|
|
3638
|
-
await rm6(localSkillDir, { recursive: true, force: true });
|
|
3639
|
-
}
|
|
3640
|
-
printStatus("Cloud mechanism skill installed");
|
|
3641
|
-
} catch {
|
|
3642
|
-
printInfo(
|
|
3643
|
-
"Could not swap mechanism skills automatically. Run flydocs update to complete."
|
|
3644
|
-
);
|
|
3645
|
-
}
|
|
4979
|
+
printInfo(
|
|
4980
|
+
"Tier changed to cloud. Run flydocs update to refresh skill scripts."
|
|
4981
|
+
);
|
|
3646
4982
|
}
|
|
3647
4983
|
console.log();
|
|
3648
4984
|
console.log(
|
|
3649
|
-
` ${
|
|
4985
|
+
` ${pc13.bold("Connected!")} Your project is now on the cloud tier.`
|
|
3650
4986
|
);
|
|
3651
4987
|
console.log();
|
|
3652
4988
|
console.log(` Next steps:`);
|
|
3653
4989
|
console.log(
|
|
3654
|
-
` 1. Run ${
|
|
4990
|
+
` 1. Run ${pc13.cyan("/flydocs-setup")} in your IDE to configure your project`
|
|
3655
4991
|
);
|
|
3656
|
-
console.log(` 2. Run ${
|
|
4992
|
+
console.log(` 2. Run ${pc13.cyan("/start-session")} to begin working`);
|
|
4993
|
+
console.log();
|
|
4994
|
+
}
|
|
4995
|
+
});
|
|
4996
|
+
}
|
|
4997
|
+
});
|
|
4998
|
+
|
|
4999
|
+
// src/commands/auth.ts
|
|
5000
|
+
var auth_exports = {};
|
|
5001
|
+
__export(auth_exports, {
|
|
5002
|
+
default: () => auth_default
|
|
5003
|
+
});
|
|
5004
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
5005
|
+
import { text as text5, confirm as confirm7, isCancel as isCancel8, cancel as cancel7 } from "@clack/prompts";
|
|
5006
|
+
import pc14 from "picocolors";
|
|
5007
|
+
var auth_default;
|
|
5008
|
+
var init_auth = __esm({
|
|
5009
|
+
"src/commands/auth.ts"() {
|
|
5010
|
+
"use strict";
|
|
5011
|
+
init_ui();
|
|
5012
|
+
init_api_key();
|
|
5013
|
+
init_global_config();
|
|
5014
|
+
auth_default = defineCommand9({
|
|
5015
|
+
meta: {
|
|
5016
|
+
name: "auth",
|
|
5017
|
+
description: "Store API key globally (~/.flydocs/credentials)"
|
|
5018
|
+
},
|
|
5019
|
+
args: {
|
|
5020
|
+
key: {
|
|
5021
|
+
type: "positional",
|
|
5022
|
+
description: "FlyDocs API key (fdk_...)",
|
|
5023
|
+
required: false
|
|
5024
|
+
}
|
|
5025
|
+
},
|
|
5026
|
+
async run({ args }) {
|
|
5027
|
+
console.log();
|
|
5028
|
+
console.log(` ${pc14.bold("FlyDocs Authentication")}`);
|
|
5029
|
+
console.log();
|
|
5030
|
+
let apiKey = args.key ?? "";
|
|
5031
|
+
const existing = await readGlobalCredential();
|
|
5032
|
+
if (existing?.apiKey && !apiKey) {
|
|
5033
|
+
printInfo(
|
|
5034
|
+
`Existing key found: ${pc14.dim(existing.apiKey.slice(0, 8) + "...")}`
|
|
5035
|
+
);
|
|
5036
|
+
const replace = await confirm7({
|
|
5037
|
+
message: "Replace with a new key?"
|
|
5038
|
+
});
|
|
5039
|
+
if (isCancel8(replace) || !replace) {
|
|
5040
|
+
console.log(` No changes made.`);
|
|
5041
|
+
return;
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
if (!apiKey) {
|
|
5045
|
+
console.log(
|
|
5046
|
+
` ${pc14.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
5047
|
+
);
|
|
5048
|
+
console.log();
|
|
5049
|
+
const keyInput = await text5({
|
|
5050
|
+
message: "Enter your API key",
|
|
5051
|
+
placeholder: "fdk_...",
|
|
5052
|
+
validate(value) {
|
|
5053
|
+
if (!value.trim()) return "API key is required";
|
|
5054
|
+
if (detectKeyType(value.trim()) !== "relay") {
|
|
5055
|
+
return "Key must start with fdk_ (FlyDocs API key)";
|
|
5056
|
+
}
|
|
5057
|
+
return void 0;
|
|
5058
|
+
}
|
|
5059
|
+
});
|
|
5060
|
+
if (isCancel8(keyInput)) {
|
|
5061
|
+
cancel7("Authentication cancelled.");
|
|
5062
|
+
process.exit(0);
|
|
5063
|
+
}
|
|
5064
|
+
apiKey = keyInput.trim();
|
|
5065
|
+
}
|
|
5066
|
+
if (detectKeyType(apiKey) !== "relay") {
|
|
5067
|
+
printError("Invalid key format. Expected fdk_ prefix (FlyDocs API key).");
|
|
5068
|
+
process.exit(1);
|
|
5069
|
+
}
|
|
5070
|
+
if (existing?.apiKey === apiKey) {
|
|
5071
|
+
printInfo("This key is already stored.");
|
|
5072
|
+
await checkCredentialPermissions();
|
|
5073
|
+
return;
|
|
5074
|
+
}
|
|
5075
|
+
printInfo("Validating API key...");
|
|
5076
|
+
try {
|
|
5077
|
+
const result = await validateRelayKey(apiKey);
|
|
5078
|
+
if (!result.valid) {
|
|
5079
|
+
printError("Invalid API key. Check your key and try again.");
|
|
5080
|
+
process.exit(1);
|
|
5081
|
+
}
|
|
5082
|
+
printStatus(`Authenticated with ${pc14.bold(result.org)}`);
|
|
5083
|
+
} catch {
|
|
5084
|
+
printError(
|
|
5085
|
+
"Could not reach FlyDocs API. Check your network and try again."
|
|
5086
|
+
);
|
|
5087
|
+
process.exit(1);
|
|
5088
|
+
}
|
|
5089
|
+
if (existing?.apiKey && existing.apiKey !== apiKey) {
|
|
5090
|
+
printWarning("Replacing existing key.");
|
|
5091
|
+
}
|
|
5092
|
+
await writeGlobalCredential({
|
|
5093
|
+
apiKey,
|
|
5094
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5095
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5096
|
+
});
|
|
5097
|
+
printStatus(`Key stored at ${pc14.dim(credentialsPath())}`);
|
|
5098
|
+
await checkCredentialPermissions();
|
|
5099
|
+
console.log();
|
|
5100
|
+
console.log(` ${pc14.bold("Authenticated!")} Key stored globally.`);
|
|
5101
|
+
console.log(` All FlyDocs projects on this machine will use this key.`);
|
|
5102
|
+
console.log();
|
|
5103
|
+
}
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
});
|
|
5107
|
+
|
|
5108
|
+
// src/commands/sync.ts
|
|
5109
|
+
var sync_exports = {};
|
|
5110
|
+
__export(sync_exports, {
|
|
5111
|
+
default: () => sync_default
|
|
5112
|
+
});
|
|
5113
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
5114
|
+
import pc15 from "picocolors";
|
|
5115
|
+
import { join as join24 } from "path";
|
|
5116
|
+
import { mkdir as mkdir11, writeFile as writeFile14 } from "fs/promises";
|
|
5117
|
+
var sync_default;
|
|
5118
|
+
var init_sync = __esm({
|
|
5119
|
+
"src/commands/sync.ts"() {
|
|
5120
|
+
"use strict";
|
|
5121
|
+
init_ui();
|
|
5122
|
+
init_global_config();
|
|
5123
|
+
init_config();
|
|
5124
|
+
init_config_integrity();
|
|
5125
|
+
init_fs_ops();
|
|
5126
|
+
init_types();
|
|
5127
|
+
init_relay_client();
|
|
5128
|
+
sync_default = defineCommand10({
|
|
5129
|
+
meta: {
|
|
5130
|
+
name: "sync",
|
|
5131
|
+
description: "Pull latest config and templates from server"
|
|
5132
|
+
},
|
|
5133
|
+
args: {
|
|
5134
|
+
path: {
|
|
5135
|
+
type: "string",
|
|
5136
|
+
description: "Path to project directory"
|
|
5137
|
+
}
|
|
5138
|
+
},
|
|
5139
|
+
async run({ args }) {
|
|
5140
|
+
const targetDir = args.path ?? process.cwd();
|
|
5141
|
+
const changes = [];
|
|
5142
|
+
console.log();
|
|
5143
|
+
printInfo("Syncing with server...");
|
|
5144
|
+
const resolved = await resolveApiKey(void 0, targetDir);
|
|
5145
|
+
if (!resolved) {
|
|
5146
|
+
printError(
|
|
5147
|
+
"No API key found. Run `flydocs auth` or `flydocs init` first."
|
|
5148
|
+
);
|
|
5149
|
+
process.exit(1);
|
|
5150
|
+
}
|
|
5151
|
+
const apiKey = resolved.key;
|
|
5152
|
+
const cred = await readGlobalCredential();
|
|
5153
|
+
const workspaceId = cred?.workspaceId;
|
|
5154
|
+
if (!workspaceId) {
|
|
5155
|
+
printError(
|
|
5156
|
+
"No workspace ID found. Run `flydocs init` to set up your workspace."
|
|
5157
|
+
);
|
|
5158
|
+
process.exit(1);
|
|
5159
|
+
}
|
|
5160
|
+
let currentTemplateVersion = 0;
|
|
5161
|
+
try {
|
|
5162
|
+
const currentConfig = await readAnyConfig(targetDir);
|
|
5163
|
+
if (isConfigV2(currentConfig)) {
|
|
5164
|
+
currentTemplateVersion = currentConfig.configVersion ?? 0;
|
|
5165
|
+
}
|
|
5166
|
+
} catch {
|
|
5167
|
+
}
|
|
5168
|
+
let serverResponse;
|
|
5169
|
+
try {
|
|
5170
|
+
serverResponse = await fetchConfigV2(apiKey, { workspaceId });
|
|
5171
|
+
} catch (err) {
|
|
5172
|
+
if (err instanceof RelayError) {
|
|
5173
|
+
printWarning(
|
|
5174
|
+
`Server unavailable (${err.status}), using cached config.`
|
|
5175
|
+
);
|
|
5176
|
+
} else {
|
|
5177
|
+
printWarning("Server unreachable, using cached config.");
|
|
5178
|
+
}
|
|
5179
|
+
console.log(` ${pc15.dim("Config may be stale. Retry when connected.")}`);
|
|
5180
|
+
return;
|
|
5181
|
+
}
|
|
5182
|
+
if (!serverResponse.valid) {
|
|
5183
|
+
printWarning("Server returned invalid config. Keeping current config.");
|
|
5184
|
+
return;
|
|
5185
|
+
}
|
|
5186
|
+
await mkdir11(join24(targetDir, ".flydocs"), { recursive: true });
|
|
5187
|
+
const configWithHash = applyConfigHash(serverResponse.config);
|
|
5188
|
+
await writeConfig(targetDir, configWithHash);
|
|
5189
|
+
changes.push("Updated .flydocs/config.json");
|
|
5190
|
+
const serverTemplateVersion = serverResponse.templates.version;
|
|
5191
|
+
if (serverTemplateVersion > currentTemplateVersion) {
|
|
5192
|
+
try {
|
|
5193
|
+
const templatesResponse = await fetchTemplates(
|
|
5194
|
+
apiKey,
|
|
5195
|
+
currentTemplateVersion,
|
|
5196
|
+
workspaceId
|
|
5197
|
+
);
|
|
5198
|
+
if (templatesResponse.templates.length > 0) {
|
|
5199
|
+
const templatesDir = join24(
|
|
5200
|
+
targetDir,
|
|
5201
|
+
".claude",
|
|
5202
|
+
"skills",
|
|
5203
|
+
"flydocs-workflow",
|
|
5204
|
+
"templates"
|
|
5205
|
+
);
|
|
5206
|
+
await mkdir11(templatesDir, { recursive: true });
|
|
5207
|
+
for (const template of templatesResponse.templates) {
|
|
5208
|
+
const filename = `${template.type}.md`;
|
|
5209
|
+
const subdir = template.category === "issue" ? "issues" : template.category === "pr" ? "pr" : template.category === "comment" ? "." : ".";
|
|
5210
|
+
const templateDir = join24(templatesDir, subdir);
|
|
5211
|
+
await mkdir11(templateDir, { recursive: true });
|
|
5212
|
+
await writeFile14(
|
|
5213
|
+
join24(templateDir, filename),
|
|
5214
|
+
template.content,
|
|
5215
|
+
"utf-8"
|
|
5216
|
+
);
|
|
5217
|
+
}
|
|
5218
|
+
changes.push(
|
|
5219
|
+
`Updated ${templatesResponse.templates.length} templates (v${currentTemplateVersion} \u2192 v${serverTemplateVersion})`
|
|
5220
|
+
);
|
|
5221
|
+
}
|
|
5222
|
+
} catch (err) {
|
|
5223
|
+
if (err instanceof RelayError) {
|
|
5224
|
+
printWarning("Could not fetch templates. Will retry on next sync.");
|
|
5225
|
+
} else {
|
|
5226
|
+
printWarning("Template sync failed. Will retry on next sync.");
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
5230
|
+
if (changes.length === 0) {
|
|
5231
|
+
printStatus("Already up to date.");
|
|
5232
|
+
} else {
|
|
5233
|
+
for (const change of changes) {
|
|
5234
|
+
printStatus(change);
|
|
5235
|
+
}
|
|
5236
|
+
}
|
|
3657
5237
|
console.log();
|
|
3658
5238
|
}
|
|
3659
5239
|
});
|
|
@@ -3665,15 +5245,15 @@ var upgrade_exports = {};
|
|
|
3665
5245
|
__export(upgrade_exports, {
|
|
3666
5246
|
default: () => upgrade_default
|
|
3667
5247
|
});
|
|
3668
|
-
import { defineCommand as
|
|
3669
|
-
import
|
|
5248
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
5249
|
+
import pc16 from "picocolors";
|
|
3670
5250
|
var upgrade_default;
|
|
3671
5251
|
var init_upgrade = __esm({
|
|
3672
5252
|
"src/commands/upgrade.ts"() {
|
|
3673
5253
|
"use strict";
|
|
3674
5254
|
init_config();
|
|
3675
5255
|
init_fs_ops();
|
|
3676
|
-
upgrade_default =
|
|
5256
|
+
upgrade_default = defineCommand11({
|
|
3677
5257
|
meta: {
|
|
3678
5258
|
name: "upgrade",
|
|
3679
5259
|
description: "Learn about FlyDocs Cloud tier and upgrade from local"
|
|
@@ -3702,38 +5282,34 @@ var init_upgrade = __esm({
|
|
|
3702
5282
|
console.log();
|
|
3703
5283
|
if (currentTier === "cloud") {
|
|
3704
5284
|
console.log(
|
|
3705
|
-
` ${
|
|
5285
|
+
` ${pc16.green("\u2713")} You're already on the ${pc16.bold("cloud")} tier.`
|
|
3706
5286
|
);
|
|
3707
5287
|
console.log();
|
|
5288
|
+
console.log(` Your issues sync with your provider via the relay API.`);
|
|
3708
5289
|
console.log(
|
|
3709
|
-
`
|
|
3710
|
-
);
|
|
3711
|
-
console.log(
|
|
3712
|
-
` Run ${pc12.cyan("flydocs connect")} to update your connection settings.`
|
|
5290
|
+
` Run ${pc16.cyan("flydocs connect")} to update your connection settings.`
|
|
3713
5291
|
);
|
|
3714
5292
|
console.log();
|
|
3715
5293
|
return;
|
|
3716
5294
|
}
|
|
3717
|
-
console.log(` ${
|
|
5295
|
+
console.log(` ${pc16.bold("FlyDocs Cloud Tier")}`);
|
|
3718
5296
|
console.log();
|
|
3719
|
-
console.log(` You're currently on the ${
|
|
5297
|
+
console.log(` You're currently on the ${pc16.yellow("local")} tier.`);
|
|
3720
5298
|
console.log(` Upgrade to cloud for:`);
|
|
3721
5299
|
console.log();
|
|
3722
|
-
console.log(
|
|
3723
|
-
|
|
3724
|
-
);
|
|
3725
|
-
console.log(` ${
|
|
3726
|
-
console.log(` ${
|
|
3727
|
-
console.log(` ${pc12.cyan("\u2192")} Project health updates and dashboards`);
|
|
3728
|
-
console.log(` ${pc12.cyan("\u2192")} Cross-project issue linking`);
|
|
5300
|
+
console.log(` ${pc16.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
|
|
5301
|
+
console.log(` ${pc16.cyan("\u2192")} Project milestones and cycle management`);
|
|
5302
|
+
console.log(` ${pc16.cyan("\u2192")} Team assignment and priority tracking`);
|
|
5303
|
+
console.log(` ${pc16.cyan("\u2192")} Project health updates and dashboards`);
|
|
5304
|
+
console.log(` ${pc16.cyan("\u2192")} Cross-project issue linking`);
|
|
3729
5305
|
console.log();
|
|
3730
|
-
console.log(` ${
|
|
5306
|
+
console.log(` ${pc16.bold("How to upgrade:")}`);
|
|
3731
5307
|
console.log();
|
|
3732
|
-
console.log(` Option 1: Run ${
|
|
5308
|
+
console.log(` Option 1: Run ${pc16.cyan("/flydocs-upgrade")} in your IDE`);
|
|
3733
5309
|
console.log(` Guided migration with issue transfer`);
|
|
3734
5310
|
console.log();
|
|
3735
5311
|
console.log(
|
|
3736
|
-
` Option 2: Run ${
|
|
5312
|
+
` Option 2: Run ${pc16.cyan("flydocs connect")} from terminal`
|
|
3737
5313
|
);
|
|
3738
5314
|
console.log(` Quick tier swap (no issue migration)`);
|
|
3739
5315
|
console.log();
|
|
@@ -3747,23 +5323,23 @@ var self_update_exports = {};
|
|
|
3747
5323
|
__export(self_update_exports, {
|
|
3748
5324
|
default: () => self_update_default
|
|
3749
5325
|
});
|
|
3750
|
-
import { defineCommand as
|
|
5326
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
3751
5327
|
import { execSync } from "child_process";
|
|
3752
|
-
import
|
|
5328
|
+
import pc17 from "picocolors";
|
|
3753
5329
|
var self_update_default;
|
|
3754
5330
|
var init_self_update = __esm({
|
|
3755
5331
|
"src/commands/self-update.ts"() {
|
|
3756
5332
|
"use strict";
|
|
3757
5333
|
init_constants();
|
|
3758
5334
|
init_ui();
|
|
3759
|
-
self_update_default =
|
|
5335
|
+
self_update_default = defineCommand12({
|
|
3760
5336
|
meta: {
|
|
3761
5337
|
name: "self-update",
|
|
3762
5338
|
description: "Update FlyDocs CLI to the latest version"
|
|
3763
5339
|
},
|
|
3764
5340
|
async run() {
|
|
3765
5341
|
console.log();
|
|
3766
|
-
console.log(` Updating ${
|
|
5342
|
+
console.log(` Updating ${pc17.cyan(PACKAGE_NAME)}...`);
|
|
3767
5343
|
console.log();
|
|
3768
5344
|
try {
|
|
3769
5345
|
execSync(`npm install -g ${PACKAGE_NAME}@beta`, {
|
|
@@ -3788,15 +5364,15 @@ var telemetry_exports = {};
|
|
|
3788
5364
|
__export(telemetry_exports, {
|
|
3789
5365
|
default: () => telemetry_default
|
|
3790
5366
|
});
|
|
3791
|
-
import { defineCommand as
|
|
3792
|
-
import
|
|
5367
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
5368
|
+
import pc18 from "picocolors";
|
|
3793
5369
|
var enable, disable, status, telemetry_default;
|
|
3794
5370
|
var init_telemetry2 = __esm({
|
|
3795
5371
|
"src/commands/telemetry.ts"() {
|
|
3796
5372
|
"use strict";
|
|
3797
5373
|
init_telemetry();
|
|
3798
5374
|
init_ui();
|
|
3799
|
-
enable =
|
|
5375
|
+
enable = defineCommand13({
|
|
3800
5376
|
meta: {
|
|
3801
5377
|
name: "enable",
|
|
3802
5378
|
description: "Enable anonymous usage analytics"
|
|
@@ -3811,7 +5387,7 @@ var init_telemetry2 = __esm({
|
|
|
3811
5387
|
}
|
|
3812
5388
|
}
|
|
3813
5389
|
});
|
|
3814
|
-
disable =
|
|
5390
|
+
disable = defineCommand13({
|
|
3815
5391
|
meta: {
|
|
3816
5392
|
name: "disable",
|
|
3817
5393
|
description: "Disable anonymous usage analytics"
|
|
@@ -3829,7 +5405,7 @@ var init_telemetry2 = __esm({
|
|
|
3829
5405
|
}
|
|
3830
5406
|
}
|
|
3831
5407
|
});
|
|
3832
|
-
status =
|
|
5408
|
+
status = defineCommand13({
|
|
3833
5409
|
meta: {
|
|
3834
5410
|
name: "status",
|
|
3835
5411
|
description: "Show current telemetry status"
|
|
@@ -3837,37 +5413,37 @@ var init_telemetry2 = __esm({
|
|
|
3837
5413
|
async run() {
|
|
3838
5414
|
const info = await getStatus();
|
|
3839
5415
|
console.log();
|
|
3840
|
-
console.log(
|
|
5416
|
+
console.log(pc18.bold("Telemetry Status"));
|
|
3841
5417
|
console.log();
|
|
3842
5418
|
const effectivelyEnabled = info.enabled && !info.envOverride;
|
|
3843
5419
|
console.log(
|
|
3844
|
-
` Enabled: ${effectivelyEnabled ?
|
|
5420
|
+
` Enabled: ${effectivelyEnabled ? pc18.green("yes") : pc18.yellow("no")}`
|
|
3845
5421
|
);
|
|
3846
5422
|
if (info.envOverride) {
|
|
3847
5423
|
console.log(
|
|
3848
|
-
` Env override: ${
|
|
5424
|
+
` Env override: ${pc18.yellow("FLYDOCS_TELEMETRY=0 (disabled via env)")}`
|
|
3849
5425
|
);
|
|
3850
5426
|
}
|
|
3851
5427
|
if (info.anonymousId) {
|
|
3852
|
-
console.log(` Anonymous ID: ${
|
|
5428
|
+
console.log(` Anonymous ID: ${pc18.dim(info.anonymousId)}`);
|
|
3853
5429
|
} else {
|
|
3854
5430
|
console.log(
|
|
3855
|
-
` Anonymous ID: ${
|
|
5431
|
+
` Anonymous ID: ${pc18.dim("(not yet created \u2014 generated on first run)")}`
|
|
3856
5432
|
);
|
|
3857
5433
|
}
|
|
3858
5434
|
console.log();
|
|
3859
5435
|
console.log(
|
|
3860
|
-
|
|
5436
|
+
pc18.dim(
|
|
3861
5437
|
" FlyDocs collects anonymous usage analytics to improve the CLI."
|
|
3862
5438
|
)
|
|
3863
5439
|
);
|
|
3864
5440
|
console.log(
|
|
3865
|
-
|
|
5441
|
+
pc18.dim(" No personal data, file contents, or code is ever collected.")
|
|
3866
5442
|
);
|
|
3867
5443
|
console.log();
|
|
3868
5444
|
}
|
|
3869
5445
|
});
|
|
3870
|
-
telemetry_default =
|
|
5446
|
+
telemetry_default = defineCommand13({
|
|
3871
5447
|
meta: {
|
|
3872
5448
|
name: "telemetry",
|
|
3873
5449
|
description: "Manage anonymous usage analytics (enable, disable, status)"
|
|
@@ -3883,14 +5459,18 @@ var init_telemetry2 = __esm({
|
|
|
3883
5459
|
|
|
3884
5460
|
// src/cli.ts
|
|
3885
5461
|
init_constants();
|
|
3886
|
-
import { defineCommand as
|
|
5462
|
+
import { defineCommand as defineCommand14, runMain } from "citty";
|
|
3887
5463
|
var SUB_COMMANDS = /* @__PURE__ */ new Set([
|
|
3888
5464
|
"install",
|
|
5465
|
+
"init",
|
|
3889
5466
|
"update",
|
|
3890
5467
|
"uninstall",
|
|
3891
5468
|
"setup",
|
|
3892
5469
|
"skills",
|
|
3893
5470
|
"connect",
|
|
5471
|
+
"auth",
|
|
5472
|
+
"sync",
|
|
5473
|
+
"cleanup",
|
|
3894
5474
|
"upgrade",
|
|
3895
5475
|
"self-update",
|
|
3896
5476
|
"telemetry"
|
|
@@ -3903,7 +5483,7 @@ var firstPositional = userArgs.find((a) => !a.startsWith("-"));
|
|
|
3903
5483
|
if (!hasMetaFlag && (!firstPositional || !SUB_COMMANDS.has(firstPositional))) {
|
|
3904
5484
|
process.argv.splice(2, 0, "install");
|
|
3905
5485
|
}
|
|
3906
|
-
var main =
|
|
5486
|
+
var main = defineCommand14({
|
|
3907
5487
|
meta: {
|
|
3908
5488
|
name: CLI_NAME,
|
|
3909
5489
|
version: CLI_VERSION,
|
|
@@ -3911,11 +5491,15 @@ var main = defineCommand10({
|
|
|
3911
5491
|
},
|
|
3912
5492
|
subCommands: {
|
|
3913
5493
|
install: () => Promise.resolve().then(() => (init_install(), install_exports)).then((m) => m.default),
|
|
5494
|
+
init: () => Promise.resolve().then(() => (init_init(), init_exports)).then((m) => m.default),
|
|
3914
5495
|
update: () => Promise.resolve().then(() => (init_update(), update_exports)).then((m) => m.default),
|
|
3915
5496
|
uninstall: () => Promise.resolve().then(() => (init_uninstall(), uninstall_exports)).then((m) => m.default),
|
|
3916
5497
|
setup: () => Promise.resolve().then(() => (init_setup(), setup_exports)).then((m) => m.default),
|
|
3917
5498
|
skills: () => Promise.resolve().then(() => (init_skills2(), skills_exports)).then((m) => m.default),
|
|
3918
5499
|
connect: () => Promise.resolve().then(() => (init_connect(), connect_exports)).then((m) => m.default),
|
|
5500
|
+
auth: () => Promise.resolve().then(() => (init_auth(), auth_exports)).then((m) => m.default),
|
|
5501
|
+
sync: () => Promise.resolve().then(() => (init_sync(), sync_exports)).then((m) => m.default),
|
|
5502
|
+
cleanup: () => Promise.resolve().then(() => (init_cleanup(), cleanup_exports)).then((m) => m.default),
|
|
3919
5503
|
upgrade: () => Promise.resolve().then(() => (init_upgrade(), upgrade_exports)).then((m) => m.default),
|
|
3920
5504
|
"self-update": () => Promise.resolve().then(() => (init_self_update(), self_update_exports)).then((m) => m.default),
|
|
3921
5505
|
telemetry: () => Promise.resolve().then(() => (init_telemetry2(), telemetry_exports)).then((m) => m.default)
|