@flydocs/cli 0.6.0-alpha.6 → 0.6.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +144 -16
- package/package.json +1 -1
- package/template/.claude/skills/flydocs-cloud/SKILL.md +1 -0
- package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +7 -0
- package/template/.claude/skills/flydocs-cloud/scripts/validate_setup.py +134 -0
- package/template/.flydocs/config.json +4 -18
- package/template/.flydocs/hooks/prompt-submit.py +21 -4
- package/template/.flydocs/version +1 -1
- package/template/manifest.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ var CLI_VERSION, CLI_NAME, PACKAGE_NAME, POSTHOG_API_KEY;
|
|
|
15
15
|
var init_constants = __esm({
|
|
16
16
|
"src/lib/constants.ts"() {
|
|
17
17
|
"use strict";
|
|
18
|
-
CLI_VERSION = "0.6.0-alpha.
|
|
18
|
+
CLI_VERSION = "0.6.0-alpha.8";
|
|
19
19
|
CLI_NAME = "flydocs";
|
|
20
20
|
PACKAGE_NAME = "@flydocs/cli";
|
|
21
21
|
POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
|
|
@@ -50,6 +50,7 @@ async function ensureDirectories(targetDir, tier) {
|
|
|
50
50
|
"flydocs/knowledge/decisions",
|
|
51
51
|
"flydocs/knowledge/notes",
|
|
52
52
|
"flydocs/knowledge/product",
|
|
53
|
+
"flydocs/knowledge/templates",
|
|
53
54
|
"flydocs/design-system"
|
|
54
55
|
];
|
|
55
56
|
if (tier === "local") {
|
|
@@ -292,10 +293,9 @@ function extractPreservedValues(config) {
|
|
|
292
293
|
return {
|
|
293
294
|
tier: config.tier,
|
|
294
295
|
setupComplete: config.setupComplete ?? false,
|
|
295
|
-
|
|
296
|
+
workspaceId: config.workspaceId ?? null,
|
|
296
297
|
workspace: config.workspace ?? {},
|
|
297
298
|
issueLabels: config.issueLabels ?? {},
|
|
298
|
-
statusMapping: config.statusMapping ?? {},
|
|
299
299
|
detectedStack: config.detectedStack ?? {},
|
|
300
300
|
skills: config.skills ?? {},
|
|
301
301
|
designSystem: config.designSystem ?? null,
|
|
@@ -311,21 +311,13 @@ async function mergeConfig(templateDir, version, tierFlag, preserved) {
|
|
|
311
311
|
config.version = version;
|
|
312
312
|
config.tier = tierFlag ?? preserved.tier;
|
|
313
313
|
config.setupComplete = preserved.setupComplete;
|
|
314
|
-
|
|
315
|
-
if (!config.provider) {
|
|
316
|
-
config.provider = { type: "linear", teamId: null };
|
|
317
|
-
}
|
|
318
|
-
config.provider.teamId = preserved.providerTeamId;
|
|
319
|
-
}
|
|
314
|
+
config.workspaceId = preserved.workspaceId;
|
|
320
315
|
if (Object.keys(preserved.workspace).length > 0) {
|
|
321
316
|
config.workspace = preserved.workspace;
|
|
322
317
|
}
|
|
323
318
|
if (Object.keys(preserved.issueLabels).length > 0) {
|
|
324
319
|
config.issueLabels = preserved.issueLabels;
|
|
325
320
|
}
|
|
326
|
-
if (Object.keys(preserved.statusMapping).length > 0) {
|
|
327
|
-
config.statusMapping = preserved.statusMapping;
|
|
328
|
-
}
|
|
329
321
|
if (Object.keys(preserved.detectedStack).length > 0) {
|
|
330
322
|
config.detectedStack = preserved.detectedStack;
|
|
331
323
|
}
|
|
@@ -1887,6 +1879,21 @@ async function validateRelayKey(apiKey) {
|
|
|
1887
1879
|
if (!data.valid) return { valid: false };
|
|
1888
1880
|
return { valid: true, org: data.org ?? "your organization" };
|
|
1889
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
|
+
}
|
|
1890
1897
|
async function validateLinearKey(apiKey) {
|
|
1891
1898
|
const response = await fetch("https://api.linear.app/graphql", {
|
|
1892
1899
|
method: "POST",
|
|
@@ -2073,6 +2080,8 @@ var init_install = __esm({
|
|
|
2073
2080
|
}
|
|
2074
2081
|
printInfo(`Tier: ${tier}`);
|
|
2075
2082
|
await capture("install_tier_selected", { tier });
|
|
2083
|
+
let selectedWorkspaceId = null;
|
|
2084
|
+
let selectedWorkspaceName = null;
|
|
2076
2085
|
if (tier === "cloud") {
|
|
2077
2086
|
console.log();
|
|
2078
2087
|
console.log(` ${pc6.bold("Connect to FlyDocs Cloud")}`);
|
|
@@ -2114,6 +2123,46 @@ var init_install = __esm({
|
|
|
2114
2123
|
}
|
|
2115
2124
|
const envFile = await storeEnvKey(targetDir, "FLYDOCS_API_KEY", apiKey);
|
|
2116
2125
|
printStatus(`API key stored in ${pc6.dim(envFile)}`);
|
|
2126
|
+
console.log();
|
|
2127
|
+
printInfo("Fetching workspaces...");
|
|
2128
|
+
try {
|
|
2129
|
+
const workspaces = await fetchWorkspaces(apiKey);
|
|
2130
|
+
if (workspaces.length === 0) {
|
|
2131
|
+
printError(
|
|
2132
|
+
"No workspaces found. Create a workspace in the FlyDocs dashboard first, then re-run install."
|
|
2133
|
+
);
|
|
2134
|
+
process.exit(1);
|
|
2135
|
+
}
|
|
2136
|
+
if (workspaces.length === 1) {
|
|
2137
|
+
selectedWorkspaceId = workspaces[0].id;
|
|
2138
|
+
selectedWorkspaceName = workspaces[0].name;
|
|
2139
|
+
printStatus(
|
|
2140
|
+
`Workspace: ${pc6.bold(selectedWorkspaceName)} (auto-selected)`
|
|
2141
|
+
);
|
|
2142
|
+
} else {
|
|
2143
|
+
const workspaceChoice = await select({
|
|
2144
|
+
message: "Select a workspace",
|
|
2145
|
+
options: workspaces.map((ws) => ({
|
|
2146
|
+
value: ws.id,
|
|
2147
|
+
label: `${ws.name} (${ws.provider.type})`,
|
|
2148
|
+
hint: ws.team.name
|
|
2149
|
+
}))
|
|
2150
|
+
});
|
|
2151
|
+
if (isCancel3(workspaceChoice)) {
|
|
2152
|
+
cancel2("Installation cancelled.");
|
|
2153
|
+
process.exit(0);
|
|
2154
|
+
}
|
|
2155
|
+
selectedWorkspaceId = workspaceChoice;
|
|
2156
|
+
const chosen = workspaces.find((ws) => ws.id === selectedWorkspaceId);
|
|
2157
|
+
selectedWorkspaceName = chosen?.name ?? "Unknown";
|
|
2158
|
+
printStatus(`Workspace: ${pc6.bold(selectedWorkspaceName)}`);
|
|
2159
|
+
}
|
|
2160
|
+
} catch {
|
|
2161
|
+
printError(
|
|
2162
|
+
"Could not fetch workspaces. Check your network and try again."
|
|
2163
|
+
);
|
|
2164
|
+
process.exit(1);
|
|
2165
|
+
}
|
|
2117
2166
|
}
|
|
2118
2167
|
let skipConfigOverwrite = false;
|
|
2119
2168
|
if (!await pathExists(join15(targetDir, ".git"))) {
|
|
@@ -2195,6 +2244,9 @@ var init_install = __esm({
|
|
|
2195
2244
|
const configPath = join15(targetDir, ".flydocs", "config.json");
|
|
2196
2245
|
if (!await pathExists(configPath)) {
|
|
2197
2246
|
const config = await createFreshConfig(templateDir, version, tier);
|
|
2247
|
+
if (selectedWorkspaceId) {
|
|
2248
|
+
config.workspaceId = selectedWorkspaceId;
|
|
2249
|
+
}
|
|
2198
2250
|
await writeConfig(targetDir, config);
|
|
2199
2251
|
printStatus(`.flydocs/config.json (new, tier: ${tier})`);
|
|
2200
2252
|
} else if (args.tier) {
|
|
@@ -2202,15 +2254,34 @@ var init_install = __esm({
|
|
|
2202
2254
|
const existing = await readConfig(targetDir);
|
|
2203
2255
|
existing.version = version;
|
|
2204
2256
|
existing.tier = tier;
|
|
2257
|
+
if (selectedWorkspaceId) {
|
|
2258
|
+
existing.workspaceId = selectedWorkspaceId;
|
|
2259
|
+
}
|
|
2205
2260
|
await writeConfig(targetDir, existing);
|
|
2206
2261
|
printStatus(`.flydocs/config.json (tier updated: ${tier})`);
|
|
2207
2262
|
} catch {
|
|
2208
2263
|
const config = await createFreshConfig(templateDir, version, tier);
|
|
2264
|
+
if (selectedWorkspaceId) {
|
|
2265
|
+
config.workspaceId = selectedWorkspaceId;
|
|
2266
|
+
}
|
|
2209
2267
|
await writeConfig(targetDir, config);
|
|
2210
2268
|
printStatus(`.flydocs/config.json (recreated, tier: ${tier})`);
|
|
2211
2269
|
}
|
|
2212
2270
|
} else {
|
|
2213
|
-
|
|
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
|
+
}
|
|
2214
2285
|
}
|
|
2215
2286
|
console.log();
|
|
2216
2287
|
console.log(`Installing skills (tier: ${tier})...`);
|
|
@@ -2357,6 +2428,45 @@ var init_install = __esm({
|
|
|
2357
2428
|
),
|
|
2358
2429
|
label: "flydocs/knowledge/product/user-flows.md"
|
|
2359
2430
|
},
|
|
2431
|
+
{
|
|
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
|
+
},
|
|
2360
2470
|
{
|
|
2361
2471
|
src: join15(templateDir, "flydocs", "design-system", "README.md"),
|
|
2362
2472
|
dest: join15(targetDir, "flydocs", "design-system", "README.md"),
|
|
@@ -2770,10 +2880,9 @@ var init_update = __esm({
|
|
|
2770
2880
|
let preserved = {
|
|
2771
2881
|
tier: "cloud",
|
|
2772
2882
|
setupComplete: false,
|
|
2773
|
-
|
|
2883
|
+
workspaceId: null,
|
|
2774
2884
|
workspace: {},
|
|
2775
2885
|
issueLabels: {},
|
|
2776
|
-
statusMapping: {},
|
|
2777
2886
|
detectedStack: {},
|
|
2778
2887
|
skills: {},
|
|
2779
2888
|
designSystem: null,
|
|
@@ -2797,6 +2906,7 @@ var init_update = __esm({
|
|
|
2797
2906
|
} else {
|
|
2798
2907
|
effectiveTier = preserved.tier;
|
|
2799
2908
|
}
|
|
2909
|
+
await ensureDirectories(targetDir, effectiveTier);
|
|
2800
2910
|
console.log("Replacing framework directories...");
|
|
2801
2911
|
await replaceDirectory(
|
|
2802
2912
|
join16(templateDir, ".flydocs", "templates"),
|
|
@@ -2911,6 +3021,22 @@ var init_update = __esm({
|
|
|
2911
3021
|
await copyFile(envExampleSrc, join16(targetDir, ".env.example"));
|
|
2912
3022
|
printStatus(".env.example");
|
|
2913
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
|
+
}
|
|
2914
3040
|
await runManifestGeneration(targetDir);
|
|
2915
3041
|
await runContextGraphBuild(targetDir);
|
|
2916
3042
|
console.log();
|
|
@@ -3616,7 +3742,9 @@ var init_connect = __esm({
|
|
|
3616
3742
|
}
|
|
3617
3743
|
const wasLocal = config.tier === "local";
|
|
3618
3744
|
config.tier = "cloud";
|
|
3619
|
-
|
|
3745
|
+
const configRecord = config;
|
|
3746
|
+
delete configRecord.statusMapping;
|
|
3747
|
+
delete configRecord.provider;
|
|
3620
3748
|
await writeConfig(targetDir, config);
|
|
3621
3749
|
printStatus("Config updated to cloud tier");
|
|
3622
3750
|
if (wasLocal) {
|
package/package.json
CHANGED
|
@@ -74,6 +74,7 @@ All scripts: `python3 .claude/skills/flydocs-cloud/scripts/<script>`
|
|
|
74
74
|
| `set_preferences.py` | `[--workspace ID] [--assignee self\|ID] [--display JSON]` | `{success, preferences}` — no flags = GET current; with flags = POST update |
|
|
75
75
|
| `get_estimate_scale.py` | (no args) | `{scale, type}` — provider's valid estimate values (fixed or freeform) |
|
|
76
76
|
| `refresh_labels.py` | `[--fix]` | `{valid, stale, details}` — validates config label IDs against relay; `--fix` updates stale IDs |
|
|
77
|
+
| `validate_setup.py` | (no args) | `{valid, checks, missing[]}` — validates workspace config, caches result, sets setupComplete |
|
|
77
78
|
|
|
78
79
|
### Script Notes
|
|
79
80
|
|
|
@@ -37,6 +37,12 @@ class FlyDocsClient:
|
|
|
37
37
|
print("Set in environment or .env/.env.local file", file=sys.stderr)
|
|
38
38
|
sys.exit(1)
|
|
39
39
|
|
|
40
|
+
self.workspace_id = self.config.get("workspaceId")
|
|
41
|
+
if not self.workspace_id:
|
|
42
|
+
print("ERROR: workspaceId not found in .flydocs/config.json", file=sys.stderr)
|
|
43
|
+
print("Run 'flydocs setup' to configure your workspace", file=sys.stderr)
|
|
44
|
+
sys.exit(1)
|
|
45
|
+
|
|
40
46
|
self.base_url = self._resolve_base_url()
|
|
41
47
|
|
|
42
48
|
def _load_config(self) -> dict:
|
|
@@ -93,6 +99,7 @@ class FlyDocsClient:
|
|
|
93
99
|
|
|
94
100
|
headers = {
|
|
95
101
|
"Authorization": f"Bearer {self.api_key}",
|
|
102
|
+
"X-Workspace": self.workspace_id,
|
|
96
103
|
"Content-Type": "application/json",
|
|
97
104
|
"Accept": "application/json",
|
|
98
105
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate workspace setup via the FlyDocs Relay API.
|
|
3
|
+
|
|
4
|
+
Read-only validation that checks provider, team, status mapping, label config,
|
|
5
|
+
and user identity. Writes result to .flydocs/validation-cache.json and sets
|
|
6
|
+
setupComplete: true in config when all required checks pass.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
15
|
+
from flydocs_api import get_client, output_json, fail
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Required checks — failure blocks setup completion
|
|
19
|
+
REQUIRED_CHECKS = {
|
|
20
|
+
"provider": {
|
|
21
|
+
"test": lambda cfg: cfg.get("provider", {}).get("connected") is True,
|
|
22
|
+
"message": "No provider connected — configure in FlyDocs dashboard",
|
|
23
|
+
},
|
|
24
|
+
"team": {
|
|
25
|
+
"test": lambda cfg: bool(cfg.get("team", {}).get("id")),
|
|
26
|
+
"message": "No team selected — configure in FlyDocs dashboard",
|
|
27
|
+
},
|
|
28
|
+
"statusMapping": {
|
|
29
|
+
"test": lambda cfg: cfg.get("statusMapping", {}).get("configured") is True,
|
|
30
|
+
"message": "Status mapping not configured — configure in FlyDocs dashboard",
|
|
31
|
+
},
|
|
32
|
+
"labelConfig": {
|
|
33
|
+
"test": lambda cfg: cfg.get("labelConfig", {}).get("configured") is True,
|
|
34
|
+
"message": "Label config not configured — configure in FlyDocs dashboard",
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Optional checks — warn but don't block
|
|
39
|
+
OPTIONAL_CHECKS = {
|
|
40
|
+
"userIdentity": {
|
|
41
|
+
"test": lambda cfg: cfg.get("userIdentity", {}).get("linked") is True,
|
|
42
|
+
"message": (
|
|
43
|
+
"Provider identity not linked — run: "
|
|
44
|
+
"python3 .claude/skills/flydocs-cloud/scripts/set_identity.py <provider> <id>"
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def main() -> None:
|
|
51
|
+
client = get_client()
|
|
52
|
+
|
|
53
|
+
# Fetch workspace config from relay
|
|
54
|
+
config_response = client.get("/auth/config")
|
|
55
|
+
|
|
56
|
+
# Run checks
|
|
57
|
+
checks: dict[str, bool] = {}
|
|
58
|
+
valid: list[str] = []
|
|
59
|
+
missing: list[dict[str, str]] = []
|
|
60
|
+
warnings: list[dict[str, str]] = []
|
|
61
|
+
|
|
62
|
+
for name, check in REQUIRED_CHECKS.items():
|
|
63
|
+
passed = check["test"](config_response)
|
|
64
|
+
checks[name] = passed
|
|
65
|
+
if passed:
|
|
66
|
+
valid.append(name)
|
|
67
|
+
else:
|
|
68
|
+
missing.append({"check": name, "action": check["message"]})
|
|
69
|
+
|
|
70
|
+
for name, check in OPTIONAL_CHECKS.items():
|
|
71
|
+
passed = check["test"](config_response)
|
|
72
|
+
checks[name] = passed
|
|
73
|
+
if passed:
|
|
74
|
+
valid.append(name)
|
|
75
|
+
else:
|
|
76
|
+
warnings.append({"check": name, "action": check["message"]})
|
|
77
|
+
|
|
78
|
+
all_required_pass = len(missing) == 0
|
|
79
|
+
|
|
80
|
+
# Build workspace info from response
|
|
81
|
+
workspace = config_response.get("workspace", {})
|
|
82
|
+
provider_type = config_response.get("provider", {}).get("type", "unknown")
|
|
83
|
+
|
|
84
|
+
# Write validation cache
|
|
85
|
+
cache = {
|
|
86
|
+
"timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
87
|
+
"valid": all_required_pass,
|
|
88
|
+
"workspace": {
|
|
89
|
+
"id": workspace.get("id", ""),
|
|
90
|
+
"name": workspace.get("name", ""),
|
|
91
|
+
},
|
|
92
|
+
"provider": provider_type,
|
|
93
|
+
"checks": checks,
|
|
94
|
+
"missing": [m["check"] for m in missing],
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
cache_path = client.project_root / ".flydocs" / "validation-cache.json"
|
|
98
|
+
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
|
99
|
+
with open(cache_path, "w") as f:
|
|
100
|
+
json.dump(cache, f, indent=2)
|
|
101
|
+
f.write("\n")
|
|
102
|
+
|
|
103
|
+
# If all required checks pass, set setupComplete in config
|
|
104
|
+
if all_required_pass:
|
|
105
|
+
config_path = client.config_path
|
|
106
|
+
if config_path.exists():
|
|
107
|
+
with open(config_path, "r") as f:
|
|
108
|
+
local_config = json.load(f)
|
|
109
|
+
else:
|
|
110
|
+
local_config = {}
|
|
111
|
+
|
|
112
|
+
local_config["setupComplete"] = True
|
|
113
|
+
with open(config_path, "w") as f:
|
|
114
|
+
json.dump(local_config, f, indent=2)
|
|
115
|
+
f.write("\n")
|
|
116
|
+
|
|
117
|
+
# Output structured report
|
|
118
|
+
report: dict = {
|
|
119
|
+
"valid": all_required_pass,
|
|
120
|
+
"checks": checks,
|
|
121
|
+
"passed": valid,
|
|
122
|
+
}
|
|
123
|
+
if missing:
|
|
124
|
+
report["missing"] = missing
|
|
125
|
+
if warnings:
|
|
126
|
+
report["warnings"] = warnings
|
|
127
|
+
if all_required_pass:
|
|
128
|
+
report["setupComplete"] = True
|
|
129
|
+
|
|
130
|
+
output_json(report)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
main()
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.6.0-alpha.
|
|
2
|
+
"version": "0.6.0-alpha.8",
|
|
3
3
|
"sourceRepo": "github.com/plastrlab/flydocs-core",
|
|
4
4
|
"tier": "local",
|
|
5
5
|
"setupComplete": false,
|
|
6
|
+
"workspaceId": null,
|
|
6
7
|
"paths": {
|
|
7
8
|
"content": "flydocs"
|
|
8
9
|
},
|
|
9
|
-
"provider": {
|
|
10
|
-
"type": null,
|
|
11
|
-
"teamId": null
|
|
12
|
-
},
|
|
13
10
|
"workspace": {
|
|
14
11
|
"activeProjects": [],
|
|
15
12
|
"defaultMilestoneId": null,
|
|
13
|
+
"repoSlug": null,
|
|
16
14
|
"product": {
|
|
17
15
|
"name": null,
|
|
18
16
|
"labelIds": [],
|
|
@@ -21,7 +19,7 @@
|
|
|
21
19
|
}
|
|
22
20
|
},
|
|
23
21
|
"issueLabels": {
|
|
24
|
-
"_note": "Run /flydocs-setup to populate these from your
|
|
22
|
+
"_note": "Run /flydocs-setup to populate these from your workspace",
|
|
25
23
|
"category": {
|
|
26
24
|
"feature": null,
|
|
27
25
|
"bug": null,
|
|
@@ -50,18 +48,6 @@
|
|
|
50
48
|
"installed": [],
|
|
51
49
|
"custom": []
|
|
52
50
|
},
|
|
53
|
-
"statusMapping": {
|
|
54
|
-
"BACKLOG": "Backlog",
|
|
55
|
-
"READY": "Todo",
|
|
56
|
-
"IMPLEMENTING": "In Progress",
|
|
57
|
-
"BLOCKED": "Blocked",
|
|
58
|
-
"REVIEW": "In Review",
|
|
59
|
-
"TESTING": "QA",
|
|
60
|
-
"COMPLETE": "Done",
|
|
61
|
-
"ARCHIVED": "Archived",
|
|
62
|
-
"CANCELED": "Canceled",
|
|
63
|
-
"DUPLICATE": "Duplicate"
|
|
64
|
-
},
|
|
65
51
|
"designSystem": null,
|
|
66
52
|
"aiLabor": {
|
|
67
53
|
"enabled": false,
|
|
@@ -168,15 +168,32 @@ def get_flydocs_version() -> str | None:
|
|
|
168
168
|
|
|
169
169
|
|
|
170
170
|
def get_setup_nudge() -> str | None:
|
|
171
|
-
"""Check if setup has been completed, return nudge if not.
|
|
171
|
+
"""Check if setup has been completed, return nudge if not.
|
|
172
|
+
|
|
173
|
+
Reads validation cache (written by validate_setup.py) for specific
|
|
174
|
+
missing items. Falls back to generic nudge if cache is absent.
|
|
175
|
+
"""
|
|
172
176
|
config_file = Path('.flydocs/config.json')
|
|
173
177
|
if not config_file.exists():
|
|
174
178
|
return None
|
|
175
179
|
try:
|
|
176
180
|
config = json.loads(config_file.read_text())
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
if config.get('setupComplete') is not False:
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
# Check validation cache for specific missing items
|
|
185
|
+
cache_file = Path('.flydocs/validation-cache.json')
|
|
186
|
+
if cache_file.exists():
|
|
187
|
+
try:
|
|
188
|
+
cache = json.loads(cache_file.read_text())
|
|
189
|
+
missing = cache.get('missing', [])
|
|
190
|
+
if missing:
|
|
191
|
+
items = ', '.join(missing)
|
|
192
|
+
return f'[Setup incomplete — missing: {items}. Run /flydocs-setup or fix in dashboard]'
|
|
193
|
+
except (json.JSONDecodeError, OSError, IOError):
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
return '[Setup incomplete — run /flydocs-setup to configure your project]'
|
|
180
197
|
except (json.JSONDecodeError, OSError, IOError):
|
|
181
198
|
pass
|
|
182
199
|
return None
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.6.0-alpha.
|
|
1
|
+
0.6.0-alpha.8
|
package/template/manifest.json
CHANGED