@farming-labs/docs 0.1.138 → 0.1.140

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.
@@ -81,6 +81,7 @@ async function main() {
81
81
  configPath: typeof flags.config === "string" ? flags.config : void 0,
82
82
  apiBaseUrl: typeof flags["api-base-url"] === "string" ? flags["api-base-url"] : typeof flags.url === "string" ? flags.url : void 0,
83
83
  apiKey: typeof flags["api-key"] === "string" ? flags["api-key"] : void 0,
84
+ apiKeyEnv: typeof flags["api-key-env"] === "string" ? flags["api-key-env"] : void 0,
84
85
  json: typeof flags.json === "boolean" ? flags.json : void 0
85
86
  };
86
87
  if (!parsedCommand.command || parsedCommand.command === "init") {
@@ -90,24 +91,27 @@ async function main() {
90
91
  const { dev } = await import("../dev-DdlhqXNU.mjs");
91
92
  await dev(devOptions);
92
93
  } else if (parsedCommand.command === "deploy") {
93
- const { runCloudDeploy } = await import("../cloud-BdSGNsah.mjs");
94
+ const { runCloudDeploy } = await import("../cloud-DcsDg7Ze.mjs");
94
95
  await runCloudDeploy(cloudOptions);
95
96
  } else if (parsedCommand.command === "preview") {
96
- const { runCloudPreview } = await import("../cloud-BdSGNsah.mjs");
97
+ const { runCloudPreview } = await import("../cloud-DcsDg7Ze.mjs");
97
98
  await runCloudPreview(cloudOptions);
98
99
  } else if (parsedCommand.command === "cloud" && subcommand === "deploy") {
99
- const { runCloudDeploy } = await import("../cloud-BdSGNsah.mjs");
100
+ const { runCloudDeploy } = await import("../cloud-DcsDg7Ze.mjs");
100
101
  await runCloudDeploy(cloudOptions);
101
102
  } else if (parsedCommand.command === "cloud" && subcommand === "preview") {
102
- const { runCloudPreview } = await import("../cloud-BdSGNsah.mjs");
103
+ const { runCloudPreview } = await import("../cloud-DcsDg7Ze.mjs");
103
104
  await runCloudPreview(cloudOptions);
105
+ } else if (parsedCommand.command === "cloud" && subcommand === "init") {
106
+ const { runCloudInit } = await import("../cloud-DcsDg7Ze.mjs");
107
+ await runCloudInit(cloudOptions);
104
108
  } else if (parsedCommand.command === "cloud" && subcommand === "sync") {
105
- const { syncCloudConfig } = await import("../cloud-BdSGNsah.mjs");
109
+ const { syncCloudConfig } = await import("../cloud-DcsDg7Ze.mjs");
106
110
  await syncCloudConfig(cloudOptions);
107
111
  } else if (parsedCommand.command === "cloud") {
108
112
  console.error(pc.red(`Unknown cloud subcommand: ${subcommand ?? "(missing)"}`));
109
113
  console.error();
110
- const { printCloudHelp } = await import("../cloud-BdSGNsah.mjs");
114
+ const { printCloudHelp } = await import("../cloud-DcsDg7Ze.mjs");
111
115
  printCloudHelp();
112
116
  process.exit(1);
113
117
  } else if (parsedCommand.command === "mcp") {
@@ -246,7 +250,7 @@ ${pc.dim("Commands:")}
246
250
  ${pc.cyan("dev")} Run frameworkless docs locally from ${pc.dim("docs.json")}
247
251
  ${pc.cyan("deploy")} Sync cloud config and deploy hosted preview docs
248
252
  ${pc.cyan("preview")} Alias for ${pc.cyan("deploy")}
249
- ${pc.cyan("cloud")} Docs Cloud utilities (${pc.dim("deploy")}, ${pc.dim("preview")}, ${pc.dim("sync")})
253
+ ${pc.cyan("cloud")} Docs Cloud utilities (${pc.dim("init")}, ${pc.dim("deploy")}, ${pc.dim("preview")}, ${pc.dim("sync")})
250
254
  ${pc.cyan("agent")} Agent utilities (${pc.dim("compact")} to generate sibling agent.md files)
251
255
  ${pc.cyan("agents")} AGENTS.md utilities (${pc.dim("generate")} for static agent instructions)
252
256
  ${pc.cyan("doctor")} Inspect and score agent or reader-facing docs quality
@@ -281,12 +285,14 @@ ${pc.dim("Options for dev:")}
281
285
  ${pc.cyan("--verbose")} Show raw runtime logs in addition to branded CLI output
282
286
 
283
287
  ${pc.dim("Options for cloud deploy:")}
288
+ ${pc.cyan("cloud init")} Add Docs Cloud config to ${pc.dim("docs.config.ts")} and ${pc.dim("docs.json")}
284
289
  ${pc.cyan("deploy")} Sync ${pc.dim("docs.config.ts")} into ${pc.dim("docs.json")} and deploy hosted preview docs
285
290
  ${pc.cyan("cloud deploy")} Same as ${pc.cyan("deploy")}
286
291
  ${pc.cyan("preview")} Alias for ${pc.cyan("deploy")}
287
292
  ${pc.cyan("cloud preview")} Compatibility alias for ${pc.cyan("cloud deploy")}
288
293
  ${pc.cyan("cloud sync")} Only materialize cloud settings into ${pc.dim("docs.json")}
289
294
  ${pc.cyan("--config <path>")} Use a custom docs config path
295
+ ${pc.cyan("--api-key-env <name>")} Env var that stores the Docs Cloud API key
290
296
  ${pc.cyan("--api-base-url <url>")} Override the Docs Cloud API base URL
291
297
  ${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
292
298
  ${pc.cyan("--json")} Print machine-readable output
@@ -9,6 +9,7 @@ import { execFileSync } from "node:child_process";
9
9
  const DOCS_JSON_FILE = "docs.json";
10
10
  const DOCS_CLOUD_SCHEMA_URL = "https://docs.farming-labs.dev/schema/docs.json";
11
11
  const DOCS_CLOUD_DEFAULT_API_KEY_ENV = "DOCS_CLOUD_API_KEY";
12
+ const DOCS_CLOUD_DEFAULT_ANALYTICS_PROJECT_ID_ENV = "NEXT_PUBLIC_DOCS_CLOUD_PROJECT_ID";
12
13
  const DEFAULT_DOCS_CLOUD_API_BASE_URL = "https://docs-app.farming-labs.dev";
13
14
  const DEFAULT_PREVIEW_TIMEOUT_MS = 300 * 1e3;
14
15
  const DEFAULT_PREVIEW_POLL_INTERVAL_MS = 2e3;
@@ -112,6 +113,299 @@ async function loadDocsConfigSnapshot(rootDir, explicitPath) {
112
113
  config: (await loadDocsConfigModule(rootDir, configPath, { silent: true }))?.config
113
114
  };
114
115
  }
116
+ function escapeRegExp(value) {
117
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
118
+ }
119
+ function findBalancedBraceEnd(content, braceStart) {
120
+ let depth = 0;
121
+ let stringQuote = null;
122
+ let escaped = false;
123
+ let lineComment = false;
124
+ let blockComment = false;
125
+ for (let index = braceStart; index < content.length; index += 1) {
126
+ const char = content[index];
127
+ const next = content[index + 1];
128
+ if (lineComment) {
129
+ if (char === "\n") lineComment = false;
130
+ continue;
131
+ }
132
+ if (blockComment) {
133
+ if (char === "*" && next === "/") {
134
+ blockComment = false;
135
+ index += 1;
136
+ }
137
+ continue;
138
+ }
139
+ if (stringQuote) {
140
+ if (escaped) {
141
+ escaped = false;
142
+ continue;
143
+ }
144
+ if (char === "\\") {
145
+ escaped = true;
146
+ continue;
147
+ }
148
+ if (char === stringQuote) stringQuote = null;
149
+ continue;
150
+ }
151
+ if (char === "/" && next === "/") {
152
+ lineComment = true;
153
+ index += 1;
154
+ continue;
155
+ }
156
+ if (char === "/" && next === "*") {
157
+ blockComment = true;
158
+ index += 1;
159
+ continue;
160
+ }
161
+ if (char === "\"" || char === "'" || char === "`") {
162
+ stringQuote = char;
163
+ continue;
164
+ }
165
+ if (char === "{") {
166
+ depth += 1;
167
+ continue;
168
+ }
169
+ if (char !== "}") continue;
170
+ depth -= 1;
171
+ if (depth === 0) return index;
172
+ }
173
+ return -1;
174
+ }
175
+ function findConfigObjectRange(content) {
176
+ for (const marker of ["defineDocs(", "export default"]) {
177
+ const markerIndex = content.indexOf(marker);
178
+ if (markerIndex === -1) continue;
179
+ const braceStart = content.indexOf("{", markerIndex);
180
+ if (braceStart === -1) continue;
181
+ const braceEnd = findBalancedBraceEnd(content, braceStart);
182
+ if (braceEnd === -1) continue;
183
+ return {
184
+ braceStart,
185
+ braceEnd,
186
+ bodyStart: braceStart + 1,
187
+ bodyEnd: braceEnd
188
+ };
189
+ }
190
+ }
191
+ function stripLeadingPropertyTrivia(content) {
192
+ let current = content;
193
+ while (true) {
194
+ const trimmed = current.replace(/^\s+/, "");
195
+ if (trimmed.startsWith("//")) {
196
+ const lineEnd = trimmed.indexOf("\n");
197
+ current = lineEnd === -1 ? "" : trimmed.slice(lineEnd + 1);
198
+ continue;
199
+ }
200
+ if (trimmed.startsWith("/*")) {
201
+ const blockEnd = trimmed.indexOf("*/");
202
+ current = blockEnd === -1 ? "" : trimmed.slice(blockEnd + 2);
203
+ continue;
204
+ }
205
+ return trimmed;
206
+ }
207
+ }
208
+ function propertyStartsWithKey(property, key) {
209
+ return new RegExp(`^${escapeRegExp(key)}\\s*:`).test(stripLeadingPropertyTrivia(property));
210
+ }
211
+ function findTopLevelPropertyRange(content, bodyStart, bodyEnd, key) {
212
+ let start = bodyStart;
213
+ let stringQuote = null;
214
+ let escaped = false;
215
+ let lineComment = false;
216
+ let blockComment = false;
217
+ let braceDepth = 0;
218
+ let bracketDepth = 0;
219
+ let parenDepth = 0;
220
+ const maybeMatch = (end) => {
221
+ if (!propertyStartsWithKey(content.slice(start, end), key)) return void 0;
222
+ return {
223
+ start,
224
+ end
225
+ };
226
+ };
227
+ for (let index = bodyStart; index <= bodyEnd; index += 1) {
228
+ const char = content[index];
229
+ const next = content[index + 1];
230
+ if (index === bodyEnd) return maybeMatch(index);
231
+ if (lineComment) {
232
+ if (char === "\n") lineComment = false;
233
+ continue;
234
+ }
235
+ if (blockComment) {
236
+ if (char === "*" && next === "/") {
237
+ blockComment = false;
238
+ index += 1;
239
+ }
240
+ continue;
241
+ }
242
+ if (stringQuote) {
243
+ if (escaped) {
244
+ escaped = false;
245
+ continue;
246
+ }
247
+ if (char === "\\") {
248
+ escaped = true;
249
+ continue;
250
+ }
251
+ if (char === stringQuote) stringQuote = null;
252
+ continue;
253
+ }
254
+ if (char === "/" && next === "/") {
255
+ lineComment = true;
256
+ index += 1;
257
+ continue;
258
+ }
259
+ if (char === "/" && next === "*") {
260
+ blockComment = true;
261
+ index += 1;
262
+ continue;
263
+ }
264
+ if (char === "\"" || char === "'" || char === "`") {
265
+ stringQuote = char;
266
+ continue;
267
+ }
268
+ if (char === "{") {
269
+ braceDepth += 1;
270
+ continue;
271
+ }
272
+ if (char === "}") {
273
+ braceDepth = Math.max(0, braceDepth - 1);
274
+ continue;
275
+ }
276
+ if (char === "[") {
277
+ bracketDepth += 1;
278
+ continue;
279
+ }
280
+ if (char === "]") {
281
+ bracketDepth = Math.max(0, bracketDepth - 1);
282
+ continue;
283
+ }
284
+ if (char === "(") {
285
+ parenDepth += 1;
286
+ continue;
287
+ }
288
+ if (char === ")") {
289
+ parenDepth = Math.max(0, parenDepth - 1);
290
+ continue;
291
+ }
292
+ if (char !== "," || braceDepth !== 0 || bracketDepth !== 0 || parenDepth !== 0) continue;
293
+ const match = maybeMatch(index);
294
+ if (match) return match;
295
+ start = index + 1;
296
+ }
297
+ }
298
+ function findObjectPropertyRange(content, object, key) {
299
+ const property = findTopLevelPropertyRange(content, object.bodyStart, object.bodyEnd, key);
300
+ if (!property) return void 0;
301
+ const colon = content.indexOf(":", property.start);
302
+ const braceStart = content.indexOf("{", colon);
303
+ if (colon === -1 || braceStart === -1 || braceStart > property.end) return void 0;
304
+ const braceEnd = findBalancedBraceEnd(content, braceStart);
305
+ if (braceEnd === -1 || braceEnd > property.end) return void 0;
306
+ return {
307
+ braceStart,
308
+ braceEnd,
309
+ bodyStart: braceStart + 1,
310
+ bodyEnd: braceEnd
311
+ };
312
+ }
313
+ function lineIndentAt(content, index) {
314
+ const lineStart = content.lastIndexOf("\n", index - 1) + 1;
315
+ return content.slice(lineStart, index).match(/^\s*/)?.[0] ?? "";
316
+ }
317
+ function insertObjectProperties(content, object, properties) {
318
+ if (properties.length === 0) return content;
319
+ const body = content.slice(object.bodyStart, object.bodyEnd);
320
+ const closingIndent = lineIndentAt(content, object.braceEnd);
321
+ const beforeObjectClose = content.slice(0, object.bodyEnd).replace(/\s*$/, "");
322
+ const suffix = content.slice(object.bodyEnd);
323
+ return `${beforeObjectClose}${body.trim().length > 0 && !beforeObjectClose.endsWith(",") ? "," : ""}\n${properties.join("\n")}\n${closingIndent}${suffix}`;
324
+ }
325
+ function renderAnalyticsConfigProperty(indent) {
326
+ return `${indent}analytics: {
327
+ ${indent} enabled: true,
328
+ ${indent} console: false,
329
+ ${indent} includeInputs: false,
330
+ ${indent}},`;
331
+ }
332
+ function renderCloudConfigProperty(indent, apiKeyEnv) {
333
+ return `${indent}cloud: {
334
+ ${indent} apiKey: { env: ${JSON.stringify(apiKeyEnv)} },
335
+ ${indent} deploy: { enabled: true },
336
+ ${indent} analytics: {
337
+ ${indent} enabled: true,
338
+ ${indent} console: false,
339
+ ${indent} includeInputs: false,
340
+ ${indent} },
341
+ ${indent} publish: { mode: "draft-pr", baseBranch: "main" },
342
+ ${indent}},`;
343
+ }
344
+ function renderCloudInitDocsConfig(apiKeyEnv) {
345
+ return `import { defineDocs } from "@farming-labs/docs";
346
+
347
+ export default defineDocs({
348
+ ${renderAnalyticsConfigProperty(" ")}
349
+ ${renderCloudConfigProperty(" ", apiKeyEnv)}
350
+ });
351
+ `;
352
+ }
353
+ function normalizeEnvName(value, fallback) {
354
+ const normalized = value?.trim();
355
+ if (!normalized) return fallback;
356
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized)) throw new Error(`Invalid environment variable name: ${normalized}`);
357
+ return normalized;
358
+ }
359
+ function ensureDocsConfigCloudInit(options) {
360
+ const resolvedConfigPath = tryResolveDocsConfigPath(options.rootDir, options.configPath);
361
+ const configPath = resolvedConfigPath ?? path.join(options.rootDir, "docs.config.ts");
362
+ if (!resolvedConfigPath) {
363
+ fs.writeFileSync(configPath, renderCloudInitDocsConfig(options.apiKeyEnv), "utf-8");
364
+ return {
365
+ configPath,
366
+ created: true,
367
+ updated: true
368
+ };
369
+ }
370
+ const original = fs.readFileSync(configPath, "utf-8");
371
+ let content = original;
372
+ let configObject = findConfigObjectRange(content);
373
+ if (!configObject) throw new Error(`Could not find an object export in ${path.relative(options.rootDir, configPath)}. Use defineDocs({ ... }) or export default { ... } before running cloud init.`);
374
+ const topLevelIndent = `${lineIndentAt(content, configObject.braceEnd)} `;
375
+ const cloudProperty = findTopLevelPropertyRange(content, configObject.bodyStart, configObject.bodyEnd, "cloud");
376
+ let cloudObject = findObjectPropertyRange(content, configObject, "cloud");
377
+ if (!cloudProperty) content = insertObjectProperties(content, configObject, [renderCloudConfigProperty(topLevelIndent, options.apiKeyEnv)]);
378
+ else if (!cloudObject) throw new Error(`Could not update cloud config in ${path.relative(options.rootDir, configPath)} because cloud is not an object literal.`);
379
+ configObject = findConfigObjectRange(content);
380
+ if (!configObject) throw new Error(`Could not re-read ${path.relative(options.rootDir, configPath)}.`);
381
+ cloudObject = findObjectPropertyRange(content, configObject, "cloud");
382
+ if (cloudObject) {
383
+ const cloudIndent = `${lineIndentAt(content, cloudObject.braceEnd)} `;
384
+ const missingCloudProperties = [];
385
+ if (!findTopLevelPropertyRange(content, cloudObject.bodyStart, cloudObject.bodyEnd, "apiKey")) missingCloudProperties.push(`${cloudIndent}apiKey: { env: ${JSON.stringify(options.apiKeyEnv)} },`);
386
+ else {
387
+ const apiKeyObject = findObjectPropertyRange(content, cloudObject, "apiKey");
388
+ if (apiKeyObject && !findTopLevelPropertyRange(content, apiKeyObject.bodyStart, apiKeyObject.bodyEnd, "env")) {
389
+ content = insertObjectProperties(content, apiKeyObject, [`${lineIndentAt(content, apiKeyObject.braceEnd)} env: ${JSON.stringify(options.apiKeyEnv)},`]);
390
+ configObject = findConfigObjectRange(content);
391
+ cloudObject = findObjectPropertyRange(content, configObject, "cloud");
392
+ }
393
+ }
394
+ if (!findTopLevelPropertyRange(content, cloudObject.bodyStart, cloudObject.bodyEnd, "deploy")) missingCloudProperties.push(`${cloudIndent}deploy: { enabled: true },`);
395
+ if (!findTopLevelPropertyRange(content, cloudObject.bodyStart, cloudObject.bodyEnd, "analytics")) missingCloudProperties.push(renderAnalyticsConfigProperty(cloudIndent));
396
+ if (!findTopLevelPropertyRange(content, cloudObject.bodyStart, cloudObject.bodyEnd, "publish")) missingCloudProperties.push(`${cloudIndent}publish: { mode: "draft-pr", baseBranch: "main" },`);
397
+ content = insertObjectProperties(content, cloudObject, missingCloudProperties);
398
+ }
399
+ configObject = findConfigObjectRange(content);
400
+ if (!configObject) throw new Error(`Could not re-read ${path.relative(options.rootDir, configPath)}.`);
401
+ if (!findTopLevelPropertyRange(content, configObject.bodyStart, configObject.bodyEnd, "analytics")) content = insertObjectProperties(content, configObject, [renderAnalyticsConfigProperty(`${lineIndentAt(content, configObject.braceEnd)} `)]);
402
+ if (content !== original) fs.writeFileSync(configPath, content, "utf-8");
403
+ return {
404
+ configPath,
405
+ created: false,
406
+ updated: content !== original
407
+ };
408
+ }
115
409
  function readExistingDocsJson(docsJsonPath) {
116
410
  if (!fs.existsSync(docsJsonPath)) return void 0;
117
411
  try {
@@ -564,6 +858,51 @@ async function syncCloudConfig(options = {}) {
564
858
  console.log(`${pc.dim("api key env")} ${result.apiKeyEnv}`);
565
859
  return result;
566
860
  }
861
+ async function initCloudConfig(options = {}) {
862
+ const rootDir = options.rootDir ?? process.cwd();
863
+ const apiKeyEnv = normalizeEnvName(options.apiKeyEnv, DOCS_CLOUD_DEFAULT_API_KEY_ENV);
864
+ const configUpdate = ensureDocsConfigCloudInit({
865
+ rootDir,
866
+ configPath: options.configPath,
867
+ apiKeyEnv
868
+ });
869
+ const materialized = await materializeCloudConfig({
870
+ ...options,
871
+ rootDir,
872
+ configPath: path.relative(rootDir, configUpdate.configPath)
873
+ });
874
+ return {
875
+ configPath: configUpdate.configPath,
876
+ docsJsonPath: materialized.docsJsonPath,
877
+ apiKeyEnv: materialized.apiKeyEnv,
878
+ analyticsProjectIdEnv: DOCS_CLOUD_DEFAULT_ANALYTICS_PROJECT_ID_ENV,
879
+ configCreated: configUpdate.created,
880
+ configUpdated: configUpdate.updated,
881
+ docsJsonCreated: materialized.created,
882
+ docsJsonUpdated: materialized.updated
883
+ };
884
+ }
885
+ async function runCloudInit(options = {}) {
886
+ const result = await initCloudConfig(options);
887
+ if (options.json) {
888
+ console.log(JSON.stringify(result, null, 2));
889
+ return result;
890
+ }
891
+ const relativeConfigPath = path.relative(process.cwd(), result.configPath) || "docs.config.ts";
892
+ const relativeDocsJsonPath = path.relative(process.cwd(), result.docsJsonPath) || DOCS_JSON_FILE;
893
+ const configAction = result.configCreated ? "Created" : result.configUpdated ? "Updated" : "Checked";
894
+ const docsJsonAction = result.docsJsonCreated ? "created" : result.docsJsonUpdated ? "updated" : "checked";
895
+ console.log(`${pc.green("ok")} ${configAction} ${pc.cyan(relativeConfigPath)}`);
896
+ console.log(`${pc.green("ok")} ${docsJsonAction} ${pc.cyan(relativeDocsJsonPath)}`);
897
+ console.log();
898
+ console.log(pc.bold("Add these env vars"));
899
+ console.log(`${pc.cyan(result.apiKeyEnv)}=${pc.dim("paste_your_docs_cloud_api_key")}`);
900
+ console.log(`${pc.cyan(result.analyticsProjectIdEnv)}=${pc.dim("paste_your_docs_cloud_project_id")}`);
901
+ console.log();
902
+ console.log(pc.dim("Use the same env vars in production. The API key value is never written to config."));
903
+ console.log(pc.dim(`Then run ${pc.cyan("pnpm dlx @farming-labs/docs deploy")}.`));
904
+ return result;
905
+ }
567
906
  async function runCloudDeployment(options = {}) {
568
907
  const rootDir = options.rootDir ?? process.cwd();
569
908
  const spinner = createSpinner("Preparing Docs Cloud deployment", options);
@@ -625,6 +964,7 @@ function printCloudHelp() {
625
964
  ${pc.bold("@farming-labs/docs cloud")}
626
965
 
627
966
  ${pc.dim("Usage:")}
967
+ ${pc.cyan("docs cloud init")} Add Docs Cloud config to ${pc.dim("docs.config.ts")} and ${pc.dim("docs.json")}
628
968
  ${pc.cyan("docs deploy")} Sync ${pc.dim("docs.config.ts")} to ${pc.dim("docs.json")} and deploy hosted preview docs
629
969
  ${pc.cyan("docs cloud deploy")} Same as ${pc.cyan("docs deploy")}
630
970
  ${pc.cyan("docs preview")} Compatibility alias for ${pc.cyan("docs deploy")}
@@ -633,6 +973,7 @@ ${pc.dim("Usage:")}
633
973
 
634
974
  ${pc.dim("Options:")}
635
975
  ${pc.cyan("--config <path>")} Use a custom docs config path
976
+ ${pc.cyan("--api-key-env <name>")} Env var that stores the Docs Cloud API key
636
977
  ${pc.cyan("--api-base-url <url>")} Override Docs Cloud API base URL
637
978
  ${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
638
979
  ${pc.cyan("--json")} Print machine-readable output
@@ -644,10 +985,11 @@ ${pc.dim("Config example:")}
644
985
  cloud: {
645
986
  apiKey: { env: "DOCS_CLOUD_API_KEY" },
646
987
  deploy: { enabled: true },
988
+ analytics: { enabled: true, console: false, includeInputs: false },
647
989
  publish: { mode: "draft-pr", baseBranch: "main" },
648
990
  }
649
991
  `);
650
992
  }
651
993
 
652
994
  //#endregion
653
- export { printCloudHelp, runCloudDeploy, runCloudPreview, syncCloudConfig };
995
+ export { printCloudHelp, runCloudDeploy, runCloudInit, runCloudPreview, syncCloudConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/docs",
3
- "version": "0.1.138",
3
+ "version": "0.1.140",
4
4
  "description": "Modern, flexible MDX-based docs framework — core types, config, and CLI",
5
5
  "keywords": [
6
6
  "docs",