@mondaydotcomorg/z2h-cli 0.1.0 → 0.1.1

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 (114) hide show
  1. package/dist/commands/__tests__/create-workspace.test.d.ts +2 -0
  2. package/dist/commands/__tests__/create-workspace.test.d.ts.map +1 -0
  3. package/dist/commands/__tests__/create.test.d.ts +2 -0
  4. package/dist/commands/__tests__/create.test.d.ts.map +1 -0
  5. package/dist/commands/create-workspace.d.ts +5 -0
  6. package/dist/commands/create-workspace.d.ts.map +1 -0
  7. package/dist/commands/create-workspace.js +80 -0
  8. package/dist/commands/create.d.ts +7 -0
  9. package/dist/commands/create.d.ts.map +1 -0
  10. package/dist/commands/create.js +97 -0
  11. package/dist/constants.d.ts +1 -14
  12. package/dist/constants.d.ts.map +1 -1
  13. package/dist/constants.js +11 -34
  14. package/dist/esm/commands/__tests__/create-workspace.test.d.ts +2 -0
  15. package/dist/esm/commands/__tests__/create-workspace.test.d.ts.map +1 -0
  16. package/dist/esm/commands/__tests__/create.test.d.ts +2 -0
  17. package/dist/esm/commands/__tests__/create.test.d.ts.map +1 -0
  18. package/dist/esm/commands/create-workspace.d.ts +5 -0
  19. package/dist/esm/commands/create-workspace.d.ts.map +1 -0
  20. package/dist/esm/commands/create-workspace.mjs +74 -0
  21. package/dist/esm/commands/create.d.ts +7 -0
  22. package/dist/esm/commands/create.d.ts.map +1 -0
  23. package/dist/esm/commands/create.mjs +91 -0
  24. package/dist/esm/constants.d.ts +1 -14
  25. package/dist/esm/constants.d.ts.map +1 -1
  26. package/dist/esm/constants.mjs +11 -32
  27. package/dist/esm/index.mjs +28 -0
  28. package/dist/esm/shadow/__tests__/consumer-pkg.test.d.ts +2 -0
  29. package/dist/esm/shadow/__tests__/consumer-pkg.test.d.ts.map +1 -0
  30. package/dist/esm/shadow/__tests__/merge-package-json.test.d.ts +2 -0
  31. package/dist/esm/shadow/__tests__/merge-package-json.test.d.ts.map +1 -0
  32. package/dist/esm/shadow/consumer-pkg.d.ts +5 -15
  33. package/dist/esm/shadow/consumer-pkg.d.ts.map +1 -1
  34. package/dist/esm/shadow/consumer-pkg.mjs +11 -72
  35. package/dist/esm/shadow/merge-package-json.d.ts +10 -4
  36. package/dist/esm/shadow/merge-package-json.d.ts.map +1 -1
  37. package/dist/esm/shadow/merge-package-json.mjs +12 -11
  38. package/dist/esm/shadow/sync.mjs +2 -2
  39. package/dist/esm/templates/app/claude-settings.mjs +10 -0
  40. package/dist/esm/types.d.ts +0 -1
  41. package/dist/esm/types.d.ts.map +1 -1
  42. package/dist/esm/util/__tests__/templates.test.d.ts +2 -0
  43. package/dist/esm/util/__tests__/templates.test.d.ts.map +1 -0
  44. package/dist/esm/util/__tests__/workspace-templates-shipped.test.d.ts +2 -0
  45. package/dist/esm/util/__tests__/workspace-templates-shipped.test.d.ts.map +1 -0
  46. package/dist/esm/util/__tests__/workspace.test.d.ts +2 -0
  47. package/dist/esm/util/__tests__/workspace.test.d.ts.map +1 -0
  48. package/dist/esm/util/s3/upload.mjs +1 -1
  49. package/dist/esm/util/templates.d.ts +22 -0
  50. package/dist/esm/util/templates.d.ts.map +1 -0
  51. package/dist/esm/util/templates.mjs +36 -0
  52. package/dist/esm/util/workspace.d.ts +20 -0
  53. package/dist/esm/util/workspace.d.ts.map +1 -0
  54. package/dist/esm/util/workspace.mjs +46 -0
  55. package/dist/index.js +28 -0
  56. package/dist/shadow/__tests__/consumer-pkg.test.d.ts +2 -0
  57. package/dist/shadow/__tests__/consumer-pkg.test.d.ts.map +1 -0
  58. package/dist/shadow/__tests__/merge-package-json.test.d.ts +2 -0
  59. package/dist/shadow/__tests__/merge-package-json.test.d.ts.map +1 -0
  60. package/dist/shadow/consumer-pkg.d.ts +5 -15
  61. package/dist/shadow/consumer-pkg.d.ts.map +1 -1
  62. package/dist/shadow/consumer-pkg.js +10 -72
  63. package/dist/shadow/merge-package-json.d.ts +10 -4
  64. package/dist/shadow/merge-package-json.d.ts.map +1 -1
  65. package/dist/shadow/merge-package-json.js +11 -10
  66. package/dist/shadow/sync.js +2 -2
  67. package/dist/templates/app/claude-settings.js +13 -0
  68. package/dist/types.d.ts +0 -1
  69. package/dist/types.d.ts.map +1 -1
  70. package/dist/util/__tests__/templates.test.d.ts +2 -0
  71. package/dist/util/__tests__/templates.test.d.ts.map +1 -0
  72. package/dist/util/__tests__/workspace-templates-shipped.test.d.ts +2 -0
  73. package/dist/util/__tests__/workspace-templates-shipped.test.d.ts.map +1 -0
  74. package/dist/util/__tests__/workspace.test.d.ts +2 -0
  75. package/dist/util/__tests__/workspace.test.d.ts.map +1 -0
  76. package/dist/util/s3/upload.js +1 -1
  77. package/dist/util/templates.d.ts +22 -0
  78. package/dist/util/templates.d.ts.map +1 -0
  79. package/dist/util/templates.js +45 -0
  80. package/dist/util/workspace.d.ts +20 -0
  81. package/dist/util/workspace.d.ts.map +1 -0
  82. package/dist/util/workspace.js +57 -0
  83. package/package.json +5 -3
  84. package/src/commands/__tests__/create-workspace.test.ts +78 -0
  85. package/src/commands/__tests__/create.test.ts +111 -0
  86. package/src/commands/create-workspace.ts +85 -0
  87. package/src/commands/create.ts +113 -0
  88. package/src/constants.ts +11 -34
  89. package/src/index.ts +28 -0
  90. package/src/shadow/__tests__/consumer-pkg.test.ts +56 -0
  91. package/src/shadow/__tests__/merge-package-json.test.ts +44 -0
  92. package/src/shadow/consumer-pkg.ts +10 -88
  93. package/src/shadow/merge-package-json.ts +12 -11
  94. package/src/shadow/sync.ts +2 -2
  95. package/src/templates/app/CLAUDE.md +72 -0
  96. package/src/templates/app/claude-settings.json +7 -0
  97. package/src/templates/app/default-index.tsx.tmpl +21 -0
  98. package/src/templates/app/gitignore +4 -0
  99. package/src/templates/app/html-index.tsx.tmpl +21 -0
  100. package/src/templates/app/package.json.tmpl +13 -0
  101. package/src/templates/workspace/README.md +39 -0
  102. package/src/templates/workspace/gitignore +23 -0
  103. package/src/templates/workspace/npmrc +3 -0
  104. package/src/templates/workspace/nvmrc +1 -0
  105. package/src/templates/workspace/package.json.tmpl +23 -0
  106. package/src/templates/workspace/yarn-releases/yarn-4.5.1.cjs +934 -0
  107. package/src/templates/workspace/yarnrc.yml +7 -0
  108. package/src/types.ts +4 -1
  109. package/src/util/__tests__/templates.test.ts +27 -0
  110. package/src/util/__tests__/workspace-templates-shipped.test.ts +35 -0
  111. package/src/util/__tests__/workspace.test.ts +65 -0
  112. package/src/util/s3/upload.ts +1 -1
  113. package/src/util/templates.ts +37 -0
  114. package/src/util/workspace.ts +52 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=create-workspace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-workspace.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/create-workspace.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=create.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/create.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export interface CreateWorkspaceOptions {
2
+ json?: boolean;
3
+ }
4
+ export declare function createWorkspaceCommand(opts: CreateWorkspaceOptions): Promise<void>;
5
+ //# sourceMappingURL=create-workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-workspace.d.ts","sourceRoot":"","sources":["../../src/commands/create-workspace.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAiBD,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDxF"}
@@ -0,0 +1,80 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const path = require('node:path');
4
+ const promises = require('node:fs/promises');
5
+ const fsExtra = require('fs-extra');
6
+ const execa = require('execa');
7
+ const util_logger = require('../util/logger.js');
8
+ const util_workspace = require('../util/workspace.js');
9
+ const util_templates = require('../util/templates.js');
10
+
11
+ const _interopDefault = e => e && e.__esModule ? e : { default: e };
12
+
13
+ const path__default = /*#__PURE__*/_interopDefault(path);
14
+
15
+ /**
16
+ * Read the current z2h-cli version from its own package.json — the version
17
+ * that gets pinned into the workspace's devDependencies. That way the
18
+ * workspace's `@mondaydotcomorg/z2h-cli` always matches the version that
19
+ * bootstrapped it.
20
+ */
21
+ async function readCurrentCliVersion() {
22
+ // util/templates.ts already locates `<package-root>/src/templates`. The
23
+ // package's own package.json sits two levels above templates/.
24
+ const templatesDir = util_templates.getTemplatesDir();
25
+ const pkgPath = path__default.default.resolve(templatesDir, '..', '..', 'package.json');
26
+ const pkg = (await fsExtra.readJson(pkgPath));
27
+ return `^${pkg.version}`;
28
+ }
29
+ async function createWorkspaceCommand(opts) {
30
+ if (opts.json) {
31
+ util_logger.setOutputMode('json');
32
+ }
33
+ const home = util_workspace.getWorkspaceHome();
34
+ const templatesDir = util_templates.getTemplatesDir();
35
+ const wsTemplatesDir = path__default.default.join(templatesDir, 'workspace');
36
+ // Refuse non-empty target.
37
+ if (await fsExtra.pathExists(home)) {
38
+ const entries = await fsExtra.readdir(home);
39
+ if (entries.length > 0) {
40
+ throw new Error(`${home} is non-empty. Wipe it before running create-workspace.`);
41
+ }
42
+ }
43
+ else {
44
+ await fsExtra.ensureDir(home);
45
+ }
46
+ const [cliVersion, pkgRaw] = await Promise.all([
47
+ readCurrentCliVersion(),
48
+ promises.readFile(path__default.default.join(wsTemplatesDir, 'package.json.tmpl'), 'utf8'),
49
+ ]);
50
+ // Sources have plain names (gitignore, yarnrc.yml, nvmrc, npmrc) so they
51
+ // aren't interpreted as the active config files of *this* package; we
52
+ // rename to dotfiles at the destination.
53
+ //
54
+ // Yarn berry ships inline at .yarn/releases/. The workspace's .yarnrc.yml
55
+ // sets `yarnPath` to point here, so any `yarn` invocation (system classic
56
+ // or otherwise) reads the rc, delegates to this binary, and runs as 4.5.1
57
+ // — no corepack, no global pin needed. Mirrors how bigbrain-z2h ships its
58
+ // own yarn release.
59
+ await Promise.all([
60
+ fsExtra.writeFile(path__default.default.join(home, 'package.json'), util_templates.renderTemplate(pkgRaw, { __Z2H_CLI_VERSION__: cliVersion })),
61
+ fsExtra.copy(path__default.default.join(wsTemplatesDir, 'gitignore'), path__default.default.join(home, '.gitignore')),
62
+ fsExtra.copy(path__default.default.join(wsTemplatesDir, 'yarnrc.yml'), path__default.default.join(home, '.yarnrc.yml')),
63
+ fsExtra.copy(path__default.default.join(wsTemplatesDir, 'nvmrc'), path__default.default.join(home, '.nvmrc')),
64
+ fsExtra.copy(path__default.default.join(wsTemplatesDir, 'npmrc'), path__default.default.join(home, '.npmrc')),
65
+ fsExtra.copy(path__default.default.join(wsTemplatesDir, 'README.md'), path__default.default.join(home, 'README.md')),
66
+ fsExtra.copy(path__default.default.join(wsTemplatesDir, 'yarn-releases', 'yarn-4.5.1.cjs'), path__default.default.join(home, '.yarn', 'releases', 'yarn-4.5.1.cjs')),
67
+ ]);
68
+ util_logger.info(`bootstrapping workspace at ${home}`);
69
+ await execa.execa('yarn', ['install'], {
70
+ cwd: home,
71
+ stdio: 'inherit',
72
+ });
73
+ util_logger.output(`[z2h-cli] workspace ready at ${home}`, {
74
+ ok: true,
75
+ workspace: home,
76
+ z2hCliVersion: cliVersion,
77
+ });
78
+ }
79
+
80
+ exports.createWorkspaceCommand = createWorkspaceCommand;
@@ -0,0 +1,7 @@
1
+ export interface CreateOptions {
2
+ template?: string;
3
+ htmlFile?: string;
4
+ json?: boolean;
5
+ }
6
+ export declare function createCommand(appName: string, opts: CreateOptions): Promise<void>;
7
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AA0BD,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA0EvF"}
@@ -0,0 +1,97 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const path = require('node:path');
4
+ const promises = require('node:fs/promises');
5
+ const fsExtra = require('fs-extra');
6
+ const execa = require('execa');
7
+ const util_logger = require('../util/logger.js');
8
+ const util_workspace = require('../util/workspace.js');
9
+ const util_templates = require('../util/templates.js');
10
+
11
+ const _interopDefault = e => e && e.__esModule ? e : { default: e };
12
+
13
+ const path__default = /*#__PURE__*/_interopDefault(path);
14
+
15
+ const APP_NAME_RE = /^[a-z][a-z0-9-]{1,49}$/;
16
+ /**
17
+ * Convert kebab-case to PascalCase. `demo-app` → `DemoApp`.
18
+ */
19
+ function pascalCase(input) {
20
+ return input
21
+ .split('-')
22
+ .filter(Boolean)
23
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
24
+ .join('');
25
+ }
26
+ /**
27
+ * Returns true iff `dir` either does not exist or is an empty directory.
28
+ */
29
+ async function isDirEmpty(dir) {
30
+ if (!(await fsExtra.pathExists(dir))) {
31
+ return true;
32
+ }
33
+ const entries = await fsExtra.readdir(dir);
34
+ return entries.length === 0;
35
+ }
36
+ async function createCommand(appName, opts) {
37
+ if (opts.json) {
38
+ util_logger.setOutputMode('json');
39
+ }
40
+ // Validate workspace.
41
+ const workspace = await util_workspace.assertWorkspace();
42
+ // Validate app name.
43
+ if (!APP_NAME_RE.test(appName)) {
44
+ throw new Error(`App name "${appName}" is invalid. Use lowercase kebab-case (a-z, 0-9, -), ` +
45
+ `2-50 chars, starting with a letter.`);
46
+ }
47
+ // Resolve template.
48
+ // `--html-file` implies `--template html-embed` unless the user already picked one.
49
+ const template = (opts.template ?? (opts.htmlFile ? 'html-embed' : 'default'));
50
+ if (!util_templates.APP_TEMPLATES.includes(template)) {
51
+ throw new Error(`Unknown template "${template}". Available: ${util_templates.APP_TEMPLATES.join(', ')}.`);
52
+ }
53
+ if (opts.htmlFile && !(await fsExtra.pathExists(opts.htmlFile))) {
54
+ throw new Error(`HTML file does not exist: ${opts.htmlFile}`);
55
+ }
56
+ // Validate target dir.
57
+ const appDir = path__default.default.join(workspace, appName);
58
+ if (!(await isDirEmpty(appDir))) {
59
+ throw new Error(`${appDir} is non-empty. Pick a different name or wipe it first.`);
60
+ }
61
+ // Compute substitutions.
62
+ const vars = {
63
+ __APP_NAME__: appName,
64
+ __APP_COMPONENT__: pascalCase(appName),
65
+ };
66
+ const appTemplatesDir = path__default.default.join(util_templates.getTemplatesDir(), 'app');
67
+ await fsExtra.ensureDir(path__default.default.join(appDir, 'src'));
68
+ await fsExtra.ensureDir(path__default.default.join(appDir, '.claude'));
69
+ // package.json (templated).
70
+ const pkgRaw = await promises.readFile(path__default.default.join(appTemplatesDir, 'package.json.tmpl'), 'utf8');
71
+ await fsExtra.writeFile(path__default.default.join(appDir, 'package.json'), util_templates.renderTemplate(pkgRaw, vars));
72
+ // src/index.tsx (templated; choice driven by template).
73
+ const indexTemplate = template === 'html-embed' ? 'html-index.tsx.tmpl' : 'default-index.tsx.tmpl';
74
+ const indexRaw = await promises.readFile(path__default.default.join(appTemplatesDir, indexTemplate), 'utf8');
75
+ await fsExtra.writeFile(path__default.default.join(appDir, 'src', 'index.tsx'), util_templates.renderTemplate(indexRaw, vars));
76
+ // Copy verbatim files.
77
+ await fsExtra.copy(path__default.default.join(appTemplatesDir, 'gitignore'), path__default.default.join(appDir, '.gitignore'));
78
+ await fsExtra.copy(path__default.default.join(appTemplatesDir, 'claude-settings.json'), path__default.default.join(appDir, '.claude', 'settings.json'));
79
+ await fsExtra.copy(path__default.default.join(appTemplatesDir, 'CLAUDE.md'), path__default.default.join(appDir, 'CLAUDE.md'));
80
+ // html-embed: also copy the source HTML.
81
+ if (template === 'html-embed' && opts.htmlFile) {
82
+ await fsExtra.copy(opts.htmlFile, path__default.default.join(appDir, 'src', 'page.html'));
83
+ }
84
+ util_logger.info(`installing into workspace at ${workspace}`);
85
+ await execa.execa('yarn', ['install'], {
86
+ cwd: workspace,
87
+ stdio: 'inherit',
88
+ });
89
+ util_logger.output(`[z2h-cli] app ready at ${appDir}`, {
90
+ ok: true,
91
+ appDir,
92
+ appName,
93
+ template,
94
+ });
95
+ }
96
+
97
+ exports.createCommand = createCommand;
@@ -3,21 +3,8 @@
3
3
  * one place) stay local to their file to keep this index small.
4
4
  */
5
5
  export declare const SHADOW_DIR_NAME = ".zth";
6
- /**
7
- * Protected runtime deps that MUST resolve to a single instance across the
8
- * wrapper (.zth) and the consumer's src. Declared as peerDependencies in
9
- * the shadow pkg so yarn hoists them from the workspace root — which is
10
- * why we enforce their presence in the consumer's own package.json.
11
- */
12
- export declare const PROTECTED_DEPS: Record<string, string>;
13
- /**
14
- * Protected dev deps. Same idempotent treatment as PROTECTED_DEPS but live
15
- * in the consumer's `devDependencies`. These give the LLM-authored consumer
16
- * code access to monday.com platform types out of the box.
17
- */
18
- export declare const PROTECTED_DEV_DEPS: Record<string, string>;
19
- export declare const WORKSPACE_DEV_DEPS: Record<string, string>;
20
6
  export declare const SHADOW_SCRIPTS: Record<string, string>;
7
+ export declare const SHADOW_DEV_DEPS: Record<string, string>;
21
8
  export declare const MIN_PORT = 13000;
22
9
  export declare const MAX_PORT = 19999;
23
10
  export declare const GITIGNORE_ENTRIES: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,eAAO,MAAM,eAAe,SAAS,CAAC;AAGtC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMjD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGrD,CAAC;AAIF,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKrD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGjD,CAAC;AAMF,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAG9B,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAG3C,eAAO,MAAM,YAAY,UAIxB,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,eAAO,MAAM,eAAe,SAAS,CAAC;AAGtC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGjD,CAAC;AAQF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAElD,CAAC;AAMF,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAG9B,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAG3C,eAAO,MAAM,YAAY,UAIxB,CAAC"}
package/dist/constants.js CHANGED
@@ -6,41 +6,20 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
6
6
  */
7
7
  // ─────────── Shadow dir layout ───────────
8
8
  const SHADOW_DIR_NAME = '.zth';
9
- // ─────────── Consumer pkg enforcement ──────
10
- /**
11
- * Protected runtime deps that MUST resolve to a single instance across the
12
- * wrapper (.zth) and the consumer's src. Declared as peerDependencies in
13
- * the shadow pkg so yarn hoists them from the workspace root — which is
14
- * why we enforce their presence in the consumer's own package.json.
15
- */
16
- const PROTECTED_DEPS = {
17
- react: '^18.2.0',
18
- 'react-dom': '^18.2.0',
19
- 'react-error-boundary': '^3.1.3',
20
- '@mondaydotcomorg/trident-runtime': '^1.24.1',
21
- '@vibe/core': '^4.0.1',
22
- };
23
- /**
24
- * Protected dev deps. Same idempotent treatment as PROTECTED_DEPS but live
25
- * in the consumer's `devDependencies`. These give the LLM-authored consumer
26
- * code access to monday.com platform types out of the box.
27
- */
28
- const PROTECTED_DEV_DEPS = {
29
- '@mondaydotcomorg/mf-api-types': '^0.1.1',
30
- '@mondaydotcomorg/bigbrain-types': '^0.3.3',
31
- };
32
- // ─────────── Shadow pkg template ───────────
33
- // TODO — Add trident mf-types / bigbrain-types opt-in.
34
- const WORKSPACE_DEV_DEPS = {
35
- '@mondaydotcomorg/trident-toolkit': '^2.9.25',
36
- '@types/react': '^18.0.14',
37
- '@types/react-dom': '^18.0.5',
38
- typescript: '^5.7.2',
39
- };
9
+ // ─────────── Shadow scripts ───────
40
10
  const SHADOW_SCRIPTS = {
41
11
  start: 'trident start',
42
12
  build: 'trident build',
43
13
  };
14
+ // ─────────── Shadow dev deps ───────
15
+ // Yarn berry only wires a workspace member's `node_modules/.bin/` PATH
16
+ // for packages that member explicitly declares. The shadow's scripts
17
+ // invoke `trident`, so trident-toolkit must be declared here for the
18
+ // bin to resolve. `*` is fine — yarn workspaces resolve the actual
19
+ // version from the workspace root's pinned entry.
20
+ const SHADOW_DEV_DEPS = {
21
+ '@mondaydotcomorg/trident-toolkit': '*',
22
+ };
44
23
  // ─────────── Auto-allocated dev port range ────
45
24
  // High enough to dodge common dev defaults (3000 Next, 5173 Vite, 8080).
46
25
  // Collisions within the 7k-port pool are rare for POC; if trident can't
@@ -59,9 +38,7 @@ const YARNRC_LINES = [
59
38
  exports.GITIGNORE_ENTRIES = GITIGNORE_ENTRIES;
60
39
  exports.MAX_PORT = MAX_PORT;
61
40
  exports.MIN_PORT = MIN_PORT;
62
- exports.PROTECTED_DEPS = PROTECTED_DEPS;
63
- exports.PROTECTED_DEV_DEPS = PROTECTED_DEV_DEPS;
41
+ exports.SHADOW_DEV_DEPS = SHADOW_DEV_DEPS;
64
42
  exports.SHADOW_DIR_NAME = SHADOW_DIR_NAME;
65
43
  exports.SHADOW_SCRIPTS = SHADOW_SCRIPTS;
66
- exports.WORKSPACE_DEV_DEPS = WORKSPACE_DEV_DEPS;
67
44
  exports.YARNRC_LINES = YARNRC_LINES;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=create-workspace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-workspace.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/__tests__/create-workspace.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=create.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/__tests__/create.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export interface CreateWorkspaceOptions {
2
+ json?: boolean;
3
+ }
4
+ export declare function createWorkspaceCommand(opts: CreateWorkspaceOptions): Promise<void>;
5
+ //# sourceMappingURL=create-workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-workspace.d.ts","sourceRoot":"","sources":["../../../src/commands/create-workspace.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAiBD,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDxF"}
@@ -0,0 +1,74 @@
1
+ import path from 'node:path';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { pathExists, readdir, ensureDir, writeFile, copy, readJson } from 'fs-extra';
4
+ import { execa } from 'execa';
5
+ import { setOutputMode, info, output } from '../util/logger.mjs';
6
+ import { getWorkspaceHome } from '../util/workspace.mjs';
7
+ import { getTemplatesDir, renderTemplate } from '../util/templates.mjs';
8
+
9
+ /**
10
+ * Read the current z2h-cli version from its own package.json — the version
11
+ * that gets pinned into the workspace's devDependencies. That way the
12
+ * workspace's `@mondaydotcomorg/z2h-cli` always matches the version that
13
+ * bootstrapped it.
14
+ */
15
+ async function readCurrentCliVersion() {
16
+ // util/templates.ts already locates `<package-root>/src/templates`. The
17
+ // package's own package.json sits two levels above templates/.
18
+ const templatesDir = getTemplatesDir();
19
+ const pkgPath = path.resolve(templatesDir, '..', '..', 'package.json');
20
+ const pkg = (await readJson(pkgPath));
21
+ return `^${pkg.version}`;
22
+ }
23
+ async function createWorkspaceCommand(opts) {
24
+ if (opts.json) {
25
+ setOutputMode('json');
26
+ }
27
+ const home = getWorkspaceHome();
28
+ const templatesDir = getTemplatesDir();
29
+ const wsTemplatesDir = path.join(templatesDir, 'workspace');
30
+ // Refuse non-empty target.
31
+ if (await pathExists(home)) {
32
+ const entries = await readdir(home);
33
+ if (entries.length > 0) {
34
+ throw new Error(`${home} is non-empty. Wipe it before running create-workspace.`);
35
+ }
36
+ }
37
+ else {
38
+ await ensureDir(home);
39
+ }
40
+ const [cliVersion, pkgRaw] = await Promise.all([
41
+ readCurrentCliVersion(),
42
+ readFile(path.join(wsTemplatesDir, 'package.json.tmpl'), 'utf8'),
43
+ ]);
44
+ // Sources have plain names (gitignore, yarnrc.yml, nvmrc, npmrc) so they
45
+ // aren't interpreted as the active config files of *this* package; we
46
+ // rename to dotfiles at the destination.
47
+ //
48
+ // Yarn berry ships inline at .yarn/releases/. The workspace's .yarnrc.yml
49
+ // sets `yarnPath` to point here, so any `yarn` invocation (system classic
50
+ // or otherwise) reads the rc, delegates to this binary, and runs as 4.5.1
51
+ // — no corepack, no global pin needed. Mirrors how bigbrain-z2h ships its
52
+ // own yarn release.
53
+ await Promise.all([
54
+ writeFile(path.join(home, 'package.json'), renderTemplate(pkgRaw, { __Z2H_CLI_VERSION__: cliVersion })),
55
+ copy(path.join(wsTemplatesDir, 'gitignore'), path.join(home, '.gitignore')),
56
+ copy(path.join(wsTemplatesDir, 'yarnrc.yml'), path.join(home, '.yarnrc.yml')),
57
+ copy(path.join(wsTemplatesDir, 'nvmrc'), path.join(home, '.nvmrc')),
58
+ copy(path.join(wsTemplatesDir, 'npmrc'), path.join(home, '.npmrc')),
59
+ copy(path.join(wsTemplatesDir, 'README.md'), path.join(home, 'README.md')),
60
+ copy(path.join(wsTemplatesDir, 'yarn-releases', 'yarn-4.5.1.cjs'), path.join(home, '.yarn', 'releases', 'yarn-4.5.1.cjs')),
61
+ ]);
62
+ info(`bootstrapping workspace at ${home}`);
63
+ await execa('yarn', ['install'], {
64
+ cwd: home,
65
+ stdio: 'inherit',
66
+ });
67
+ output(`[z2h-cli] workspace ready at ${home}`, {
68
+ ok: true,
69
+ workspace: home,
70
+ z2hCliVersion: cliVersion,
71
+ });
72
+ }
73
+
74
+ export { createWorkspaceCommand };
@@ -0,0 +1,7 @@
1
+ export interface CreateOptions {
2
+ template?: string;
3
+ htmlFile?: string;
4
+ json?: boolean;
5
+ }
6
+ export declare function createCommand(appName: string, opts: CreateOptions): Promise<void>;
7
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/create.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AA0BD,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA0EvF"}
@@ -0,0 +1,91 @@
1
+ import path from 'node:path';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { pathExists, ensureDir, writeFile, copy, readdir } from 'fs-extra';
4
+ import { execa } from 'execa';
5
+ import { setOutputMode, info, output } from '../util/logger.mjs';
6
+ import { assertWorkspace } from '../util/workspace.mjs';
7
+ import { APP_TEMPLATES, getTemplatesDir, renderTemplate } from '../util/templates.mjs';
8
+
9
+ const APP_NAME_RE = /^[a-z][a-z0-9-]{1,49}$/;
10
+ /**
11
+ * Convert kebab-case to PascalCase. `demo-app` → `DemoApp`.
12
+ */
13
+ function pascalCase(input) {
14
+ return input
15
+ .split('-')
16
+ .filter(Boolean)
17
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
18
+ .join('');
19
+ }
20
+ /**
21
+ * Returns true iff `dir` either does not exist or is an empty directory.
22
+ */
23
+ async function isDirEmpty(dir) {
24
+ if (!(await pathExists(dir))) {
25
+ return true;
26
+ }
27
+ const entries = await readdir(dir);
28
+ return entries.length === 0;
29
+ }
30
+ async function createCommand(appName, opts) {
31
+ if (opts.json) {
32
+ setOutputMode('json');
33
+ }
34
+ // Validate workspace.
35
+ const workspace = await assertWorkspace();
36
+ // Validate app name.
37
+ if (!APP_NAME_RE.test(appName)) {
38
+ throw new Error(`App name "${appName}" is invalid. Use lowercase kebab-case (a-z, 0-9, -), ` +
39
+ `2-50 chars, starting with a letter.`);
40
+ }
41
+ // Resolve template.
42
+ // `--html-file` implies `--template html-embed` unless the user already picked one.
43
+ const template = (opts.template ?? (opts.htmlFile ? 'html-embed' : 'default'));
44
+ if (!APP_TEMPLATES.includes(template)) {
45
+ throw new Error(`Unknown template "${template}". Available: ${APP_TEMPLATES.join(', ')}.`);
46
+ }
47
+ if (opts.htmlFile && !(await pathExists(opts.htmlFile))) {
48
+ throw new Error(`HTML file does not exist: ${opts.htmlFile}`);
49
+ }
50
+ // Validate target dir.
51
+ const appDir = path.join(workspace, appName);
52
+ if (!(await isDirEmpty(appDir))) {
53
+ throw new Error(`${appDir} is non-empty. Pick a different name or wipe it first.`);
54
+ }
55
+ // Compute substitutions.
56
+ const vars = {
57
+ __APP_NAME__: appName,
58
+ __APP_COMPONENT__: pascalCase(appName),
59
+ };
60
+ const appTemplatesDir = path.join(getTemplatesDir(), 'app');
61
+ await ensureDir(path.join(appDir, 'src'));
62
+ await ensureDir(path.join(appDir, '.claude'));
63
+ // package.json (templated).
64
+ const pkgRaw = await readFile(path.join(appTemplatesDir, 'package.json.tmpl'), 'utf8');
65
+ await writeFile(path.join(appDir, 'package.json'), renderTemplate(pkgRaw, vars));
66
+ // src/index.tsx (templated; choice driven by template).
67
+ const indexTemplate = template === 'html-embed' ? 'html-index.tsx.tmpl' : 'default-index.tsx.tmpl';
68
+ const indexRaw = await readFile(path.join(appTemplatesDir, indexTemplate), 'utf8');
69
+ await writeFile(path.join(appDir, 'src', 'index.tsx'), renderTemplate(indexRaw, vars));
70
+ // Copy verbatim files.
71
+ await copy(path.join(appTemplatesDir, 'gitignore'), path.join(appDir, '.gitignore'));
72
+ await copy(path.join(appTemplatesDir, 'claude-settings.json'), path.join(appDir, '.claude', 'settings.json'));
73
+ await copy(path.join(appTemplatesDir, 'CLAUDE.md'), path.join(appDir, 'CLAUDE.md'));
74
+ // html-embed: also copy the source HTML.
75
+ if (template === 'html-embed' && opts.htmlFile) {
76
+ await copy(opts.htmlFile, path.join(appDir, 'src', 'page.html'));
77
+ }
78
+ info(`installing into workspace at ${workspace}`);
79
+ await execa('yarn', ['install'], {
80
+ cwd: workspace,
81
+ stdio: 'inherit',
82
+ });
83
+ output(`[z2h-cli] app ready at ${appDir}`, {
84
+ ok: true,
85
+ appDir,
86
+ appName,
87
+ template,
88
+ });
89
+ }
90
+
91
+ export { createCommand };
@@ -3,21 +3,8 @@
3
3
  * one place) stay local to their file to keep this index small.
4
4
  */
5
5
  export declare const SHADOW_DIR_NAME = ".zth";
6
- /**
7
- * Protected runtime deps that MUST resolve to a single instance across the
8
- * wrapper (.zth) and the consumer's src. Declared as peerDependencies in
9
- * the shadow pkg so yarn hoists them from the workspace root — which is
10
- * why we enforce their presence in the consumer's own package.json.
11
- */
12
- export declare const PROTECTED_DEPS: Record<string, string>;
13
- /**
14
- * Protected dev deps. Same idempotent treatment as PROTECTED_DEPS but live
15
- * in the consumer's `devDependencies`. These give the LLM-authored consumer
16
- * code access to monday.com platform types out of the box.
17
- */
18
- export declare const PROTECTED_DEV_DEPS: Record<string, string>;
19
- export declare const WORKSPACE_DEV_DEPS: Record<string, string>;
20
6
  export declare const SHADOW_SCRIPTS: Record<string, string>;
7
+ export declare const SHADOW_DEV_DEPS: Record<string, string>;
21
8
  export declare const MIN_PORT = 13000;
22
9
  export declare const MAX_PORT = 19999;
23
10
  export declare const GITIGNORE_ENTRIES: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,eAAO,MAAM,eAAe,SAAS,CAAC;AAGtC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMjD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGrD,CAAC;AAIF,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKrD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGjD,CAAC;AAMF,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAG9B,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAG3C,eAAO,MAAM,YAAY,UAIxB,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,eAAO,MAAM,eAAe,SAAS,CAAC;AAGtC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGjD,CAAC;AAQF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAElD,CAAC;AAMF,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAG9B,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAG3C,eAAO,MAAM,YAAY,UAIxB,CAAC"}
@@ -4,41 +4,20 @@
4
4
  */
5
5
  // ─────────── Shadow dir layout ───────────
6
6
  const SHADOW_DIR_NAME = '.zth';
7
- // ─────────── Consumer pkg enforcement ──────
8
- /**
9
- * Protected runtime deps that MUST resolve to a single instance across the
10
- * wrapper (.zth) and the consumer's src. Declared as peerDependencies in
11
- * the shadow pkg so yarn hoists them from the workspace root — which is
12
- * why we enforce their presence in the consumer's own package.json.
13
- */
14
- const PROTECTED_DEPS = {
15
- react: '^18.2.0',
16
- 'react-dom': '^18.2.0',
17
- 'react-error-boundary': '^3.1.3',
18
- '@mondaydotcomorg/trident-runtime': '^1.24.1',
19
- '@vibe/core': '^4.0.1',
20
- };
21
- /**
22
- * Protected dev deps. Same idempotent treatment as PROTECTED_DEPS but live
23
- * in the consumer's `devDependencies`. These give the LLM-authored consumer
24
- * code access to monday.com platform types out of the box.
25
- */
26
- const PROTECTED_DEV_DEPS = {
27
- '@mondaydotcomorg/mf-api-types': '^0.1.1',
28
- '@mondaydotcomorg/bigbrain-types': '^0.3.3',
29
- };
30
- // ─────────── Shadow pkg template ───────────
31
- // TODO — Add trident mf-types / bigbrain-types opt-in.
32
- const WORKSPACE_DEV_DEPS = {
33
- '@mondaydotcomorg/trident-toolkit': '^2.9.25',
34
- '@types/react': '^18.0.14',
35
- '@types/react-dom': '^18.0.5',
36
- typescript: '^5.7.2',
37
- };
7
+ // ─────────── Shadow scripts ───────
38
8
  const SHADOW_SCRIPTS = {
39
9
  start: 'trident start',
40
10
  build: 'trident build',
41
11
  };
12
+ // ─────────── Shadow dev deps ───────
13
+ // Yarn berry only wires a workspace member's `node_modules/.bin/` PATH
14
+ // for packages that member explicitly declares. The shadow's scripts
15
+ // invoke `trident`, so trident-toolkit must be declared here for the
16
+ // bin to resolve. `*` is fine — yarn workspaces resolve the actual
17
+ // version from the workspace root's pinned entry.
18
+ const SHADOW_DEV_DEPS = {
19
+ '@mondaydotcomorg/trident-toolkit': '*',
20
+ };
42
21
  // ─────────── Auto-allocated dev port range ────
43
22
  // High enough to dodge common dev defaults (3000 Next, 5173 Vite, 8080).
44
23
  // Collisions within the 7k-port pool are rare for POC; if trident can't
@@ -54,4 +33,4 @@ const YARNRC_LINES = [
54
33
  "npmRegistryServer: 'https://registry.npmjs.org/'",
55
34
  ];
56
35
 
57
- export { GITIGNORE_ENTRIES, MAX_PORT, MIN_PORT, PROTECTED_DEPS, PROTECTED_DEV_DEPS, SHADOW_DIR_NAME, SHADOW_SCRIPTS, WORKSPACE_DEV_DEPS, YARNRC_LINES };
36
+ export { GITIGNORE_ENTRIES, MAX_PORT, MIN_PORT, SHADOW_DEV_DEPS, SHADOW_DIR_NAME, SHADOW_SCRIPTS, YARNRC_LINES };
@@ -3,6 +3,8 @@ import { devCommand } from './commands/dev.mjs';
3
3
  import { buildCommand } from './commands/build.mjs';
4
4
  import { cleanCommand } from './commands/clean.mjs';
5
5
  import { deployCommand } from './commands/deploy.mjs';
6
+ import { createWorkspaceCommand } from './commands/create-workspace.mjs';
7
+ import { createCommand } from './commands/create.mjs';
6
8
  import { errorLog, fail } from './util/logger.mjs';
7
9
 
8
10
  async function main() {
@@ -74,6 +76,32 @@ async function main() {
74
76
  fail(`deploy failed: ${err.message}`, { command: 'deploy' }, 2);
75
77
  }
76
78
  });
79
+ program
80
+ .command('create-workspace')
81
+ .description('Bootstrap the ~/zth-projects/ yarn workspace (one-time setup)')
82
+ .option('--json', 'emit structured JSON on stdout')
83
+ .action(async (opts) => {
84
+ try {
85
+ await createWorkspaceCommand(opts);
86
+ }
87
+ catch (err) {
88
+ fail(`create-workspace failed: ${err.message}`, { command: 'create-workspace' }, 1);
89
+ }
90
+ });
91
+ program
92
+ .command('create <app-name>')
93
+ .description('Scaffold a new Z2H consumer app inside ~/zth-projects/')
94
+ .option('--template <name>', 'Template name (default | html-embed)')
95
+ .option('--html-file <path>', 'Path to an HTML file to embed (implies --template html-embed)')
96
+ .option('--json', 'emit structured JSON on stdout')
97
+ .action(async (appName, opts) => {
98
+ try {
99
+ await createCommand(appName, opts);
100
+ }
101
+ catch (err) {
102
+ fail(`create failed: ${err.message}`, { command: 'create' }, 1);
103
+ }
104
+ });
77
105
  await program.parseAsync(process.argv);
78
106
  }
79
107
  void main()
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=consumer-pkg.test.d.ts.map