@flydocs/cli 0.5.0-beta.9 → 0.6.0-alpha.10
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/README.md +6 -0
- package/dist/cli.js +1553 -414
- package/package.json +1 -1
- package/template/.claude/CLAUDE.md +11 -9
- package/template/.claude/agents/implementation-agent.md +0 -1
- package/template/.claude/agents/pm-agent.md +0 -1
- package/template/.claude/agents/research-agent.md +0 -1
- package/template/.claude/agents/review-agent.md +0 -1
- package/template/.claude/commands/flydocs-setup.md +202 -35
- package/template/.claude/commands/flydocs-upgrade.md +342 -0
- package/template/.claude/commands/knowledge.md +61 -0
- package/template/.claude/skills/flydocs-cloud/SKILL.md +66 -39
- package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +5 -5
- package/template/.claude/skills/flydocs-cloud/scripts/assign.py +17 -27
- package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +14 -30
- package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +10 -32
- package/template/.claude/skills/flydocs-cloud/scripts/comment.py +15 -25
- package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +42 -59
- package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +26 -37
- package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +24 -31
- package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +39 -0
- package/template/.claude/skills/flydocs-cloud/scripts/delete_milestone.py +21 -0
- package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +17 -22
- package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +113 -169
- package/template/.claude/skills/flydocs-cloud/scripts/get_estimate_scale.py +23 -0
- package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +6 -59
- package/template/.claude/skills/flydocs-cloud/scripts/link.py +16 -35
- package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +21 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +16 -77
- package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +19 -0
- package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +21 -33
- package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +24 -38
- package/template/.claude/skills/flydocs-cloud/scripts/list_providers.py +19 -0
- package/template/.claude/skills/flydocs-cloud/scripts/list_statuses.py +19 -0
- package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +19 -0
- package/template/.claude/skills/flydocs-cloud/scripts/priority.py +10 -19
- package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +36 -50
- package/template/.claude/skills/flydocs-cloud/scripts/refresh_labels.py +87 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_identity.py +38 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +68 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_preferences.py +49 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_provider.py +46 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_status_mapping.py +69 -0
- package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +42 -0
- package/template/.claude/skills/flydocs-cloud/scripts/transition.py +11 -52
- package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +16 -27
- package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +43 -54
- package/template/.claude/skills/flydocs-cloud/scripts/update_milestone.py +42 -0
- package/template/.claude/skills/flydocs-cloud/scripts/validate_setup.py +139 -0
- package/template/.claude/skills/flydocs-local/SKILL.md +1 -1
- package/template/.claude/skills/flydocs-local/scripts/assign.py +13 -4
- package/template/.claude/skills/flydocs-local/scripts/flydocs_api.py +5 -2
- package/template/.claude/skills/flydocs-workflow/SKILL.md +23 -18
- package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
- package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +105 -0
- package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
- package/template/.claude/skills/flydocs-workflow/session.md +24 -16
- package/template/.claude/skills/flydocs-workflow/stages/capture.md +8 -3
- package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
- package/template/.claude/skills/flydocs-workflow/stages/implement.md +28 -4
- package/template/.claude/skills/flydocs-workflow/stages/refine.md +20 -4
- package/template/.claude/skills/flydocs-workflow/stages/review.md +14 -2
- package/template/.env.example +16 -7
- package/template/.flydocs/config.json +4 -18
- package/template/.flydocs/hooks/prompt-submit.py +27 -4
- package/template/.flydocs/version +1 -1
- package/template/AGENTS.md +8 -8
- package/template/CHANGELOG.md +183 -0
- 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 +12 -4
package/dist/cli.js
CHANGED
|
@@ -11,13 +11,14 @@ var __export = (target, all) => {
|
|
|
11
11
|
|
|
12
12
|
// src/lib/constants.ts
|
|
13
13
|
import pc from "picocolors";
|
|
14
|
-
var CLI_VERSION, CLI_NAME, PACKAGE_NAME;
|
|
14
|
+
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.
|
|
18
|
+
CLI_VERSION = "0.6.0-alpha.10";
|
|
19
19
|
CLI_NAME = "flydocs";
|
|
20
20
|
PACKAGE_NAME = "@flydocs/cli";
|
|
21
|
+
POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
|
|
21
22
|
}
|
|
22
23
|
});
|
|
23
24
|
|
|
@@ -42,15 +43,14 @@ async function ensureDirectories(targetDir, tier) {
|
|
|
42
43
|
const dirs = [
|
|
43
44
|
".flydocs",
|
|
44
45
|
".claude/skills",
|
|
45
|
-
".claude/agents",
|
|
46
46
|
".claude/commands",
|
|
47
|
-
".cursor/agents",
|
|
48
47
|
".cursor/commands",
|
|
49
48
|
".cursor/rules",
|
|
50
49
|
"flydocs/context",
|
|
51
50
|
"flydocs/knowledge/decisions",
|
|
52
51
|
"flydocs/knowledge/notes",
|
|
53
52
|
"flydocs/knowledge/product",
|
|
53
|
+
"flydocs/knowledge/templates",
|
|
54
54
|
"flydocs/design-system"
|
|
55
55
|
];
|
|
56
56
|
if (tier === "local") {
|
|
@@ -136,8 +136,58 @@ var init_template = __esm({
|
|
|
136
136
|
|
|
137
137
|
// src/lib/ui.ts
|
|
138
138
|
import pc2 from "picocolors";
|
|
139
|
-
function
|
|
140
|
-
return `\x1B
|
|
139
|
+
function shadow(text4) {
|
|
140
|
+
return `\x1B[38;2;55;45;70m${text4}\x1B[0m`;
|
|
141
|
+
}
|
|
142
|
+
function renderBannerBlock() {
|
|
143
|
+
const height = BANNER_ROWS.length;
|
|
144
|
+
const width = BANNER_ROWS[0].length;
|
|
145
|
+
const padded = BANNER_ROWS.map((r) => r.padEnd(width));
|
|
146
|
+
console.log(` ${GRADIENT[0](padded[0])}`);
|
|
147
|
+
for (let i = 1; i < height; i++) {
|
|
148
|
+
let line = "";
|
|
149
|
+
const shadowSrc2 = padded[i - 1];
|
|
150
|
+
const textSrc = padded[i];
|
|
151
|
+
for (let c = 0; c < width; c++) {
|
|
152
|
+
const textChar = textSrc[c];
|
|
153
|
+
const shadowChar = c > 0 ? shadowSrc2[c - 1] : " ";
|
|
154
|
+
if (textChar === "\u2588") {
|
|
155
|
+
line += "\u2588";
|
|
156
|
+
} else if (shadowChar === "\u2588") {
|
|
157
|
+
line += "\u2593";
|
|
158
|
+
} else {
|
|
159
|
+
line += " ";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
let colored = " ";
|
|
163
|
+
let j = 0;
|
|
164
|
+
while (j < line.length) {
|
|
165
|
+
const ch = line[j];
|
|
166
|
+
let run = "";
|
|
167
|
+
while (j < line.length && line[j] === ch) {
|
|
168
|
+
run += line[j];
|
|
169
|
+
j++;
|
|
170
|
+
}
|
|
171
|
+
if (ch === "\u2588") {
|
|
172
|
+
colored += GRADIENT[i](run);
|
|
173
|
+
} else if (ch === "\u2593") {
|
|
174
|
+
colored += shadow(run);
|
|
175
|
+
} else {
|
|
176
|
+
colored += run;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
console.log(colored);
|
|
180
|
+
}
|
|
181
|
+
const shadowSrc = padded[height - 1];
|
|
182
|
+
let bottomShadow = "";
|
|
183
|
+
for (let c = 0; c < width + 1; c++) {
|
|
184
|
+
const shadowChar = c > 0 ? shadowSrc[c - 1] : " ";
|
|
185
|
+
bottomShadow += shadowChar === "\u2588" ? "\u2593" : " ";
|
|
186
|
+
}
|
|
187
|
+
bottomShadow = bottomShadow.replace(/\s+$/, "");
|
|
188
|
+
if (bottomShadow.trim()) {
|
|
189
|
+
console.log(` ${shadow(" " + bottomShadow)}`);
|
|
190
|
+
}
|
|
141
191
|
}
|
|
142
192
|
function printStatus(message) {
|
|
143
193
|
console.log(`${pc2.green("\u2714")} ${message}`);
|
|
@@ -152,26 +202,13 @@ function printInfo(message) {
|
|
|
152
202
|
console.log(`${pc2.cyan("\u2139")} ${message}`);
|
|
153
203
|
}
|
|
154
204
|
function printBanner(version) {
|
|
155
|
-
const pink = (t) => pc2.bold(pc2.magenta(t));
|
|
156
|
-
const purple = (t) => pc2.bold(pc2.cyan(t));
|
|
157
|
-
const dim = pc2.dim;
|
|
158
|
-
const bold = pc2.bold;
|
|
159
|
-
const pinkBlock12 = pink("\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588");
|
|
160
|
-
const pinkBlock4 = pink("\u2588\u2588\u2588\u2588");
|
|
161
|
-
const purpleBlock12 = purple("\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588");
|
|
162
|
-
const purpleBlock4 = purple("\u2588\u2588\u2588\u2588");
|
|
163
205
|
console.log();
|
|
164
|
-
|
|
165
|
-
console.log(
|
|
166
|
-
console.log(` ${pinkBlock4}`);
|
|
167
|
-
console.log(
|
|
168
|
-
` ${pinkBlock4} ${purpleBlock12} ${bold("FlyDocs")} ${dim("(Beta)")}`
|
|
169
|
-
);
|
|
206
|
+
renderBannerBlock();
|
|
207
|
+
console.log();
|
|
170
208
|
console.log(
|
|
171
|
-
`
|
|
209
|
+
` ${pc2.bold(pc2.white("Structured context for AI coding tools"))} ${pc2.dim("(Beta)")}`
|
|
172
210
|
);
|
|
173
|
-
console.log(`
|
|
174
|
-
console.log(` ${purpleBlock4} ${dim(`v${version}`)}`);
|
|
211
|
+
console.log(` ${pc2.dim(`Version: ${version}`)}`);
|
|
175
212
|
console.log();
|
|
176
213
|
}
|
|
177
214
|
function printCompletionBox(title, lines) {
|
|
@@ -192,22 +229,42 @@ function printCompletionBox(title, lines) {
|
|
|
192
229
|
}
|
|
193
230
|
function printBetaCta() {
|
|
194
231
|
const dim = pc2.dim;
|
|
195
|
-
const
|
|
232
|
+
const discordUrl = "https://discord.com/invite/YAkjePmZTQ";
|
|
233
|
+
const siteUrl = "https://www.flydocs.ai?utm_source=cli&utm_medium=install&utm_campaign=beta";
|
|
196
234
|
console.log(
|
|
197
235
|
dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")
|
|
198
236
|
);
|
|
199
237
|
console.log();
|
|
200
|
-
console.log(` ${pc2.bold("Join the FlyDocs Closed Beta")}`);
|
|
201
238
|
console.log(
|
|
202
|
-
` ${
|
|
239
|
+
` ${pc2.bold(pc2.cyan("Join the Discord"))} for upcoming features, support, and early access to what's next.`
|
|
240
|
+
);
|
|
241
|
+
console.log(` ${dim("Invite link:")}`);
|
|
242
|
+
console.log(` ${pc2.cyan(discordUrl)}`);
|
|
243
|
+
console.log();
|
|
244
|
+
console.log(
|
|
245
|
+
` ${dim("Docs and updates:")} ${pc2.cyan("https://www.flydocs.ai")}`
|
|
203
246
|
);
|
|
204
|
-
console.log(` ${pc2.cyan(hyperlink("flydocs.ai", url))}`);
|
|
205
247
|
console.log();
|
|
206
248
|
}
|
|
249
|
+
var GRADIENT, BANNER_ROWS;
|
|
207
250
|
var init_ui = __esm({
|
|
208
251
|
"src/lib/ui.ts"() {
|
|
209
252
|
"use strict";
|
|
210
253
|
init_constants();
|
|
254
|
+
GRADIENT = [
|
|
255
|
+
(t) => `\x1B[1;38;2;255;0;128m${t}\x1B[0m`,
|
|
256
|
+
(t) => `\x1B[1;38;2;225;15;158m${t}\x1B[0m`,
|
|
257
|
+
(t) => `\x1B[1;38;2;190;30;190m${t}\x1B[0m`,
|
|
258
|
+
(t) => `\x1B[1;38;2;158;44;214m${t}\x1B[0m`,
|
|
259
|
+
(t) => `\x1B[1;38;2;124;58;237m${t}\x1B[0m`
|
|
260
|
+
];
|
|
261
|
+
BANNER_ROWS = [
|
|
262
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588",
|
|
263
|
+
"\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 ",
|
|
264
|
+
"\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 ",
|
|
265
|
+
"\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588",
|
|
266
|
+
"\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588"
|
|
267
|
+
];
|
|
211
268
|
}
|
|
212
269
|
});
|
|
213
270
|
|
|
@@ -236,10 +293,9 @@ function extractPreservedValues(config) {
|
|
|
236
293
|
return {
|
|
237
294
|
tier: config.tier,
|
|
238
295
|
setupComplete: config.setupComplete ?? false,
|
|
239
|
-
|
|
296
|
+
workspaceId: config.workspaceId ?? null,
|
|
240
297
|
workspace: config.workspace ?? {},
|
|
241
298
|
issueLabels: config.issueLabels ?? {},
|
|
242
|
-
statusMapping: config.statusMapping ?? {},
|
|
243
299
|
detectedStack: config.detectedStack ?? {},
|
|
244
300
|
skills: config.skills ?? {},
|
|
245
301
|
designSystem: config.designSystem ?? null,
|
|
@@ -255,21 +311,13 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
|
|
|
255
311
|
config.version = version;
|
|
256
312
|
config.tier = tierFlag ?? preserved.tier;
|
|
257
313
|
config.setupComplete = preserved.setupComplete;
|
|
258
|
-
|
|
259
|
-
if (!config.provider) {
|
|
260
|
-
config.provider = { type: "linear", teamId: null };
|
|
261
|
-
}
|
|
262
|
-
config.provider.teamId = preserved.providerTeamId;
|
|
263
|
-
}
|
|
314
|
+
config.workspaceId = preserved.workspaceId;
|
|
264
315
|
if (Object.keys(preserved.workspace).length > 0) {
|
|
265
316
|
config.workspace = preserved.workspace;
|
|
266
317
|
}
|
|
267
318
|
if (Object.keys(preserved.issueLabels).length > 0) {
|
|
268
319
|
config.issueLabels = preserved.issueLabels;
|
|
269
320
|
}
|
|
270
|
-
if (Object.keys(preserved.statusMapping).length > 0) {
|
|
271
|
-
config.statusMapping = preserved.statusMapping;
|
|
272
|
-
}
|
|
273
321
|
if (Object.keys(preserved.detectedStack).length > 0) {
|
|
274
322
|
config.detectedStack = preserved.detectedStack;
|
|
275
323
|
}
|
|
@@ -305,18 +353,26 @@ async function installOwnedSkills(templateDir, targetDir, tier) {
|
|
|
305
353
|
join4(templateSkillsDir, activeMech),
|
|
306
354
|
join4(skillsDir, activeMech)
|
|
307
355
|
);
|
|
308
|
-
const { rm:
|
|
356
|
+
const { rm: rm6 } = await import("fs/promises");
|
|
309
357
|
const inactivePath = join4(skillsDir, inactiveMech);
|
|
310
358
|
if (await pathExists(inactivePath)) {
|
|
311
|
-
await
|
|
359
|
+
await rm6(inactivePath, { recursive: true, force: true });
|
|
312
360
|
}
|
|
313
|
-
for (const skill of
|
|
361
|
+
for (const skill of CORE_SKILLS) {
|
|
314
362
|
if (skill === "flydocs-workflow") continue;
|
|
315
363
|
const src = join4(templateSkillsDir, skill);
|
|
316
364
|
if (await pathExists(src)) {
|
|
317
365
|
await replaceDirectory(src, join4(skillsDir, skill));
|
|
318
366
|
}
|
|
319
367
|
}
|
|
368
|
+
if (tier === "cloud") {
|
|
369
|
+
for (const skill of CLOUD_ONLY_SKILLS) {
|
|
370
|
+
const src = join4(templateSkillsDir, skill);
|
|
371
|
+
if (await pathExists(src)) {
|
|
372
|
+
await replaceDirectory(src, join4(skillsDir, skill));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
320
376
|
const readmeSrc = join4(templateSkillsDir, "README.md");
|
|
321
377
|
if (await pathExists(readmeSrc)) {
|
|
322
378
|
await copyFile(readmeSrc, join4(skillsDir, "README.md"));
|
|
@@ -325,18 +381,29 @@ async function installOwnedSkills(templateDir, targetDir, tier) {
|
|
|
325
381
|
async function replaceOwnedSkills(templateDir, targetDir, tier) {
|
|
326
382
|
const skillsDir = join4(targetDir, ".claude", "skills");
|
|
327
383
|
const templateSkillsDir = join4(templateDir, ".claude", "skills");
|
|
328
|
-
|
|
384
|
+
const { rm: rm6 } = await import("fs/promises");
|
|
385
|
+
for (const skill of CORE_SKILLS) {
|
|
329
386
|
const src = join4(templateSkillsDir, skill);
|
|
330
387
|
if (await pathExists(src)) {
|
|
331
388
|
await replaceDirectory(src, join4(skillsDir, skill));
|
|
332
389
|
}
|
|
333
390
|
}
|
|
334
|
-
const
|
|
391
|
+
for (const skill of CLOUD_ONLY_SKILLS) {
|
|
392
|
+
const dest = join4(skillsDir, skill);
|
|
393
|
+
if (tier === "cloud") {
|
|
394
|
+
const src = join4(templateSkillsDir, skill);
|
|
395
|
+
if (await pathExists(src)) {
|
|
396
|
+
await replaceDirectory(src, dest);
|
|
397
|
+
}
|
|
398
|
+
} else if (await pathExists(dest)) {
|
|
399
|
+
await rm6(dest, { recursive: true, force: true });
|
|
400
|
+
}
|
|
401
|
+
}
|
|
335
402
|
const activeMech = MECHANISM_SKILLS[tier];
|
|
336
403
|
const inactiveMech = tier === "local" ? MECHANISM_SKILLS.cloud : MECHANISM_SKILLS.local;
|
|
337
404
|
const inactivePath = join4(skillsDir, inactiveMech);
|
|
338
405
|
if (await pathExists(inactivePath)) {
|
|
339
|
-
await
|
|
406
|
+
await rm6(inactivePath, { recursive: true, force: true });
|
|
340
407
|
}
|
|
341
408
|
await replaceDirectory(
|
|
342
409
|
join4(templateSkillsDir, activeMech),
|
|
@@ -348,9 +415,9 @@ async function replaceOwnedSkills(templateDir, targetDir, tier) {
|
|
|
348
415
|
}
|
|
349
416
|
}
|
|
350
417
|
async function copyCursorRules(targetDir) {
|
|
351
|
-
const { mkdir:
|
|
418
|
+
const { mkdir: mkdir9 } = await import("fs/promises");
|
|
352
419
|
const rulesDir = join4(targetDir, ".cursor", "rules");
|
|
353
|
-
await
|
|
420
|
+
await mkdir9(rulesDir, { recursive: true });
|
|
354
421
|
const workflowRule = join4(
|
|
355
422
|
targetDir,
|
|
356
423
|
".claude",
|
|
@@ -384,18 +451,17 @@ async function copyCursorRules(targetDir) {
|
|
|
384
451
|
await copyFile(context7Rule, join4(rulesDir, "flydocs-context7.mdc"));
|
|
385
452
|
}
|
|
386
453
|
}
|
|
387
|
-
var
|
|
454
|
+
var CORE_SKILLS, CLOUD_ONLY_SKILLS, MECHANISM_SKILLS;
|
|
388
455
|
var init_skills = __esm({
|
|
389
456
|
"src/lib/skills.ts"() {
|
|
390
457
|
"use strict";
|
|
391
458
|
init_fs_ops();
|
|
392
|
-
|
|
459
|
+
CORE_SKILLS = [
|
|
393
460
|
"flydocs-workflow",
|
|
394
|
-
"flydocs-figma",
|
|
395
|
-
"flydocs-estimates",
|
|
396
461
|
"flydocs-context-graph",
|
|
397
462
|
"flydocs-context7"
|
|
398
463
|
];
|
|
464
|
+
CLOUD_ONLY_SKILLS = ["flydocs-figma", "flydocs-estimates"];
|
|
399
465
|
MECHANISM_SKILLS = {
|
|
400
466
|
local: "flydocs-local",
|
|
401
467
|
cloud: "flydocs-cloud"
|
|
@@ -403,14 +469,89 @@ var init_skills = __esm({
|
|
|
403
469
|
}
|
|
404
470
|
});
|
|
405
471
|
|
|
472
|
+
// src/lib/user-content.ts
|
|
473
|
+
import {
|
|
474
|
+
copyFile as fsCopyFile2,
|
|
475
|
+
mkdir as mkdir2,
|
|
476
|
+
readFile as readFile3,
|
|
477
|
+
writeFile as writeFile2
|
|
478
|
+
} from "fs/promises";
|
|
479
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
480
|
+
async function detectExistingConfigs(targetDir) {
|
|
481
|
+
const existing = [];
|
|
482
|
+
const backupDir = join5(targetDir, BACKUP_ORIGINALS_DIR);
|
|
483
|
+
for (const relativePath of RESTORABLE_FILES) {
|
|
484
|
+
const filePath = join5(targetDir, relativePath);
|
|
485
|
+
const backupPath = join5(backupDir, relativePath);
|
|
486
|
+
if (await pathExists(filePath) && !await pathExists(backupPath)) {
|
|
487
|
+
existing.push(relativePath);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return existing;
|
|
491
|
+
}
|
|
492
|
+
async function backupOriginals(targetDir, files = RESTORABLE_FILES) {
|
|
493
|
+
const backedUp = [];
|
|
494
|
+
const backupDir = join5(targetDir, BACKUP_ORIGINALS_DIR);
|
|
495
|
+
for (const relativePath of files) {
|
|
496
|
+
const srcPath = join5(targetDir, relativePath);
|
|
497
|
+
const destPath = join5(backupDir, relativePath);
|
|
498
|
+
if (!await pathExists(srcPath)) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
if (await pathExists(destPath)) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
await mkdir2(dirname2(destPath), { recursive: true });
|
|
505
|
+
await fsCopyFile2(srcPath, destPath);
|
|
506
|
+
backedUp.push(relativePath);
|
|
507
|
+
}
|
|
508
|
+
return backedUp;
|
|
509
|
+
}
|
|
510
|
+
async function readBackupOriginals(targetDir) {
|
|
511
|
+
const backupDir = join5(targetDir, BACKUP_ORIGINALS_DIR);
|
|
512
|
+
if (!await pathExists(backupDir)) {
|
|
513
|
+
return [];
|
|
514
|
+
}
|
|
515
|
+
const files = [];
|
|
516
|
+
for (const relativePath of RESTORABLE_FILES) {
|
|
517
|
+
const backupPath = join5(backupDir, relativePath);
|
|
518
|
+
if (await pathExists(backupPath)) {
|
|
519
|
+
const content = await readFile3(backupPath);
|
|
520
|
+
files.push({ relativePath, content });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return files;
|
|
524
|
+
}
|
|
525
|
+
async function writeRestoredFiles(targetDir, files) {
|
|
526
|
+
for (const { relativePath, content } of files) {
|
|
527
|
+
const destPath = join5(targetDir, relativePath);
|
|
528
|
+
await mkdir2(dirname2(destPath), { recursive: true });
|
|
529
|
+
await writeFile2(destPath, content);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
var BACKUP_ORIGINALS_DIR, RESTORABLE_FILES;
|
|
533
|
+
var init_user_content = __esm({
|
|
534
|
+
"src/lib/user-content.ts"() {
|
|
535
|
+
"use strict";
|
|
536
|
+
init_fs_ops();
|
|
537
|
+
BACKUP_ORIGINALS_DIR = ".flydocs/backup-originals";
|
|
538
|
+
RESTORABLE_FILES = [
|
|
539
|
+
".claude/CLAUDE.md",
|
|
540
|
+
".claude/settings.json",
|
|
541
|
+
".cursor/hooks.json",
|
|
542
|
+
"AGENTS.md"
|
|
543
|
+
];
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
406
547
|
// src/lib/stack.ts
|
|
407
|
-
import { readFile as
|
|
408
|
-
import { join as
|
|
548
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
549
|
+
import { join as join6 } from "path";
|
|
409
550
|
async function parseProjectMdStack(targetDir) {
|
|
410
551
|
const detected = /* @__PURE__ */ new Set();
|
|
411
|
-
const projectMdPath =
|
|
552
|
+
const projectMdPath = join6(targetDir, "flydocs", "context", "project.md");
|
|
412
553
|
try {
|
|
413
|
-
const content = await
|
|
554
|
+
const content = await readFile4(projectMdPath, "utf-8");
|
|
414
555
|
const stackMatch = content.match(/## Stack\n([\s\S]*?)(?=\n## |\n---|\z)/);
|
|
415
556
|
if (!stackMatch) return detected;
|
|
416
557
|
const stackSection = stackMatch[1].toLowerCase();
|
|
@@ -453,10 +594,10 @@ async function parseProjectMdStack(targetDir) {
|
|
|
453
594
|
}
|
|
454
595
|
async function detectStack(targetDir) {
|
|
455
596
|
const detected = /* @__PURE__ */ new Set();
|
|
456
|
-
const pkgPath =
|
|
597
|
+
const pkgPath = join6(targetDir, "package.json");
|
|
457
598
|
let pkg;
|
|
458
599
|
try {
|
|
459
|
-
const content = await
|
|
600
|
+
const content = await readFile4(pkgPath, "utf-8");
|
|
460
601
|
pkg = JSON.parse(content);
|
|
461
602
|
} catch {
|
|
462
603
|
}
|
|
@@ -484,39 +625,39 @@ async function detectStack(targetDir) {
|
|
|
484
625
|
detected.add("testing-library");
|
|
485
626
|
}
|
|
486
627
|
for (const f of ["convex/schema.ts", "convex/schema.js"]) {
|
|
487
|
-
if (await pathExists(
|
|
628
|
+
if (await pathExists(join6(targetDir, f))) detected.add("convex");
|
|
488
629
|
}
|
|
489
630
|
for (const f of ["next.config.ts", "next.config.js", "next.config.mjs"]) {
|
|
490
|
-
if (await pathExists(
|
|
631
|
+
if (await pathExists(join6(targetDir, f))) detected.add("nextjs");
|
|
491
632
|
}
|
|
492
|
-
const appJsonPath =
|
|
633
|
+
const appJsonPath = join6(targetDir, "app.json");
|
|
493
634
|
if (await pathExists(appJsonPath)) {
|
|
494
635
|
try {
|
|
495
|
-
const appContent = await
|
|
636
|
+
const appContent = await readFile4(appJsonPath, "utf-8");
|
|
496
637
|
if (appContent.includes('"expo"')) detected.add("expo");
|
|
497
638
|
} catch {
|
|
498
639
|
}
|
|
499
640
|
}
|
|
500
|
-
if (await pathExists(
|
|
641
|
+
if (await pathExists(join6(targetDir, "tsconfig.json"))) {
|
|
501
642
|
detected.add("typescript");
|
|
502
643
|
}
|
|
503
|
-
if (await pathExists(
|
|
644
|
+
if (await pathExists(join6(targetDir, "nuxt.config.ts")) || await pathExists(join6(targetDir, "nuxt.config.js"))) {
|
|
504
645
|
detected.add("nuxt");
|
|
505
646
|
detected.add("vue");
|
|
506
647
|
}
|
|
507
|
-
if (await pathExists(
|
|
648
|
+
if (await pathExists(join6(targetDir, "angular.json"))) {
|
|
508
649
|
detected.add("angular");
|
|
509
650
|
}
|
|
510
|
-
if (await pathExists(
|
|
651
|
+
if (await pathExists(join6(targetDir, "svelte.config.js")) || await pathExists(join6(targetDir, "svelte.config.ts"))) {
|
|
511
652
|
detected.add("svelte");
|
|
512
653
|
}
|
|
513
|
-
if (await pathExists(
|
|
654
|
+
if (await pathExists(join6(targetDir, "pyproject.toml")) || await pathExists(join6(targetDir, "requirements.txt")) || await pathExists(join6(targetDir, "setup.py"))) {
|
|
514
655
|
detected.add("python");
|
|
515
656
|
}
|
|
516
|
-
if (await pathExists(
|
|
657
|
+
if (await pathExists(join6(targetDir, "go.mod"))) {
|
|
517
658
|
detected.add("go");
|
|
518
659
|
}
|
|
519
|
-
if (await pathExists(
|
|
660
|
+
if (await pathExists(join6(targetDir, "Cargo.toml"))) {
|
|
520
661
|
detected.add("rust");
|
|
521
662
|
}
|
|
522
663
|
const projectMdStack = await parseProjectMdStack(targetDir);
|
|
@@ -562,9 +703,9 @@ var init_stack = __esm({
|
|
|
562
703
|
|
|
563
704
|
// src/lib/post-install.ts
|
|
564
705
|
import { execFileSync } from "child_process";
|
|
565
|
-
import { join as
|
|
706
|
+
import { join as join7 } from "path";
|
|
566
707
|
async function runManifestGeneration(targetDir) {
|
|
567
|
-
const scriptPath =
|
|
708
|
+
const scriptPath = join7(
|
|
568
709
|
targetDir,
|
|
569
710
|
".flydocs",
|
|
570
711
|
"scripts",
|
|
@@ -582,7 +723,7 @@ async function runManifestGeneration(targetDir) {
|
|
|
582
723
|
}
|
|
583
724
|
}
|
|
584
725
|
async function runContextGraphBuild(targetDir) {
|
|
585
|
-
const scriptPath =
|
|
726
|
+
const scriptPath = join7(
|
|
586
727
|
targetDir,
|
|
587
728
|
".claude",
|
|
588
729
|
"skills",
|
|
@@ -610,8 +751,8 @@ var init_post_install = __esm({
|
|
|
610
751
|
});
|
|
611
752
|
|
|
612
753
|
// src/lib/skill-manager.ts
|
|
613
|
-
import { readFile as
|
|
614
|
-
import { join as
|
|
754
|
+
import { readFile as readFile5, readdir, rm as rm2, mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
755
|
+
import { join as join8 } from "path";
|
|
615
756
|
import pc3 from "picocolors";
|
|
616
757
|
function flushFrontmatterValue(mode, lines) {
|
|
617
758
|
switch (mode) {
|
|
@@ -623,8 +764,8 @@ function flushFrontmatterValue(mode, lines) {
|
|
|
623
764
|
return lines.join("\n").trim();
|
|
624
765
|
}
|
|
625
766
|
}
|
|
626
|
-
function parseFrontmatter(
|
|
627
|
-
const match =
|
|
767
|
+
function parseFrontmatter(text4) {
|
|
768
|
+
const match = text4.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
628
769
|
if (!match) return null;
|
|
629
770
|
const block = match[1];
|
|
630
771
|
const result = {};
|
|
@@ -738,13 +879,13 @@ async function downloadFileContent(downloadUrl) {
|
|
|
738
879
|
async function downloadRecursive(url, destDir) {
|
|
739
880
|
const items = await fetchGitHubJson(url);
|
|
740
881
|
if (items === null) return false;
|
|
741
|
-
await
|
|
882
|
+
await mkdir3(destDir, { recursive: true });
|
|
742
883
|
for (const item of items) {
|
|
743
|
-
const destPath =
|
|
884
|
+
const destPath = join8(destDir, item.name);
|
|
744
885
|
if (item.type === "file") {
|
|
745
886
|
if (item.download_url) {
|
|
746
887
|
const content = await downloadFileContent(item.download_url);
|
|
747
|
-
await
|
|
888
|
+
await writeFile3(destPath, content);
|
|
748
889
|
}
|
|
749
890
|
} else if (item.type === "dir") {
|
|
750
891
|
await downloadRecursive(item.url, destPath);
|
|
@@ -757,7 +898,7 @@ async function downloadSkillTree(repo, skillName, targetDir) {
|
|
|
757
898
|
return downloadRecursive(apiUrl, targetDir);
|
|
758
899
|
}
|
|
759
900
|
async function listSkills(targetDir) {
|
|
760
|
-
const skillsDir =
|
|
901
|
+
const skillsDir = join8(targetDir, ".claude", "skills");
|
|
761
902
|
const platform = [];
|
|
762
903
|
const community = [];
|
|
763
904
|
if (!await pathExists(skillsDir)) {
|
|
@@ -770,11 +911,11 @@ async function listSkills(targetDir) {
|
|
|
770
911
|
return { platform, community };
|
|
771
912
|
}
|
|
772
913
|
for (const entry of entries.sort()) {
|
|
773
|
-
const skillFile =
|
|
914
|
+
const skillFile = join8(skillsDir, entry, "SKILL.md");
|
|
774
915
|
if (!await pathExists(skillFile)) continue;
|
|
775
916
|
let content;
|
|
776
917
|
try {
|
|
777
|
-
content = await
|
|
918
|
+
content = await readFile5(skillFile, "utf-8");
|
|
778
919
|
} catch {
|
|
779
920
|
continue;
|
|
780
921
|
}
|
|
@@ -810,7 +951,8 @@ function searchCatalog(keyword) {
|
|
|
810
951
|
return searchable.includes(lower);
|
|
811
952
|
});
|
|
812
953
|
}
|
|
813
|
-
async function addSkill(targetDir, source) {
|
|
954
|
+
async function addSkill(targetDir, source, options) {
|
|
955
|
+
const quiet = options?.quiet ?? false;
|
|
814
956
|
if (isPlatformSkill(source)) {
|
|
815
957
|
printError(`Cannot install platform skill '${source}'.`);
|
|
816
958
|
console.log(" Platform skills (flydocs-*) are managed by the installer.");
|
|
@@ -822,17 +964,19 @@ async function addSkill(targetDir, source) {
|
|
|
822
964
|
console.log(" Platform skills (flydocs-*) are managed by the installer.");
|
|
823
965
|
return;
|
|
824
966
|
}
|
|
825
|
-
const skillsDir =
|
|
967
|
+
const skillsDir = join8(targetDir, ".claude", "skills", skillName);
|
|
826
968
|
if (await pathExists(skillsDir)) {
|
|
827
969
|
printWarning(`Skill '${skillName}' is already installed.`);
|
|
828
970
|
console.log(` Remove first: flydocs skills remove ${skillName}`);
|
|
829
971
|
return;
|
|
830
972
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
973
|
+
if (!quiet) {
|
|
974
|
+
console.log();
|
|
975
|
+
console.log(
|
|
976
|
+
`${pc3.blue("->")} Installing ${pc3.cyan(skillName)} from ${repo}...`
|
|
977
|
+
);
|
|
978
|
+
console.log();
|
|
979
|
+
}
|
|
836
980
|
let success;
|
|
837
981
|
try {
|
|
838
982
|
success = await downloadSkillTree(repo, skillName, skillsDir);
|
|
@@ -851,13 +995,13 @@ async function addSkill(targetDir, source) {
|
|
|
851
995
|
printError(`Skill not found at ${repo}/skills/${skillName}`);
|
|
852
996
|
return;
|
|
853
997
|
}
|
|
854
|
-
const skillMdPath =
|
|
998
|
+
const skillMdPath = join8(skillsDir, "SKILL.md");
|
|
855
999
|
if (!await pathExists(skillMdPath)) {
|
|
856
1000
|
await rm2(skillsDir, { recursive: true, force: true });
|
|
857
1001
|
printError("Invalid skill: SKILL.md not found.");
|
|
858
1002
|
return;
|
|
859
1003
|
}
|
|
860
|
-
const skillMdContent = await
|
|
1004
|
+
const skillMdContent = await readFile5(skillMdPath, "utf-8");
|
|
861
1005
|
const fm = parseFrontmatter(skillMdContent);
|
|
862
1006
|
if (fm === null || !fm["name"] || !fm["description"]) {
|
|
863
1007
|
await rm2(skillsDir, { recursive: true, force: true });
|
|
@@ -866,19 +1010,23 @@ async function addSkill(targetDir, source) {
|
|
|
866
1010
|
);
|
|
867
1011
|
return;
|
|
868
1012
|
}
|
|
869
|
-
if (!fm["triggers"]) {
|
|
1013
|
+
if (!fm["triggers"] && !quiet) {
|
|
870
1014
|
printWarning(
|
|
871
1015
|
"SKILL.md has no triggers -- skill won't appear in manifest index."
|
|
872
1016
|
);
|
|
873
1017
|
}
|
|
874
|
-
|
|
875
|
-
|
|
1018
|
+
if (!quiet) {
|
|
1019
|
+
printStatus("Downloaded skill files");
|
|
1020
|
+
}
|
|
1021
|
+
const cursorRuleSrc = join8(skillsDir, "cursor-rule.mdc");
|
|
876
1022
|
if (await pathExists(cursorRuleSrc)) {
|
|
877
|
-
const cursorRulesDir =
|
|
878
|
-
await
|
|
879
|
-
const cursorRuleDest =
|
|
1023
|
+
const cursorRulesDir = join8(targetDir, ".cursor", "rules");
|
|
1024
|
+
await mkdir3(cursorRulesDir, { recursive: true });
|
|
1025
|
+
const cursorRuleDest = join8(cursorRulesDir, `${skillName}.mdc`);
|
|
880
1026
|
await copyFile(cursorRuleSrc, cursorRuleDest);
|
|
881
|
-
|
|
1027
|
+
if (!quiet) {
|
|
1028
|
+
printStatus("Installed cursor rule");
|
|
1029
|
+
}
|
|
882
1030
|
}
|
|
883
1031
|
const config = await readConfig(targetDir);
|
|
884
1032
|
const installed = config.skills?.installed ?? [];
|
|
@@ -890,12 +1038,16 @@ async function addSkill(targetDir, source) {
|
|
|
890
1038
|
}
|
|
891
1039
|
config.skills.installed = installed;
|
|
892
1040
|
await writeConfig(targetDir, config);
|
|
893
|
-
|
|
1041
|
+
if (!quiet) {
|
|
1042
|
+
printStatus("Updated config.json");
|
|
1043
|
+
}
|
|
894
1044
|
}
|
|
895
1045
|
await runManifestGeneration(targetDir);
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1046
|
+
if (!quiet) {
|
|
1047
|
+
console.log();
|
|
1048
|
+
printStatus(`Installed ${pc3.cyan(skillName)}`);
|
|
1049
|
+
console.log();
|
|
1050
|
+
}
|
|
899
1051
|
}
|
|
900
1052
|
async function removeSkill(targetDir, name) {
|
|
901
1053
|
if (isPlatformSkill(name)) {
|
|
@@ -903,14 +1055,14 @@ async function removeSkill(targetDir, name) {
|
|
|
903
1055
|
console.log(" Platform skills (flydocs-*) are managed by the installer.");
|
|
904
1056
|
return;
|
|
905
1057
|
}
|
|
906
|
-
const skillDir =
|
|
1058
|
+
const skillDir = join8(targetDir, ".claude", "skills", name);
|
|
907
1059
|
if (!await pathExists(skillDir)) {
|
|
908
1060
|
printError(`Skill '${name}' is not installed.`);
|
|
909
1061
|
return;
|
|
910
1062
|
}
|
|
911
1063
|
await rm2(skillDir, { recursive: true, force: true });
|
|
912
1064
|
printStatus("Removed skill directory");
|
|
913
|
-
const cursorRule =
|
|
1065
|
+
const cursorRule = join8(targetDir, ".cursor", "rules", `${name}.mdc`);
|
|
914
1066
|
if (await pathExists(cursorRule)) {
|
|
915
1067
|
await rm2(cursorRule, { force: true });
|
|
916
1068
|
printStatus("Removed cursor rule");
|
|
@@ -975,7 +1127,7 @@ var init_skill_manager = __esm({
|
|
|
975
1127
|
});
|
|
976
1128
|
|
|
977
1129
|
// src/lib/community-skills.ts
|
|
978
|
-
import { join as
|
|
1130
|
+
import { join as join9 } from "path";
|
|
979
1131
|
import { multiselect, isCancel, cancel } from "@clack/prompts";
|
|
980
1132
|
import pc4 from "picocolors";
|
|
981
1133
|
function suggestSkills(stack) {
|
|
@@ -994,10 +1146,10 @@ function suggestSkills(stack) {
|
|
|
994
1146
|
}
|
|
995
1147
|
async function promptCommunitySkills(targetDir, stack, autoYes) {
|
|
996
1148
|
const suggestions = suggestSkills(stack);
|
|
997
|
-
const skillsDir =
|
|
1149
|
+
const skillsDir = join9(targetDir, ".claude", "skills");
|
|
998
1150
|
const filtered = [];
|
|
999
1151
|
for (const skill of suggestions) {
|
|
1000
|
-
if (!await pathExists(
|
|
1152
|
+
if (!await pathExists(join9(skillsDir, skill.name))) {
|
|
1001
1153
|
filtered.push(skill);
|
|
1002
1154
|
}
|
|
1003
1155
|
}
|
|
@@ -1050,7 +1202,8 @@ async function promptCommunitySkills(targetDir, stack, autoYes) {
|
|
|
1050
1202
|
let successCount = 0;
|
|
1051
1203
|
for (const skill of selected) {
|
|
1052
1204
|
try {
|
|
1053
|
-
await addSkill(targetDir, `${skill.repo}:${skill.name}
|
|
1205
|
+
await addSkill(targetDir, `${skill.repo}:${skill.name}`, { quiet: true });
|
|
1206
|
+
printStatus(`Installed ${pc4.cyan(skill.name)}`);
|
|
1054
1207
|
successCount++;
|
|
1055
1208
|
} catch {
|
|
1056
1209
|
printError(`${skill.name} (failed)`);
|
|
@@ -1154,48 +1307,48 @@ var init_community_skills = __esm({
|
|
|
1154
1307
|
});
|
|
1155
1308
|
|
|
1156
1309
|
// src/lib/deprecated.ts
|
|
1157
|
-
import { readdir as readdir2, mkdir as
|
|
1158
|
-
import { join as
|
|
1310
|
+
import { readdir as readdir2, mkdir as mkdir4, rename, rm as rm3 } from "fs/promises";
|
|
1311
|
+
import { join as join10 } from "path";
|
|
1159
1312
|
import { confirm, isCancel as isCancel2 } from "@clack/prompts";
|
|
1160
1313
|
async function scanDeprecated(targetDir) {
|
|
1161
1314
|
const found = [];
|
|
1162
1315
|
for (const item of [...DEPRECATED_DIRS, ...DEPRECATED_FILES]) {
|
|
1163
|
-
if (await pathExists(
|
|
1316
|
+
if (await pathExists(join10(targetDir, item))) {
|
|
1164
1317
|
found.push(item);
|
|
1165
1318
|
}
|
|
1166
1319
|
}
|
|
1167
1320
|
for (const skill of DEPRECATED_SKILLS) {
|
|
1168
|
-
const p =
|
|
1321
|
+
const p = join10(targetDir, ".claude", "skills", skill);
|
|
1169
1322
|
if (await pathExists(p)) {
|
|
1170
1323
|
found.push(`.claude/skills/${skill}`);
|
|
1171
1324
|
}
|
|
1172
1325
|
}
|
|
1173
1326
|
for (const dir of DEPRECATED_RULES_DIR) {
|
|
1174
|
-
if (await pathExists(
|
|
1327
|
+
if (await pathExists(join10(targetDir, dir))) {
|
|
1175
1328
|
found.push(dir);
|
|
1176
1329
|
}
|
|
1177
1330
|
}
|
|
1178
1331
|
for (const hook of DEPRECATED_HOOKS) {
|
|
1179
|
-
const p =
|
|
1332
|
+
const p = join10(targetDir, ".flydocs", "hooks", hook);
|
|
1180
1333
|
if (await pathExists(p)) {
|
|
1181
1334
|
found.push(`.flydocs/hooks/${hook}`);
|
|
1182
1335
|
}
|
|
1183
1336
|
}
|
|
1184
1337
|
for (const rule of DEPRECATED_CURSOR_RULES) {
|
|
1185
|
-
const p =
|
|
1338
|
+
const p = join10(targetDir, ".cursor", "rules", rule);
|
|
1186
1339
|
if (await pathExists(p)) {
|
|
1187
1340
|
found.push(`.cursor/rules/${rule}`);
|
|
1188
1341
|
}
|
|
1189
1342
|
}
|
|
1190
1343
|
for (const dir of DEPRECATED_CURSOR_RULE_DIRS) {
|
|
1191
|
-
const p =
|
|
1344
|
+
const p = join10(targetDir, ".cursor", "rules", dir);
|
|
1192
1345
|
if (await pathExists(p)) {
|
|
1193
1346
|
found.push(`.cursor/rules/${dir}`);
|
|
1194
1347
|
}
|
|
1195
1348
|
}
|
|
1196
1349
|
for (const cmd of DEPRECATED_COMMANDS) {
|
|
1197
1350
|
for (const prefix of [".cursor/commands", ".claude/commands"]) {
|
|
1198
|
-
const p =
|
|
1351
|
+
const p = join10(targetDir, prefix, cmd);
|
|
1199
1352
|
if (await pathExists(p)) {
|
|
1200
1353
|
found.push(`${prefix}/${cmd}`);
|
|
1201
1354
|
}
|
|
@@ -1204,22 +1357,22 @@ async function scanDeprecated(targetDir) {
|
|
|
1204
1357
|
return found;
|
|
1205
1358
|
}
|
|
1206
1359
|
async function handleLegacyContext(targetDir) {
|
|
1207
|
-
const contextDir =
|
|
1208
|
-
const legacyDir =
|
|
1360
|
+
const contextDir = join10(targetDir, "flydocs", "context");
|
|
1361
|
+
const legacyDir = join10(contextDir, "legacy");
|
|
1209
1362
|
const oldFiles = ["overview.md", "stack.md", "standards.md"];
|
|
1210
1363
|
let hasOld = false;
|
|
1211
1364
|
for (const f of oldFiles) {
|
|
1212
|
-
if (await pathExists(
|
|
1365
|
+
if (await pathExists(join10(contextDir, f))) {
|
|
1213
1366
|
hasOld = true;
|
|
1214
1367
|
break;
|
|
1215
1368
|
}
|
|
1216
1369
|
}
|
|
1217
1370
|
if (hasOld) {
|
|
1218
|
-
await
|
|
1371
|
+
await mkdir4(legacyDir, { recursive: true });
|
|
1219
1372
|
for (const f of oldFiles) {
|
|
1220
|
-
const src =
|
|
1373
|
+
const src = join10(contextDir, f);
|
|
1221
1374
|
if (await pathExists(src)) {
|
|
1222
|
-
await rename(src,
|
|
1375
|
+
await rename(src, join10(legacyDir, f));
|
|
1223
1376
|
printStatus(`Moved flydocs/context/${f} \u2192 legacy/`);
|
|
1224
1377
|
}
|
|
1225
1378
|
}
|
|
@@ -1229,7 +1382,7 @@ async function handleLegacyContext(targetDir) {
|
|
|
1229
1382
|
}
|
|
1230
1383
|
}
|
|
1231
1384
|
async function checkLegacyFolder(targetDir) {
|
|
1232
|
-
const legacyDir =
|
|
1385
|
+
const legacyDir = join10(targetDir, "flydocs", "context", "legacy");
|
|
1233
1386
|
if (!await pathExists(legacyDir)) return null;
|
|
1234
1387
|
try {
|
|
1235
1388
|
const entries = await readdir2(legacyDir);
|
|
@@ -1259,7 +1412,7 @@ async function promptCleanup(targetDir, paths) {
|
|
|
1259
1412
|
return;
|
|
1260
1413
|
}
|
|
1261
1414
|
for (const p of paths) {
|
|
1262
|
-
await rm3(
|
|
1415
|
+
await rm3(join10(targetDir, p), { recursive: true, force: true });
|
|
1263
1416
|
printStatus(`Deleted: ${p}`);
|
|
1264
1417
|
}
|
|
1265
1418
|
}
|
|
@@ -1310,26 +1463,26 @@ var init_deprecated = __esm({
|
|
|
1310
1463
|
});
|
|
1311
1464
|
|
|
1312
1465
|
// src/lib/gitignore.ts
|
|
1313
|
-
import { readFile as
|
|
1314
|
-
import { join as
|
|
1466
|
+
import { readFile as readFile6, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
1467
|
+
import { join as join11 } from "path";
|
|
1315
1468
|
async function ensureGitignore(targetDir) {
|
|
1316
|
-
const gitignorePath =
|
|
1469
|
+
const gitignorePath = join11(targetDir, ".gitignore");
|
|
1317
1470
|
if (await pathExists(gitignorePath)) {
|
|
1318
|
-
const content = await
|
|
1471
|
+
const content = await readFile6(gitignorePath, "utf-8");
|
|
1319
1472
|
if (!content.includes("# FlyDocs")) {
|
|
1320
1473
|
const section = "\n# FlyDocs\n" + FLYDOCS_GITIGNORE_ENTRIES.join("\n") + "\n";
|
|
1321
1474
|
await appendFile(gitignorePath, section, "utf-8");
|
|
1322
1475
|
printStatus("Added FlyDocs entries to .gitignore");
|
|
1323
1476
|
}
|
|
1324
1477
|
} else {
|
|
1325
|
-
await
|
|
1478
|
+
await writeFile4(gitignorePath, FULL_GITIGNORE_TEMPLATE, "utf-8");
|
|
1326
1479
|
printStatus(".gitignore (new)");
|
|
1327
1480
|
}
|
|
1328
1481
|
}
|
|
1329
1482
|
async function migrateGitignore(targetDir) {
|
|
1330
|
-
const gitignorePath =
|
|
1483
|
+
const gitignorePath = join11(targetDir, ".gitignore");
|
|
1331
1484
|
if (!await pathExists(gitignorePath)) return;
|
|
1332
|
-
const content = await
|
|
1485
|
+
const content = await readFile6(gitignorePath, "utf-8");
|
|
1333
1486
|
if (!content.includes("flydocs/context/graph.json")) {
|
|
1334
1487
|
if (content.includes("# FlyDocs")) {
|
|
1335
1488
|
const lines = content.split("\n");
|
|
@@ -1340,7 +1493,7 @@ async function migrateGitignore(targetDir) {
|
|
|
1340
1493
|
insertIdx++;
|
|
1341
1494
|
}
|
|
1342
1495
|
lines.splice(insertIdx, 0, "flydocs/context/graph.json");
|
|
1343
|
-
await
|
|
1496
|
+
await writeFile4(gitignorePath, lines.join("\n"), "utf-8");
|
|
1344
1497
|
} else {
|
|
1345
1498
|
await appendFile(
|
|
1346
1499
|
gitignorePath,
|
|
@@ -1409,7 +1562,7 @@ __pycache__/
|
|
|
1409
1562
|
});
|
|
1410
1563
|
|
|
1411
1564
|
// src/lib/version.ts
|
|
1412
|
-
import { readFile as
|
|
1565
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
1413
1566
|
function compareVersions(v1, v2) {
|
|
1414
1567
|
const [core1, pre1] = v1.split("-", 2);
|
|
1415
1568
|
const [core2, pre2] = v2.split("-", 2);
|
|
@@ -1448,7 +1601,7 @@ function compareVersions(v1, v2) {
|
|
|
1448
1601
|
async function getWhatsNew(changelogPath, fromVersion, toVersion) {
|
|
1449
1602
|
let content;
|
|
1450
1603
|
try {
|
|
1451
|
-
content = await
|
|
1604
|
+
content = await readFile7(changelogPath, "utf-8");
|
|
1452
1605
|
} catch {
|
|
1453
1606
|
return [];
|
|
1454
1607
|
}
|
|
@@ -1477,13 +1630,13 @@ var init_version = __esm({
|
|
|
1477
1630
|
});
|
|
1478
1631
|
|
|
1479
1632
|
// src/lib/update-check.ts
|
|
1480
|
-
import { readFile as
|
|
1481
|
-
import { join as
|
|
1633
|
+
import { readFile as readFile8, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
1634
|
+
import { join as join12 } from "path";
|
|
1482
1635
|
import { homedir } from "os";
|
|
1483
1636
|
import pc5 from "picocolors";
|
|
1484
1637
|
async function readCache() {
|
|
1485
1638
|
try {
|
|
1486
|
-
const raw = await
|
|
1639
|
+
const raw = await readFile8(CACHE_FILE, "utf-8");
|
|
1487
1640
|
const parsed = JSON.parse(raw);
|
|
1488
1641
|
if (typeof parsed === "object" && parsed !== null && "checkedAt" in parsed && "latestVersion" in parsed && typeof parsed.checkedAt === "number" && typeof parsed.latestVersion === "string") {
|
|
1489
1642
|
return parsed;
|
|
@@ -1495,8 +1648,8 @@ async function readCache() {
|
|
|
1495
1648
|
}
|
|
1496
1649
|
async function writeCache(cache) {
|
|
1497
1650
|
try {
|
|
1498
|
-
await
|
|
1499
|
-
await
|
|
1651
|
+
await mkdir5(join12(homedir(), ".flydocs"), { recursive: true });
|
|
1652
|
+
await writeFile5(CACHE_FILE, JSON.stringify(cache), "utf-8");
|
|
1500
1653
|
} catch {
|
|
1501
1654
|
}
|
|
1502
1655
|
}
|
|
@@ -1564,12 +1717,230 @@ var init_update_check = __esm({
|
|
|
1564
1717
|
"use strict";
|
|
1565
1718
|
init_constants();
|
|
1566
1719
|
init_version();
|
|
1567
|
-
CACHE_FILE =
|
|
1720
|
+
CACHE_FILE = join12(homedir(), ".flydocs", "update-check.json");
|
|
1568
1721
|
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1569
1722
|
FETCH_TIMEOUT_MS = 5e3;
|
|
1570
1723
|
}
|
|
1571
1724
|
});
|
|
1572
1725
|
|
|
1726
|
+
// src/lib/telemetry.ts
|
|
1727
|
+
import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
|
|
1728
|
+
import { randomUUID } from "crypto";
|
|
1729
|
+
import { join as join13 } from "path";
|
|
1730
|
+
import { homedir as homedir2 } from "os";
|
|
1731
|
+
async function readConfig2() {
|
|
1732
|
+
try {
|
|
1733
|
+
const raw = await readFile9(CONFIG_FILE, "utf-8");
|
|
1734
|
+
const parsed = JSON.parse(raw);
|
|
1735
|
+
if (typeof parsed === "object" && parsed !== null && "enabled" in parsed && "anonymousId" in parsed && typeof parsed.enabled === "boolean" && typeof parsed.anonymousId === "string") {
|
|
1736
|
+
return parsed;
|
|
1737
|
+
}
|
|
1738
|
+
return null;
|
|
1739
|
+
} catch {
|
|
1740
|
+
return null;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
async function writeConfig2(config) {
|
|
1744
|
+
try {
|
|
1745
|
+
await mkdir6(CONFIG_DIR, { recursive: true });
|
|
1746
|
+
await writeFile6(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
1747
|
+
} catch {
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
async function ensureConfig() {
|
|
1751
|
+
const existing = await readConfig2();
|
|
1752
|
+
if (existing) {
|
|
1753
|
+
return existing;
|
|
1754
|
+
}
|
|
1755
|
+
const config = {
|
|
1756
|
+
enabled: false,
|
|
1757
|
+
anonymousId: randomUUID()
|
|
1758
|
+
};
|
|
1759
|
+
await writeConfig2(config);
|
|
1760
|
+
return config;
|
|
1761
|
+
}
|
|
1762
|
+
async function isEnabled() {
|
|
1763
|
+
try {
|
|
1764
|
+
if (process.env.FLYDOCS_TELEMETRY === "0") {
|
|
1765
|
+
return false;
|
|
1766
|
+
}
|
|
1767
|
+
const config = await readConfig2();
|
|
1768
|
+
if (config) {
|
|
1769
|
+
return config.enabled;
|
|
1770
|
+
}
|
|
1771
|
+
return false;
|
|
1772
|
+
} catch {
|
|
1773
|
+
return false;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
function baseProperties() {
|
|
1777
|
+
return {
|
|
1778
|
+
cli_version: CLI_VERSION,
|
|
1779
|
+
os: process.platform,
|
|
1780
|
+
os_arch: process.arch,
|
|
1781
|
+
node_version: process.version,
|
|
1782
|
+
is_ci: Boolean(
|
|
1783
|
+
process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.BUILD_NUMBER
|
|
1784
|
+
),
|
|
1785
|
+
$ip: null,
|
|
1786
|
+
$process_person_profile: false
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
async function capture(event, properties = {}) {
|
|
1790
|
+
try {
|
|
1791
|
+
if (!await isEnabled()) {
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
const config = await ensureConfig();
|
|
1795
|
+
eventQueue.push({
|
|
1796
|
+
event,
|
|
1797
|
+
properties: {
|
|
1798
|
+
distinct_id: config.anonymousId,
|
|
1799
|
+
...baseProperties(),
|
|
1800
|
+
...properties
|
|
1801
|
+
},
|
|
1802
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1803
|
+
});
|
|
1804
|
+
} catch {
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
async function flush() {
|
|
1808
|
+
try {
|
|
1809
|
+
if (eventQueue.length === 0) {
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
if (!await isEnabled()) {
|
|
1813
|
+
eventQueue.length = 0;
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
const batch = eventQueue.splice(0, eventQueue.length);
|
|
1817
|
+
const controller = new AbortController();
|
|
1818
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
|
|
1819
|
+
await fetch(POSTHOG_BATCH_URL, {
|
|
1820
|
+
method: "POST",
|
|
1821
|
+
headers: { "Content-Type": "application/json" },
|
|
1822
|
+
body: JSON.stringify({
|
|
1823
|
+
api_key: POSTHOG_API_KEY,
|
|
1824
|
+
batch
|
|
1825
|
+
}),
|
|
1826
|
+
signal: controller.signal
|
|
1827
|
+
});
|
|
1828
|
+
clearTimeout(timeout);
|
|
1829
|
+
} catch {
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
async function setEnabled(enabled) {
|
|
1833
|
+
const config = await ensureConfig();
|
|
1834
|
+
config.enabled = enabled;
|
|
1835
|
+
await writeConfig2(config);
|
|
1836
|
+
}
|
|
1837
|
+
async function getStatus() {
|
|
1838
|
+
const envOverride = process.env.FLYDOCS_TELEMETRY === "0";
|
|
1839
|
+
const config = await readConfig2();
|
|
1840
|
+
return {
|
|
1841
|
+
enabled: config?.enabled ?? false,
|
|
1842
|
+
envOverride,
|
|
1843
|
+
anonymousId: config?.anonymousId ?? null
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
var CONFIG_DIR, CONFIG_FILE, POSTHOG_BATCH_URL, FETCH_TIMEOUT_MS2, eventQueue;
|
|
1847
|
+
var init_telemetry = __esm({
|
|
1848
|
+
"src/lib/telemetry.ts"() {
|
|
1849
|
+
"use strict";
|
|
1850
|
+
init_constants();
|
|
1851
|
+
CONFIG_DIR = join13(homedir2(), ".flydocs");
|
|
1852
|
+
CONFIG_FILE = join13(CONFIG_DIR, "telemetry.json");
|
|
1853
|
+
POSTHOG_BATCH_URL = "https://us.i.posthog.com/batch/";
|
|
1854
|
+
FETCH_TIMEOUT_MS2 = 5e3;
|
|
1855
|
+
eventQueue = [];
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
// src/lib/api-key.ts
|
|
1860
|
+
import { readFile as readFile10, writeFile as writeFile7, appendFile as appendFile2 } from "fs/promises";
|
|
1861
|
+
import { join as join14 } from "path";
|
|
1862
|
+
function detectKeyType(key) {
|
|
1863
|
+
if (key.startsWith("fdk_")) return "relay";
|
|
1864
|
+
if (key.startsWith("lin_api_")) return "direct";
|
|
1865
|
+
return "unknown";
|
|
1866
|
+
}
|
|
1867
|
+
async function validateRelayKey(apiKey) {
|
|
1868
|
+
const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
|
|
1869
|
+
const response = await fetch(`${baseUrl}/auth/validate`, {
|
|
1870
|
+
method: "POST",
|
|
1871
|
+
headers: {
|
|
1872
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1873
|
+
"Content-Type": "application/json"
|
|
1874
|
+
},
|
|
1875
|
+
signal: AbortSignal.timeout(15e3)
|
|
1876
|
+
});
|
|
1877
|
+
if (!response.ok) return { valid: false };
|
|
1878
|
+
const data = await response.json();
|
|
1879
|
+
if (!data.valid) return { valid: false };
|
|
1880
|
+
return { valid: true, org: data.org ?? "your organization" };
|
|
1881
|
+
}
|
|
1882
|
+
async function fetchWorkspaces(apiKey) {
|
|
1883
|
+
const baseUrl = process.env.FLYDOCS_RELAY_URL?.replace(/\/$/, "") ?? "https://app.flydocs.ai/api/relay";
|
|
1884
|
+
const response = await fetch(`${baseUrl}/auth/workspaces`, {
|
|
1885
|
+
method: "GET",
|
|
1886
|
+
headers: {
|
|
1887
|
+
Authorization: `Bearer ${apiKey}`
|
|
1888
|
+
},
|
|
1889
|
+
signal: AbortSignal.timeout(15e3)
|
|
1890
|
+
});
|
|
1891
|
+
if (!response.ok) {
|
|
1892
|
+
throw new Error(`Failed to fetch workspaces: ${response.status}`);
|
|
1893
|
+
}
|
|
1894
|
+
const data = await response.json();
|
|
1895
|
+
return data;
|
|
1896
|
+
}
|
|
1897
|
+
async function validateLinearKey(apiKey) {
|
|
1898
|
+
const response = await fetch("https://api.linear.app/graphql", {
|
|
1899
|
+
method: "POST",
|
|
1900
|
+
headers: {
|
|
1901
|
+
Authorization: apiKey,
|
|
1902
|
+
"Content-Type": "application/json"
|
|
1903
|
+
},
|
|
1904
|
+
body: JSON.stringify({ query: "{ viewer { id name email } }" }),
|
|
1905
|
+
signal: AbortSignal.timeout(15e3)
|
|
1906
|
+
});
|
|
1907
|
+
if (!response.ok) return { valid: false };
|
|
1908
|
+
const data = await response.json();
|
|
1909
|
+
if (!data.data?.viewer) return { valid: false };
|
|
1910
|
+
return {
|
|
1911
|
+
valid: true,
|
|
1912
|
+
name: data.data.viewer.name,
|
|
1913
|
+
email: data.data.viewer.email
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
async function storeEnvKey(targetDir, envVarName, value) {
|
|
1917
|
+
const envPath = join14(targetDir, ".env");
|
|
1918
|
+
const envLocalPath = join14(targetDir, ".env.local");
|
|
1919
|
+
const targetEnvPath = await pathExists(envLocalPath) ? envLocalPath : envPath;
|
|
1920
|
+
const pattern = new RegExp(`${envVarName}=.*`);
|
|
1921
|
+
if (await pathExists(targetEnvPath)) {
|
|
1922
|
+
const envContent = await readFile10(targetEnvPath, "utf-8");
|
|
1923
|
+
if (pattern.test(envContent)) {
|
|
1924
|
+
const updated = envContent.replace(pattern, `${envVarName}=${value}`);
|
|
1925
|
+
await writeFile7(targetEnvPath, updated, "utf-8");
|
|
1926
|
+
} else {
|
|
1927
|
+
await appendFile2(targetEnvPath, `
|
|
1928
|
+
${envVarName}=${value}
|
|
1929
|
+
`);
|
|
1930
|
+
}
|
|
1931
|
+
} else {
|
|
1932
|
+
await writeFile7(targetEnvPath, `${envVarName}=${value}
|
|
1933
|
+
`, "utf-8");
|
|
1934
|
+
}
|
|
1935
|
+
return targetEnvPath === envLocalPath ? ".env.local" : ".env";
|
|
1936
|
+
}
|
|
1937
|
+
var init_api_key = __esm({
|
|
1938
|
+
"src/lib/api-key.ts"() {
|
|
1939
|
+
"use strict";
|
|
1940
|
+
init_fs_ops();
|
|
1941
|
+
}
|
|
1942
|
+
});
|
|
1943
|
+
|
|
1573
1944
|
// src/commands/install.ts
|
|
1574
1945
|
var install_exports = {};
|
|
1575
1946
|
__export(install_exports, {
|
|
@@ -1577,8 +1948,10 @@ __export(install_exports, {
|
|
|
1577
1948
|
});
|
|
1578
1949
|
import { defineCommand } from "citty";
|
|
1579
1950
|
import { resolve as resolve2 } from "path";
|
|
1580
|
-
import { join as
|
|
1581
|
-
import {
|
|
1951
|
+
import { join as join15 } from "path";
|
|
1952
|
+
import { mkdir as mkdir7 } from "fs/promises";
|
|
1953
|
+
import { confirm as confirm2, select, text, isCancel as isCancel3, cancel as cancel2 } from "@clack/prompts";
|
|
1954
|
+
import pc6 from "picocolors";
|
|
1582
1955
|
var install_default;
|
|
1583
1956
|
var init_install = __esm({
|
|
1584
1957
|
"src/commands/install.ts"() {
|
|
@@ -1588,12 +1961,15 @@ var init_install = __esm({
|
|
|
1588
1961
|
init_ui();
|
|
1589
1962
|
init_config();
|
|
1590
1963
|
init_skills();
|
|
1964
|
+
init_user_content();
|
|
1591
1965
|
init_stack();
|
|
1592
1966
|
init_community_skills();
|
|
1593
1967
|
init_deprecated();
|
|
1594
1968
|
init_gitignore();
|
|
1595
1969
|
init_post_install();
|
|
1596
1970
|
init_update_check();
|
|
1971
|
+
init_telemetry();
|
|
1972
|
+
init_api_key();
|
|
1597
1973
|
install_default = defineCommand({
|
|
1598
1974
|
meta: {
|
|
1599
1975
|
name: "install",
|
|
@@ -1602,7 +1978,7 @@ var init_install = __esm({
|
|
|
1602
1978
|
args: {
|
|
1603
1979
|
tier: {
|
|
1604
1980
|
type: "string",
|
|
1605
|
-
description: "Set tier: 'local' (free) or 'cloud' (
|
|
1981
|
+
description: "Set tier: 'local' (free) or 'cloud' (managed)"
|
|
1606
1982
|
},
|
|
1607
1983
|
path: {
|
|
1608
1984
|
type: "string",
|
|
@@ -1629,6 +2005,7 @@ var init_install = __esm({
|
|
|
1629
2005
|
const templateDir = await resolveTemplatePath(args["local-source"]);
|
|
1630
2006
|
const version = await readTemplateVersion(templateDir);
|
|
1631
2007
|
printBanner(version);
|
|
2008
|
+
await capture("install_started", { template_version: version });
|
|
1632
2009
|
let targetDir;
|
|
1633
2010
|
if (args.path) {
|
|
1634
2011
|
targetDir = resolve2(args.path.replace(/^~/, process.env.HOME ?? "~"));
|
|
@@ -1654,62 +2031,222 @@ var init_install = __esm({
|
|
|
1654
2031
|
process.exit(1);
|
|
1655
2032
|
}
|
|
1656
2033
|
tier = args.tier;
|
|
1657
|
-
|
|
1658
|
-
} else if (await pathExists(join12(targetDir, ".flydocs", "config.json"))) {
|
|
2034
|
+
} else if (await pathExists(join15(targetDir, ".flydocs", "config.json"))) {
|
|
1659
2035
|
try {
|
|
1660
2036
|
const existing = await readConfig(targetDir);
|
|
1661
2037
|
if (existing.tier) {
|
|
1662
2038
|
tier = existing.tier;
|
|
1663
|
-
printInfo(`Tier from config: ${tier}`);
|
|
1664
2039
|
}
|
|
1665
2040
|
} catch {
|
|
1666
2041
|
}
|
|
1667
2042
|
}
|
|
1668
|
-
if (!tier) {
|
|
1669
|
-
|
|
1670
|
-
|
|
2043
|
+
if (!args.tier) {
|
|
2044
|
+
if (!tier) {
|
|
2045
|
+
const tierChoice = await select({
|
|
2046
|
+
message: `Install FlyDocs in: ${targetDir}`,
|
|
2047
|
+
options: [
|
|
2048
|
+
{
|
|
2049
|
+
value: "local",
|
|
2050
|
+
label: "Local (free)",
|
|
2051
|
+
hint: "File-based issues, no account needed"
|
|
2052
|
+
},
|
|
2053
|
+
{
|
|
2054
|
+
value: "cloud",
|
|
2055
|
+
label: "Cloud (managed)",
|
|
2056
|
+
hint: "Sync with Linear, Jira, and more"
|
|
2057
|
+
}
|
|
2058
|
+
]
|
|
2059
|
+
});
|
|
2060
|
+
if (isCancel3(tierChoice)) {
|
|
2061
|
+
cancel2("Installation cancelled.");
|
|
2062
|
+
process.exit(0);
|
|
2063
|
+
}
|
|
2064
|
+
tier = tierChoice;
|
|
2065
|
+
console.log();
|
|
2066
|
+
} else {
|
|
2067
|
+
const shouldInstall = await confirm2({
|
|
2068
|
+
message: `Install FlyDocs here? (${tier} tier)
|
|
1671
2069
|
Directory: ${targetDir}`
|
|
2070
|
+
});
|
|
2071
|
+
if (isCancel3(shouldInstall) || !shouldInstall) {
|
|
2072
|
+
cancel2("Installation cancelled.");
|
|
2073
|
+
process.exit(0);
|
|
2074
|
+
}
|
|
2075
|
+
console.log();
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
if (!tier) {
|
|
2079
|
+
tier = "local";
|
|
2080
|
+
}
|
|
2081
|
+
printInfo(`Tier: ${tier}`);
|
|
2082
|
+
await capture("install_tier_selected", { tier });
|
|
2083
|
+
let selectedWorkspaceId = null;
|
|
2084
|
+
let selectedWorkspaceName = null;
|
|
2085
|
+
if (tier === "cloud") {
|
|
2086
|
+
console.log();
|
|
2087
|
+
console.log(` ${pc6.bold("Connect to FlyDocs Cloud")}`);
|
|
2088
|
+
console.log();
|
|
2089
|
+
console.log(
|
|
2090
|
+
` ${pc6.dim("Get your API key from your FlyDocs dashboard (fdk_...)")}`
|
|
2091
|
+
);
|
|
2092
|
+
console.log();
|
|
2093
|
+
const keyInput = await text({
|
|
2094
|
+
message: "Enter your FlyDocs API key",
|
|
2095
|
+
placeholder: "fdk_...",
|
|
2096
|
+
validate(value) {
|
|
2097
|
+
if (!value.trim()) return "API key is required";
|
|
2098
|
+
const type = detectKeyType(value.trim());
|
|
2099
|
+
if (type !== "relay")
|
|
2100
|
+
return "Cloud tier requires a FlyDocs API key (fdk_...)";
|
|
2101
|
+
return void 0;
|
|
2102
|
+
}
|
|
1672
2103
|
});
|
|
1673
|
-
if (isCancel3(
|
|
2104
|
+
if (isCancel3(keyInput)) {
|
|
1674
2105
|
cancel2("Installation cancelled.");
|
|
1675
2106
|
process.exit(0);
|
|
1676
2107
|
}
|
|
1677
|
-
|
|
2108
|
+
const apiKey = keyInput.trim();
|
|
2109
|
+
printInfo("Validating API key...");
|
|
2110
|
+
try {
|
|
2111
|
+
const result = await validateRelayKey(apiKey);
|
|
2112
|
+
if (!result.valid) {
|
|
2113
|
+
printError("Invalid API key or relay API unreachable.");
|
|
2114
|
+
console.log(` Check your key and try again.`);
|
|
2115
|
+
process.exit(1);
|
|
2116
|
+
}
|
|
2117
|
+
printStatus(`Connected to ${pc6.bold(result.org)}`);
|
|
2118
|
+
} catch {
|
|
2119
|
+
printError(
|
|
2120
|
+
"Could not reach relay API. Check your network and try again."
|
|
2121
|
+
);
|
|
2122
|
+
process.exit(1);
|
|
2123
|
+
}
|
|
2124
|
+
const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
|
|
2125
|
+
printStatus(`API key stored in ${pc6.dim(envFile)}`);
|
|
1678
2126
|
console.log();
|
|
2127
|
+
printInfo("Fetching workspaces...");
|
|
2128
|
+
try {
|
|
2129
|
+
const workspaces = await fetchWorkspaces(apiKey);
|
|
2130
|
+
if (workspaces.length === 0) {
|
|
2131
|
+
printError(
|
|
2132
|
+
"No workspaces found. Create a workspace in the FlyDocs dashboard first, then re-run install."
|
|
2133
|
+
);
|
|
2134
|
+
process.exit(1);
|
|
2135
|
+
}
|
|
2136
|
+
if (workspaces.length === 1) {
|
|
2137
|
+
selectedWorkspaceId = workspaces[0].id;
|
|
2138
|
+
selectedWorkspaceName = workspaces[0].name;
|
|
2139
|
+
printStatus(
|
|
2140
|
+
`Workspace: ${pc6.bold(selectedWorkspaceName)} (auto-selected)`
|
|
2141
|
+
);
|
|
2142
|
+
} else {
|
|
2143
|
+
const workspaceChoice = await select({
|
|
2144
|
+
message: "Select a workspace",
|
|
2145
|
+
options: workspaces.map((ws) => ({
|
|
2146
|
+
value: ws.id,
|
|
2147
|
+
label: `${ws.name} (${ws.provider.type})`,
|
|
2148
|
+
hint: ws.team.name
|
|
2149
|
+
}))
|
|
2150
|
+
});
|
|
2151
|
+
if (isCancel3(workspaceChoice)) {
|
|
2152
|
+
cancel2("Installation cancelled.");
|
|
2153
|
+
process.exit(0);
|
|
2154
|
+
}
|
|
2155
|
+
selectedWorkspaceId = workspaceChoice;
|
|
2156
|
+
const chosen = workspaces.find((ws) => ws.id === selectedWorkspaceId);
|
|
2157
|
+
selectedWorkspaceName = chosen?.name ?? "Unknown";
|
|
2158
|
+
printStatus(`Workspace: ${pc6.bold(selectedWorkspaceName)}`);
|
|
2159
|
+
}
|
|
2160
|
+
} catch {
|
|
2161
|
+
printError(
|
|
2162
|
+
"Could not fetch workspaces. Check your network and try again."
|
|
2163
|
+
);
|
|
2164
|
+
process.exit(1);
|
|
2165
|
+
}
|
|
1679
2166
|
}
|
|
1680
|
-
|
|
2167
|
+
let skipConfigOverwrite = false;
|
|
2168
|
+
if (!await pathExists(join15(targetDir, ".git"))) {
|
|
1681
2169
|
printWarning("No git repository detected. Run git init when ready.");
|
|
1682
2170
|
}
|
|
1683
2171
|
await ensureDirectories(targetDir, tier);
|
|
2172
|
+
const existingFiles = await detectExistingConfigs(targetDir);
|
|
2173
|
+
if (existingFiles.length > 0) {
|
|
2174
|
+
console.log();
|
|
2175
|
+
printWarning(
|
|
2176
|
+
`Found ${existingFiles.length} existing config file(s) that FlyDocs will overwrite:`
|
|
2177
|
+
);
|
|
2178
|
+
for (const f of existingFiles) {
|
|
2179
|
+
console.log(` ${pc6.dim(f)}`);
|
|
2180
|
+
}
|
|
2181
|
+
console.log();
|
|
2182
|
+
if (!args.yes) {
|
|
2183
|
+
const overwriteChoice = await select({
|
|
2184
|
+
message: "How should FlyDocs handle these files?",
|
|
2185
|
+
options: [
|
|
2186
|
+
{
|
|
2187
|
+
value: "backup",
|
|
2188
|
+
label: "Overwrite (backed up)",
|
|
2189
|
+
hint: "Files are saved to .flydocs/backup-originals/ and restored on uninstall"
|
|
2190
|
+
},
|
|
2191
|
+
{
|
|
2192
|
+
value: "skip",
|
|
2193
|
+
label: "Skip overwriting",
|
|
2194
|
+
hint: "Keep your existing files \u2014 FlyDocs config may be incomplete"
|
|
2195
|
+
}
|
|
2196
|
+
]
|
|
2197
|
+
});
|
|
2198
|
+
if (isCancel3(overwriteChoice)) {
|
|
2199
|
+
cancel2("Installation cancelled.");
|
|
2200
|
+
process.exit(0);
|
|
2201
|
+
}
|
|
2202
|
+
if (overwriteChoice === "skip") {
|
|
2203
|
+
printInfo(
|
|
2204
|
+
"Keeping existing files. Run /flydocs-setup to merge your config manually."
|
|
2205
|
+
);
|
|
2206
|
+
skipConfigOverwrite = true;
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
if (!skipConfigOverwrite) {
|
|
2210
|
+
const backedUp = await backupOriginals(targetDir);
|
|
2211
|
+
if (backedUp.length > 0) {
|
|
2212
|
+
printStatus(
|
|
2213
|
+
`Backed up ${backedUp.length} file(s) to .flydocs/backup-originals/`
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
1684
2218
|
console.log("Installing framework files...");
|
|
1685
2219
|
await replaceDirectory(
|
|
1686
|
-
|
|
1687
|
-
|
|
2220
|
+
join15(templateDir, ".flydocs", "templates"),
|
|
2221
|
+
join15(targetDir, ".flydocs", "templates")
|
|
1688
2222
|
);
|
|
1689
2223
|
await replaceDirectory(
|
|
1690
|
-
|
|
1691
|
-
|
|
2224
|
+
join15(templateDir, ".flydocs", "hooks"),
|
|
2225
|
+
join15(targetDir, ".flydocs", "hooks")
|
|
1692
2226
|
);
|
|
1693
2227
|
await replaceDirectory(
|
|
1694
|
-
|
|
1695
|
-
|
|
2228
|
+
join15(templateDir, ".flydocs", "scripts"),
|
|
2229
|
+
join15(targetDir, ".flydocs", "scripts")
|
|
1696
2230
|
);
|
|
1697
2231
|
await copyFile(
|
|
1698
|
-
|
|
1699
|
-
|
|
2232
|
+
join15(templateDir, ".flydocs", "version"),
|
|
2233
|
+
join15(targetDir, ".flydocs", "version")
|
|
1700
2234
|
);
|
|
1701
|
-
const manifestSrc =
|
|
2235
|
+
const manifestSrc = join15(templateDir, "manifest.json");
|
|
1702
2236
|
if (await pathExists(manifestSrc)) {
|
|
1703
|
-
await copyFile(manifestSrc,
|
|
2237
|
+
await copyFile(manifestSrc, join15(targetDir, ".flydocs", "manifest.json"));
|
|
1704
2238
|
}
|
|
1705
|
-
const changelogSrc =
|
|
2239
|
+
const changelogSrc = join15(templateDir, "CHANGELOG.md");
|
|
1706
2240
|
if (await pathExists(changelogSrc)) {
|
|
1707
|
-
await copyFile(changelogSrc,
|
|
2241
|
+
await copyFile(changelogSrc, join15(targetDir, ".flydocs", "CHANGELOG.md"));
|
|
1708
2242
|
}
|
|
1709
2243
|
printStatus(".flydocs/templates, hooks, version, manifest, changelog");
|
|
1710
|
-
const configPath =
|
|
2244
|
+
const configPath = join15(targetDir, ".flydocs", "config.json");
|
|
1711
2245
|
if (!await pathExists(configPath)) {
|
|
1712
2246
|
const config = await createFreshConfig(templateDir, version, tier);
|
|
2247
|
+
if (selectedWorkspaceId) {
|
|
2248
|
+
config.workspaceId = selectedWorkspaceId;
|
|
2249
|
+
}
|
|
1713
2250
|
await writeConfig(targetDir, config);
|
|
1714
2251
|
printStatus(`.flydocs/config.json (new, tier: ${tier})`);
|
|
1715
2252
|
} else if (args.tier) {
|
|
@@ -1717,105 +2254,172 @@ var init_install = __esm({
|
|
|
1717
2254
|
const existing = await readConfig(targetDir);
|
|
1718
2255
|
existing.version = version;
|
|
1719
2256
|
existing.tier = tier;
|
|
2257
|
+
if (selectedWorkspaceId) {
|
|
2258
|
+
existing.workspaceId = selectedWorkspaceId;
|
|
2259
|
+
}
|
|
1720
2260
|
await writeConfig(targetDir, existing);
|
|
1721
2261
|
printStatus(`.flydocs/config.json (tier updated: ${tier})`);
|
|
1722
2262
|
} catch {
|
|
1723
2263
|
const config = await createFreshConfig(templateDir, version, tier);
|
|
2264
|
+
if (selectedWorkspaceId) {
|
|
2265
|
+
config.workspaceId = selectedWorkspaceId;
|
|
2266
|
+
}
|
|
1724
2267
|
await writeConfig(targetDir, config);
|
|
1725
2268
|
printStatus(`.flydocs/config.json (recreated, tier: ${tier})`);
|
|
1726
2269
|
}
|
|
1727
2270
|
} else {
|
|
1728
|
-
|
|
2271
|
+
if (selectedWorkspaceId) {
|
|
2272
|
+
try {
|
|
2273
|
+
const existing = await readConfig(targetDir);
|
|
2274
|
+
existing.workspaceId = selectedWorkspaceId;
|
|
2275
|
+
await writeConfig(targetDir, existing);
|
|
2276
|
+
printWarning(
|
|
2277
|
+
".flydocs/config.json exists, preserving (workspace updated)"
|
|
2278
|
+
);
|
|
2279
|
+
} catch {
|
|
2280
|
+
printWarning(".flydocs/config.json exists, preserving");
|
|
2281
|
+
}
|
|
2282
|
+
} else {
|
|
2283
|
+
printWarning(".flydocs/config.json exists, preserving");
|
|
2284
|
+
}
|
|
1729
2285
|
}
|
|
1730
2286
|
console.log();
|
|
1731
2287
|
console.log(`Installing skills (tier: ${tier})...`);
|
|
1732
2288
|
await installOwnedSkills(templateDir, targetDir, tier);
|
|
1733
2289
|
printStatus(`Skills installed (tier: ${tier})`);
|
|
1734
2290
|
console.log();
|
|
1735
|
-
console.log("
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
claudeAgentsSrc,
|
|
1740
|
-
join12(targetDir, ".claude", "agents")
|
|
1741
|
-
);
|
|
1742
|
-
}
|
|
1743
|
-
await copyDirectoryContents(
|
|
1744
|
-
join12(templateDir, ".claude", "commands"),
|
|
1745
|
-
join12(targetDir, ".claude", "commands")
|
|
2291
|
+
console.log(` ${pc6.bold(pc6.yellow("Sub-Agents (Recommended)"))}`);
|
|
2292
|
+
console.log();
|
|
2293
|
+
console.log(
|
|
2294
|
+
" Sub-agents are specialized roles (PM, implementation, review,"
|
|
1746
2295
|
);
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
join12(targetDir, ".claude", "CLAUDE.md")
|
|
2296
|
+
console.log(
|
|
2297
|
+
" research) that your AI tool can delegate tasks to. They help"
|
|
1750
2298
|
);
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
join12(targetDir, ".claude", "settings.json")
|
|
2299
|
+
console.log(
|
|
2300
|
+
" structure work but are not required \u2014 everything works without them."
|
|
1754
2301
|
);
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
if (
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
2302
|
+
console.log();
|
|
2303
|
+
let installAgents;
|
|
2304
|
+
if (args.yes) {
|
|
2305
|
+
installAgents = true;
|
|
2306
|
+
console.log(" Auto-accepting (--yes)");
|
|
2307
|
+
} else {
|
|
2308
|
+
const agentConfirm = await confirm2({
|
|
2309
|
+
message: "Install sub-agents?",
|
|
2310
|
+
initialValue: true
|
|
2311
|
+
});
|
|
2312
|
+
if (isCancel3(agentConfirm)) {
|
|
2313
|
+
installAgents = false;
|
|
2314
|
+
} else {
|
|
2315
|
+
installAgents = agentConfirm;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
await capture("install_agents_chosen", { install_agents: installAgents });
|
|
2319
|
+
if (installAgents) {
|
|
2320
|
+
const claudeAgentsSrc = join15(templateDir, ".claude", "agents");
|
|
2321
|
+
if (await pathExists(claudeAgentsSrc)) {
|
|
2322
|
+
await mkdir7(join15(targetDir, ".claude", "agents"), { recursive: true });
|
|
2323
|
+
await copyDirectoryContents(
|
|
2324
|
+
claudeAgentsSrc,
|
|
2325
|
+
join15(targetDir, ".claude", "agents")
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
2328
|
+
const cursorAgentsSrc = join15(templateDir, ".cursor", "agents");
|
|
2329
|
+
if (await pathExists(cursorAgentsSrc)) {
|
|
2330
|
+
await mkdir7(join15(targetDir, ".cursor", "agents"), { recursive: true });
|
|
2331
|
+
await copyDirectoryContents(
|
|
2332
|
+
cursorAgentsSrc,
|
|
2333
|
+
join15(targetDir, ".cursor", "agents")
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
printStatus("Sub-agents installed (.claude/agents, .cursor/agents)");
|
|
2337
|
+
} else {
|
|
2338
|
+
printInfo("Skipped sub-agents");
|
|
1762
2339
|
}
|
|
2340
|
+
console.log();
|
|
2341
|
+
console.log("Installing commands and settings...");
|
|
1763
2342
|
await copyDirectoryContents(
|
|
1764
|
-
|
|
1765
|
-
|
|
2343
|
+
join15(templateDir, ".claude", "commands"),
|
|
2344
|
+
join15(targetDir, ".claude", "commands")
|
|
1766
2345
|
);
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
2346
|
+
if (!skipConfigOverwrite) {
|
|
2347
|
+
await copyFile(
|
|
2348
|
+
join15(templateDir, ".claude", "CLAUDE.md"),
|
|
2349
|
+
join15(targetDir, ".claude", "CLAUDE.md")
|
|
2350
|
+
);
|
|
2351
|
+
await copyFile(
|
|
2352
|
+
join15(templateDir, ".claude", "settings.json"),
|
|
2353
|
+
join15(targetDir, ".claude", "settings.json")
|
|
2354
|
+
);
|
|
2355
|
+
printStatus(".claude/ (commands, CLAUDE.md, settings)");
|
|
2356
|
+
} else {
|
|
2357
|
+
printStatus(".claude/ (commands only \u2014 existing config preserved)");
|
|
2358
|
+
}
|
|
2359
|
+
await copyDirectoryContents(
|
|
2360
|
+
join15(templateDir, ".claude", "commands"),
|
|
2361
|
+
join15(targetDir, ".cursor", "commands")
|
|
1770
2362
|
);
|
|
1771
|
-
|
|
2363
|
+
if (!skipConfigOverwrite) {
|
|
2364
|
+
await copyFile(
|
|
2365
|
+
join15(templateDir, ".cursor", "hooks.json"),
|
|
2366
|
+
join15(targetDir, ".cursor", "hooks.json")
|
|
2367
|
+
);
|
|
2368
|
+
printStatus(".cursor/ (commands, hooks)");
|
|
2369
|
+
} else {
|
|
2370
|
+
printStatus(".cursor/ (commands only \u2014 existing hooks preserved)");
|
|
2371
|
+
}
|
|
1772
2372
|
await copyCursorRules(targetDir);
|
|
1773
2373
|
printStatus(".cursor/rules/");
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
2374
|
+
if (!skipConfigOverwrite) {
|
|
2375
|
+
await copyFile(
|
|
2376
|
+
join15(templateDir, "AGENTS.md"),
|
|
2377
|
+
join15(targetDir, "AGENTS.md")
|
|
2378
|
+
);
|
|
2379
|
+
printStatus("AGENTS.md");
|
|
2380
|
+
} else {
|
|
2381
|
+
printInfo("AGENTS.md preserved (existing)");
|
|
2382
|
+
}
|
|
1779
2383
|
await runManifestGeneration(targetDir);
|
|
1780
2384
|
await runContextGraphBuild(targetDir);
|
|
1781
2385
|
console.log();
|
|
1782
2386
|
console.log("Installing project templates...");
|
|
1783
2387
|
const userFiles = [
|
|
1784
2388
|
{
|
|
1785
|
-
src:
|
|
1786
|
-
dest:
|
|
2389
|
+
src: join15(templateDir, "flydocs", "context", "project.md"),
|
|
2390
|
+
dest: join15(targetDir, "flydocs", "context", "project.md"),
|
|
1787
2391
|
label: "flydocs/context/project.md"
|
|
1788
2392
|
},
|
|
1789
2393
|
{
|
|
1790
|
-
src:
|
|
1791
|
-
dest:
|
|
2394
|
+
src: join15(templateDir, "flydocs", "knowledge", "INDEX.md"),
|
|
2395
|
+
dest: join15(targetDir, "flydocs", "knowledge", "INDEX.md"),
|
|
1792
2396
|
label: "flydocs/knowledge/INDEX.md"
|
|
1793
2397
|
},
|
|
1794
2398
|
{
|
|
1795
|
-
src:
|
|
1796
|
-
dest:
|
|
2399
|
+
src: join15(templateDir, "flydocs", "knowledge", "README.md"),
|
|
2400
|
+
dest: join15(targetDir, "flydocs", "knowledge", "README.md"),
|
|
1797
2401
|
label: "flydocs/knowledge/README.md"
|
|
1798
2402
|
},
|
|
1799
2403
|
{
|
|
1800
|
-
src:
|
|
2404
|
+
src: join15(
|
|
1801
2405
|
templateDir,
|
|
1802
2406
|
"flydocs",
|
|
1803
2407
|
"knowledge",
|
|
1804
2408
|
"product",
|
|
1805
2409
|
"personas.md"
|
|
1806
2410
|
),
|
|
1807
|
-
dest:
|
|
2411
|
+
dest: join15(targetDir, "flydocs", "knowledge", "product", "personas.md"),
|
|
1808
2412
|
label: "flydocs/knowledge/product/personas.md"
|
|
1809
2413
|
},
|
|
1810
2414
|
{
|
|
1811
|
-
src:
|
|
2415
|
+
src: join15(
|
|
1812
2416
|
templateDir,
|
|
1813
2417
|
"flydocs",
|
|
1814
2418
|
"knowledge",
|
|
1815
2419
|
"product",
|
|
1816
2420
|
"user-flows.md"
|
|
1817
2421
|
),
|
|
1818
|
-
dest:
|
|
2422
|
+
dest: join15(
|
|
1819
2423
|
targetDir,
|
|
1820
2424
|
"flydocs",
|
|
1821
2425
|
"knowledge",
|
|
@@ -1825,18 +2429,57 @@ var init_install = __esm({
|
|
|
1825
2429
|
label: "flydocs/knowledge/product/user-flows.md"
|
|
1826
2430
|
},
|
|
1827
2431
|
{
|
|
1828
|
-
src:
|
|
1829
|
-
|
|
2432
|
+
src: join15(
|
|
2433
|
+
templateDir,
|
|
2434
|
+
"flydocs",
|
|
2435
|
+
"knowledge",
|
|
2436
|
+
"templates",
|
|
2437
|
+
"decision.md"
|
|
2438
|
+
),
|
|
2439
|
+
dest: join15(
|
|
2440
|
+
targetDir,
|
|
2441
|
+
"flydocs",
|
|
2442
|
+
"knowledge",
|
|
2443
|
+
"templates",
|
|
2444
|
+
"decision.md"
|
|
2445
|
+
),
|
|
2446
|
+
label: "flydocs/knowledge/templates/decision.md"
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
src: join15(
|
|
2450
|
+
templateDir,
|
|
2451
|
+
"flydocs",
|
|
2452
|
+
"knowledge",
|
|
2453
|
+
"templates",
|
|
2454
|
+
"feature.md"
|
|
2455
|
+
),
|
|
2456
|
+
dest: join15(
|
|
2457
|
+
targetDir,
|
|
2458
|
+
"flydocs",
|
|
2459
|
+
"knowledge",
|
|
2460
|
+
"templates",
|
|
2461
|
+
"feature.md"
|
|
2462
|
+
),
|
|
2463
|
+
label: "flydocs/knowledge/templates/feature.md"
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
src: join15(templateDir, "flydocs", "knowledge", "templates", "note.md"),
|
|
2467
|
+
dest: join15(targetDir, "flydocs", "knowledge", "templates", "note.md"),
|
|
2468
|
+
label: "flydocs/knowledge/templates/note.md"
|
|
2469
|
+
},
|
|
2470
|
+
{
|
|
2471
|
+
src: join15(templateDir, "flydocs", "design-system", "README.md"),
|
|
2472
|
+
dest: join15(targetDir, "flydocs", "design-system", "README.md"),
|
|
1830
2473
|
label: "flydocs/design-system/README.md"
|
|
1831
2474
|
},
|
|
1832
2475
|
{
|
|
1833
|
-
src:
|
|
2476
|
+
src: join15(
|
|
1834
2477
|
templateDir,
|
|
1835
2478
|
"flydocs",
|
|
1836
2479
|
"design-system",
|
|
1837
2480
|
"component-patterns.md"
|
|
1838
2481
|
),
|
|
1839
|
-
dest:
|
|
2482
|
+
dest: join15(
|
|
1840
2483
|
targetDir,
|
|
1841
2484
|
"flydocs",
|
|
1842
2485
|
"design-system",
|
|
@@ -1845,13 +2488,13 @@ var init_install = __esm({
|
|
|
1845
2488
|
label: "flydocs/design-system/component-patterns.md"
|
|
1846
2489
|
},
|
|
1847
2490
|
{
|
|
1848
|
-
src:
|
|
1849
|
-
dest:
|
|
2491
|
+
src: join15(templateDir, "flydocs", "design-system", "token-mapping.md"),
|
|
2492
|
+
dest: join15(targetDir, "flydocs", "design-system", "token-mapping.md"),
|
|
1850
2493
|
label: "flydocs/design-system/token-mapping.md"
|
|
1851
2494
|
},
|
|
1852
2495
|
{
|
|
1853
|
-
src:
|
|
1854
|
-
dest:
|
|
2496
|
+
src: join15(templateDir, "flydocs", "README.md"),
|
|
2497
|
+
dest: join15(targetDir, "flydocs", "README.md"),
|
|
1855
2498
|
label: "flydocs/README.md"
|
|
1856
2499
|
}
|
|
1857
2500
|
];
|
|
@@ -1865,14 +2508,10 @@ var init_install = __esm({
|
|
|
1865
2508
|
}
|
|
1866
2509
|
}
|
|
1867
2510
|
}
|
|
1868
|
-
const envExampleSrc =
|
|
2511
|
+
const envExampleSrc = join15(templateDir, ".env.example");
|
|
1869
2512
|
if (await pathExists(envExampleSrc)) {
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
if (!hasEnv && !hasEnvExample) {
|
|
1873
|
-
await copyFile(envExampleSrc, join12(targetDir, ".env.example"));
|
|
1874
|
-
printStatus(".env.example (new)");
|
|
1875
|
-
}
|
|
2513
|
+
await copyFile(envExampleSrc, join15(targetDir, ".env.example"));
|
|
2514
|
+
printStatus(".env.example");
|
|
1876
2515
|
}
|
|
1877
2516
|
await ensureGitignore(targetDir);
|
|
1878
2517
|
console.log();
|
|
@@ -1890,6 +2529,9 @@ var init_install = __esm({
|
|
|
1890
2529
|
} else {
|
|
1891
2530
|
printInfo("No framework detected in package.json");
|
|
1892
2531
|
}
|
|
2532
|
+
await capture("install_skills_chosen", {
|
|
2533
|
+
stack_detected: stack.raw
|
|
2534
|
+
});
|
|
1893
2535
|
console.log();
|
|
1894
2536
|
console.log("Checking for deprecated files...");
|
|
1895
2537
|
const deprecated = await scanDeprecated(targetDir);
|
|
@@ -1902,17 +2544,16 @@ var init_install = __esm({
|
|
|
1902
2544
|
" 1. Run /flydocs-setup to configure your project",
|
|
1903
2545
|
" 2. Start working with /start-session",
|
|
1904
2546
|
"",
|
|
1905
|
-
"
|
|
2547
|
+
"Docs: https://www.flydocs.ai/docs"
|
|
1906
2548
|
] : [
|
|
1907
2549
|
`Tier: ${tier}`,
|
|
1908
2550
|
`Version: ${version}`,
|
|
1909
2551
|
"",
|
|
1910
2552
|
"Next steps:",
|
|
1911
|
-
" 1.
|
|
1912
|
-
" 2.
|
|
1913
|
-
" 3. Start working with /start-session",
|
|
2553
|
+
" 1. Run /flydocs-setup to configure your workspace",
|
|
2554
|
+
" 2. Start working with /start-session",
|
|
1914
2555
|
"",
|
|
1915
|
-
"
|
|
2556
|
+
"Docs: https://www.flydocs.ai/docs"
|
|
1916
2557
|
];
|
|
1917
2558
|
let copiedToClipboard = false;
|
|
1918
2559
|
try {
|
|
@@ -1931,6 +2572,7 @@ var init_install = __esm({
|
|
|
1931
2572
|
}
|
|
1932
2573
|
printCompletionBox("Installation Complete!", nextSteps);
|
|
1933
2574
|
printBetaCta();
|
|
2575
|
+
const detectedIdes = [];
|
|
1934
2576
|
try {
|
|
1935
2577
|
const { execSync: execSync2, spawn } = await import("child_process");
|
|
1936
2578
|
const isInstalled = (cmd) => {
|
|
@@ -1966,6 +2608,7 @@ var init_install = __esm({
|
|
|
1966
2608
|
passCommand: false
|
|
1967
2609
|
});
|
|
1968
2610
|
}
|
|
2611
|
+
detectedIdes.push(...ideOptions.map((o) => o.cmd));
|
|
1969
2612
|
if (ideOptions.length === 1) {
|
|
1970
2613
|
const ide = ideOptions[0];
|
|
1971
2614
|
const launchConfirm = await confirm2({
|
|
@@ -2024,6 +2667,9 @@ var init_install = __esm({
|
|
|
2024
2667
|
}
|
|
2025
2668
|
} catch {
|
|
2026
2669
|
}
|
|
2670
|
+
await capture("install_ide_detected", { ides: detectedIdes });
|
|
2671
|
+
await capture("install_completed", { tier, version });
|
|
2672
|
+
await flush();
|
|
2027
2673
|
try {
|
|
2028
2674
|
const updateResult = await checkForUpdate();
|
|
2029
2675
|
if (updateResult) {
|
|
@@ -2042,10 +2688,10 @@ __export(update_exports, {
|
|
|
2042
2688
|
default: () => update_default
|
|
2043
2689
|
});
|
|
2044
2690
|
import { defineCommand as defineCommand2 } from "citty";
|
|
2045
|
-
import { resolve as resolve3, join as
|
|
2046
|
-
import { mkdir as
|
|
2047
|
-
import { select as select2, text, confirm as confirm3, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
|
|
2048
|
-
import
|
|
2691
|
+
import { resolve as resolve3, join as join16 } from "path";
|
|
2692
|
+
import { mkdir as mkdir8, cp as cp2, readFile as readFile11, readdir as readdir3, rm as rm4 } from "fs/promises";
|
|
2693
|
+
import { select as select2, text as text2, confirm as confirm3, isCancel as isCancel4, cancel as cancel3 } from "@clack/prompts";
|
|
2694
|
+
import pc7 from "picocolors";
|
|
2049
2695
|
var update_default;
|
|
2050
2696
|
var init_update = __esm({
|
|
2051
2697
|
"src/commands/update.ts"() {
|
|
@@ -2062,6 +2708,7 @@ var init_update = __esm({
|
|
|
2062
2708
|
init_gitignore();
|
|
2063
2709
|
init_post_install();
|
|
2064
2710
|
init_update_check();
|
|
2711
|
+
init_telemetry();
|
|
2065
2712
|
update_default = defineCommand2({
|
|
2066
2713
|
meta: {
|
|
2067
2714
|
name: "update",
|
|
@@ -2079,7 +2726,7 @@ var init_update = __esm({
|
|
|
2079
2726
|
},
|
|
2080
2727
|
tier: {
|
|
2081
2728
|
type: "string",
|
|
2082
|
-
description: "Change tier: 'local' (free) or 'cloud' (
|
|
2729
|
+
description: "Change tier: 'local' (free) or 'cloud' (managed)"
|
|
2083
2730
|
},
|
|
2084
2731
|
force: {
|
|
2085
2732
|
type: "boolean",
|
|
@@ -2104,6 +2751,7 @@ var init_update = __esm({
|
|
|
2104
2751
|
printBanner(version);
|
|
2105
2752
|
printInfo("Running in update mode \u2014 framework files will be refreshed");
|
|
2106
2753
|
console.log();
|
|
2754
|
+
await capture("update_started", { template_version: version });
|
|
2107
2755
|
let targetDir;
|
|
2108
2756
|
if (args.path) {
|
|
2109
2757
|
targetDir = resolve3(args.path.replace(/^~/, process.env.HOME ?? "~"));
|
|
@@ -2130,7 +2778,7 @@ var init_update = __esm({
|
|
|
2130
2778
|
if (choice === "cwd") {
|
|
2131
2779
|
targetDir = process.cwd();
|
|
2132
2780
|
} else {
|
|
2133
|
-
const enteredPath = await
|
|
2781
|
+
const enteredPath = await text2({
|
|
2134
2782
|
message: "Enter project path:"
|
|
2135
2783
|
});
|
|
2136
2784
|
if (isCancel4(enteredPath)) {
|
|
@@ -2148,9 +2796,9 @@ var init_update = __esm({
|
|
|
2148
2796
|
}
|
|
2149
2797
|
targetDir = resolve3(targetDir);
|
|
2150
2798
|
process.chdir(targetDir);
|
|
2151
|
-
const hasVersion = await pathExists(
|
|
2799
|
+
const hasVersion = await pathExists(join16(targetDir, ".flydocs", "version"));
|
|
2152
2800
|
const hasConfig = await pathExists(
|
|
2153
|
-
|
|
2801
|
+
join16(targetDir, ".flydocs", "config.json")
|
|
2154
2802
|
);
|
|
2155
2803
|
if (!hasVersion && !hasConfig) {
|
|
2156
2804
|
printError(`Not a FlyDocs project: ${targetDir}`);
|
|
@@ -2161,8 +2809,8 @@ var init_update = __esm({
|
|
|
2161
2809
|
console.log();
|
|
2162
2810
|
let currentVersion = "0.1.0";
|
|
2163
2811
|
if (hasVersion) {
|
|
2164
|
-
const vContent = await
|
|
2165
|
-
|
|
2812
|
+
const vContent = await readFile11(
|
|
2813
|
+
join16(targetDir, ".flydocs", "version"),
|
|
2166
2814
|
"utf-8"
|
|
2167
2815
|
);
|
|
2168
2816
|
currentVersion = vContent.trim();
|
|
@@ -2188,12 +2836,18 @@ var init_update = __esm({
|
|
|
2188
2836
|
process.exit(0);
|
|
2189
2837
|
}
|
|
2190
2838
|
}
|
|
2839
|
+
await capture("update_version_compared", {
|
|
2840
|
+
current_version: currentVersion,
|
|
2841
|
+
target_version: version,
|
|
2842
|
+
version_status: versionStatus,
|
|
2843
|
+
force: args.force
|
|
2844
|
+
});
|
|
2191
2845
|
console.log(`Updating: v${currentVersion} \u2192 v${version}`);
|
|
2192
2846
|
console.log();
|
|
2193
|
-
const changelogPath =
|
|
2847
|
+
const changelogPath = join16(templateDir, "CHANGELOG.md");
|
|
2194
2848
|
const whatsNew = await getWhatsNew(changelogPath, currentVersion, version);
|
|
2195
2849
|
if (whatsNew.length > 0) {
|
|
2196
|
-
console.log(
|
|
2850
|
+
console.log(pc7.cyan("What's new:"));
|
|
2197
2851
|
console.log();
|
|
2198
2852
|
for (const entry of whatsNew) {
|
|
2199
2853
|
console.log(` ${entry}`);
|
|
@@ -2202,23 +2856,23 @@ var init_update = __esm({
|
|
|
2202
2856
|
}
|
|
2203
2857
|
const now = /* @__PURE__ */ new Date();
|
|
2204
2858
|
const ts = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}${String(now.getSeconds()).padStart(2, "0")}`;
|
|
2205
|
-
const backupDir =
|
|
2206
|
-
await
|
|
2859
|
+
const backupDir = join16(targetDir, ".flydocs", `backup-${ts}`);
|
|
2860
|
+
await mkdir8(backupDir, { recursive: true });
|
|
2207
2861
|
if (hasConfig) {
|
|
2208
2862
|
await cp2(
|
|
2209
|
-
|
|
2210
|
-
|
|
2863
|
+
join16(targetDir, ".flydocs", "config.json"),
|
|
2864
|
+
join16(backupDir, "config.json")
|
|
2211
2865
|
);
|
|
2212
2866
|
printStatus(`Config backed up to .flydocs/backup-${ts}/`);
|
|
2213
2867
|
}
|
|
2214
2868
|
try {
|
|
2215
|
-
const flydocsDir =
|
|
2869
|
+
const flydocsDir = join16(targetDir, ".flydocs");
|
|
2216
2870
|
const entries = await readdir3(flydocsDir);
|
|
2217
2871
|
const backups = entries.filter((e) => e.startsWith("backup-")).sort();
|
|
2218
2872
|
if (backups.length > 3) {
|
|
2219
2873
|
const toRemove = backups.slice(0, backups.length - 3);
|
|
2220
2874
|
for (const old of toRemove) {
|
|
2221
|
-
await rm4(
|
|
2875
|
+
await rm4(join16(flydocsDir, old), { recursive: true, force: true });
|
|
2222
2876
|
}
|
|
2223
2877
|
}
|
|
2224
2878
|
} catch {
|
|
@@ -2226,10 +2880,9 @@ var init_update = __esm({
|
|
|
2226
2880
|
let preserved = {
|
|
2227
2881
|
tier: "cloud",
|
|
2228
2882
|
setupComplete: false,
|
|
2229
|
-
|
|
2883
|
+
workspaceId: null,
|
|
2230
2884
|
workspace: {},
|
|
2231
2885
|
issueLabels: {},
|
|
2232
|
-
statusMapping: {},
|
|
2233
2886
|
detectedStack: {},
|
|
2234
2887
|
skills: {},
|
|
2235
2888
|
designSystem: null,
|
|
@@ -2253,76 +2906,137 @@ var init_update = __esm({
|
|
|
2253
2906
|
} else {
|
|
2254
2907
|
effectiveTier = preserved.tier;
|
|
2255
2908
|
}
|
|
2909
|
+
await ensureDirectories(targetDir, effectiveTier);
|
|
2256
2910
|
console.log("Replacing framework directories...");
|
|
2257
2911
|
await replaceDirectory(
|
|
2258
|
-
|
|
2259
|
-
|
|
2912
|
+
join16(templateDir, ".flydocs", "templates"),
|
|
2913
|
+
join16(targetDir, ".flydocs", "templates")
|
|
2260
2914
|
);
|
|
2261
2915
|
await replaceDirectory(
|
|
2262
|
-
|
|
2263
|
-
|
|
2916
|
+
join16(templateDir, ".flydocs", "hooks"),
|
|
2917
|
+
join16(targetDir, ".flydocs", "hooks")
|
|
2264
2918
|
);
|
|
2265
2919
|
await replaceDirectory(
|
|
2266
|
-
|
|
2267
|
-
|
|
2920
|
+
join16(templateDir, ".flydocs", "scripts"),
|
|
2921
|
+
join16(targetDir, ".flydocs", "scripts")
|
|
2268
2922
|
);
|
|
2269
2923
|
printStatus(".flydocs/templates, hooks, scripts");
|
|
2270
|
-
const
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2924
|
+
const hasExistingAgents = await pathExists(
|
|
2925
|
+
join16(targetDir, ".claude", "agents")
|
|
2926
|
+
);
|
|
2927
|
+
let installAgents;
|
|
2928
|
+
if (args.yes) {
|
|
2929
|
+
installAgents = hasExistingAgents ? true : true;
|
|
2930
|
+
} else if (hasExistingAgents) {
|
|
2931
|
+
installAgents = true;
|
|
2932
|
+
} else {
|
|
2933
|
+
console.log();
|
|
2934
|
+
console.log(` ${pc7.bold(pc7.yellow("Sub-Agents (Recommended)"))}`);
|
|
2935
|
+
console.log();
|
|
2936
|
+
console.log(
|
|
2937
|
+
" Sub-agents are specialized roles (PM, implementation, review,"
|
|
2275
2938
|
);
|
|
2939
|
+
console.log(
|
|
2940
|
+
" research) that your AI tool can delegate tasks to. They help"
|
|
2941
|
+
);
|
|
2942
|
+
console.log(
|
|
2943
|
+
" structure work but are not required \u2014 everything works without them."
|
|
2944
|
+
);
|
|
2945
|
+
console.log();
|
|
2946
|
+
const agentConfirm = await confirm3({
|
|
2947
|
+
message: "Install sub-agents?",
|
|
2948
|
+
initialValue: true
|
|
2949
|
+
});
|
|
2950
|
+
if (isCancel4(agentConfirm)) {
|
|
2951
|
+
installAgents = false;
|
|
2952
|
+
} else {
|
|
2953
|
+
installAgents = agentConfirm;
|
|
2954
|
+
}
|
|
2276
2955
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2956
|
+
if (installAgents) {
|
|
2957
|
+
const claudeAgentsSrc = join16(templateDir, ".claude", "agents");
|
|
2958
|
+
if (await pathExists(claudeAgentsSrc)) {
|
|
2959
|
+
await mkdir8(join16(targetDir, ".claude", "agents"), { recursive: true });
|
|
2960
|
+
await copyDirectoryContents(
|
|
2961
|
+
claudeAgentsSrc,
|
|
2962
|
+
join16(targetDir, ".claude", "agents")
|
|
2963
|
+
);
|
|
2964
|
+
}
|
|
2965
|
+
const cursorAgentsSrc = join16(templateDir, ".cursor", "agents");
|
|
2966
|
+
if (await pathExists(cursorAgentsSrc)) {
|
|
2967
|
+
await mkdir8(join16(targetDir, ".cursor", "agents"), { recursive: true });
|
|
2968
|
+
await copyDirectoryContents(
|
|
2969
|
+
cursorAgentsSrc,
|
|
2970
|
+
join16(targetDir, ".cursor", "agents")
|
|
2971
|
+
);
|
|
2972
|
+
}
|
|
2973
|
+
printStatus(
|
|
2974
|
+
hasExistingAgents ? "Sub-agents updated (.claude/agents, .cursor/agents)" : "Sub-agents installed (.claude/agents, .cursor/agents)"
|
|
2285
2975
|
);
|
|
2976
|
+
} else {
|
|
2977
|
+
printInfo("Skipped sub-agents");
|
|
2286
2978
|
}
|
|
2287
|
-
|
|
2979
|
+
await replaceOwnedSkills(templateDir, targetDir, effectiveTier);
|
|
2980
|
+
printStatus(`.claude/skills (tier: ${effectiveTier})`);
|
|
2288
2981
|
console.log();
|
|
2289
2982
|
console.log("Replacing framework files...");
|
|
2290
2983
|
await copyFile(
|
|
2291
|
-
|
|
2292
|
-
|
|
2984
|
+
join16(templateDir, ".claude", "CLAUDE.md"),
|
|
2985
|
+
join16(targetDir, ".claude", "CLAUDE.md")
|
|
2293
2986
|
);
|
|
2294
2987
|
await copyFile(
|
|
2295
|
-
|
|
2296
|
-
|
|
2988
|
+
join16(templateDir, ".claude", "settings.json"),
|
|
2989
|
+
join16(targetDir, ".claude", "settings.json")
|
|
2297
2990
|
);
|
|
2298
2991
|
printStatus(".claude/CLAUDE.md, settings.json");
|
|
2299
2992
|
await copyDirectoryContents(
|
|
2300
|
-
|
|
2301
|
-
|
|
2993
|
+
join16(templateDir, ".claude", "commands"),
|
|
2994
|
+
join16(targetDir, ".claude", "commands")
|
|
2302
2995
|
);
|
|
2303
2996
|
await copyDirectoryContents(
|
|
2304
|
-
|
|
2305
|
-
|
|
2997
|
+
join16(templateDir, ".claude", "commands"),
|
|
2998
|
+
join16(targetDir, ".cursor", "commands")
|
|
2306
2999
|
);
|
|
2307
3000
|
printStatus(".claude/commands, .cursor/commands");
|
|
2308
|
-
const skillsReadmeSrc =
|
|
3001
|
+
const skillsReadmeSrc = join16(templateDir, ".claude", "skills", "README.md");
|
|
2309
3002
|
if (await pathExists(skillsReadmeSrc)) {
|
|
2310
3003
|
await copyFile(
|
|
2311
3004
|
skillsReadmeSrc,
|
|
2312
|
-
|
|
3005
|
+
join16(targetDir, ".claude", "skills", "README.md")
|
|
2313
3006
|
);
|
|
2314
3007
|
}
|
|
2315
3008
|
printStatus(".claude/skills/README.md");
|
|
2316
3009
|
await copyFile(
|
|
2317
|
-
|
|
2318
|
-
|
|
3010
|
+
join16(templateDir, ".cursor", "hooks.json"),
|
|
3011
|
+
join16(targetDir, ".cursor", "hooks.json")
|
|
2319
3012
|
);
|
|
2320
3013
|
printStatus(".cursor/hooks.json");
|
|
2321
3014
|
await copyFile(
|
|
2322
|
-
|
|
2323
|
-
|
|
3015
|
+
join16(templateDir, "AGENTS.md"),
|
|
3016
|
+
join16(targetDir, "AGENTS.md")
|
|
2324
3017
|
);
|
|
2325
3018
|
printStatus("AGENTS.md");
|
|
3019
|
+
const envExampleSrc = join16(templateDir, ".env.example");
|
|
3020
|
+
if (await pathExists(envExampleSrc)) {
|
|
3021
|
+
await copyFile(envExampleSrc, join16(targetDir, ".env.example"));
|
|
3022
|
+
printStatus(".env.example");
|
|
3023
|
+
}
|
|
3024
|
+
const knowledgeTemplatesDir = join16(
|
|
3025
|
+
targetDir,
|
|
3026
|
+
"flydocs",
|
|
3027
|
+
"knowledge",
|
|
3028
|
+
"templates"
|
|
3029
|
+
);
|
|
3030
|
+
if (!await pathExists(knowledgeTemplatesDir)) {
|
|
3031
|
+
await mkdir8(knowledgeTemplatesDir, { recursive: true });
|
|
3032
|
+
}
|
|
3033
|
+
for (const tmpl of ["decision.md", "feature.md", "note.md"]) {
|
|
3034
|
+
const src = join16(templateDir, "flydocs", "knowledge", "templates", tmpl);
|
|
3035
|
+
const dest = join16(knowledgeTemplatesDir, tmpl);
|
|
3036
|
+
if (await pathExists(src) && !await pathExists(dest)) {
|
|
3037
|
+
await copyFile(src, dest);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
2326
3040
|
await runManifestGeneration(targetDir);
|
|
2327
3041
|
await runContextGraphBuild(targetDir);
|
|
2328
3042
|
console.log();
|
|
@@ -2345,18 +3059,18 @@ var init_update = __esm({
|
|
|
2345
3059
|
printWarning("Config merge failed \u2014 config.json preserved as-is");
|
|
2346
3060
|
}
|
|
2347
3061
|
await copyFile(
|
|
2348
|
-
|
|
2349
|
-
|
|
3062
|
+
join16(templateDir, ".flydocs", "version"),
|
|
3063
|
+
join16(targetDir, ".flydocs", "version")
|
|
2350
3064
|
);
|
|
2351
3065
|
printStatus(`.flydocs/version \u2192 ${version}`);
|
|
2352
|
-
const clSrc =
|
|
3066
|
+
const clSrc = join16(templateDir, "CHANGELOG.md");
|
|
2353
3067
|
if (await pathExists(clSrc)) {
|
|
2354
|
-
await copyFile(clSrc,
|
|
3068
|
+
await copyFile(clSrc, join16(targetDir, ".flydocs", "CHANGELOG.md"));
|
|
2355
3069
|
printStatus(".flydocs/CHANGELOG.md");
|
|
2356
3070
|
}
|
|
2357
|
-
const mfSrc =
|
|
3071
|
+
const mfSrc = join16(templateDir, "manifest.json");
|
|
2358
3072
|
if (await pathExists(mfSrc)) {
|
|
2359
|
-
await copyFile(mfSrc,
|
|
3073
|
+
await copyFile(mfSrc, join16(targetDir, ".flydocs", "manifest.json"));
|
|
2360
3074
|
printStatus(".flydocs/manifest.json");
|
|
2361
3075
|
}
|
|
2362
3076
|
console.log();
|
|
@@ -2392,6 +3106,12 @@ var init_update = __esm({
|
|
|
2392
3106
|
`Backup: .flydocs/backup-${ts}/`
|
|
2393
3107
|
]);
|
|
2394
3108
|
printBetaCta();
|
|
3109
|
+
await capture("update_completed", {
|
|
3110
|
+
current_version: currentVersion,
|
|
3111
|
+
version,
|
|
3112
|
+
tier: effectiveTier
|
|
3113
|
+
});
|
|
3114
|
+
await flush();
|
|
2395
3115
|
try {
|
|
2396
3116
|
const updateResult = await checkForUpdate();
|
|
2397
3117
|
if (updateResult) {
|
|
@@ -2404,33 +3124,352 @@ var init_update = __esm({
|
|
|
2404
3124
|
}
|
|
2405
3125
|
});
|
|
2406
3126
|
|
|
3127
|
+
// src/commands/uninstall.ts
|
|
3128
|
+
var uninstall_exports = {};
|
|
3129
|
+
__export(uninstall_exports, {
|
|
3130
|
+
default: () => uninstall_default
|
|
3131
|
+
});
|
|
3132
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
3133
|
+
import { resolve as resolve4, join as join17 } from "path";
|
|
3134
|
+
import { readdir as readdir4, rm as rm5, rename as rename2 } from "fs/promises";
|
|
3135
|
+
import { confirm as confirm4, select as select3, isCancel as isCancel5, cancel as cancel4 } from "@clack/prompts";
|
|
3136
|
+
import pc8 from "picocolors";
|
|
3137
|
+
async function removeOwnedSkills(targetDir) {
|
|
3138
|
+
const skillsDir = join17(targetDir, ".claude", "skills");
|
|
3139
|
+
const removed = [];
|
|
3140
|
+
if (!await pathExists(skillsDir)) {
|
|
3141
|
+
return removed;
|
|
3142
|
+
}
|
|
3143
|
+
try {
|
|
3144
|
+
const entries = await readdir4(skillsDir);
|
|
3145
|
+
for (const entry of entries) {
|
|
3146
|
+
if (entry.startsWith(OWNED_SKILL_PREFIX)) {
|
|
3147
|
+
await rm5(join17(skillsDir, entry), { recursive: true, force: true });
|
|
3148
|
+
removed.push(`.claude/skills/${entry}`);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
} catch {
|
|
3152
|
+
}
|
|
3153
|
+
return removed;
|
|
3154
|
+
}
|
|
3155
|
+
async function removeOwnedCursorRules(targetDir) {
|
|
3156
|
+
const rulesDir = join17(targetDir, ".cursor", "rules");
|
|
3157
|
+
const removed = [];
|
|
3158
|
+
if (!await pathExists(rulesDir)) {
|
|
3159
|
+
return removed;
|
|
3160
|
+
}
|
|
3161
|
+
try {
|
|
3162
|
+
const entries = await readdir4(rulesDir);
|
|
3163
|
+
for (const entry of entries) {
|
|
3164
|
+
if (entry.startsWith(OWNED_RULE_PREFIX) && entry.endsWith(".mdc")) {
|
|
3165
|
+
await rm5(join17(rulesDir, entry), { force: true });
|
|
3166
|
+
removed.push(`.cursor/rules/${entry}`);
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
} catch {
|
|
3170
|
+
}
|
|
3171
|
+
return removed;
|
|
3172
|
+
}
|
|
3173
|
+
async function isEmptyDir(dirPath) {
|
|
3174
|
+
try {
|
|
3175
|
+
const entries = await readdir4(dirPath);
|
|
3176
|
+
return entries.length === 0;
|
|
3177
|
+
} catch {
|
|
3178
|
+
return false;
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
async function cleanupEmptyParents(targetDir, dirs) {
|
|
3182
|
+
const cleaned = [];
|
|
3183
|
+
for (const dir of dirs) {
|
|
3184
|
+
const fullPath = join17(targetDir, dir);
|
|
3185
|
+
if (await pathExists(fullPath) && await isEmptyDir(fullPath)) {
|
|
3186
|
+
await rm5(fullPath, { recursive: true, force: true });
|
|
3187
|
+
cleaned.push(dir);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
return cleaned;
|
|
3191
|
+
}
|
|
3192
|
+
var ALWAYS_REMOVED, OWNED_SKILL_PREFIX, OWNED_RULE_PREFIX, uninstall_default;
|
|
3193
|
+
var init_uninstall = __esm({
|
|
3194
|
+
"src/commands/uninstall.ts"() {
|
|
3195
|
+
"use strict";
|
|
3196
|
+
init_fs_ops();
|
|
3197
|
+
init_user_content();
|
|
3198
|
+
init_ui();
|
|
3199
|
+
init_constants();
|
|
3200
|
+
ALWAYS_REMOVED = [
|
|
3201
|
+
[".claude/CLAUDE.md", "file"],
|
|
3202
|
+
[".claude/settings.json", "file"],
|
|
3203
|
+
[".claude/agents", "dir"],
|
|
3204
|
+
[".claude/commands", "dir"],
|
|
3205
|
+
[".claude/skills/README.md", "file"],
|
|
3206
|
+
[".cursor/hooks.json", "file"],
|
|
3207
|
+
[".cursor/agents", "dir"],
|
|
3208
|
+
[".flydocs", "dir"],
|
|
3209
|
+
["AGENTS.md", "file"],
|
|
3210
|
+
[".env.example", "file"]
|
|
3211
|
+
];
|
|
3212
|
+
OWNED_SKILL_PREFIX = "flydocs-";
|
|
3213
|
+
OWNED_RULE_PREFIX = "flydocs-";
|
|
3214
|
+
uninstall_default = defineCommand3({
|
|
3215
|
+
meta: {
|
|
3216
|
+
name: "uninstall",
|
|
3217
|
+
description: "Remove FlyDocs from a project directory"
|
|
3218
|
+
},
|
|
3219
|
+
args: {
|
|
3220
|
+
path: {
|
|
3221
|
+
type: "string",
|
|
3222
|
+
description: "Uninstall from the specified directory"
|
|
3223
|
+
},
|
|
3224
|
+
here: {
|
|
3225
|
+
type: "boolean",
|
|
3226
|
+
description: "Uninstall from the current directory",
|
|
3227
|
+
default: false
|
|
3228
|
+
},
|
|
3229
|
+
all: {
|
|
3230
|
+
type: "boolean",
|
|
3231
|
+
description: "Remove everything including flydocs/ user content",
|
|
3232
|
+
default: false
|
|
3233
|
+
},
|
|
3234
|
+
yes: {
|
|
3235
|
+
type: "boolean",
|
|
3236
|
+
alias: ["y"],
|
|
3237
|
+
description: "Skip confirmation prompts",
|
|
3238
|
+
default: false
|
|
3239
|
+
},
|
|
3240
|
+
force: {
|
|
3241
|
+
type: "boolean",
|
|
3242
|
+
description: "Alias for --all --yes (complete removal, no prompts)",
|
|
3243
|
+
default: false
|
|
3244
|
+
}
|
|
3245
|
+
},
|
|
3246
|
+
async run({ args }) {
|
|
3247
|
+
printBanner(CLI_VERSION);
|
|
3248
|
+
let targetDir;
|
|
3249
|
+
if (args.path) {
|
|
3250
|
+
targetDir = resolve4(args.path.replace(/^~/, process.env.HOME ?? "~"));
|
|
3251
|
+
} else if (args.here) {
|
|
3252
|
+
targetDir = process.cwd();
|
|
3253
|
+
} else {
|
|
3254
|
+
targetDir = process.cwd();
|
|
3255
|
+
}
|
|
3256
|
+
if (!await pathExists(targetDir)) {
|
|
3257
|
+
printError(`Directory does not exist: ${targetDir}`);
|
|
3258
|
+
process.exit(1);
|
|
3259
|
+
}
|
|
3260
|
+
targetDir = resolve4(targetDir);
|
|
3261
|
+
const hasFlydocs = await pathExists(join17(targetDir, ".flydocs"));
|
|
3262
|
+
const hasAgentsMd = await pathExists(join17(targetDir, "AGENTS.md"));
|
|
3263
|
+
if (!hasFlydocs && !hasAgentsMd) {
|
|
3264
|
+
printError(`Not a FlyDocs project: ${targetDir}`);
|
|
3265
|
+
printInfo("No .flydocs/ directory or AGENTS.md found.");
|
|
3266
|
+
process.exit(1);
|
|
3267
|
+
}
|
|
3268
|
+
printInfo(`Project: ${targetDir}`);
|
|
3269
|
+
console.log();
|
|
3270
|
+
const forceAll = args.force;
|
|
3271
|
+
const removeAll = forceAll || args.all;
|
|
3272
|
+
const skipPrompts = forceAll || args.yes;
|
|
3273
|
+
let contentAction = "preserve";
|
|
3274
|
+
const hasUserContent = await pathExists(join17(targetDir, "flydocs"));
|
|
3275
|
+
if (hasUserContent) {
|
|
3276
|
+
if (removeAll) {
|
|
3277
|
+
contentAction = "remove";
|
|
3278
|
+
} else if (!skipPrompts) {
|
|
3279
|
+
const choice = await select3({
|
|
3280
|
+
message: "What should happen to your flydocs/ content (project docs, knowledge base)?",
|
|
3281
|
+
options: [
|
|
3282
|
+
{
|
|
3283
|
+
value: "archive",
|
|
3284
|
+
label: "Archive",
|
|
3285
|
+
hint: "Rename to flydocs-archive/ (safe, reversible)"
|
|
3286
|
+
},
|
|
3287
|
+
{
|
|
3288
|
+
value: "remove",
|
|
3289
|
+
label: "Remove completely",
|
|
3290
|
+
hint: "Permanently delete flydocs/ and all contents"
|
|
3291
|
+
},
|
|
3292
|
+
{
|
|
3293
|
+
value: "preserve",
|
|
3294
|
+
label: "Keep as-is",
|
|
3295
|
+
hint: "Leave flydocs/ untouched"
|
|
3296
|
+
}
|
|
3297
|
+
]
|
|
3298
|
+
});
|
|
3299
|
+
if (isCancel5(choice)) {
|
|
3300
|
+
cancel4("Uninstall cancelled.");
|
|
3301
|
+
process.exit(0);
|
|
3302
|
+
}
|
|
3303
|
+
contentAction = choice;
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
if (!skipPrompts) {
|
|
3307
|
+
console.log();
|
|
3308
|
+
console.log(pc8.bold("The following will be removed:"));
|
|
3309
|
+
console.log();
|
|
3310
|
+
console.log(" Framework files:");
|
|
3311
|
+
for (const [path] of ALWAYS_REMOVED) {
|
|
3312
|
+
console.log(` ${pc8.dim(path)}`);
|
|
3313
|
+
}
|
|
3314
|
+
console.log(` ${pc8.dim(".claude/skills/flydocs-*")}`);
|
|
3315
|
+
console.log(` ${pc8.dim(".cursor/rules/flydocs-*.mdc")}`);
|
|
3316
|
+
if (hasUserContent) {
|
|
3317
|
+
if (contentAction === "archive") {
|
|
3318
|
+
console.log();
|
|
3319
|
+
console.log(
|
|
3320
|
+
` User content: ${pc8.yellow("flydocs/ -> flydocs-archive/")}`
|
|
3321
|
+
);
|
|
3322
|
+
} else if (contentAction === "remove") {
|
|
3323
|
+
console.log();
|
|
3324
|
+
console.log(` User content: ${pc8.red("flydocs/ (deleted)")}`);
|
|
3325
|
+
} else {
|
|
3326
|
+
console.log();
|
|
3327
|
+
console.log(` User content: ${pc8.green("flydocs/ (preserved)")}`);
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
console.log();
|
|
3331
|
+
console.log(pc8.bold("Preserved:"));
|
|
3332
|
+
console.log(
|
|
3333
|
+
` ${pc8.dim(".claude/skills/ (non-flydocs community skills)")}`
|
|
3334
|
+
);
|
|
3335
|
+
console.log(` ${pc8.dim(".env, .env.local")}`);
|
|
3336
|
+
console.log();
|
|
3337
|
+
const shouldContinue = await confirm4({
|
|
3338
|
+
message: "Proceed with uninstall?"
|
|
3339
|
+
});
|
|
3340
|
+
if (isCancel5(shouldContinue) || !shouldContinue) {
|
|
3341
|
+
cancel4("Uninstall cancelled.");
|
|
3342
|
+
process.exit(0);
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
const originals = await readBackupOriginals(targetDir);
|
|
3346
|
+
const result = {
|
|
3347
|
+
removed: [],
|
|
3348
|
+
skipped: [],
|
|
3349
|
+
archived: [],
|
|
3350
|
+
restored: []
|
|
3351
|
+
};
|
|
3352
|
+
const removedSkills = await removeOwnedSkills(targetDir);
|
|
3353
|
+
result.removed.push(...removedSkills);
|
|
3354
|
+
const removedRules = await removeOwnedCursorRules(targetDir);
|
|
3355
|
+
result.removed.push(...removedRules);
|
|
3356
|
+
for (const [relativePath, type] of ALWAYS_REMOVED) {
|
|
3357
|
+
const fullPath = join17(targetDir, relativePath);
|
|
3358
|
+
if (!await pathExists(fullPath)) {
|
|
3359
|
+
result.skipped.push(relativePath);
|
|
3360
|
+
continue;
|
|
3361
|
+
}
|
|
3362
|
+
try {
|
|
3363
|
+
if (type === "dir") {
|
|
3364
|
+
await rm5(fullPath, { recursive: true, force: true });
|
|
3365
|
+
} else {
|
|
3366
|
+
await rm5(fullPath, { force: true });
|
|
3367
|
+
}
|
|
3368
|
+
result.removed.push(relativePath);
|
|
3369
|
+
} catch {
|
|
3370
|
+
printWarning(`Could not remove: ${relativePath}`);
|
|
3371
|
+
result.skipped.push(relativePath);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
if (hasUserContent) {
|
|
3375
|
+
const flydocsPath = join17(targetDir, "flydocs");
|
|
3376
|
+
if (contentAction === "archive") {
|
|
3377
|
+
const archivePath = join17(targetDir, "flydocs-archive");
|
|
3378
|
+
if (await pathExists(archivePath)) {
|
|
3379
|
+
await rm5(archivePath, { recursive: true, force: true });
|
|
3380
|
+
}
|
|
3381
|
+
await rename2(flydocsPath, archivePath);
|
|
3382
|
+
result.archived.push("flydocs/ -> flydocs-archive/");
|
|
3383
|
+
} else if (contentAction === "remove") {
|
|
3384
|
+
await rm5(flydocsPath, { recursive: true, force: true });
|
|
3385
|
+
result.removed.push("flydocs/");
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
const emptyCandidates = [
|
|
3389
|
+
".claude/skills",
|
|
3390
|
+
".claude",
|
|
3391
|
+
".cursor/rules",
|
|
3392
|
+
".cursor"
|
|
3393
|
+
];
|
|
3394
|
+
const cleaned = await cleanupEmptyParents(targetDir, emptyCandidates);
|
|
3395
|
+
for (const dir of cleaned) {
|
|
3396
|
+
result.removed.push(`${dir}/ (empty, cleaned up)`);
|
|
3397
|
+
}
|
|
3398
|
+
if (originals.length > 0) {
|
|
3399
|
+
await writeRestoredFiles(targetDir, originals);
|
|
3400
|
+
result.restored = originals.map((f) => f.relativePath);
|
|
3401
|
+
}
|
|
3402
|
+
console.log();
|
|
3403
|
+
console.log(pc8.bold("Uninstall Summary"));
|
|
3404
|
+
console.log();
|
|
3405
|
+
if (result.removed.length > 0) {
|
|
3406
|
+
console.log(` ${pc8.green("Removed")} (${result.removed.length}):`);
|
|
3407
|
+
for (const item of result.removed) {
|
|
3408
|
+
printStatus(item);
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
if (result.archived.length > 0) {
|
|
3412
|
+
console.log();
|
|
3413
|
+
console.log(` ${pc8.yellow("Archived")} (${result.archived.length}):`);
|
|
3414
|
+
for (const item of result.archived) {
|
|
3415
|
+
printInfo(item);
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
if (result.restored.length > 0) {
|
|
3419
|
+
console.log();
|
|
3420
|
+
console.log(
|
|
3421
|
+
` ${pc8.green("Restored")} (${result.restored.length} pre-FlyDocs original(s)):`
|
|
3422
|
+
);
|
|
3423
|
+
for (const item of result.restored) {
|
|
3424
|
+
printInfo(item);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
if (result.skipped.length > 0) {
|
|
3428
|
+
console.log();
|
|
3429
|
+
console.log(
|
|
3430
|
+
` ${pc8.dim("Skipped")} (${result.skipped.length} \u2014 not found):`
|
|
3431
|
+
);
|
|
3432
|
+
for (const item of result.skipped) {
|
|
3433
|
+
console.log(` ${pc8.dim(item)}`);
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
console.log();
|
|
3437
|
+
printStatus("FlyDocs has been removed from this project.");
|
|
3438
|
+
console.log();
|
|
3439
|
+
printInfo(`To reinstall: ${pc8.cyan("npx @flydocs/cli install --here")}`);
|
|
3440
|
+
console.log();
|
|
3441
|
+
}
|
|
3442
|
+
});
|
|
3443
|
+
}
|
|
3444
|
+
});
|
|
3445
|
+
|
|
2407
3446
|
// src/commands/setup.ts
|
|
2408
3447
|
var setup_exports = {};
|
|
2409
3448
|
__export(setup_exports, {
|
|
2410
3449
|
default: () => setup_default
|
|
2411
3450
|
});
|
|
2412
|
-
import { defineCommand as
|
|
2413
|
-
import
|
|
3451
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
3452
|
+
import pc9 from "picocolors";
|
|
2414
3453
|
var setup_default;
|
|
2415
3454
|
var init_setup = __esm({
|
|
2416
3455
|
"src/commands/setup.ts"() {
|
|
2417
3456
|
"use strict";
|
|
2418
|
-
setup_default =
|
|
3457
|
+
setup_default = defineCommand4({
|
|
2419
3458
|
meta: {
|
|
2420
3459
|
name: "setup",
|
|
2421
3460
|
description: "Configure FlyDocs settings for this project"
|
|
2422
3461
|
},
|
|
2423
3462
|
run() {
|
|
2424
3463
|
console.log();
|
|
2425
|
-
console.log(` ${
|
|
3464
|
+
console.log(` ${pc9.bold("FlyDocs Setup")}`);
|
|
2426
3465
|
console.log();
|
|
2427
3466
|
console.log(` Setup runs inside your IDE as an interactive AI command.`);
|
|
2428
3467
|
console.log();
|
|
2429
3468
|
console.log(
|
|
2430
|
-
` ${
|
|
3469
|
+
` ${pc9.cyan("Claude Code:")} Type ${pc9.bold("/flydocs-setup")} in chat`
|
|
2431
3470
|
);
|
|
2432
3471
|
console.log(
|
|
2433
|
-
` ${
|
|
3472
|
+
` ${pc9.cyan("Cursor:")} Type ${pc9.bold("/flydocs-setup")} in chat`
|
|
2434
3473
|
);
|
|
2435
3474
|
console.log();
|
|
2436
3475
|
console.log(` This configures your project context, detects your stack,`);
|
|
@@ -2446,14 +3485,14 @@ var skills_exports = {};
|
|
|
2446
3485
|
__export(skills_exports, {
|
|
2447
3486
|
default: () => skills_default
|
|
2448
3487
|
});
|
|
2449
|
-
import { defineCommand as
|
|
2450
|
-
import
|
|
3488
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
3489
|
+
import pc10 from "picocolors";
|
|
2451
3490
|
var list, search, add, remove, skills_default;
|
|
2452
3491
|
var init_skills2 = __esm({
|
|
2453
3492
|
"src/commands/skills.ts"() {
|
|
2454
3493
|
"use strict";
|
|
2455
3494
|
init_skill_manager();
|
|
2456
|
-
list =
|
|
3495
|
+
list = defineCommand5({
|
|
2457
3496
|
meta: {
|
|
2458
3497
|
name: "list",
|
|
2459
3498
|
description: "List installed skills"
|
|
@@ -2469,26 +3508,26 @@ var init_skills2 = __esm({
|
|
|
2469
3508
|
console.log(`${total} skill(s) installed:`);
|
|
2470
3509
|
if (result.platform.length > 0) {
|
|
2471
3510
|
console.log();
|
|
2472
|
-
console.log(
|
|
3511
|
+
console.log(pc10.bold("Platform"));
|
|
2473
3512
|
for (const skill of result.platform) {
|
|
2474
3513
|
console.log(
|
|
2475
|
-
` ${skill.name} ${
|
|
3514
|
+
` ${skill.name} ${pc10.dim(`(${skill.triggers} triggers)`)}`
|
|
2476
3515
|
);
|
|
2477
3516
|
}
|
|
2478
3517
|
}
|
|
2479
3518
|
if (result.community.length > 0) {
|
|
2480
3519
|
console.log();
|
|
2481
|
-
console.log(
|
|
3520
|
+
console.log(pc10.bold("Community"));
|
|
2482
3521
|
for (const skill of result.community) {
|
|
2483
3522
|
console.log(
|
|
2484
|
-
` ${skill.name} ${
|
|
3523
|
+
` ${skill.name} ${pc10.dim(`(${skill.triggers} triggers)`)}`
|
|
2485
3524
|
);
|
|
2486
3525
|
}
|
|
2487
3526
|
}
|
|
2488
3527
|
console.log();
|
|
2489
3528
|
}
|
|
2490
3529
|
});
|
|
2491
|
-
search =
|
|
3530
|
+
search = defineCommand5({
|
|
2492
3531
|
meta: {
|
|
2493
3532
|
name: "search",
|
|
2494
3533
|
description: "Search community skills"
|
|
@@ -2504,24 +3543,24 @@ var init_skills2 = __esm({
|
|
|
2504
3543
|
const results = await searchCatalog(args.keyword);
|
|
2505
3544
|
if (results.length === 0) {
|
|
2506
3545
|
console.log(`No skills found for "${args.keyword}".`);
|
|
2507
|
-
console.log(` Browse the catalog at: ${
|
|
3546
|
+
console.log(` Browse the catalog at: ${pc10.cyan("https://skills.sh/")}`);
|
|
2508
3547
|
return;
|
|
2509
3548
|
}
|
|
2510
3549
|
console.log();
|
|
2511
3550
|
console.log(`${results.length} skill(s) matching "${args.keyword}":`);
|
|
2512
3551
|
console.log();
|
|
2513
3552
|
for (const skill of results) {
|
|
2514
|
-
console.log(` ${
|
|
3553
|
+
console.log(` ${pc10.bold(skill.name)}`);
|
|
2515
3554
|
console.log(` ${skill.description}`);
|
|
2516
|
-
console.log(` ${
|
|
3555
|
+
console.log(` ${pc10.dim(skill.repo)}`);
|
|
2517
3556
|
if (skill.tags.length > 0) {
|
|
2518
|
-
console.log(` ${
|
|
3557
|
+
console.log(` ${pc10.dim(skill.tags.join(", "))}`);
|
|
2519
3558
|
}
|
|
2520
3559
|
console.log();
|
|
2521
3560
|
}
|
|
2522
3561
|
}
|
|
2523
3562
|
});
|
|
2524
|
-
add =
|
|
3563
|
+
add = defineCommand5({
|
|
2525
3564
|
meta: {
|
|
2526
3565
|
name: "add",
|
|
2527
3566
|
description: "Install a community skill"
|
|
@@ -2537,7 +3576,7 @@ var init_skills2 = __esm({
|
|
|
2537
3576
|
await addSkill(process.cwd(), args.source);
|
|
2538
3577
|
}
|
|
2539
3578
|
});
|
|
2540
|
-
remove =
|
|
3579
|
+
remove = defineCommand5({
|
|
2541
3580
|
meta: {
|
|
2542
3581
|
name: "remove",
|
|
2543
3582
|
description: "Remove an installed community skill"
|
|
@@ -2553,7 +3592,7 @@ var init_skills2 = __esm({
|
|
|
2553
3592
|
await removeSkill(process.cwd(), args.name);
|
|
2554
3593
|
}
|
|
2555
3594
|
});
|
|
2556
|
-
skills_default =
|
|
3595
|
+
skills_default = defineCommand5({
|
|
2557
3596
|
meta: {
|
|
2558
3597
|
name: "skills",
|
|
2559
3598
|
description: "Manage FlyDocs skills (list, search, add, remove)"
|
|
@@ -2573,11 +3612,10 @@ var connect_exports = {};
|
|
|
2573
3612
|
__export(connect_exports, {
|
|
2574
3613
|
default: () => connect_default
|
|
2575
3614
|
});
|
|
2576
|
-
import { defineCommand as
|
|
2577
|
-
import { text as
|
|
2578
|
-
import
|
|
2579
|
-
import {
|
|
2580
|
-
import { join as join14 } from "path";
|
|
3615
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
3616
|
+
import { text as text3, confirm as confirm5, isCancel as isCancel6, cancel as cancel5 } from "@clack/prompts";
|
|
3617
|
+
import pc11 from "picocolors";
|
|
3618
|
+
import { join as join18 } from "path";
|
|
2581
3619
|
var connect_default;
|
|
2582
3620
|
var init_connect = __esm({
|
|
2583
3621
|
"src/commands/connect.ts"() {
|
|
@@ -2586,10 +3624,11 @@ var init_connect = __esm({
|
|
|
2586
3624
|
init_fs_ops();
|
|
2587
3625
|
init_template();
|
|
2588
3626
|
init_ui();
|
|
2589
|
-
|
|
3627
|
+
init_api_key();
|
|
3628
|
+
connect_default = defineCommand6({
|
|
2590
3629
|
meta: {
|
|
2591
3630
|
name: "connect",
|
|
2592
|
-
description: "Connect FlyDocs to a cloud provider
|
|
3631
|
+
description: "Connect FlyDocs to a cloud provider"
|
|
2593
3632
|
},
|
|
2594
3633
|
args: {
|
|
2595
3634
|
path: {
|
|
@@ -2606,16 +3645,16 @@ var init_connect = __esm({
|
|
|
2606
3645
|
},
|
|
2607
3646
|
key: {
|
|
2608
3647
|
type: "string",
|
|
2609
|
-
description: "
|
|
3648
|
+
description: "FlyDocs API key (fdk_...)"
|
|
2610
3649
|
}
|
|
2611
3650
|
},
|
|
2612
3651
|
async run({ args }) {
|
|
2613
3652
|
const targetDir = args.path ?? process.cwd();
|
|
2614
|
-
const configPath =
|
|
3653
|
+
const configPath = join18(targetDir, ".flydocs", "config.json");
|
|
2615
3654
|
if (!await pathExists(configPath)) {
|
|
2616
3655
|
printError("Not a FlyDocs project (.flydocs/config.json not found).");
|
|
2617
3656
|
console.log(
|
|
2618
|
-
` Run ${
|
|
3657
|
+
` Run ${pc11.cyan("flydocs")} first to install FlyDocs in this project.`
|
|
2619
3658
|
);
|
|
2620
3659
|
process.exit(1);
|
|
2621
3660
|
}
|
|
@@ -2623,91 +3662,89 @@ var init_connect = __esm({
|
|
|
2623
3662
|
if (config.tier === "cloud") {
|
|
2624
3663
|
printInfo("This project is already connected to the cloud tier.");
|
|
2625
3664
|
console.log();
|
|
2626
|
-
const reconnect = await
|
|
3665
|
+
const reconnect = await confirm5({
|
|
2627
3666
|
message: "Want to update your API key?"
|
|
2628
3667
|
});
|
|
2629
|
-
if (
|
|
3668
|
+
if (isCancel6(reconnect) || !reconnect) {
|
|
2630
3669
|
console.log(` No changes made.`);
|
|
2631
3670
|
return;
|
|
2632
3671
|
}
|
|
2633
3672
|
}
|
|
2634
3673
|
console.log();
|
|
2635
|
-
console.log(` ${
|
|
3674
|
+
console.log(` ${pc11.bold("Connect to FlyDocs Cloud")}`);
|
|
2636
3675
|
console.log();
|
|
2637
3676
|
console.log(
|
|
2638
|
-
` ${
|
|
3677
|
+
` ${pc11.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
|
|
2639
3678
|
);
|
|
2640
3679
|
console.log();
|
|
2641
3680
|
let apiKey = args.key ?? "";
|
|
2642
3681
|
if (!apiKey) {
|
|
2643
|
-
const keyInput = await
|
|
2644
|
-
message: "Enter your
|
|
2645
|
-
placeholder: "
|
|
3682
|
+
const keyInput = await text3({
|
|
3683
|
+
message: "Enter your API key",
|
|
3684
|
+
placeholder: "fdk_...",
|
|
2646
3685
|
validate(value) {
|
|
2647
3686
|
if (!value.trim()) return "API key is required";
|
|
2648
|
-
|
|
2649
|
-
|
|
3687
|
+
const type = detectKeyType(value.trim());
|
|
3688
|
+
if (type === "unknown")
|
|
3689
|
+
return "Key must start with fdk_ (FlyDocs API key)";
|
|
2650
3690
|
return void 0;
|
|
2651
3691
|
}
|
|
2652
3692
|
});
|
|
2653
|
-
if (
|
|
2654
|
-
|
|
3693
|
+
if (isCancel6(keyInput)) {
|
|
3694
|
+
cancel5("Connection cancelled.");
|
|
2655
3695
|
process.exit(0);
|
|
2656
3696
|
}
|
|
2657
|
-
apiKey = keyInput;
|
|
3697
|
+
apiKey = keyInput.trim();
|
|
2658
3698
|
}
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
Authorization: apiKey,
|
|
2665
|
-
"Content-Type": "application/json"
|
|
2666
|
-
},
|
|
2667
|
-
body: JSON.stringify({ query: "{ viewer { id name email } }" }),
|
|
2668
|
-
signal: AbortSignal.timeout(15e3)
|
|
2669
|
-
});
|
|
2670
|
-
if (!response.ok) {
|
|
2671
|
-
throw new Error(`HTTP ${response.status}`);
|
|
2672
|
-
}
|
|
2673
|
-
const data = await response.json();
|
|
2674
|
-
if (!data.data?.viewer) {
|
|
2675
|
-
throw new Error("Invalid response");
|
|
2676
|
-
}
|
|
2677
|
-
const viewer = data.data.viewer;
|
|
2678
|
-
printStatus(`Authenticated as ${pc9.bold(viewer.name)} (${viewer.email})`);
|
|
2679
|
-
} catch {
|
|
2680
|
-
printError("Invalid API key or network error.");
|
|
2681
|
-
console.log(` Check your key and try again.`);
|
|
3699
|
+
const keyType = detectKeyType(apiKey);
|
|
3700
|
+
if (keyType === "unknown") {
|
|
3701
|
+
printError(
|
|
3702
|
+
"Unrecognized key format. Expected fdk_ prefix (FlyDocs API key)."
|
|
3703
|
+
);
|
|
2682
3704
|
process.exit(1);
|
|
2683
3705
|
}
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
3706
|
+
printInfo("Validating API key...");
|
|
3707
|
+
if (keyType === "relay") {
|
|
3708
|
+
try {
|
|
3709
|
+
const result = await validateRelayKey(apiKey);
|
|
3710
|
+
if (!result.valid) {
|
|
3711
|
+
printError("Invalid API key or relay API unreachable.");
|
|
3712
|
+
console.log(` Check your key and try again.`);
|
|
3713
|
+
process.exit(1);
|
|
3714
|
+
}
|
|
3715
|
+
printStatus(`Connected to ${pc11.bold(result.org)}`);
|
|
3716
|
+
} catch {
|
|
3717
|
+
printError(
|
|
3718
|
+
"Could not reach relay API. Check your network and try again."
|
|
2693
3719
|
);
|
|
2694
|
-
|
|
2695
|
-
} else {
|
|
2696
|
-
await appendFile2(targetEnvPath, `
|
|
2697
|
-
LINEAR_API_KEY=${apiKey}
|
|
2698
|
-
`);
|
|
3720
|
+
process.exit(1);
|
|
2699
3721
|
}
|
|
3722
|
+
const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
|
|
3723
|
+
printStatus(`API key stored in ${pc11.dim(envFile)}`);
|
|
2700
3724
|
} else {
|
|
2701
|
-
|
|
2702
|
-
|
|
3725
|
+
try {
|
|
3726
|
+
const result = await validateLinearKey(apiKey);
|
|
3727
|
+
if (!result.valid) {
|
|
3728
|
+
printError("Invalid API key or network error.");
|
|
3729
|
+
console.log(` Check your key and try again.`);
|
|
3730
|
+
process.exit(1);
|
|
3731
|
+
}
|
|
3732
|
+
printStatus(
|
|
3733
|
+
`Authenticated as ${pc11.bold(result.name)} (${result.email})`
|
|
3734
|
+
);
|
|
3735
|
+
} catch {
|
|
3736
|
+
printError("Invalid API key or network error.");
|
|
3737
|
+
console.log(` Check your key and try again.`);
|
|
3738
|
+
process.exit(1);
|
|
3739
|
+
}
|
|
3740
|
+
const envFile = await storeEnvKey(targetDir, "LINEAR_API_KEY", apiKey);
|
|
3741
|
+
printStatus(`API key stored in ${pc11.dim(envFile)}`);
|
|
2703
3742
|
}
|
|
2704
|
-
printStatus(
|
|
2705
|
-
`API key stored in ${pc9.dim(targetEnvPath === envLocalPath ? ".env.local" : ".env")}`
|
|
2706
|
-
);
|
|
2707
3743
|
const wasLocal = config.tier === "local";
|
|
2708
3744
|
config.tier = "cloud";
|
|
2709
|
-
|
|
2710
|
-
|
|
3745
|
+
const configRecord = config;
|
|
3746
|
+
delete configRecord.statusMapping;
|
|
3747
|
+
delete configRecord.provider;
|
|
2711
3748
|
await writeConfig(targetDir, config);
|
|
2712
3749
|
printStatus("Config updated to cloud tier");
|
|
2713
3750
|
if (wasLocal) {
|
|
@@ -2715,16 +3752,16 @@ LINEAR_API_KEY=${apiKey}
|
|
|
2715
3752
|
const templateDir = await resolveTemplatePath(
|
|
2716
3753
|
args["local-source"] || void 0
|
|
2717
3754
|
);
|
|
2718
|
-
const templateSkillsDir =
|
|
2719
|
-
const skillsDir =
|
|
3755
|
+
const templateSkillsDir = join18(templateDir, ".claude", "skills");
|
|
3756
|
+
const skillsDir = join18(targetDir, ".claude", "skills");
|
|
2720
3757
|
await replaceDirectory(
|
|
2721
|
-
|
|
2722
|
-
|
|
3758
|
+
join18(templateSkillsDir, "flydocs-cloud"),
|
|
3759
|
+
join18(skillsDir, "flydocs-cloud")
|
|
2723
3760
|
);
|
|
2724
|
-
const { rm:
|
|
2725
|
-
const localSkillDir =
|
|
3761
|
+
const { rm: rm6 } = await import("fs/promises");
|
|
3762
|
+
const localSkillDir = join18(skillsDir, "flydocs-local");
|
|
2726
3763
|
if (await pathExists(localSkillDir)) {
|
|
2727
|
-
await
|
|
3764
|
+
await rm6(localSkillDir, { recursive: true, force: true });
|
|
2728
3765
|
}
|
|
2729
3766
|
printStatus("Cloud mechanism skill installed");
|
|
2730
3767
|
} catch {
|
|
@@ -2735,14 +3772,14 @@ LINEAR_API_KEY=${apiKey}
|
|
|
2735
3772
|
}
|
|
2736
3773
|
console.log();
|
|
2737
3774
|
console.log(
|
|
2738
|
-
` ${
|
|
3775
|
+
` ${pc11.bold("Connected!")} Your project is now on the cloud tier.`
|
|
2739
3776
|
);
|
|
2740
3777
|
console.log();
|
|
2741
3778
|
console.log(` Next steps:`);
|
|
2742
3779
|
console.log(
|
|
2743
|
-
` 1. Run ${
|
|
3780
|
+
` 1. Run ${pc11.cyan("/flydocs-setup")} in your IDE to configure your project`
|
|
2744
3781
|
);
|
|
2745
|
-
console.log(` 2. Run ${
|
|
3782
|
+
console.log(` 2. Run ${pc11.cyan("/start-session")} to begin working`);
|
|
2746
3783
|
console.log();
|
|
2747
3784
|
}
|
|
2748
3785
|
});
|
|
@@ -2754,15 +3791,15 @@ var upgrade_exports = {};
|
|
|
2754
3791
|
__export(upgrade_exports, {
|
|
2755
3792
|
default: () => upgrade_default
|
|
2756
3793
|
});
|
|
2757
|
-
import { defineCommand as
|
|
2758
|
-
import
|
|
3794
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
3795
|
+
import pc12 from "picocolors";
|
|
2759
3796
|
var upgrade_default;
|
|
2760
3797
|
var init_upgrade = __esm({
|
|
2761
3798
|
"src/commands/upgrade.ts"() {
|
|
2762
3799
|
"use strict";
|
|
2763
3800
|
init_config();
|
|
2764
3801
|
init_fs_ops();
|
|
2765
|
-
upgrade_default =
|
|
3802
|
+
upgrade_default = defineCommand7({
|
|
2766
3803
|
meta: {
|
|
2767
3804
|
name: "upgrade",
|
|
2768
3805
|
description: "Learn about FlyDocs Cloud tier and upgrade from local"
|
|
@@ -2791,38 +3828,38 @@ var init_upgrade = __esm({
|
|
|
2791
3828
|
console.log();
|
|
2792
3829
|
if (currentTier === "cloud") {
|
|
2793
3830
|
console.log(
|
|
2794
|
-
` ${
|
|
3831
|
+
` ${pc12.green("\u2713")} You're already on the ${pc12.bold("cloud")} tier.`
|
|
2795
3832
|
);
|
|
2796
3833
|
console.log();
|
|
2797
3834
|
console.log(
|
|
2798
|
-
` Your issues sync with
|
|
3835
|
+
` Your issues sync with your provider via the cloud mechanism skill.`
|
|
2799
3836
|
);
|
|
2800
3837
|
console.log(
|
|
2801
|
-
` Run ${
|
|
3838
|
+
` Run ${pc12.cyan("flydocs connect")} to update your connection settings.`
|
|
2802
3839
|
);
|
|
2803
3840
|
console.log();
|
|
2804
3841
|
return;
|
|
2805
3842
|
}
|
|
2806
|
-
console.log(` ${
|
|
3843
|
+
console.log(` ${pc12.bold("FlyDocs Cloud Tier")}`);
|
|
2807
3844
|
console.log();
|
|
2808
|
-
console.log(` You're currently on the ${
|
|
3845
|
+
console.log(` You're currently on the ${pc12.yellow("local")} tier.`);
|
|
2809
3846
|
console.log(` Upgrade to cloud for:`);
|
|
2810
3847
|
console.log();
|
|
2811
|
-
console.log(
|
|
2812
|
-
|
|
2813
|
-
);
|
|
2814
|
-
console.log(` ${
|
|
2815
|
-
console.log(` ${
|
|
2816
|
-
console.log(` ${pc10.cyan("\u2192")} Project health updates and dashboards`);
|
|
2817
|
-
console.log(` ${pc10.cyan("\u2192")} Cross-project issue linking`);
|
|
3848
|
+
console.log(` ${pc12.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
|
|
3849
|
+
console.log(` ${pc12.cyan("\u2192")} Project milestones and cycle management`);
|
|
3850
|
+
console.log(` ${pc12.cyan("\u2192")} Team assignment and priority tracking`);
|
|
3851
|
+
console.log(` ${pc12.cyan("\u2192")} Project health updates and dashboards`);
|
|
3852
|
+
console.log(` ${pc12.cyan("\u2192")} Cross-project issue linking`);
|
|
2818
3853
|
console.log();
|
|
2819
|
-
console.log(` ${
|
|
3854
|
+
console.log(` ${pc12.bold("How to upgrade:")}`);
|
|
3855
|
+
console.log();
|
|
3856
|
+
console.log(` Option 1: Run ${pc12.cyan("/flydocs-upgrade")} in your IDE`);
|
|
3857
|
+
console.log(` Guided migration with issue transfer`);
|
|
2820
3858
|
console.log();
|
|
2821
|
-
console.log(` 1. Sign up at ${pc10.cyan("https://www.flydocs.ai")}`);
|
|
2822
|
-
console.log(` 2. Get your Linear API key from Linear \u2192 Settings \u2192 API`);
|
|
2823
3859
|
console.log(
|
|
2824
|
-
`
|
|
3860
|
+
` Option 2: Run ${pc12.cyan("flydocs connect")} from terminal`
|
|
2825
3861
|
);
|
|
3862
|
+
console.log(` Quick tier swap (no issue migration)`);
|
|
2826
3863
|
console.log();
|
|
2827
3864
|
}
|
|
2828
3865
|
});
|
|
@@ -2834,23 +3871,23 @@ var self_update_exports = {};
|
|
|
2834
3871
|
__export(self_update_exports, {
|
|
2835
3872
|
default: () => self_update_default
|
|
2836
3873
|
});
|
|
2837
|
-
import { defineCommand as
|
|
3874
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
2838
3875
|
import { execSync } from "child_process";
|
|
2839
|
-
import
|
|
3876
|
+
import pc13 from "picocolors";
|
|
2840
3877
|
var self_update_default;
|
|
2841
3878
|
var init_self_update = __esm({
|
|
2842
3879
|
"src/commands/self-update.ts"() {
|
|
2843
3880
|
"use strict";
|
|
2844
3881
|
init_constants();
|
|
2845
3882
|
init_ui();
|
|
2846
|
-
self_update_default =
|
|
3883
|
+
self_update_default = defineCommand8({
|
|
2847
3884
|
meta: {
|
|
2848
3885
|
name: "self-update",
|
|
2849
3886
|
description: "Update FlyDocs CLI to the latest version"
|
|
2850
3887
|
},
|
|
2851
3888
|
async run() {
|
|
2852
3889
|
console.log();
|
|
2853
|
-
console.log(` Updating ${
|
|
3890
|
+
console.log(` Updating ${pc13.cyan(PACKAGE_NAME)}...`);
|
|
2854
3891
|
console.log();
|
|
2855
3892
|
try {
|
|
2856
3893
|
execSync(`npm install -g ${PACKAGE_NAME}@beta`, {
|
|
@@ -2870,17 +3907,117 @@ var init_self_update = __esm({
|
|
|
2870
3907
|
}
|
|
2871
3908
|
});
|
|
2872
3909
|
|
|
3910
|
+
// src/commands/telemetry.ts
|
|
3911
|
+
var telemetry_exports = {};
|
|
3912
|
+
__export(telemetry_exports, {
|
|
3913
|
+
default: () => telemetry_default
|
|
3914
|
+
});
|
|
3915
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
3916
|
+
import pc14 from "picocolors";
|
|
3917
|
+
var enable, disable, status, telemetry_default;
|
|
3918
|
+
var init_telemetry2 = __esm({
|
|
3919
|
+
"src/commands/telemetry.ts"() {
|
|
3920
|
+
"use strict";
|
|
3921
|
+
init_telemetry();
|
|
3922
|
+
init_ui();
|
|
3923
|
+
enable = defineCommand9({
|
|
3924
|
+
meta: {
|
|
3925
|
+
name: "enable",
|
|
3926
|
+
description: "Enable anonymous usage analytics"
|
|
3927
|
+
},
|
|
3928
|
+
async run() {
|
|
3929
|
+
try {
|
|
3930
|
+
await setEnabled(true);
|
|
3931
|
+
printStatus("Telemetry enabled");
|
|
3932
|
+
} catch {
|
|
3933
|
+
printError("Failed to update telemetry settings");
|
|
3934
|
+
process.exit(1);
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
});
|
|
3938
|
+
disable = defineCommand9({
|
|
3939
|
+
meta: {
|
|
3940
|
+
name: "disable",
|
|
3941
|
+
description: "Disable anonymous usage analytics"
|
|
3942
|
+
},
|
|
3943
|
+
async run() {
|
|
3944
|
+
try {
|
|
3945
|
+
await setEnabled(false);
|
|
3946
|
+
printStatus("Telemetry disabled");
|
|
3947
|
+
printInfo(
|
|
3948
|
+
"You can also set FLYDOCS_TELEMETRY=0 in your shell environment."
|
|
3949
|
+
);
|
|
3950
|
+
} catch {
|
|
3951
|
+
printError("Failed to update telemetry settings");
|
|
3952
|
+
process.exit(1);
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
});
|
|
3956
|
+
status = defineCommand9({
|
|
3957
|
+
meta: {
|
|
3958
|
+
name: "status",
|
|
3959
|
+
description: "Show current telemetry status"
|
|
3960
|
+
},
|
|
3961
|
+
async run() {
|
|
3962
|
+
const info = await getStatus();
|
|
3963
|
+
console.log();
|
|
3964
|
+
console.log(pc14.bold("Telemetry Status"));
|
|
3965
|
+
console.log();
|
|
3966
|
+
const effectivelyEnabled = info.enabled && !info.envOverride;
|
|
3967
|
+
console.log(
|
|
3968
|
+
` Enabled: ${effectivelyEnabled ? pc14.green("yes") : pc14.yellow("no")}`
|
|
3969
|
+
);
|
|
3970
|
+
if (info.envOverride) {
|
|
3971
|
+
console.log(
|
|
3972
|
+
` Env override: ${pc14.yellow("FLYDOCS_TELEMETRY=0 (disabled via env)")}`
|
|
3973
|
+
);
|
|
3974
|
+
}
|
|
3975
|
+
if (info.anonymousId) {
|
|
3976
|
+
console.log(` Anonymous ID: ${pc14.dim(info.anonymousId)}`);
|
|
3977
|
+
} else {
|
|
3978
|
+
console.log(
|
|
3979
|
+
` Anonymous ID: ${pc14.dim("(not yet created \u2014 generated on first run)")}`
|
|
3980
|
+
);
|
|
3981
|
+
}
|
|
3982
|
+
console.log();
|
|
3983
|
+
console.log(
|
|
3984
|
+
pc14.dim(
|
|
3985
|
+
" FlyDocs collects anonymous usage analytics to improve the CLI."
|
|
3986
|
+
)
|
|
3987
|
+
);
|
|
3988
|
+
console.log(
|
|
3989
|
+
pc14.dim(" No personal data, file contents, or code is ever collected.")
|
|
3990
|
+
);
|
|
3991
|
+
console.log();
|
|
3992
|
+
}
|
|
3993
|
+
});
|
|
3994
|
+
telemetry_default = defineCommand9({
|
|
3995
|
+
meta: {
|
|
3996
|
+
name: "telemetry",
|
|
3997
|
+
description: "Manage anonymous usage analytics (enable, disable, status)"
|
|
3998
|
+
},
|
|
3999
|
+
subCommands: {
|
|
4000
|
+
enable,
|
|
4001
|
+
disable,
|
|
4002
|
+
status
|
|
4003
|
+
}
|
|
4004
|
+
});
|
|
4005
|
+
}
|
|
4006
|
+
});
|
|
4007
|
+
|
|
2873
4008
|
// src/cli.ts
|
|
2874
4009
|
init_constants();
|
|
2875
|
-
import { defineCommand as
|
|
4010
|
+
import { defineCommand as defineCommand10, runMain } from "citty";
|
|
2876
4011
|
var SUB_COMMANDS = /* @__PURE__ */ new Set([
|
|
2877
4012
|
"install",
|
|
2878
4013
|
"update",
|
|
4014
|
+
"uninstall",
|
|
2879
4015
|
"setup",
|
|
2880
4016
|
"skills",
|
|
2881
4017
|
"connect",
|
|
2882
4018
|
"upgrade",
|
|
2883
|
-
"self-update"
|
|
4019
|
+
"self-update",
|
|
4020
|
+
"telemetry"
|
|
2884
4021
|
]);
|
|
2885
4022
|
var userArgs = process.argv.slice(2);
|
|
2886
4023
|
var hasMetaFlag = userArgs.some(
|
|
@@ -2890,7 +4027,7 @@ var firstPositional = userArgs.find((a) => !a.startsWith("-"));
|
|
|
2890
4027
|
if (!hasMetaFlag && (!firstPositional || !SUB_COMMANDS.has(firstPositional))) {
|
|
2891
4028
|
process.argv.splice(2, 0, "install");
|
|
2892
4029
|
}
|
|
2893
|
-
var main =
|
|
4030
|
+
var main = defineCommand10({
|
|
2894
4031
|
meta: {
|
|
2895
4032
|
name: CLI_NAME,
|
|
2896
4033
|
version: CLI_VERSION,
|
|
@@ -2899,11 +4036,13 @@ var main = defineCommand8({
|
|
|
2899
4036
|
subCommands: {
|
|
2900
4037
|
install: () => Promise.resolve().then(() => (init_install(), install_exports)).then((m) => m.default),
|
|
2901
4038
|
update: () => Promise.resolve().then(() => (init_update(), update_exports)).then((m) => m.default),
|
|
4039
|
+
uninstall: () => Promise.resolve().then(() => (init_uninstall(), uninstall_exports)).then((m) => m.default),
|
|
2902
4040
|
setup: () => Promise.resolve().then(() => (init_setup(), setup_exports)).then((m) => m.default),
|
|
2903
4041
|
skills: () => Promise.resolve().then(() => (init_skills2(), skills_exports)).then((m) => m.default),
|
|
2904
4042
|
connect: () => Promise.resolve().then(() => (init_connect(), connect_exports)).then((m) => m.default),
|
|
2905
4043
|
upgrade: () => Promise.resolve().then(() => (init_upgrade(), upgrade_exports)).then((m) => m.default),
|
|
2906
|
-
"self-update": () => Promise.resolve().then(() => (init_self_update(), self_update_exports)).then((m) => m.default)
|
|
4044
|
+
"self-update": () => Promise.resolve().then(() => (init_self_update(), self_update_exports)).then((m) => m.default),
|
|
4045
|
+
telemetry: () => Promise.resolve().then(() => (init_telemetry2(), telemetry_exports)).then((m) => m.default)
|
|
2907
4046
|
}
|
|
2908
4047
|
});
|
|
2909
4048
|
runMain(main);
|