handoff-app 2.0.0-4 → 2.0.0-6

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 (234) hide show
  1. package/AGENTS.md +7 -0
  2. package/CLAUDE.md +7 -0
  3. package/README.md +346 -377
  4. package/dist/app-builder/build.js +11 -4
  5. package/dist/app-builder/client-config.js +1 -0
  6. package/dist/app-builder/nav-shell.js +8 -15
  7. package/dist/app-builder/vercel-output.js +4 -4
  8. package/dist/cli/make.js +2 -2
  9. package/dist/cli.js +38 -6
  10. package/dist/commands/build/app.js +1 -5
  11. package/dist/commands/build/components.js +1 -5
  12. package/dist/commands/build/index.js +1 -5
  13. package/dist/commands/checkout/index.d.ts +15 -12
  14. package/dist/commands/checkout/index.js +15 -54
  15. package/dist/commands/compat/deprecations.d.ts +46 -0
  16. package/dist/commands/compat/deprecations.js +62 -0
  17. package/dist/commands/compat/push-args.d.ts +48 -0
  18. package/dist/commands/compat/push-args.js +73 -0
  19. package/dist/commands/db/migrate.js +1 -5
  20. package/dist/commands/dev/index.js +1 -5
  21. package/dist/commands/eject/config.js +1 -5
  22. package/dist/commands/eject/pages.js +1 -5
  23. package/dist/commands/eject/theme.js +1 -5
  24. package/dist/commands/fetch/index.js +1 -5
  25. package/dist/commands/index.js +4 -0
  26. package/dist/commands/login/index.js +1 -5
  27. package/dist/commands/logout/index.js +1 -5
  28. package/dist/commands/make/component.js +1 -5
  29. package/dist/commands/make/page.js +3 -7
  30. package/dist/commands/make/template.js +1 -5
  31. package/dist/commands/publish/index.d.ts +18 -12
  32. package/dist/commands/publish/index.js +18 -54
  33. package/dist/commands/pull/index.d.ts +13 -0
  34. package/dist/commands/pull/index.js +20 -0
  35. package/dist/commands/push/index.d.ts +23 -0
  36. package/dist/commands/push/index.js +58 -0
  37. package/dist/commands/scaffold/index.js +1 -5
  38. package/dist/commands/start/index.js +1 -5
  39. package/dist/commands/types.d.ts +10 -0
  40. package/dist/commands/utils.d.ts +74 -6
  41. package/dist/commands/utils.js +89 -10
  42. package/dist/commands/validate/components.js +1 -5
  43. package/dist/config/defaults.d.ts +2 -0
  44. package/dist/config/defaults.js +4 -1
  45. package/dist/config/index.d.ts +3 -2
  46. package/dist/config/index.js +3 -1
  47. package/dist/config/loader.d.ts +19 -6
  48. package/dist/config/loader.js +43 -13
  49. package/dist/config/normalizers/page.d.ts +4 -4
  50. package/dist/config/normalizers/page.js +2 -2
  51. package/dist/config/runtime.js +9 -25
  52. package/dist/index.d.ts +69 -22
  53. package/dist/index.js +148 -35
  54. package/dist/nav/resolver.js +3 -1
  55. package/dist/nav/workspace.js +2 -2
  56. package/dist/registry/auth/access-tokens.d.ts +11 -3
  57. package/dist/registry/auth/access-tokens.js +29 -3
  58. package/dist/registry/auth/index.d.ts +4 -3
  59. package/dist/registry/auth/index.js +10 -2
  60. package/dist/registry/auth/sync-secret.d.ts +27 -0
  61. package/dist/registry/auth/sync-secret.js +65 -0
  62. package/dist/registry/auth/types.d.ts +20 -1
  63. package/dist/registry/auth/types.js +3 -1
  64. package/dist/registry/auth/validation.d.ts +11 -0
  65. package/dist/registry/auth/validation.js +18 -1
  66. package/dist/registry/checkout/assets.d.ts +1 -1
  67. package/dist/registry/checkout/assets.js +21 -6
  68. package/dist/registry/checkout/index.d.ts +8 -1
  69. package/dist/registry/checkout/index.js +39 -6
  70. package/dist/registry/checkout/tokens.d.ts +1 -1
  71. package/dist/registry/checkout/tokens.js +12 -5
  72. package/dist/registry/connection.d.ts +10 -0
  73. package/dist/registry/connection.js +52 -7
  74. package/dist/registry/content-kinds.d.ts +27 -0
  75. package/dist/registry/content-kinds.js +33 -0
  76. package/dist/registry/db/client.d.ts +6 -1
  77. package/dist/registry/db/client.js +16 -3
  78. package/dist/registry/db/schema.d.ts +37 -3
  79. package/dist/registry/db/schema.js +9 -7
  80. package/dist/registry/publish/assets.d.ts +2 -2
  81. package/dist/registry/publish/assets.js +46 -35
  82. package/dist/registry/publish/index.d.ts +29 -5
  83. package/dist/registry/publish/index.js +87 -34
  84. package/dist/registry/publish/tokens.d.ts +2 -2
  85. package/dist/registry/publish/tokens.js +30 -24
  86. package/dist/registry/selection.d.ts +12 -0
  87. package/dist/registry/selection.js +22 -0
  88. package/dist/store/registry.d.ts +48 -0
  89. package/dist/store/registry.js +75 -12
  90. package/dist/transformers/preview/types.d.ts +2 -2
  91. package/dist/types/config.d.ts +7 -0
  92. package/dist/utils/markdown.d.ts +10 -0
  93. package/dist/utils/markdown.js +17 -0
  94. package/dist/utils/menu-shell.js +14 -7
  95. package/dist/utils/pages.d.ts +22 -0
  96. package/dist/utils/pages.js +73 -0
  97. package/dist/utils/path.d.ts +5 -0
  98. package/dist/utils/path.js +9 -0
  99. package/docs/api.md +1 -3
  100. package/docs/cli.md +1 -1
  101. package/drizzle/0002_add_pages.sql +5 -3
  102. package/drizzle/meta/0002_snapshot.json +17 -8
  103. package/drizzle/meta/0003_snapshot.json +17 -8
  104. package/drizzle/meta/0004_snapshot.json +17 -8
  105. package/drizzle/meta/0005_snapshot.json +17 -8
  106. package/package.json +4 -2
  107. package/src/app/components/CopyToClipboard.tsx +27 -26
  108. package/src/app/components/Layout/Header.tsx +4 -12
  109. package/src/app/components/Layout/RuntimeModeBadge.tsx +18 -0
  110. package/src/app/components/McpIntegration/McpConfigDialog.tsx +349 -0
  111. package/src/app/components/McpIntegration/McpIcon.tsx +10 -0
  112. package/src/app/components/Navigation/MobileNav.tsx +3 -1
  113. package/src/app/components/ui/dialog.tsx +73 -0
  114. package/src/app/components/util/index.ts +5 -23
  115. package/src/app/lib/docs-api/backend.ts +16 -1
  116. package/src/app/lib/docs-api/errors.ts +8 -7
  117. package/src/app/lib/docs-api/handler.ts +2 -3
  118. package/src/app/lib/docs-api/index.ts +2 -0
  119. package/src/app/lib/docs-api/page-discovery.ts +121 -0
  120. package/src/app/lib/docs-api/page-rendering.ts +37 -0
  121. package/src/app/lib/docs-api/page-search.ts +191 -0
  122. package/src/app/lib/docs-api/records.ts +31 -24
  123. package/src/app/lib/docs-api/registry.ts +92 -11
  124. package/src/app/lib/docs-api/runtime-config.ts +5 -0
  125. package/src/app/lib/docs-api/search.ts +120 -0
  126. package/src/app/lib/mcp/auth.ts +64 -0
  127. package/src/app/lib/mcp/index.ts +2 -0
  128. package/src/app/lib/mcp/server.ts +343 -0
  129. package/src/app/lib/mcp/shape.ts +595 -0
  130. package/src/app/lib/registry-api/allowlist.ts +7 -5
  131. package/src/app/lib/registry-api/asset-transfer.ts +58 -31
  132. package/src/app/lib/registry-api/auth.ts +89 -9
  133. package/src/app/lib/registry-api/entity-routes.ts +17 -7
  134. package/src/app/lib/registry-api/errors.ts +10 -4
  135. package/src/app/lib/registry-api/handler.ts +13 -15
  136. package/src/app/lib/registry-api/meta.ts +4 -0
  137. package/src/app/lib/registry-api/revalidate.ts +31 -12
  138. package/src/app/lib/registry-api/store.ts +89 -26
  139. package/src/app/lib/registry-api/sync-compat.ts +166 -0
  140. package/src/app/lib/registry-api/token-transfer.ts +44 -12
  141. package/src/app/lib/registry-api/transfer-batch.ts +116 -0
  142. package/src/app/lib/registry-api/transfer.ts +62 -35
  143. package/src/app/lib/registry-api/validation.ts +33 -1
  144. package/src/app/lib/registry-connection.ts +1 -1
  145. package/src/app/next.config.mjs +9 -0
  146. package/src/app/pages/[...slug]/index.tsx +52 -51
  147. package/src/app/pages/api/account/tokens/index.ts +4 -13
  148. package/src/app/pages/api/docs/search/pages.json.ts +25 -0
  149. package/src/app/pages/api/mcp.ts +56 -0
  150. package/src/app/pages/api/oauth/revoke.ts +14 -7
  151. package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +1 -1
  152. package/src/app/pages/api/registry/components/[id].ts +2 -2
  153. package/src/app/pages/api/registry/components/index.ts +1 -1
  154. package/src/app/pages/api/registry/pages/[...id].ts +20 -0
  155. package/src/app/pages/api/registry/pages/index.ts +19 -0
  156. package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +1 -1
  157. package/src/app/pages/api/registry/patterns/[id].ts +2 -2
  158. package/src/app/pages/api/registry/patterns/index.ts +1 -1
  159. package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +2 -2
  160. package/src/app/pages/api/sync/upload.ts +21 -0
  161. package/src/app/pages/index/index.tsx +14 -0
  162. package/src/app/pages/index.tsx +31 -5
  163. package/src/app-builder/build.ts +11 -4
  164. package/src/app-builder/client-config.ts +2 -1
  165. package/src/app-builder/nav-shell.ts +8 -13
  166. package/src/app-builder/vercel-output.ts +4 -4
  167. package/src/cli/make.ts +2 -2
  168. package/src/cli.ts +30 -6
  169. package/src/commands/build/app.ts +2 -3
  170. package/src/commands/build/components.ts +2 -3
  171. package/src/commands/build/index.ts +2 -3
  172. package/src/commands/checkout/index.ts +18 -44
  173. package/src/commands/compat/deprecations.ts +79 -0
  174. package/src/commands/compat/push-args.ts +106 -0
  175. package/src/commands/db/migrate.ts +2 -3
  176. package/src/commands/dev/index.ts +2 -3
  177. package/src/commands/eject/config.ts +2 -3
  178. package/src/commands/eject/pages.ts +2 -3
  179. package/src/commands/eject/theme.ts +2 -3
  180. package/src/commands/fetch/index.ts +2 -3
  181. package/src/commands/index.ts +4 -0
  182. package/src/commands/login/index.ts +2 -3
  183. package/src/commands/logout/index.ts +2 -3
  184. package/src/commands/make/component.ts +2 -3
  185. package/src/commands/make/page.ts +4 -5
  186. package/src/commands/make/template.ts +2 -3
  187. package/src/commands/publish/index.ts +21 -44
  188. package/src/commands/pull/index.ts +32 -0
  189. package/src/commands/push/index.ts +115 -0
  190. package/src/commands/scaffold/index.ts +2 -3
  191. package/src/commands/start/index.ts +2 -3
  192. package/src/commands/types.ts +11 -1
  193. package/src/commands/utils.ts +80 -11
  194. package/src/commands/validate/components.ts +2 -3
  195. package/src/config/defaults.ts +3 -0
  196. package/src/config/index.ts +3 -2
  197. package/src/config/loader.ts +57 -12
  198. package/src/config/normalizers/page.ts +4 -4
  199. package/src/config/runtime.ts +9 -24
  200. package/src/index.ts +183 -42
  201. package/src/nav/resolver.ts +3 -1
  202. package/src/nav/workspace.ts +2 -2
  203. package/src/registry/auth/access-tokens.ts +29 -5
  204. package/src/registry/auth/index.ts +7 -1
  205. package/src/registry/auth/sync-secret.ts +67 -0
  206. package/src/registry/auth/types.ts +24 -1
  207. package/src/registry/auth/validation.ts +18 -0
  208. package/src/registry/checkout/assets.ts +24 -7
  209. package/src/registry/checkout/index.ts +41 -5
  210. package/src/registry/checkout/tokens.ts +12 -6
  211. package/src/registry/connection.ts +53 -5
  212. package/src/registry/content-kinds.ts +39 -0
  213. package/src/registry/db/client.ts +22 -2
  214. package/src/registry/db/schema.ts +9 -7
  215. package/src/registry/publish/assets.ts +47 -37
  216. package/src/registry/publish/index.ts +102 -38
  217. package/src/registry/publish/tokens.ts +35 -26
  218. package/src/registry/selection.ts +19 -0
  219. package/src/store/registry.ts +118 -13
  220. package/src/transformers/preview/types.ts +2 -2
  221. package/src/types/config.ts +7 -0
  222. package/src/utils/README.md +1 -0
  223. package/src/utils/markdown.ts +11 -0
  224. package/src/utils/menu-shell.ts +14 -7
  225. package/src/utils/pages.ts +52 -0
  226. package/src/utils/path.ts +15 -6
  227. package/templates/default/README.md.tpl +10 -0
  228. package/templates/default/handoff.config.js.tpl +6 -3
  229. package/templates/default/handoff.config.ts.tpl +6 -3
  230. package/templates/default/handoff.config.with-examples.js.tpl +6 -3
  231. package/templates/default/handoff.config.with-examples.ts.tpl +6 -3
  232. package/templates/default/package.json.tpl +4 -0
  233. package/config/docs/foundations/logos.md +0 -7
  234. package/src/app/lib/api/redact.ts +0 -46
@@ -1,10 +1,15 @@
1
1
  import { Argv } from 'yargs';
2
- import type { TransferEntityKind } from '../registry/transfer';
3
- /** Plural entity kinds accepted by the `publish`/`checkout` commands. */
4
- export declare const REGISTRY_ENTITY_KINDS: readonly ["components", "patterns", "pages", "tokens", "assets"];
5
- export type RegistryEntityKind = (typeof REGISTRY_ENTITY_KINDS)[number];
6
- /** Map the plural CLI kind to the singular wire kind used by entity publish/checkout. */
7
- export declare const ENTITY_WIRE_KIND: Record<'components' | 'patterns' | 'pages', TransferEntityKind>;
2
+ import Handoff from '../';
3
+ import { SharedArgs } from './types';
4
+ /** The `type` argument accepted by publish/checkout: any content kind, or `all` for every kind. */
5
+ export declare const REGISTRY_TARGET_KINDS: readonly ["components", "patterns", "pages", "tokens", "assets", "all"];
6
+ export type RegistryTargetKind = (typeof REGISTRY_TARGET_KINDS)[number];
7
+ export declare const createHandoff: (args: SharedArgs) => Handoff;
8
+ /**
9
+ * Run a registry command against a fresh Handoff. Registry failures are the user's to act on, so they
10
+ * surface as one actionable line and a non-zero exit rather than a stack trace.
11
+ */
12
+ export declare const runRegistryCommand: (args: SharedArgs, run: (handoff: Handoff) => Promise<unknown>) => Promise<void>;
8
13
  export declare const getSharedOptions: (yargs: Argv) => Argv<import("yargs").Omit<{}, "debug" | "config" | "force"> & import("yargs").InferredOptionTypes<{
9
14
  config: {
10
15
  alias: string;
@@ -22,3 +27,66 @@ export declare const getSharedOptions: (yargs: Argv) => Argv<import("yargs").Omi
22
27
  description: string;
23
28
  };
24
29
  }>>;
30
+ /** Shared options plus the transfer options: publish and its compatibility aliases use these. */
31
+ export declare const getPublishOptions: (yargs: Argv) => Argv<import("yargs").Omit<import("yargs").Omit<{}, "debug" | "config" | "force"> & import("yargs").InferredOptionTypes<{
32
+ config: {
33
+ alias: string;
34
+ type: "string";
35
+ description: string;
36
+ };
37
+ force: {
38
+ alias: string;
39
+ type: "boolean";
40
+ description: string;
41
+ };
42
+ debug: {
43
+ alias: string;
44
+ type: "boolean";
45
+ description: string;
46
+ };
47
+ }>, "build" | "dry-run"> & import("yargs").InferredOptionTypes<{
48
+ 'dry-run': {
49
+ type: "boolean";
50
+ description: string;
51
+ };
52
+ build: {
53
+ type: "boolean";
54
+ default: boolean;
55
+ description: string;
56
+ };
57
+ }>>;
58
+ /** Shared options plus `--dry-run`: checkout and its compatibility aliases use these. */
59
+ export declare const getCheckoutOptions: (yargs: Argv) => Argv<import("yargs").Omit<import("yargs").Omit<{}, "debug" | "config" | "force"> & import("yargs").InferredOptionTypes<{
60
+ config: {
61
+ alias: string;
62
+ type: "string";
63
+ description: string;
64
+ };
65
+ force: {
66
+ alias: string;
67
+ type: "boolean";
68
+ description: string;
69
+ };
70
+ debug: {
71
+ alias: string;
72
+ type: "boolean";
73
+ description: string;
74
+ };
75
+ }>, "dry-run"> & import("yargs").InferredOptionTypes<{
76
+ 'dry-run': {
77
+ type: "boolean";
78
+ description: string;
79
+ };
80
+ }>>;
81
+ /**
82
+ * Positional `type` + variadic `id`, shared by publish, checkout, and their compatibility aliases.
83
+ * `choices` is always declared: yargs allows an omitted optional positional while still rejecting an
84
+ * unrecognized one, so the deprecated commands get the same guard as the canonical ones.
85
+ */
86
+ export declare const withTargetPositionals: (yargs: Argv, verb: "publish" | "checkout") => Argv<{
87
+ type: "pages" | "components" | "patterns" | "assets" | "tokens" | "all";
88
+ } & {
89
+ id: string[];
90
+ }>;
91
+ /** Dispatch one `type` argument to the matching Handoff entry point. */
92
+ export declare const runTarget: (handoff: Handoff, type: RegistryTargetKind, ids: string[] | undefined, verb: "publish" | "checkout") => Promise<Handoff>;
@@ -1,20 +1,53 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getSharedOptions = exports.ENTITY_WIRE_KIND = exports.REGISTRY_ENTITY_KINDS = void 0;
4
- /** Plural entity kinds accepted by the `publish`/`checkout` commands. */
5
- exports.REGISTRY_ENTITY_KINDS = ['components', 'patterns', 'pages', 'tokens', 'assets'];
6
- /** Map the plural CLI kind to the singular wire kind used by entity publish/checkout. */
7
- exports.ENTITY_WIRE_KIND = {
8
- components: 'component',
9
- patterns: 'pattern',
10
- pages: 'page',
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
13
  };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.runTarget = exports.withTargetPositionals = exports.getCheckoutOptions = exports.getPublishOptions = exports.getSharedOptions = exports.runRegistryCommand = exports.createHandoff = exports.REGISTRY_TARGET_KINDS = void 0;
16
+ const __1 = __importDefault(require("../"));
17
+ const content_kinds_1 = require("../registry/content-kinds");
18
+ const logger_1 = require("../utils/logger");
19
+ /** The `type` argument accepted by publish/checkout: any content kind, or `all` for every kind. */
20
+ exports.REGISTRY_TARGET_KINDS = [...content_kinds_1.REGISTRY_ENTITY_KINDS, 'all'];
21
+ const createHandoff = (args) => new __1.default({
22
+ debug: args.debug,
23
+ force: args.force,
24
+ configPath: args.config,
25
+ dryRun: args.dryRun,
26
+ // yargs gives `--no-build` as `build: false`; anything else leaves the build in place.
27
+ skipBuild: args.build === false,
28
+ });
29
+ exports.createHandoff = createHandoff;
30
+ /**
31
+ * Run a registry command against a fresh Handoff. Registry failures are the user's to act on, so they
32
+ * surface as one actionable line and a non-zero exit rather than a stack trace.
33
+ */
34
+ const runRegistryCommand = (args, run) => __awaiter(void 0, void 0, void 0, function* () {
35
+ const handoff = (0, exports.createHandoff)(args);
36
+ try {
37
+ yield run(handoff);
38
+ }
39
+ catch (error) {
40
+ logger_1.Logger.error(error instanceof Error ? error.message : String(error));
41
+ process.exitCode = 1;
42
+ }
43
+ });
44
+ exports.runRegistryCommand = runRegistryCommand;
12
45
  const getSharedOptions = (yargs) => {
13
46
  return yargs.options({
14
47
  config: {
15
48
  alias: 'c',
16
49
  type: 'string',
17
- description: 'Path to config file',
50
+ description: 'Path to config file, relative to the working directory',
18
51
  },
19
52
  force: {
20
53
  alias: 'f',
@@ -29,3 +62,49 @@ const getSharedOptions = (yargs) => {
29
62
  });
30
63
  };
31
64
  exports.getSharedOptions = getSharedOptions;
65
+ /** Shared options plus the transfer options: publish and its compatibility aliases use these. */
66
+ const getPublishOptions = (yargs) => (0, exports.getSharedOptions)(yargs).options({
67
+ 'dry-run': {
68
+ type: 'boolean',
69
+ description: 'Report what would be published without contacting the registry (the build still runs; add --no-build to skip it)',
70
+ },
71
+ build: {
72
+ type: 'boolean',
73
+ default: true,
74
+ description: 'Run a fresh build first; use --no-build to publish the existing output',
75
+ },
76
+ });
77
+ exports.getPublishOptions = getPublishOptions;
78
+ /** Shared options plus `--dry-run`: checkout and its compatibility aliases use these. */
79
+ const getCheckoutOptions = (yargs) => (0, exports.getSharedOptions)(yargs).options({
80
+ 'dry-run': {
81
+ type: 'boolean',
82
+ description: 'Report what would be written without changing any workspace file',
83
+ },
84
+ });
85
+ exports.getCheckoutOptions = getCheckoutOptions;
86
+ /**
87
+ * Positional `type` + variadic `id`, shared by publish, checkout, and their compatibility aliases.
88
+ * `choices` is always declared: yargs allows an omitted optional positional while still rejecting an
89
+ * unrecognized one, so the deprecated commands get the same guard as the canonical ones.
90
+ */
91
+ const withTargetPositionals = (yargs, verb) => yargs
92
+ .positional('type', {
93
+ describe: `The kind of content to ${verb}, or "all" for every kind`,
94
+ choices: exports.REGISTRY_TARGET_KINDS,
95
+ type: 'string',
96
+ })
97
+ .positional('id', {
98
+ describe: `The stable id (component/pattern/page id, token set id, or asset collection); omit to ${verb} all of that kind`,
99
+ type: 'string',
100
+ array: true,
101
+ });
102
+ exports.withTargetPositionals = withTargetPositionals;
103
+ /** Dispatch one `type` argument to the matching Handoff entry point. */
104
+ const runTarget = (handoff, type, ids, verb) => {
105
+ if (type === 'all') {
106
+ return verb === 'publish' ? handoff.publishAll() : handoff.checkoutAll();
107
+ }
108
+ return verb === 'publish' ? handoff.publishKind(type, ids) : handoff.checkoutKind(type, ids);
109
+ };
110
+ exports.runTarget = runTarget;
@@ -8,11 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
- const __1 = __importDefault(require("../../"));
16
12
  const utils_1 = require("../utils");
17
13
  const command = {
18
14
  command: 'validate:components',
@@ -27,7 +23,7 @@ const command = {
27
23
  },
28
24
  handler: (args) => __awaiter(void 0, void 0, void 0, function* () {
29
25
  var _a;
30
- const handoff = new __1.default(args.debug, args.force);
26
+ const handoff = (0, utils_1.createHandoff)(args);
31
27
  yield handoff.validateComponents((_a = args.skipBuild) !== null && _a !== void 0 ? _a : false);
32
28
  }),
33
29
  };
@@ -6,6 +6,8 @@ export interface ImageStyle {
6
6
  width: number;
7
7
  description: string;
8
8
  }
9
+ /** Whether the MCP endpoint is served. On unless `runtime.mcp` is explicitly `false`. */
10
+ export declare const isMcpEnabled: (config: Config | null | undefined) => boolean;
9
11
  export declare const defaultConfig: () => Config;
10
12
  /**
11
13
  * Retrieves the client configuration from the provided handoff configuration.
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getClientConfig = exports.defaultConfig = void 0;
3
+ exports.getClientConfig = exports.defaultConfig = exports.isMcpEnabled = void 0;
4
4
  const connection_1 = require("../registry/connection");
5
+ /** Whether the MCP endpoint is served. On unless `runtime.mcp` is explicitly `false`. */
6
+ const isMcpEnabled = (config) => { var _a; return ((_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.mcp) !== false; };
7
+ exports.isMcpEnabled = isMcpEnabled;
5
8
  const defaultConfig = () => {
6
9
  var _a, _b, _c, _d;
7
10
  return ({
@@ -1,6 +1,7 @@
1
- export { defaultConfig, getClientConfig } from './defaults';
1
+ export { defaultConfig, getClientConfig, isMcpEnabled } from './defaults';
2
2
  export type { ImageStyle } from './defaults';
3
- export { initConfig, initConfigWithMetadata } from './loader';
3
+ export { HandoffConfigError, initConfig, initConfigWithMetadata } from './loader';
4
+ export type { ConfigLoadContext } from './loader';
4
5
  export { defineConfig } from './helpers';
5
6
  export { initRuntimeConfig } from './runtime';
6
7
  export { validateConfig } from './validator';
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writeEntries = exports.isEntryCovered = exports.validateConfig = exports.initRuntimeConfig = exports.defineConfig = exports.initConfigWithMetadata = exports.initConfig = exports.getClientConfig = exports.defaultConfig = void 0;
3
+ exports.writeEntries = exports.isEntryCovered = exports.validateConfig = exports.initRuntimeConfig = exports.defineConfig = exports.initConfigWithMetadata = exports.initConfig = exports.HandoffConfigError = exports.isMcpEnabled = exports.getClientConfig = exports.defaultConfig = void 0;
4
4
  // Config defaults and client config
5
5
  var defaults_1 = require("./defaults");
6
6
  Object.defineProperty(exports, "defaultConfig", { enumerable: true, get: function () { return defaults_1.defaultConfig; } });
7
7
  Object.defineProperty(exports, "getClientConfig", { enumerable: true, get: function () { return defaults_1.getClientConfig; } });
8
+ Object.defineProperty(exports, "isMcpEnabled", { enumerable: true, get: function () { return defaults_1.isMcpEnabled; } });
8
9
  // Config file loading
9
10
  var loader_1 = require("./loader");
11
+ Object.defineProperty(exports, "HandoffConfigError", { enumerable: true, get: function () { return loader_1.HandoffConfigError; } });
10
12
  Object.defineProperty(exports, "initConfig", { enumerable: true, get: function () { return loader_1.initConfig; } });
11
13
  Object.defineProperty(exports, "initConfigWithMetadata", { enumerable: true, get: function () { return loader_1.initConfigWithMetadata; } });
12
14
  // Config helpers
@@ -1,20 +1,33 @@
1
1
  import { Config } from '../types/config';
2
+ /** Raised when an explicitly requested config file cannot be used. */
3
+ export declare class HandoffConfigError extends Error {
4
+ constructor(message: string);
5
+ }
2
6
  type ConfigLoadResult = {
3
7
  config: Config;
4
8
  configPath?: string;
5
9
  };
10
+ /** Where to look for the config: the working directory, plus an optional explicit file. */
11
+ export type ConfigLoadContext = {
12
+ /** Directory relative config paths resolve from, and the root that gets searched. */
13
+ workingPath?: string;
14
+ /** Explicit config file (the CLI's `-c, --config`), replacing discovery entirely. */
15
+ configPath?: string;
16
+ };
6
17
  /**
7
- * Loads the handoff configuration from the project root and returns metadata.
18
+ * Loads the handoff configuration for the given working directory and returns metadata.
8
19
  */
9
- export declare const initConfigWithMetadata: (configOverride?: Partial<Config>) => ConfigLoadResult;
20
+ export declare const initConfigWithMetadata: (configOverride?: Partial<Config>, context?: ConfigLoadContext) => ConfigLoadResult;
10
21
  /**
11
- * Loads the handoff configuration from the project root.
22
+ * Loads the handoff configuration for the given working directory.
12
23
  *
13
- * Searches for config files in order: handoff.config.ts, handoff.config.js, handoff.config.cjs, handoff.config.json.
14
- * Merges file config with any provided overrides, then applies defaults for missing values.
24
+ * Searches for config files in order: handoff.config.ts, handoff.config.js, handoff.config.cjs, handoff.config.json,
25
+ * unless `context.configPath` names one explicitly. Merges file config with any provided overrides,
26
+ * then applies defaults for missing values.
15
27
  *
16
28
  * @param configOverride - Optional partial config to override file-loaded values.
29
+ * @param context - Optional working directory and explicit config file to load.
17
30
  * @returns The fully resolved Config object.
18
31
  */
19
- export declare const initConfig: (configOverride?: Partial<Config>) => Config;
32
+ export declare const initConfig: (configOverride?: Partial<Config>, context?: ConfigLoadContext) => Config;
20
33
  export {};
@@ -3,13 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.initConfig = exports.initConfigWithMetadata = void 0;
6
+ exports.initConfig = exports.initConfigWithMetadata = exports.HandoffConfigError = void 0;
7
7
  const esbuild_1 = __importDefault(require("esbuild"));
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const merge_1 = __importDefault(require("lodash/merge"));
10
10
  const module_1 = require("module");
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const logger_1 = require("../utils/logger");
13
+ const path_2 = require("../utils/path");
13
14
  const defaults_1 = require("./defaults");
14
15
  const CONFIG_FILE_PREFERENCE = [
15
16
  'handoff.config.ts',
@@ -17,6 +18,16 @@ const CONFIG_FILE_PREFERENCE = [
17
18
  'handoff.config.cjs',
18
19
  'handoff.config.json',
19
20
  ];
21
+ /** Extensions the loader knows how to evaluate, derived from the discoverable file names. */
22
+ const SUPPORTED_CONFIG_EXTENSIONS = Array.from(new Set(CONFIG_FILE_PREFERENCE.map((fileName) => path_1.default.extname(fileName))));
23
+ /** Raised when an explicitly requested config file cannot be used. */
24
+ class HandoffConfigError extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = 'HandoffConfigError';
28
+ }
29
+ }
30
+ exports.HandoffConfigError = HandoffConfigError;
20
31
  const evaluateTypeScriptConfig = (filePath, handoffModulePath) => {
21
32
  var _a, _b;
22
33
  const buildResult = esbuild_1.default.buildSync({
@@ -49,11 +60,12 @@ const evaluateTypeScriptConfig = (filePath, handoffModulePath) => {
49
60
  return mod.exports;
50
61
  };
51
62
  const loadConfigFile = (configPath) => {
52
- if (configPath.endsWith('.json')) {
63
+ const extension = path_1.default.extname(configPath);
64
+ if (extension === '.json') {
53
65
  const buffer = fs_extra_1.default.readFileSync(configPath);
54
66
  return JSON.parse(buffer.toString());
55
67
  }
56
- if (configPath.endsWith('.ts')) {
68
+ if (extension === '.ts') {
57
69
  const handoffModulePath = path_1.default.resolve(__dirname, '../..');
58
70
  const importedConfig = evaluateTypeScriptConfig(configPath, handoffModulePath);
59
71
  return (importedConfig.default || importedConfig);
@@ -63,9 +75,24 @@ const loadConfigFile = (configPath) => {
63
75
  const importedConfig = require(configPath);
64
76
  return (importedConfig.default || importedConfig);
65
77
  };
66
- const resolveConfigFilePath = () => {
78
+ /**
79
+ * Picks the config file to load. An explicitly requested file replaces discovery - it is resolved
80
+ * from `workingPath` and must exist in a format the loader can evaluate. Otherwise every known
81
+ * config name is looked for in `workingPath`, the first match winning.
82
+ */
83
+ const resolveConfigFilePath = (workingPath, requestedPath) => {
84
+ if (requestedPath) {
85
+ const selected = path_1.default.resolve(workingPath, requestedPath);
86
+ if (!fs_extra_1.default.existsSync(selected)) {
87
+ throw new HandoffConfigError(`Config file not found: "${requestedPath}" (resolved to ${selected})`);
88
+ }
89
+ if (!SUPPORTED_CONFIG_EXTENSIONS.includes(path_1.default.extname(selected))) {
90
+ throw new HandoffConfigError(`Unsupported config file "${path_1.default.basename(selected)}". Supported extensions: ${SUPPORTED_CONFIG_EXTENSIONS.join(', ')}`);
91
+ }
92
+ return { selected, ignored: [] };
93
+ }
67
94
  const existing = CONFIG_FILE_PREFERENCE
68
- .map((fileName) => path_1.default.resolve(process.cwd(), fileName))
95
+ .map((fileName) => path_1.default.resolve(workingPath, fileName))
69
96
  .filter((filePath) => fs_extra_1.default.existsSync(filePath));
70
97
  if (!existing.length) {
71
98
  return { selected: undefined, ignored: [] };
@@ -74,11 +101,12 @@ const resolveConfigFilePath = () => {
74
101
  return { selected, ignored };
75
102
  };
76
103
  /**
77
- * Loads the handoff configuration from the project root and returns metadata.
104
+ * Loads the handoff configuration for the given working directory and returns metadata.
78
105
  */
79
- const initConfigWithMetadata = (configOverride) => {
106
+ const initConfigWithMetadata = (configOverride, context) => {
107
+ var _a;
80
108
  let config = {};
81
- const { selected: configPath, ignored } = resolveConfigFilePath();
109
+ const { selected: configPath, ignored } = resolveConfigFilePath((_a = context === null || context === void 0 ? void 0 : context.workingPath) !== null && _a !== void 0 ? _a : (0, path_2.resolveWorkingPath)(), context === null || context === void 0 ? void 0 : context.configPath);
82
110
  if (ignored.length > 0 && configPath) {
83
111
  logger_1.Logger.warn(`Multiple config files found. Using "${path_1.default.basename(configPath)}" and ignoring: ${ignored
84
112
  .map((filePath) => `"${path_1.default.basename(filePath)}"`)
@@ -104,15 +132,17 @@ const initConfigWithMetadata = (configOverride) => {
104
132
  };
105
133
  exports.initConfigWithMetadata = initConfigWithMetadata;
106
134
  /**
107
- * Loads the handoff configuration from the project root.
135
+ * Loads the handoff configuration for the given working directory.
108
136
  *
109
- * Searches for config files in order: handoff.config.ts, handoff.config.js, handoff.config.cjs, handoff.config.json.
110
- * Merges file config with any provided overrides, then applies defaults for missing values.
137
+ * Searches for config files in order: handoff.config.ts, handoff.config.js, handoff.config.cjs, handoff.config.json,
138
+ * unless `context.configPath` names one explicitly. Merges file config with any provided overrides,
139
+ * then applies defaults for missing values.
111
140
  *
112
141
  * @param configOverride - Optional partial config to override file-loaded values.
142
+ * @param context - Optional working directory and explicit config file to load.
113
143
  * @returns The fully resolved Config object.
114
144
  */
115
- const initConfig = (configOverride) => {
116
- return (0, exports.initConfigWithMetadata)(configOverride).config;
145
+ const initConfig = (configOverride, context) => {
146
+ return (0, exports.initConfigWithMetadata)(configOverride, context).config;
117
147
  };
118
148
  exports.initConfig = initConfig;
@@ -1,18 +1,18 @@
1
1
  import { PageListObject } from '../../transformers/preview/types';
2
2
  type RawPageFrontmatter = Record<string, any>;
3
3
  type NormalizePageOptions = {
4
- /** The page id — its slug path (e.g. `guides/setup`), derived from the file location. */
4
+ /** The page id derived from its file location (`index` is reserved for the root home page). */
5
5
  id: string;
6
6
  /** Logical route the page is served at (e.g. `/guides/setup`). */
7
7
  routePath: string;
8
8
  /** Absolute filesystem path of the source `.md` (workspace-only, stripped on publish). */
9
- sourcePath: string;
9
+ sourcePath?: string;
10
10
  };
11
11
  /**
12
12
  * Normalize a page's parsed markdown frontmatter into a {@link PageListObject}. Pages have no
13
13
  * required authored fields (a bare `.md` is valid); the title falls back to a humanized last slug
14
- * segment. The id is always the slug path (a page is addressed by its URL, so id ≡ route): the file
15
- * location is the single source of identity for both filesystem and registry serving.
14
+ * segment. The file location is the source of identity for filesystem and registry serving. Normal
15
+ * page ids match their routes; the reserved `index` id is served at `/`.
16
16
  */
17
17
  export declare const normalizePageDeclaration: (raw: RawPageFrontmatter, options: NormalizePageOptions) => PageListObject;
18
18
  export {};
@@ -9,8 +9,8 @@ const humanize = (segment) => segment
9
9
  /**
10
10
  * Normalize a page's parsed markdown frontmatter into a {@link PageListObject}. Pages have no
11
11
  * required authored fields (a bare `.md` is valid); the title falls back to a humanized last slug
12
- * segment. The id is always the slug path (a page is addressed by its URL, so id ≡ route): the file
13
- * location is the single source of identity for both filesystem and registry serving.
12
+ * segment. The file location is the source of identity for filesystem and registry serving. Normal
13
+ * page ids match their routes; the reserved `index` id is served at `/`.
14
14
  */
15
15
  const normalizePageDeclaration = (raw, options) => {
16
16
  var _a;
@@ -6,12 +6,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.toLowerCaseKeysAndValues = exports.getItemsForPath = exports.getComponentsForPath = exports.isComponentDirectory = exports.initRuntimeConfig = exports.resolveComponentDeclaration = void 0;
7
7
  const esbuild_1 = __importDefault(require("esbuild"));
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
- const gray_matter_1 = __importDefault(require("gray-matter"));
10
9
  const module_1 = require("module");
11
10
  const path_1 = __importDefault(require("path"));
11
+ const content_kinds_1 = require("../registry/content-kinds");
12
12
  const csf_1 = require("../transformers/utils/csf");
13
13
  const module_2 = require("../transformers/utils/module");
14
14
  const logger_1 = require("../utils/logger");
15
+ const markdown_1 = require("../utils/markdown");
16
+ const pages_1 = require("../utils/pages");
15
17
  const path_2 = require("../utils/path");
16
18
  const declaration_1 = require("./normalizers/declaration");
17
19
  const page_1 = require("./normalizers/page");
@@ -240,15 +242,16 @@ const initRuntimeConfig = (handoff) => {
240
242
  // Discover markdown pages
241
243
  // -------------------------------------------------------------------------
242
244
  // Working pages under `<workingPath>/pages/` are first-class registry entities (publish/checkout).
243
- // Discovery mirrors the catch-all/static-path walk: every `.md` except `index.md` becomes a page
244
- // keyed by its slug path. Package `config/docs` defaults are NOT entities (they ship with the tool).
245
+ // The root `index.md` uses the stable id `index` but is served at `/`; nested index files remain
246
+ // excluded because section indexes are owned by dedicated routes. Package defaults are not entities.
245
247
  const pagesRoot = path_1.default.resolve(handoff.workingPath, 'pages');
246
- for (const segments of collectPageMarkdownPaths(pagesRoot)) {
248
+ for (const segments of (0, pages_1.collectPageSlugSegments)(pagesRoot, { includeRootIndex: true })) {
247
249
  const slug = segments.join('/');
248
250
  const sourcePath = path_1.default.resolve(pagesRoot, `${slug}.md`);
249
251
  try {
250
- const { data: frontmatter } = (0, gray_matter_1.default)(fs_extra_1.default.readFileSync(sourcePath, 'utf-8'));
251
- const page = (0, page_1.normalizePageDeclaration)(frontmatter, { id: slug, routePath: `/${slug}`, sourcePath });
252
+ const { data: frontmatter } = (0, markdown_1.parseMarkdown)(fs_extra_1.default.readFileSync(sourcePath, 'utf-8'));
253
+ const routePath = slug === content_kinds_1.HOME_PAGE_ID ? content_kinds_1.HOME_PAGE_PATH : `/${slug}`;
254
+ const page = (0, page_1.normalizePageDeclaration)(frontmatter, { id: slug, routePath, sourcePath });
252
255
  result.entries.pages[page.id] = page;
253
256
  }
254
257
  catch (err) {
@@ -263,25 +266,6 @@ const initRuntimeConfig = (handoff) => {
263
266
  return [result, Array.from(configFiles), configFileIndex];
264
267
  };
265
268
  exports.initRuntimeConfig = initRuntimeConfig;
266
- /**
267
- * Recursively collect `.md` files under a root, returning slug segments (relative path without the
268
- * `.md`). `index.md` is excluded — section index content is served by dedicated/home routes.
269
- */
270
- const collectPageMarkdownPaths = (rootDir, relativeParts = []) => {
271
- if (!fs_extra_1.default.existsSync(rootDir))
272
- return [];
273
- const results = [];
274
- for (const entry of fs_extra_1.default.readdirSync(rootDir)) {
275
- const fullPath = path_1.default.join(rootDir, entry);
276
- if (fs_extra_1.default.lstatSync(fullPath).isDirectory()) {
277
- results.push(...collectPageMarkdownPaths(fullPath, [...relativeParts, entry]));
278
- }
279
- else if (entry.endsWith('.md') && entry !== 'index.md') {
280
- results.push([...relativeParts, entry.replace(/\.md$/, '')]);
281
- }
282
- }
283
- return results;
284
- };
285
269
  /**
286
270
  * True when `searchPath` is itself a single component directory: it holds a declaration file named
287
271
  * after the directory (`{dirname}.handoff.*`, or a legacy `{dirname}.{json,js,cjs}`). This is what