@shazhou/proman-core 0.9.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 (129) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +18 -0
  3. package/dist/commands/bump.d.ts +13 -0
  4. package/dist/commands/bump.d.ts.map +1 -0
  5. package/dist/commands/bump.js +115 -0
  6. package/dist/commands/deploy.d.ts +9 -0
  7. package/dist/commands/deploy.d.ts.map +1 -0
  8. package/dist/commands/deploy.js +42 -0
  9. package/dist/commands/dev.d.ts +15 -0
  10. package/dist/commands/dev.d.ts.map +1 -0
  11. package/dist/commands/dev.js +175 -0
  12. package/dist/commands/index.d.ts +7 -0
  13. package/dist/commands/index.d.ts.map +1 -0
  14. package/dist/commands/index.js +7 -0
  15. package/dist/commands/init.d.ts +5 -0
  16. package/dist/commands/init.d.ts.map +1 -0
  17. package/dist/commands/init.js +262 -0
  18. package/dist/commands/link.d.ts +19 -0
  19. package/dist/commands/link.d.ts.map +1 -0
  20. package/dist/commands/link.js +155 -0
  21. package/dist/commands/publish.d.ts +18 -0
  22. package/dist/commands/publish.d.ts.map +1 -0
  23. package/dist/commands/publish.js +125 -0
  24. package/dist/config/index.d.ts +4 -0
  25. package/dist/config/index.d.ts.map +1 -0
  26. package/dist/config/index.js +2 -0
  27. package/dist/config/load-config.d.ts +6 -0
  28. package/dist/config/load-config.d.ts.map +1 -0
  29. package/dist/config/load-config.js +29 -0
  30. package/dist/config/types.d.ts +17 -0
  31. package/dist/config/types.d.ts.map +1 -0
  32. package/dist/config/types.js +1 -0
  33. package/dist/config/validate-config.d.ts +7 -0
  34. package/dist/config/validate-config.d.ts.map +1 -0
  35. package/dist/config/validate-config.js +72 -0
  36. package/dist/index.d.ts +6 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +6 -0
  39. package/dist/utils/changeset.d.ts +16 -0
  40. package/dist/utils/changeset.d.ts.map +1 -0
  41. package/dist/utils/changeset.js +80 -0
  42. package/dist/utils/fingerprint.d.ts +38 -0
  43. package/dist/utils/fingerprint.d.ts.map +1 -0
  44. package/dist/utils/fingerprint.js +182 -0
  45. package/dist/utils/git.d.ts +23 -0
  46. package/dist/utils/git.d.ts.map +1 -0
  47. package/dist/utils/git.js +105 -0
  48. package/dist/utils/index.d.ts +8 -0
  49. package/dist/utils/index.d.ts.map +1 -0
  50. package/dist/utils/index.js +8 -0
  51. package/dist/utils/npm.d.ts +30 -0
  52. package/dist/utils/npm.d.ts.map +1 -0
  53. package/dist/utils/npm.js +85 -0
  54. package/dist/utils/smoke-test.d.ts +7 -0
  55. package/dist/utils/smoke-test.d.ts.map +1 -0
  56. package/dist/utils/smoke-test.js +59 -0
  57. package/dist/utils/version.d.ts +5 -0
  58. package/dist/utils/version.d.ts.map +1 -0
  59. package/dist/utils/version.js +36 -0
  60. package/dist/utils/workspace.d.ts +21 -0
  61. package/dist/utils/workspace.d.ts.map +1 -0
  62. package/dist/utils/workspace.js +73 -0
  63. package/package.json +45 -0
  64. package/src/commands/bump.ts +131 -0
  65. package/src/commands/deploy.ts +52 -0
  66. package/src/commands/dev.ts +214 -0
  67. package/src/commands/index.ts +7 -0
  68. package/src/commands/init.integration.test.ts +59 -0
  69. package/src/commands/init.test.ts +179 -0
  70. package/src/commands/init.ts +290 -0
  71. package/src/commands/link.ts +195 -0
  72. package/src/commands/publish.ts +168 -0
  73. package/src/config/index.ts +8 -0
  74. package/src/config/load-config.ts +33 -0
  75. package/src/config/types.ts +19 -0
  76. package/src/config/validate-config.ts +81 -0
  77. package/src/index.ts +29 -0
  78. package/src/utils/changeset.ts +98 -0
  79. package/src/utils/fingerprint.ts +199 -0
  80. package/src/utils/git.ts +119 -0
  81. package/src/utils/index.ts +8 -0
  82. package/src/utils/npm.ts +110 -0
  83. package/src/utils/smoke-test.ts +79 -0
  84. package/src/utils/version.ts +41 -0
  85. package/src/utils/workspace.ts +94 -0
  86. package/tests/build-fingerprint-integration.test.ts +403 -0
  87. package/tests/bump.test.ts +261 -0
  88. package/tests/changeset.test.ts +147 -0
  89. package/tests/deploy.test.ts +98 -0
  90. package/tests/dev.test.ts +756 -0
  91. package/tests/fingerprint.test.ts +316 -0
  92. package/tests/fixtures/api-only/packages/api/.gitkeep +0 -0
  93. package/tests/fixtures/api-only/proman.yaml +4 -0
  94. package/tests/fixtures/bad-packages/proman.yaml +1 -0
  95. package/tests/fixtures/bun-project/packages/a/.gitkeep +0 -0
  96. package/tests/fixtures/bun-project/proman.yaml +4 -0
  97. package/tests/fixtures/defaults/proman.yaml +3 -0
  98. package/tests/fixtures/no-deployable/packages/core/.gitkeep +0 -0
  99. package/tests/fixtures/no-deployable/packages/mycli/.gitkeep +0 -0
  100. package/tests/fixtures/no-deployable/proman.yaml +7 -0
  101. package/tests/fixtures/node-runtime/packages/a/package.json +5 -0
  102. package/tests/fixtures/node-runtime/proman.yaml +3 -0
  103. package/tests/fixtures/pnpm-project/packages/a/package.json +1 -0
  104. package/tests/fixtures/pnpm-project/pnpm-lock.yaml +0 -0
  105. package/tests/fixtures/pnpm-project/proman.yaml +3 -0
  106. package/tests/fixtures/typed/packages/api/.gitkeep +0 -0
  107. package/tests/fixtures/typed/packages/core/.gitkeep +0 -0
  108. package/tests/fixtures/typed/packages/dashboard/.gitkeep +0 -0
  109. package/tests/fixtures/typed/packages/mycli/.gitkeep +0 -0
  110. package/tests/fixtures/typed/proman.yaml +13 -0
  111. package/tests/fixtures/valid/packages/cli/package.json +5 -0
  112. package/tests/fixtures/valid/packages/core/package.json +5 -0
  113. package/tests/fixtures/valid/packages/fs/package.json +5 -0
  114. package/tests/fixtures/valid/proman.yaml +13 -0
  115. package/tests/fixtures/webui-only/packages/dashboard/.gitkeep +0 -0
  116. package/tests/fixtures/webui-only/proman.yaml +4 -0
  117. package/tests/link.test.ts +419 -0
  118. package/tests/load-config.test.ts +44 -0
  119. package/tests/npm.test.ts +199 -0
  120. package/tests/publish.test.ts +599 -0
  121. package/tests/smoke-test.test.ts +211 -0
  122. package/tests/validate-config.test.ts +67 -0
  123. package/tests/version.test.ts +86 -0
  124. package/tests/workflow-schema.test.ts +72 -0
  125. package/tests/workspace.test.ts +160 -0
  126. package/tsconfig.build.json +14 -0
  127. package/tsconfig.json +8 -0
  128. package/tsconfig.tsbuildinfo +1 -0
  129. package/vitest.config.ts +8 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ ## 0.9.0 — 2026-06-13
4
+
5
+ - Refactor proman into a monorepo with `@shazhou/proman-core` and `@shazhou/proman` packages.
6
+
7
+ **Breaking**: None - CLI interface remains unchanged
8
+
9
+ **New packages**:
10
+ - `@shazhou/proman-core` - Core library with config loader, utils, and command logic as pure functions for programmatic use
11
+ - `@shazhou/proman` - CLI package that parses args and delegates to core functions
12
+
13
+ **Features**:
14
+ - Proman now manages itself as a monorepo (dogfooding)
15
+ - Single-package projects work as one-package monorepos (eliminates need for unirepo support)
16
+ - Core exports are pure functions with no CLI-specific side effects
17
+ - Root `proman.yaml` configures packages array for monorepo management
18
+
19
+ **Migration**: Users only need to upgrade the `@shazhou/proman` CLI package. The core package is automatically installed as a dependency.
20
+ - Migrate tests from root to package-specific directories and fix monorepo dependencies
21
+
22
+ - Migrate all test files from root `tests/` to `packages/core/tests/` and `packages/cli/tests/`
23
+ - Update test imports to reference correct package structure
24
+ - Remove `yaml` dependency from root package.json (now only in core package where it's used)
25
+ - Fix workflow schema test paths to reference root `.workflows/` directory
26
+
package/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xiaoju
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ export type BumpOptions = {
2
+ type?: 'major' | 'minor' | 'patch';
3
+ cwd?: string;
4
+ now?: () => Date;
5
+ };
6
+ /**
7
+ * Bump package versions independently.
8
+ * --type: bump all packages with the given type.
9
+ * No --type: infer per-package bumps from changesets; only bump packages mentioned in changesets.
10
+ * Returns a map of package name → new version.
11
+ */
12
+ export declare function bump(opts?: BumpOptions): Promise<Record<string, string>>;
13
+ //# sourceMappingURL=bump.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bump.d.ts","sourceRoot":"","sources":["../../src/commands/bump.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;IAClC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB,CAAA;AA2BD;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkFlF"}
@@ -0,0 +1,115 @@
1
+ import { readFile, stat, unlink, writeFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { loadConfig } from '../config/load-config.js';
4
+ import { buildChangelogEntry, prependChangelog, readChangesets, } from '../utils/changeset.js';
5
+ import { bumpVersion, inferBump } from '../utils/version.js';
6
+ async function readJson(path) {
7
+ const text = await readFile(path, 'utf8');
8
+ return JSON.parse(text);
9
+ }
10
+ async function writeJson(path, data) {
11
+ await writeFile(path, `${JSON.stringify(data, null, 2)}\n`);
12
+ }
13
+ function formatDate(d) {
14
+ const y = d.getUTCFullYear();
15
+ const m = String(d.getUTCMonth() + 1).padStart(2, '0');
16
+ const day = String(d.getUTCDate()).padStart(2, '0');
17
+ return `${y}-${m}-${day}`;
18
+ }
19
+ async function fileExists(path) {
20
+ try {
21
+ await stat(path);
22
+ return true;
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ }
28
+ /**
29
+ * Bump package versions independently.
30
+ * --type: bump all packages with the given type.
31
+ * No --type: infer per-package bumps from changesets; only bump packages mentioned in changesets.
32
+ * Returns a map of package name → new version.
33
+ */
34
+ export async function bump(opts = {}) {
35
+ const cwd = opts.cwd ?? process.cwd();
36
+ const now = opts.now ?? (() => new Date());
37
+ const cfg = loadConfig(cwd);
38
+ const bumped = {};
39
+ if (opts.type) {
40
+ // Explicit --type: bump all packages
41
+ for (const pkg of cfg.packages) {
42
+ const pkgPath = resolve(cwd, pkg.path, 'package.json');
43
+ const json = await readJson(pkgPath);
44
+ const current = json.version;
45
+ if (!current)
46
+ throw new Error(`missing version in ${pkgPath}`);
47
+ const version = bumpVersion(current, opts.type);
48
+ json.version = version;
49
+ await writeJson(pkgPath, json);
50
+ bumped[pkg.name] = version;
51
+ }
52
+ }
53
+ else {
54
+ // Infer from changesets: per-package independent bump
55
+ const changesets = await readChangesets(cwd);
56
+ if (changesets.length === 0) {
57
+ throw new Error('no --type specified and no pending changesets found');
58
+ }
59
+ const bumpMap = inferBump(changesets);
60
+ if (Object.keys(bumpMap).length === 0) {
61
+ throw new Error('no inferable bump from changeset entries');
62
+ }
63
+ const pkgByName = new Map(cfg.packages.map((p) => [p.name, p]));
64
+ for (const [pkgName, bumpType] of Object.entries(bumpMap)) {
65
+ const pkg = pkgByName.get(pkgName);
66
+ if (!pkg)
67
+ continue; // changeset mentions unknown package, skip
68
+ const pkgPath = resolve(cwd, pkg.path, 'package.json');
69
+ const json = await readJson(pkgPath);
70
+ const current = json.version;
71
+ if (!current)
72
+ throw new Error(`missing version in ${pkgPath}`);
73
+ const version = bumpVersion(current, bumpType);
74
+ json.version = version;
75
+ await writeJson(pkgPath, json);
76
+ bumped[pkgName] = version;
77
+ }
78
+ // Generate CHANGELOG.md per bumped package
79
+ const date = formatDate(now());
80
+ const byPackage = {};
81
+ for (const cs of changesets) {
82
+ for (const pkg of Object.keys(cs.packages)) {
83
+ if (!pkgByName.has(pkg))
84
+ continue;
85
+ const arr = byPackage[pkg] ?? [];
86
+ arr.push(cs);
87
+ byPackage[pkg] = arr;
88
+ }
89
+ }
90
+ for (const [pkgName, version] of Object.entries(bumped)) {
91
+ const list = byPackage[pkgName];
92
+ if (!list || list.length === 0)
93
+ continue;
94
+ const pkg = pkgByName.get(pkgName);
95
+ if (!pkg)
96
+ continue;
97
+ const entry = buildChangelogEntry({
98
+ version,
99
+ date,
100
+ bodies: list.map((c) => c.body),
101
+ });
102
+ const path = resolve(cwd, pkg.path, 'CHANGELOG.md');
103
+ let existing = null;
104
+ if (await fileExists(path)) {
105
+ existing = await readFile(path, 'utf8');
106
+ }
107
+ await writeFile(path, prependChangelog(existing, entry));
108
+ }
109
+ // Delete consumed changeset files
110
+ for (const cs of changesets) {
111
+ await unlink(cs.file);
112
+ }
113
+ }
114
+ return bumped;
115
+ }
@@ -0,0 +1,9 @@
1
+ import { type SpawnFn } from '../utils/npm.js';
2
+ export type DeployCommandOptions = {
3
+ cwd: string;
4
+ pkg?: string;
5
+ env?: string;
6
+ spawn?: SpawnFn;
7
+ };
8
+ export declare function deploy(opts: DeployCommandOptions): Promise<void>;
9
+ //# sourceMappingURL=deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAExE,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAMD,wBAAsB,MAAM,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCtE"}
@@ -0,0 +1,42 @@
1
+ import { resolve } from 'node:path';
2
+ import { loadConfig } from '../config/index.js';
3
+ import { defaultSpawn, runOrThrow } from '../utils/npm.js';
4
+ function pnpmExec(bin, ...args) {
5
+ return ['pnpm', 'exec', bin, ...args];
6
+ }
7
+ export async function deploy(opts) {
8
+ const spawn = opts.spawn ?? defaultSpawn;
9
+ const cwd = resolve(opts.cwd);
10
+ const cfg = loadConfig(cwd);
11
+ let targets = cfg.packages;
12
+ if (opts.pkg !== undefined) {
13
+ const match = cfg.packages.find((p) => p.name === opts.pkg);
14
+ if (!match) {
15
+ throw new Error(`package not found: ${opts.pkg}`);
16
+ }
17
+ if (match.type !== 'webui' && match.type !== 'api') {
18
+ throw new Error(`package ${opts.pkg} (type=${match.type}) is not deployable; cannot deploy non-webui/api packages`);
19
+ }
20
+ targets = [match];
21
+ }
22
+ else {
23
+ targets = cfg.packages.filter((p) => p.type === 'webui' || p.type === 'api');
24
+ }
25
+ for (const pkg of targets) {
26
+ const pkgDir = resolve(cwd, pkg.path);
27
+ let argv;
28
+ if (pkg.type === 'webui') {
29
+ argv = pnpmExec('wrangler', 'pages', 'deploy', 'dist');
30
+ }
31
+ else if (pkg.type === 'api') {
32
+ argv = pnpmExec('wrangler', 'deploy');
33
+ }
34
+ else {
35
+ continue;
36
+ }
37
+ if (opts.env !== undefined) {
38
+ argv.push('--env', opts.env);
39
+ }
40
+ await runOrThrow(spawn, argv, pkgDir);
41
+ }
42
+ }
@@ -0,0 +1,15 @@
1
+ import { type SpawnFn } from '../utils/npm.js';
2
+ export type DevCommandOptions = {
3
+ cwd: string;
4
+ spawn?: SpawnFn;
5
+ /** When provided, enables fingerprint caching.
6
+ * - false: check fingerprint, skip if match
7
+ * - true: always run (--force / CI)
8
+ * - undefined: legacy behavior — always run, no fingerprint logic */
9
+ force?: boolean;
10
+ };
11
+ export declare function build(opts: DevCommandOptions): Promise<void>;
12
+ export declare function runTests(opts: DevCommandOptions): Promise<void>;
13
+ export declare function check(opts: DevCommandOptions): Promise<void>;
14
+ export declare function format(opts: DevCommandOptions): Promise<void>;
15
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAUA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAExE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;0EAGsE;IACtE,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAMD,wBAAsB,KAAK,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuElE;AAsBD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrE;AAED,wBAAsB,KAAK,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBlE;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAInE"}
@@ -0,0 +1,175 @@
1
+ import { chmodSync, existsSync, readdirSync, readFileSync, rmSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { loadConfig } from '../config/index.js';
4
+ import { computeBuildFingerprints, computeRootFingerprint, fingerprintPath, readFingerprint, writeFingerprint, } from '../utils/fingerprint.js';
5
+ import { defaultSpawn, runOrThrow } from '../utils/npm.js';
6
+ function pnpmExec(bin, ...args) {
7
+ return ['pnpm', 'exec', bin, ...args];
8
+ }
9
+ export async function build(opts) {
10
+ const spawn = opts.spawn ?? defaultSpawn;
11
+ const cwd = resolve(opts.cwd);
12
+ const cfg = loadConfig(cwd);
13
+ const useFingerprint = opts.force !== undefined;
14
+ const force = opts.force ?? false;
15
+ // Compute fingerprints only when fingerprint caching is enabled
16
+ const fingerprints = useFingerprint ? computeBuildFingerprints(cwd, cfg.packages) : null;
17
+ // Determine which packages to build
18
+ const toRun = [];
19
+ for (let i = 0; i < cfg.packages.length; i++) {
20
+ const pkg = cfg.packages[i];
21
+ const pkgDir = resolve(cwd, pkg.path);
22
+ const fpPath = fingerprintPath(pkgDir, 'build', pkg.name);
23
+ const fpValue = fingerprints?.get(pkg.name) ?? '';
24
+ if (useFingerprint && !force) {
25
+ const stored = readFingerprint(fpPath);
26
+ if (stored === fpValue) {
27
+ console.log(`⏭ build: ${pkg.name} (unchanged)`);
28
+ continue; // skip — fingerprint matches
29
+ }
30
+ }
31
+ toRun.push({ idx: i, pkgDir, fpPath, fpValue });
32
+ }
33
+ // Execute builds
34
+ for (const { idx, pkgDir } of toRun) {
35
+ const pkg = cfg.packages[idx];
36
+ // Clean output dir + tsbuildinfo before build to prevent stale artifacts.
37
+ // Note: since build fingerprints live inside dist/ (see fingerprintPath()),
38
+ // removing dist/ intentionally invalidates the build cache for this package.
39
+ const outDir = join(pkgDir, 'dist');
40
+ if (existsSync(outDir)) {
41
+ rmSync(outDir, { recursive: true });
42
+ }
43
+ const buildInfo = join(pkgDir, 'tsconfig.tsbuildinfo');
44
+ if (existsSync(buildInfo)) {
45
+ rmSync(buildInfo);
46
+ }
47
+ let argv;
48
+ switch (pkg.type) {
49
+ case 'webui':
50
+ argv = pnpmExec('vite', 'build');
51
+ break;
52
+ case 'cli':
53
+ // cli → use package's own build script (may be esbuild, tsc, etc.)
54
+ argv = ['pnpm', 'run', 'build'];
55
+ break;
56
+ default:
57
+ // lib | api → tsc --build
58
+ argv = pnpmExec('tsc', '--build');
59
+ break;
60
+ }
61
+ await runOrThrow(spawn, argv, pkgDir);
62
+ // chmod +x bin entries so linked CLIs survive tsc rebuild
63
+ chmodBinEntries(pkgDir);
64
+ }
65
+ // Write fingerprints only after ALL builds succeed (and only when enabled)
66
+ if (useFingerprint) {
67
+ for (const { fpPath, fpValue } of toRun) {
68
+ writeFingerprint(fpPath, fpValue);
69
+ }
70
+ }
71
+ }
72
+ function chmodBinEntries(pkgDir) {
73
+ const pkgJsonPath = join(pkgDir, 'package.json');
74
+ if (!existsSync(pkgJsonPath))
75
+ return;
76
+ const json = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
77
+ const bin = json.bin;
78
+ if (bin == null)
79
+ return;
80
+ const paths = typeof bin === 'string'
81
+ ? [bin]
82
+ : typeof bin === 'object'
83
+ ? Object.values(bin)
84
+ : [];
85
+ for (const rel of paths) {
86
+ const abs = resolve(pkgDir, rel);
87
+ if (existsSync(abs)) {
88
+ chmodSync(abs, 0o755);
89
+ }
90
+ }
91
+ }
92
+ export async function runTests(opts) {
93
+ const spawn = opts.spawn ?? defaultSpawn;
94
+ const cwd = resolve(opts.cwd);
95
+ const useFingerprint = opts.force !== undefined;
96
+ const force = opts.force ?? false;
97
+ if (useFingerprint) {
98
+ const fpPath = fingerprintPath(cwd, 'test');
99
+ const fpValue = computeRootFingerprint(cwd, 'test');
100
+ if (!force) {
101
+ const stored = readFingerprint(fpPath);
102
+ if (stored === fpValue) {
103
+ console.log('⏭ test (unchanged)');
104
+ return; // skip
105
+ }
106
+ }
107
+ await runOrThrow(spawn, pnpmExec('vitest', 'run'), cwd);
108
+ writeFingerprint(fpPath, fpValue);
109
+ }
110
+ else {
111
+ await runOrThrow(spawn, pnpmExec('vitest', 'run'), cwd);
112
+ }
113
+ }
114
+ export async function check(opts) {
115
+ const spawn = opts.spawn ?? defaultSpawn;
116
+ const cwd = resolve(opts.cwd);
117
+ const useFingerprint = opts.force !== undefined;
118
+ const force = opts.force ?? false;
119
+ if (useFingerprint) {
120
+ const fpPath = fingerprintPath(cwd, 'check');
121
+ const fpValue = computeRootFingerprint(cwd, 'check');
122
+ if (!force) {
123
+ const stored = readFingerprint(fpPath);
124
+ if (stored === fpValue) {
125
+ console.log('⏭ check (unchanged)');
126
+ return; // skip
127
+ }
128
+ }
129
+ await runOrThrow(spawn, pnpmExec('biome', 'check', '.'), cwd);
130
+ await validateWorkflows(spawn, cwd);
131
+ writeFingerprint(fpPath, fpValue);
132
+ }
133
+ else {
134
+ await runOrThrow(spawn, pnpmExec('biome', 'check', '.'), cwd);
135
+ await validateWorkflows(spawn, cwd);
136
+ }
137
+ }
138
+ export async function format(opts) {
139
+ const spawn = opts.spawn ?? defaultSpawn;
140
+ const cwd = resolve(opts.cwd);
141
+ await runOrThrow(spawn, pnpmExec('biome', 'format', '--write', '.'), cwd);
142
+ }
143
+ /** Discover .workflows/*.yaml and validate each with `uwf workflow validate`. Skips if uwf is not installed. */
144
+ async function validateWorkflows(spawn, cwd) {
145
+ const dirs = [join(cwd, '.workflows'), join(cwd, '.workflow')];
146
+ const files = [];
147
+ for (const dir of dirs) {
148
+ if (!existsSync(dir))
149
+ continue;
150
+ for (const entry of readdirSync(dir)) {
151
+ if (entry.endsWith('.yaml') || entry.endsWith('.yml')) {
152
+ files.push(join(dir, entry));
153
+ }
154
+ }
155
+ }
156
+ if (files.length === 0)
157
+ return;
158
+ // Check if uwf is available
159
+ const { code: uwfCheck } = await spawn(['which', 'uwf'], cwd);
160
+ if (uwfCheck !== 0) {
161
+ console.log('⚠ uwf not installed, skipping workflow validation');
162
+ return;
163
+ }
164
+ const errors = [];
165
+ for (const file of files) {
166
+ const { code, stderr } = await spawn(['uwf', 'workflow', 'validate', file], cwd);
167
+ if (code !== 0) {
168
+ errors.push(`${file}: ${stderr.trim() || 'validation failed'}`);
169
+ }
170
+ }
171
+ if (errors.length > 0) {
172
+ throw new Error(`Workflow validation failed:\n${errors.join('\n')}`);
173
+ }
174
+ console.log(`✓ ${files.length} workflow(s) validated`);
175
+ }
@@ -0,0 +1,7 @@
1
+ export { type BumpOptions, bump } from './bump.js';
2
+ export { type DeployCommandOptions, deploy } from './deploy.js';
3
+ export { build, check, type DevCommandOptions, format, runTests } from './dev.js';
4
+ export { type InitOptions, init } from './init.js';
5
+ export { type LinkCommandOptions, link, linkStatus, unlink } from './link.js';
6
+ export { type PublishOptions, publish } from './publish.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,iBAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACjF,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,KAAK,kBAAkB,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,KAAK,cAAc,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,7 @@
1
+ // Export all command functions
2
+ export { bump } from './bump.js';
3
+ export { deploy } from './deploy.js';
4
+ export { build, check, format, runTests } from './dev.js';
5
+ export { init } from './init.js';
6
+ export { link, linkStatus, unlink } from './link.js';
7
+ export { publish } from './publish.js';
@@ -0,0 +1,5 @@
1
+ export type InitOptions = {
2
+ targetDir: string;
3
+ };
4
+ export declare function init(opts: InitOptions): Promise<void>;
5
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AA6BD,wBAAsB,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C3D"}