contentrain 0.4.3 → 0.5.0

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.
Files changed (109) hide show
  1. package/README.md +112 -34
  2. package/dist/{activity-CbkxcfDf.mjs → activity-B8j9gfsK.mjs} +3 -3
  3. package/dist/{branches-D2UWex1N.mjs → branches-CvpFbyrJ.mjs} +3 -3
  4. package/dist/{cdn-build-BzC2QPNi.mjs → cdn-build-DS9FprWU.mjs} +3 -3
  5. package/dist/{cdn-init-CsmqJIaQ.mjs → cdn-init-Bm4dpEJn.mjs} +3 -3
  6. package/dist/{connect-DJo9PSiw.mjs → connect-Bb0BrZsx.mjs} +3 -3
  7. package/dist/describe-DJ5H9Cxm.mjs +106 -0
  8. package/dist/describe-format-CUAqB0C4.mjs +50 -0
  9. package/dist/{diff--halNUpG.mjs → diff-ov1j9bAb.mjs} +67 -59
  10. package/dist/doctor-MOf_LQEL.mjs +95 -0
  11. package/dist/{generate-DrUojrk8.mjs → generate-ByscV5SN.mjs} +26 -9
  12. package/dist/{ide-4ZGiAtYB.mjs → ide-DPDVYiaq.mjs} +6 -8
  13. package/dist/index.mjs +50 -12
  14. package/dist/{init-BVlisNcW.mjs → init-540qiNQJ.mjs} +6 -6
  15. package/dist/{login-DCXkOrQV.mjs → login-xvzL-UvR.mjs} +3 -3
  16. package/dist/{logout-nPt2UAeQ.mjs → logout-DzspSatR.mjs} +2 -2
  17. package/dist/mcp-client-DVWTex2i.mjs +44 -0
  18. package/dist/merge-DdPRttF-.mjs +82 -0
  19. package/dist/{resolve-context-Cg6JoVZt.mjs → resolve-context-hKVYPniC.mjs} +1 -1
  20. package/dist/scaffold-iuvrMh78.mjs +115 -0
  21. package/dist/{serve-CNeFIQ1i.mjs → serve-CxA2pKbp.mjs} +58 -5
  22. package/dist/serve-ui/assets/{AgentPromptGroup.vue_vue_type_script_setup_true_lang-Ulb-9Tr3.js → AgentPromptGroup.vue_vue_type_script_setup_true_lang-Bftt9tx_.js} +1 -1
  23. package/dist/serve-ui/assets/AppLayout-B-fbrfMk.js +21 -0
  24. package/dist/serve-ui/assets/BranchDetailPage-B3PL9PGp.js +17 -0
  25. package/dist/serve-ui/assets/{BranchesPage-CSwikNkd.js → BranchesPage-4YpE_szf.js} +2 -2
  26. package/dist/serve-ui/assets/CardContent.vue_vue_type_script_setup_true_lang-D1L2MnBU.js +1 -0
  27. package/dist/serve-ui/assets/CollapsibleTrigger.vue_vue_type_script_setup_true_lang-B4uDtllm.js +1 -0
  28. package/dist/serve-ui/assets/{Collection-DTB4xCCE.js → Collection-9URFko-Y.js} +2 -2
  29. package/dist/serve-ui/assets/ConfigProvider-CwkmRpT5.js +1 -0
  30. package/dist/serve-ui/assets/{ContentListPage-DF-LjAaS.js → ContentListPage-BMfSfg8x.js} +2 -2
  31. package/dist/serve-ui/assets/ContentPage-BXUnNJlB.js +1 -0
  32. package/dist/serve-ui/assets/DashboardPage-1UyYgg5J.js +21 -0
  33. package/dist/serve-ui/assets/{DialogTitle.vue_vue_type_script_setup_true_lang-2sQAJn_Y.js → DialogTitle.vue_vue_type_script_setup_true_lang-M4LOzTva.js} +1 -1
  34. package/dist/serve-ui/assets/DoctorPage-WnQ8xYWy.js +1 -0
  35. package/dist/serve-ui/assets/FormatReferencePage-DoKEaawl.js +1 -0
  36. package/dist/serve-ui/assets/{Input.vue_vue_type_script_setup_true_lang-BveqfZsR.js → Input.vue_vue_type_script_setup_true_lang-tW0hgReH.js} +2 -2
  37. package/dist/serve-ui/assets/{ModelDetailPage-_TIV9itx.js → ModelDetailPage-BysqN8IL.js} +3 -3
  38. package/dist/serve-ui/assets/ModelsPage-DFZrdBIi.js +1 -0
  39. package/dist/serve-ui/assets/{NormalizePage-Cde8vdk2.js → NormalizePage-4f5Ib_ES.js} +2 -2
  40. package/dist/serve-ui/assets/{PageHeader.vue_vue_type_script_setup_true_lang-CEnjONvd.js → PageHeader.vue_vue_type_script_setup_true_lang-Bln1czg7.js} +1 -1
  41. package/dist/serve-ui/assets/Presence-CA3xTrht.js +3 -0
  42. package/dist/serve-ui/assets/{Separator.vue_vue_type_script_setup_true_lang-1pFZ2PR2.js → Separator.vue_vue_type_script_setup_true_lang-PECBq3nc.js} +1 -1
  43. package/dist/serve-ui/assets/{StudioHint.vue_vue_type_script_setup_true_lang-C6JN5tPq.js → StudioHint.vue_vue_type_script_setup_true_lang-DYTEoyjw.js} +1 -1
  44. package/dist/serve-ui/assets/{TrustBadge.vue_vue_type_script_setup_true_lang-KRiTnLVk.js → TrustBadge.vue_vue_type_script_setup_true_lang-BRXWvOYU.js} +2 -2
  45. package/dist/serve-ui/assets/ValidatePage-BMTpV2KL.js +16 -0
  46. package/dist/serve-ui/assets/{arrow-down-BAJwpryD.js → arrow-down-IJu6H7Es.js} +1 -1
  47. package/dist/serve-ui/assets/{arrow-left-DANf_wrK.js → arrow-left-D8ZjVYAI.js} +1 -1
  48. package/dist/serve-ui/assets/{book-marked-DS8FXgjz.js → book-marked-BNyyZrq3.js} +1 -1
  49. package/dist/serve-ui/assets/{book-open-DnH5T78M.js → book-open-Dw1fQtSI.js} +1 -1
  50. package/dist/serve-ui/assets/{box-UJk5SvJJ.js → box-BoGMZDZR.js} +1 -1
  51. package/dist/serve-ui/assets/{chevron-down-C1nYx5Eu.js → chevron-down-UcXxhyyY.js} +1 -1
  52. package/dist/serve-ui/assets/{chevron-right-XnJVrezx.js → chevron-right-SpdM5LBc.js} +1 -1
  53. package/dist/serve-ui/assets/circle-alert-CEwKJpYl.js +6 -0
  54. package/dist/serve-ui/assets/{content-Ctqretwl.js → content-BQEzysvq.js} +1 -1
  55. package/dist/serve-ui/assets/{database-Wd_i2bM1.js → database-U6JU8oc9.js} +1 -1
  56. package/dist/serve-ui/assets/{file-code-ScWZN40B.js → file-code-_sq1QHQo.js} +1 -1
  57. package/dist/serve-ui/assets/{file-text-J3Wzwieh.js → file-text-BiMGJckO.js} +1 -1
  58. package/dist/serve-ui/assets/{git-branch-DqEFTZDt.js → git-branch-CdZMOuSP.js} +1 -1
  59. package/dist/serve-ui/assets/{git-merge-Bmz-MVBm.js → git-merge-D8Y-BwIh.js} +1 -1
  60. package/dist/serve-ui/assets/{hash-pCwLSzjB.js → hash-CrXakbCy.js} +1 -1
  61. package/dist/serve-ui/assets/index-DL_dw3XX.js +80 -0
  62. package/dist/serve-ui/assets/index-KZ2rT4mJ.css +1 -0
  63. package/dist/serve-ui/assets/{languages-CQqNMim-.js → languages-DvyMGPwy.js} +1 -1
  64. package/dist/serve-ui/assets/{plus-Cw1OnTR2.js → plus-Ddujj7Qx.js} +1 -1
  65. package/dist/serve-ui/assets/project-Dg2BVay_.js +1 -0
  66. package/dist/serve-ui/assets/{scan-search-UJ-_oiOR.js → scan-search-CsPYGmEs.js} +1 -1
  67. package/dist/serve-ui/assets/{settings-Dnm4yTIj.js → settings-IQvevsKM.js} +1 -1
  68. package/dist/serve-ui/assets/{shield-check-B67ELGUT.js → shield-check-BmSBU8Ai.js} +1 -1
  69. package/dist/serve-ui/assets/stethoscope-DgCa_7BL.js +6 -0
  70. package/dist/serve-ui/assets/{trash-2-B1QgZW1G.js → trash-2-CNTkluuz.js} +1 -1
  71. package/dist/serve-ui/assets/useDirection-DfPL7a9d.js +1 -0
  72. package/dist/serve-ui/assets/{useId-CPFWZ4Bo.js → useId-B-RTM02R.js} +1 -1
  73. package/dist/serve-ui/assets/{useNonce-BffIzuhE.js → useNonce-DR6M3tLq.js} +1 -1
  74. package/dist/serve-ui/assets/useWatch-D4Y-epQR.js +1 -0
  75. package/dist/serve-ui/index.html +2 -2
  76. package/dist/{server-DTdmutjb.mjs → server-B1PSjXbu.mjs} +313 -112
  77. package/dist/{setup-4mS8RuCy.mjs → setup-BMqKibZt.mjs} +3 -3
  78. package/dist/{skills-4nhto0AZ.mjs → skills-CZ54F-97.mjs} +3 -3
  79. package/dist/{status-Cyv-jo1y.mjs → status-BJ6Lu3J4.mjs} +3 -3
  80. package/dist/{status-CoHgz6HN.mjs → status-Bmjfrk-B.mjs} +24 -11
  81. package/dist/studio-DPVJ-2oG.mjs +24 -0
  82. package/dist/{submissions-bIG-MWvv.mjs → submissions-zH_S7JIQ.mjs} +3 -3
  83. package/dist/{usage-B2xuc2DT.mjs → usage-z6eyT64a.mjs} +3 -3
  84. package/dist/{validate-B10L7RL7.mjs → validate-BG0X5g09.mjs} +102 -4
  85. package/dist/{webhooks-Dq3zFLP7.mjs → webhooks-CBG2Bs9P.mjs} +3 -3
  86. package/dist/{whoami-Ub5RznbU.mjs → whoami-CXVfvo7x.mjs} +2 -2
  87. package/package.json +7 -6
  88. package/dist/doctor-DyKjAIKH.mjs +0 -187
  89. package/dist/serve-ui/assets/AppLayout-C91DN08z.js +0 -21
  90. package/dist/serve-ui/assets/BranchDetailPage-BokOnhE7.js +0 -17
  91. package/dist/serve-ui/assets/CollapsibleTrigger.vue_vue_type_script_setup_true_lang-BRzXVXDG.js +0 -1
  92. package/dist/serve-ui/assets/ConfigProvider-Dvd7YoEV.js +0 -1
  93. package/dist/serve-ui/assets/ContentPage-BABg8pgn.js +0 -1
  94. package/dist/serve-ui/assets/DashboardPage-BFLFEJNZ.js +0 -21
  95. package/dist/serve-ui/assets/ModelsPage-DrQemHQ-.js +0 -1
  96. package/dist/serve-ui/assets/Presence-BXKCIfUk.js +0 -3
  97. package/dist/serve-ui/assets/ValidatePage-DRFAySNR.js +0 -21
  98. package/dist/serve-ui/assets/index-CG5NtNtt.css +0 -1
  99. package/dist/serve-ui/assets/index-De_z0714.js +0 -1
  100. package/dist/serve-ui/assets/index-RDdffBTh.js +0 -80
  101. package/dist/serve-ui/assets/project-BlZcHJw6.js +0 -1
  102. package/dist/serve-ui/assets/useDirection-B4S5kPsI.js +0 -1
  103. package/dist/serve-ui/assets/useWatch-B2FLRWr8.js +0 -1
  104. package/dist/studio-CJUGiIMd.mjs +0 -24
  105. package/dist/{client-C400qHcL.mjs → client-C9E9D4mb.mjs} +1 -1
  106. package/dist/{context-An3xrvcP.mjs → context-OskMGy8y.mjs} +1 -1
  107. package/dist/{demo-DcBt6fw8.mjs → demo-BOUcAoFZ.mjs} +3 -3
  108. package/dist/{oauth-server-Beppljfz.mjs → oauth-server-smSxDoH5.mjs} +1 -1
  109. /package/dist/{ui-B5mXontH.mjs → ui-B8l_LC36.mjs} +0 -0
@@ -0,0 +1,95 @@
1
+ import { i as pc, o as statusIcon } from "./ui-B8l_LC36.mjs";
2
+ import { r as resolveProjectRoot } from "./context-OskMGy8y.mjs";
3
+ import { defineCommand } from "citty";
4
+ import { intro, log, outro, spinner } from "@clack/prompts";
5
+ import { runDoctor } from "@contentrain/mcp/core/doctor";
6
+ //#region src/commands/doctor.ts
7
+ /**
8
+ * Thin CLI wrapper over the shared `runDoctor()` from `@contentrain/mcp`.
9
+ *
10
+ * All health-check logic lives in the MCP core so the same report
11
+ * drives three consumers: this command, the `contentrain_doctor` MCP
12
+ * tool, and the Serve UI's `/api/doctor` route. Behaviour in the
13
+ * default (non-`--json`) mode is unchanged — same check labels, same
14
+ * details, same grouped usage output.
15
+ */
16
+ var doctor_default = defineCommand({
17
+ meta: {
18
+ name: "doctor",
19
+ description: "Check project health and environment"
20
+ },
21
+ args: {
22
+ root: {
23
+ type: "string",
24
+ description: "Project root path",
25
+ required: false
26
+ },
27
+ usage: {
28
+ type: "boolean",
29
+ description: "Analyze content key usage in source files",
30
+ required: false
31
+ },
32
+ json: {
33
+ type: "boolean",
34
+ description: "Emit the raw DoctorReport JSON",
35
+ required: false
36
+ }
37
+ },
38
+ async run({ args }) {
39
+ const projectRoot = await resolveProjectRoot(args.root);
40
+ if (Boolean(args.json)) {
41
+ const report = await runDoctor(projectRoot, { usage: Boolean(args.usage) });
42
+ process.stdout.write(JSON.stringify(report, null, 2));
43
+ if (report.summary.failed > 0) process.exitCode = 1;
44
+ return;
45
+ }
46
+ intro(pc.bold("contentrain doctor"));
47
+ const s = spinner();
48
+ s.start("Running health checks...");
49
+ if (args.usage) s.message("Analyzing content key usage...");
50
+ const report = await runDoctor(projectRoot, { usage: Boolean(args.usage) });
51
+ s.stop("Health checks complete");
52
+ for (const check of report.checks) log.message(`${statusIcon(check.pass)} ${pc.bold(check.name)}: ${check.detail}`);
53
+ if (report.usage?.unusedKeys.length) {
54
+ log.message("");
55
+ log.message(pc.bold(" Unused keys:"));
56
+ const grouped = groupBy(report.usage.unusedKeys, (e) => e.model);
57
+ for (const [model, entries] of Object.entries(grouped)) {
58
+ const keyList = entries.length <= 5 ? entries.map((e) => e.key).join(", ") : `${entries.slice(0, 5).map((e) => e.key).join(", ")} (+${entries.length - 5} more)`;
59
+ log.message(` ${pc.dim(model)}: ${pc.yellow(keyList)}`);
60
+ }
61
+ }
62
+ if (report.usage?.duplicateValues.length) {
63
+ log.message("");
64
+ log.message(pc.bold(" Duplicate values:"));
65
+ for (const dv of report.usage.duplicateValues.slice(0, 10)) {
66
+ const truncated = dv.value.length > 30 ? `${dv.value.slice(0, 30)}...` : dv.value;
67
+ log.message(` ${pc.dim(`${dv.model}/${dv.locale}`)}: ${pc.yellow(`"${truncated}"`)} → [${dv.keys.join(", ")}]`);
68
+ }
69
+ if (report.usage.duplicateValues.length > 10) log.message(` ... and ${report.usage.duplicateValues.length - 10} more`);
70
+ }
71
+ if (report.usage?.missingLocaleKeys.length) {
72
+ log.message("");
73
+ log.message(pc.bold(" Missing translations:"));
74
+ const grouped = groupBy(report.usage.missingLocaleKeys, (e) => `${e.model}/${e.missingIn}`);
75
+ for (const [label, entries] of Object.entries(grouped)) log.message(` ${pc.dim(label)}: ${pc.yellow(`${entries.length} key(s)`)}`);
76
+ }
77
+ const { passed, failed } = report.summary;
78
+ if (failed === 0) outro(pc.green(`All ${passed} checks passed!`));
79
+ else {
80
+ outro(pc.yellow(`${passed} passed, ${failed} failed`));
81
+ process.exitCode = 1;
82
+ }
83
+ }
84
+ });
85
+ function groupBy(arr, keyFn) {
86
+ const result = {};
87
+ for (const item of arr) {
88
+ const key = keyFn(item);
89
+ if (!result[key]) result[key] = [];
90
+ result[key].push(item);
91
+ }
92
+ return result;
93
+ }
94
+ //#endregion
95
+ export { doctor_default as default };
@@ -1,5 +1,5 @@
1
- import { n as requireInitialized, r as resolveProjectRoot, t as loadProjectContext } from "./context-An3xrvcP.mjs";
2
- import { i as pc } from "./ui-B5mXontH.mjs";
1
+ import { i as pc } from "./ui-B8l_LC36.mjs";
2
+ import { n as requireInitialized, r as resolveProjectRoot, t as loadProjectContext } from "./context-OskMGy8y.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  import { intro, log, outro, spinner } from "@clack/prompts";
5
5
  import { join } from "node:path";
@@ -24,18 +24,33 @@ var generate_default = defineCommand({
24
24
  type: "boolean",
25
25
  description: "Watch for changes and regenerate",
26
26
  required: false
27
+ },
28
+ json: {
29
+ type: "boolean",
30
+ description: "Emit the generate result as JSON (silences pretty output)",
31
+ required: false
27
32
  }
28
33
  },
29
34
  async run({ args }) {
30
35
  const projectRoot = await resolveProjectRoot(args.root);
31
36
  requireInitialized(await loadProjectContext(projectRoot));
32
- intro(pc.bold("contentrain generate"));
33
- const s = spinner();
34
- s.start("Generating SDK client...");
37
+ const useJson = Boolean(args.json);
38
+ if (!useJson) intro(pc.bold("contentrain generate"));
39
+ const s = useJson ? null : spinner();
40
+ s?.start("Generating SDK client...");
35
41
  try {
36
42
  const { generate } = await import("@contentrain/query/generate");
37
43
  const result = await generate({ projectRoot });
38
- s.stop("SDK client generated");
44
+ s?.stop("SDK client generated");
45
+ if (useJson) {
46
+ process.stdout.write(JSON.stringify({
47
+ generatedFiles: result.generatedFiles,
48
+ typesCount: result.typesCount,
49
+ dataModulesCount: result.dataModulesCount,
50
+ packageJsonUpdated: Boolean(result.packageJsonUpdated)
51
+ }, null, 2));
52
+ return;
53
+ }
39
54
  log.success(`Output: ${pc.cyan(".contentrain/client/")}`);
40
55
  log.message(` Files: ${result.generatedFiles.length}`);
41
56
  log.message(` Types: ${result.typesCount}`);
@@ -66,11 +81,13 @@ var generate_default = defineCommand({
66
81
  await new Promise(() => {});
67
82
  }
68
83
  } catch (error) {
69
- s.stop("Generation failed");
70
- log.error(error instanceof Error ? error.message : String(error));
84
+ s?.stop("Generation failed");
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ if (useJson) process.stdout.write(JSON.stringify({ error: message }, null, 2));
87
+ else log.error(message);
71
88
  process.exitCode = 1;
72
89
  }
73
- outro("");
90
+ if (!useJson) outro("");
74
91
  }
75
92
  });
76
93
  //#endregion
@@ -1,6 +1,6 @@
1
- import { simpleGit } from "simple-git";
2
1
  import { join } from "node:path";
3
2
  import { ensureDir, pathExists } from "@contentrain/mcp/util/fs";
3
+ import { simpleGit } from "simple-git";
4
4
  import { appendFile, readFile, writeFile } from "node:fs/promises";
5
5
  //#region src/utils/ide.ts
6
6
  const AGENT_SKILL_NAMES = [
@@ -205,12 +205,10 @@ async function installIdeRulesAndSkills(projectRoot, ide, resolveRuleFile, resol
205
205
  } catch {}
206
206
  }
207
207
  }
208
- if (filesToAdd.length > 0) {
208
+ if (filesToAdd.length > 0) try {
209
209
  await git.add(filesToAdd);
210
- try {
211
- await git.commit(`[contentrain] install ${ide.name} rules and skills`);
212
- } catch {}
213
- }
210
+ await git.commit(`[contentrain] install ${ide.name} rules and skills`);
211
+ } catch {}
214
212
  return {
215
213
  installed,
216
214
  updated
@@ -232,15 +230,15 @@ async function addClaudeMdReference(projectRoot) {
232
230
  if (await pathExists(claudeMdPath)) {
233
231
  if (!(await readFile(claudeMdPath, "utf-8")).includes(marker)) {
234
232
  await appendFile(claudeMdPath, `\n\n${reference}\n`);
235
- await git.add(claudeMdPath);
236
233
  try {
234
+ await git.add(claudeMdPath);
237
235
  await git.commit("[contentrain] add CLAUDE.md reference");
238
236
  } catch {}
239
237
  }
240
238
  } else {
241
239
  await writeFile(claudeMdPath, `${reference}\n`, "utf-8");
242
- await git.add(claudeMdPath);
243
240
  try {
241
+ await git.add(claudeMdPath);
244
242
  await git.commit("[contentrain] add CLAUDE.md reference");
245
243
  } catch {}
246
244
  }
package/dist/index.mjs CHANGED
@@ -1,25 +1,63 @@
1
1
  #!/usr/bin/env node
2
+ import { i as pc } from "./ui-B8l_LC36.mjs";
2
3
  import { defineCommand, runMain } from "citty";
4
+ //#region package.json
5
+ var version = "0.5.0";
6
+ //#endregion
7
+ //#region src/utils/debug.ts
8
+ /**
9
+ * Debug logging — silent by default, enabled by either the
10
+ * `--debug` flag (recognised per-command) or the
11
+ * `CONTENTRAIN_DEBUG=1` env var. Intended for diagnosing
12
+ * git transactions, MCP calls, and file writes when the
13
+ * user-facing output isn't enough.
14
+ *
15
+ * The tiny helper here is deliberately local — we do not want
16
+ * to ship a logging dependency for something this small.
17
+ * Everything routes through stderr so `--json` stdout payloads
18
+ * stay clean and CI can still capture diagnostic output.
19
+ */
20
+ let enabled = Boolean(process.env["CONTENTRAIN_DEBUG"]);
21
+ /** Turn debug on for the rest of the process. Command runners call
22
+ * this when `--debug` is passed. Once on, stays on. */
23
+ function enableDebug() {
24
+ enabled = true;
25
+ }
26
+ /** Opening banner for a debug block. `context` is a short label — the
27
+ * command name or helper — that lets the reader grep the noise. */
28
+ function debug(context, message) {
29
+ if (!enabled) return;
30
+ process.stderr.write(`${pc.dim(`[debug:${context}]`)} ${message}\n`);
31
+ }
3
32
  //#endregion
4
33
  //#region src/index.ts
34
+ const debugIndex = process.argv.findIndex((a) => a === "--debug");
35
+ if (debugIndex !== -1) {
36
+ enableDebug();
37
+ process.argv.splice(debugIndex, 1);
38
+ }
5
39
  runMain(defineCommand({
6
40
  meta: {
7
41
  name: "contentrain",
8
- version: "0.4.3",
42
+ version,
9
43
  description: "Contentrain CLI — AI content governance infrastructure"
10
44
  },
11
45
  subCommands: {
12
- init: () => import("./init-BVlisNcW.mjs").then((m) => m.default),
13
- status: () => import("./status-CoHgz6HN.mjs").then((m) => m.default),
14
- doctor: () => import("./doctor-DyKjAIKH.mjs").then((m) => m.default),
15
- validate: () => import("./validate-B10L7RL7.mjs").then((m) => m.default),
16
- serve: () => import("./serve-CNeFIQ1i.mjs").then((m) => m.default),
17
- generate: () => import("./generate-DrUojrk8.mjs").then((m) => m.default),
18
- diff: () => import("./diff--halNUpG.mjs").then((m) => m.default),
19
- setup: () => import("./setup-4mS8RuCy.mjs").then((m) => m.default),
20
- skills: () => import("./skills-4nhto0AZ.mjs").then((m) => m.default),
21
- studio: () => import("./studio-CJUGiIMd.mjs").then((m) => m.default)
46
+ init: () => import("./init-540qiNQJ.mjs").then((m) => m.default),
47
+ status: () => import("./status-Bmjfrk-B.mjs").then((m) => m.default),
48
+ doctor: () => import("./doctor-MOf_LQEL.mjs").then((m) => m.default),
49
+ validate: () => import("./validate-BG0X5g09.mjs").then((m) => m.default),
50
+ serve: () => import("./serve-CxA2pKbp.mjs").then((m) => m.default),
51
+ generate: () => import("./generate-ByscV5SN.mjs").then((m) => m.default),
52
+ diff: () => import("./diff-ov1j9bAb.mjs").then((m) => m.default),
53
+ merge: () => import("./merge-DdPRttF-.mjs").then((m) => m.default),
54
+ describe: () => import("./describe-DJ5H9Cxm.mjs").then((m) => m.default),
55
+ "describe-format": () => import("./describe-format-CUAqB0C4.mjs").then((m) => m.default),
56
+ scaffold: () => import("./scaffold-iuvrMh78.mjs").then((m) => m.default),
57
+ setup: () => import("./setup-BMqKibZt.mjs").then((m) => m.default),
58
+ skills: () => import("./skills-CZ54F-97.mjs").then((m) => m.default),
59
+ studio: () => import("./studio-DPVJ-2oG.mjs").then((m) => m.default)
22
60
  }
23
61
  }));
24
62
  //#endregion
25
- export {};
63
+ export { debug as t };
@@ -1,17 +1,17 @@
1
- import { r as resolveProjectRoot, t as loadProjectContext } from "./context-An3xrvcP.mjs";
2
- import { i as pc } from "./ui-B5mXontH.mjs";
3
- import { a as createPackageResolver, c as writeMcpConfig, i as addClaudeMdReference, n as IDE_CONFIGS, o as detectIdes, s as installIdeRulesAndSkills } from "./ide-4ZGiAtYB.mjs";
1
+ import { i as pc } from "./ui-B8l_LC36.mjs";
2
+ import { r as resolveProjectRoot, t as loadProjectContext } from "./context-OskMGy8y.mjs";
3
+ import { a as createPackageResolver, c as writeMcpConfig, i as addClaudeMdReference, n as IDE_CONFIGS, o as detectIdes, s as installIdeRulesAndSkills } from "./ide-DPDVYiaq.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { confirm, intro, isCancel, log, multiselect, outro, select, spinner } from "@clack/prompts";
6
- import { simpleGit } from "simple-git";
7
6
  import { join } from "node:path";
8
7
  import { writeModel } from "@contentrain/mcp/core/model-manager";
9
8
  import { ensureDir, pathExists, writeJson } from "@contentrain/mcp/util/fs";
10
- import { appendFile, readFile, writeFile } from "node:fs/promises";
9
+ import { simpleGit } from "simple-git";
10
+ import { buildBranchName, createTransaction } from "@contentrain/mcp/git/transaction";
11
11
  import { detectStackInfo } from "@contentrain/mcp/util/detect";
12
12
  import { getTemplate, listTemplates } from "@contentrain/mcp/templates";
13
- import { buildBranchName, createTransaction } from "@contentrain/mcp/git/transaction";
14
13
  import { scanSummary } from "@contentrain/mcp/core/scanner";
14
+ import { appendFile, readFile, writeFile } from "node:fs/promises";
15
15
  //#region src/commands/init.ts
16
16
  const COMMON_LOCALES = [
17
17
  {
@@ -1,6 +1,6 @@
1
- import { i as pc } from "./ui-B5mXontH.mjs";
2
- import { i as checkPermissions, o as loadCredentials, s as saveCredentials, t as StudioApiClient } from "./client-C400qHcL.mjs";
3
- import { n as openBrowser, t as startOAuthServer } from "./oauth-server-Beppljfz.mjs";
1
+ import { i as pc } from "./ui-B8l_LC36.mjs";
2
+ import { i as checkPermissions, o as loadCredentials, s as saveCredentials, t as StudioApiClient } from "./client-C9E9D4mb.mjs";
3
+ import { n as openBrowser, t as startOAuthServer } from "./oauth-server-smSxDoH5.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { confirm, intro, isCancel, log, outro, select, spinner } from "@clack/prompts";
6
6
  //#region src/studio/commands/login.ts
@@ -1,5 +1,5 @@
1
- import { i as pc } from "./ui-B5mXontH.mjs";
2
- import { a as clearCredentials, o as loadCredentials, t as StudioApiClient } from "./client-C400qHcL.mjs";
1
+ import { i as pc } from "./ui-B8l_LC36.mjs";
2
+ import { a as clearCredentials, o as loadCredentials, t as StudioApiClient } from "./client-C9E9D4mb.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  import { intro, log, outro } from "@clack/prompts";
5
5
  //#region src/studio/commands/logout.ts
@@ -0,0 +1,44 @@
1
+ import { createServer } from "@contentrain/mcp/server";
2
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
3
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
+ //#region src/utils/mcp-client.ts
5
+ /**
6
+ * Start an in-memory MCP session against a local project root. Always
7
+ * `await session.close()` — typically in a `finally` block — so the
8
+ * underlying transports release cleanly.
9
+ */
10
+ async function openMcpSession(projectRoot) {
11
+ const server = createServer(projectRoot);
12
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
13
+ const client = new Client({
14
+ name: "contentrain-cli",
15
+ version: "1.0.0"
16
+ });
17
+ await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
18
+ return {
19
+ async call(name, args = {}, options = {}) {
20
+ const result = await client.callTool({
21
+ name,
22
+ arguments: args
23
+ });
24
+ const textContent = (result.content ?? []).find((c) => c.type === "text");
25
+ if (!textContent || typeof textContent.text !== "string") throw new Error(`Tool ${name} returned no text content`);
26
+ let parsed;
27
+ try {
28
+ parsed = JSON.parse(textContent.text);
29
+ } catch {
30
+ parsed = textContent.text;
31
+ }
32
+ if (result.isError && options.throwOnError !== false) {
33
+ const msg = typeof parsed === "object" && parsed !== null && "error" in parsed ? String(parsed.error) : textContent.text;
34
+ throw new Error(msg);
35
+ }
36
+ return parsed;
37
+ },
38
+ async close() {
39
+ await Promise.all([client.close().catch(() => {}), server.close().catch(() => {})]);
40
+ }
41
+ };
42
+ }
43
+ //#endregion
44
+ export { openMcpSession as t };
@@ -0,0 +1,82 @@
1
+ import { i as pc } from "./ui-B8l_LC36.mjs";
2
+ import { r as resolveProjectRoot } from "./context-OskMGy8y.mjs";
3
+ import { defineCommand } from "citty";
4
+ import { confirm, intro, isCancel, log, outro } from "@clack/prompts";
5
+ import { simpleGit } from "simple-git";
6
+ import { CONTENTRAIN_BRANCH } from "@contentrain/types";
7
+ import { mergeBranch } from "@contentrain/mcp/git/transaction";
8
+ //#region src/commands/merge.ts
9
+ /**
10
+ * One-shot merge of a single contentrain feature branch.
11
+ *
12
+ * `contentrain diff` drives an interactive review over every pending
13
+ * branch; this command is the scriptable, one-branch sibling —
14
+ * shell-friendly for agents, CI, and manual triage. It reuses the exact
15
+ * same MCP helper (`mergeBranch`) that the interactive command
16
+ * delegates to, so the worktree transaction + selective-sync + dirty-
17
+ * file protections stay on one code path.
18
+ */
19
+ var merge_default = defineCommand({
20
+ meta: {
21
+ name: "merge",
22
+ description: "Merge one contentrain feature branch into the content branch"
23
+ },
24
+ args: {
25
+ branch: {
26
+ type: "positional",
27
+ description: "Feature branch name (e.g. cr/content/blog/...)",
28
+ required: true
29
+ },
30
+ root: {
31
+ type: "string",
32
+ description: "Project root path",
33
+ required: false
34
+ },
35
+ yes: {
36
+ type: "boolean",
37
+ description: "Skip confirmation prompt",
38
+ required: false
39
+ }
40
+ },
41
+ async run({ args }) {
42
+ const projectRoot = await resolveProjectRoot(args.root);
43
+ const branch = String(args.branch);
44
+ intro(pc.bold("contentrain merge"));
45
+ if (branch === CONTENTRAIN_BRANCH) {
46
+ log.error(`Cannot merge the content-tracking branch (${CONTENTRAIN_BRANCH}) into itself.`);
47
+ outro("");
48
+ process.exitCode = 1;
49
+ return;
50
+ }
51
+ if (!(await simpleGit(projectRoot).branchLocal()).all.includes(branch)) {
52
+ log.error(`Branch "${branch}" does not exist locally.`);
53
+ outro("");
54
+ process.exitCode = 1;
55
+ return;
56
+ }
57
+ if (!args.yes) {
58
+ const ok = await confirm({ message: `Merge ${branch} into ${CONTENTRAIN_BRANCH} and fast-forward the base branch?` });
59
+ if (isCancel(ok) || !ok) {
60
+ outro("");
61
+ return;
62
+ }
63
+ }
64
+ try {
65
+ const result = await mergeBranch(projectRoot, branch);
66
+ log.success(`Merged ${branch} (commit ${result.commit.slice(0, 8)})`);
67
+ if (result.sync?.synced?.length) log.message(pc.dim(` Synced ${result.sync.synced.length} file(s) to working tree.`));
68
+ if (result.sync?.skipped?.length) {
69
+ log.warning(`${result.sync.skipped.length} file(s) skipped during sync — you have uncommitted changes:`);
70
+ for (const f of result.sync.skipped) log.message(pc.dim(` ${f}`));
71
+ if (result.sync.warning) log.message(pc.dim(` ${result.sync.warning}`));
72
+ }
73
+ } catch (error) {
74
+ log.error(`Merge failed: ${error instanceof Error ? error.message : String(error)}`);
75
+ process.exitCode = 1;
76
+ return;
77
+ }
78
+ outro("");
79
+ }
80
+ });
81
+ //#endregion
82
+ export { merge_default as default };
@@ -1,4 +1,4 @@
1
- import { c as saveDefaults, o as loadCredentials } from "./client-C400qHcL.mjs";
1
+ import { c as saveDefaults, o as loadCredentials } from "./client-C9E9D4mb.mjs";
2
2
  import { confirm, isCancel, select } from "@clack/prompts";
3
3
  //#region src/studio/resolve-context.ts
4
4
  /**
@@ -0,0 +1,115 @@
1
+ import { i as pc } from "./ui-B8l_LC36.mjs";
2
+ import { r as resolveProjectRoot } from "./context-OskMGy8y.mjs";
3
+ import { t as openMcpSession } from "./mcp-client-DVWTex2i.mjs";
4
+ import { defineCommand } from "citty";
5
+ import { intro, isCancel, log, outro, select, spinner } from "@clack/prompts";
6
+ //#region src/commands/scaffold.ts
7
+ const KNOWN_TEMPLATES = [
8
+ "blog",
9
+ "landing",
10
+ "docs",
11
+ "ecommerce",
12
+ "saas",
13
+ "i18n",
14
+ "mobile"
15
+ ];
16
+ /**
17
+ * Template-based project seeding, wrapping `contentrain_scaffold`.
18
+ *
19
+ * Agents normally drive scaffolding through the MCP tool directly; this
20
+ * command gives humans the same entry point with an interactive
21
+ * template picker and a bit more actionable next-step output.
22
+ */
23
+ var scaffold_default = defineCommand({
24
+ meta: {
25
+ name: "scaffold",
26
+ description: "Apply a Contentrain template (models + sample content + vocabulary)"
27
+ },
28
+ args: {
29
+ root: {
30
+ type: "string",
31
+ description: "Project root path",
32
+ required: false
33
+ },
34
+ template: {
35
+ type: "string",
36
+ description: `Template ID (${KNOWN_TEMPLATES.join(", ")})`,
37
+ required: false
38
+ },
39
+ locales: {
40
+ type: "string",
41
+ description: "Comma-separated locale override (e.g. \"en,tr,de\")",
42
+ required: false
43
+ },
44
+ "no-sample": {
45
+ type: "boolean",
46
+ description: "Skip sample content",
47
+ required: false
48
+ },
49
+ json: {
50
+ type: "boolean",
51
+ description: "Emit raw JSON for scripts",
52
+ required: false
53
+ }
54
+ },
55
+ async run({ args }) {
56
+ const projectRoot = await resolveProjectRoot(args.root);
57
+ let templateId = args.template ? String(args.template) : void 0;
58
+ if (!templateId && !args.json) {
59
+ intro(pc.bold("contentrain scaffold"));
60
+ const pick = await select({
61
+ message: "Choose a template",
62
+ options: KNOWN_TEMPLATES.map((t) => ({
63
+ value: t,
64
+ label: t
65
+ }))
66
+ });
67
+ if (isCancel(pick)) {
68
+ outro("");
69
+ return;
70
+ }
71
+ templateId = pick;
72
+ }
73
+ if (!templateId) {
74
+ log.error("Template is required. Pass --template <id> or run interactively.");
75
+ process.exitCode = 1;
76
+ return;
77
+ }
78
+ const locales = args.locales ? String(args.locales).split(",").map((s) => s.trim()).filter(Boolean) : void 0;
79
+ const session = await openMcpSession(projectRoot);
80
+ const s = args.json ? null : spinner();
81
+ s?.start(`Applying template "${templateId}"...`);
82
+ try {
83
+ const result = await session.call("contentrain_scaffold", {
84
+ template: templateId,
85
+ ...locales ? { locales } : {},
86
+ with_sample_content: !args["no-sample"]
87
+ });
88
+ s?.stop(`Template "${templateId}" applied.`);
89
+ if (args.json) {
90
+ process.stdout.write(JSON.stringify(result, null, 2));
91
+ return;
92
+ }
93
+ const git = result["git"];
94
+ log.success(`Scaffolded ${pc.bold(templateId)} on ${pc.cyan(git?.branch ?? "(branch)")} (${git?.action ?? "committed"})`);
95
+ const models = result["models_created"];
96
+ if (models?.length) log.message(` Models: ${models.map((m) => m.id).join(", ")}`);
97
+ if (typeof result["content_created"] === "number") log.message(` Sample entries: ${result["content_created"]}`);
98
+ if (typeof result["vocabulary_terms_added"] === "number" && result["vocabulary_terms_added"]) log.message(` Vocabulary terms added: ${result["vocabulary_terms_added"]}`);
99
+ const next = result["next_steps"];
100
+ if (next?.length) {
101
+ log.info(pc.bold("\nNext steps"));
102
+ for (const step of next) log.message(` • ${step}`);
103
+ }
104
+ outro("");
105
+ } catch (error) {
106
+ s?.stop("Scaffold failed.");
107
+ log.error(error instanceof Error ? error.message : String(error));
108
+ process.exitCode = 1;
109
+ } finally {
110
+ await session.close();
111
+ }
112
+ }
113
+ });
114
+ //#endregion
115
+ export { scaffold_default as default };