@procedures/lib 0.0.0-stage → 0.2.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 (114) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +119 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/cli-flags.d.ts +27 -0
  6. package/dist/cli-flags.d.ts.map +1 -0
  7. package/dist/cli-flags.js +33 -0
  8. package/dist/cli-flags.js.map +1 -0
  9. package/dist/dag/builder.d.ts +18 -0
  10. package/dist/dag/builder.d.ts.map +1 -0
  11. package/dist/dag/builder.js +35 -0
  12. package/dist/dag/builder.js.map +1 -0
  13. package/dist/dag/index.d.ts +12 -0
  14. package/dist/dag/index.d.ts.map +1 -0
  15. package/dist/dag/index.js +12 -0
  16. package/dist/dag/index.js.map +1 -0
  17. package/dist/git/index.d.ts +5 -0
  18. package/dist/git/index.d.ts.map +1 -0
  19. package/dist/git/index.js +5 -0
  20. package/dist/git/index.js.map +1 -0
  21. package/dist/git/ref-parser.d.ts +34 -0
  22. package/dist/git/ref-parser.d.ts.map +1 -0
  23. package/dist/git/ref-parser.js +70 -0
  24. package/dist/git/ref-parser.js.map +1 -0
  25. package/dist/index.d.ts +14 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +19 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/module.d.ts +22 -0
  30. package/dist/module.d.ts.map +1 -0
  31. package/dist/module.js +180 -0
  32. package/dist/module.js.map +1 -0
  33. package/dist/procedures/core/catch.d.ts +33 -0
  34. package/dist/procedures/core/catch.d.ts.map +1 -0
  35. package/dist/procedures/core/catch.js +118 -0
  36. package/dist/procedures/core/catch.js.map +1 -0
  37. package/dist/procedures/core/index.d.ts +9 -0
  38. package/dist/procedures/core/index.d.ts.map +1 -0
  39. package/dist/procedures/core/index.js +8 -0
  40. package/dist/procedures/core/index.js.map +1 -0
  41. package/dist/procedures/dag/index.d.ts +5 -0
  42. package/dist/procedures/dag/index.d.ts.map +1 -0
  43. package/dist/procedures/dag/index.js +5 -0
  44. package/dist/procedures/dag/index.js.map +1 -0
  45. package/dist/procedures/dag/traverse.d.ts +54 -0
  46. package/dist/procedures/dag/traverse.d.ts.map +1 -0
  47. package/dist/procedures/dag/traverse.js +252 -0
  48. package/dist/procedures/dag/traverse.js.map +1 -0
  49. package/dist/procedures/ecosystem/index.d.ts +7 -0
  50. package/dist/procedures/ecosystem/index.d.ts.map +1 -0
  51. package/dist/procedures/ecosystem/index.js +6 -0
  52. package/dist/procedures/ecosystem/index.js.map +1 -0
  53. package/dist/procedures/ecosystem/procedures.d.ts +63 -0
  54. package/dist/procedures/ecosystem/procedures.d.ts.map +1 -0
  55. package/dist/procedures/ecosystem/procedures.js +215 -0
  56. package/dist/procedures/ecosystem/procedures.js.map +1 -0
  57. package/dist/procedures/lib/audit.d.ts +29 -0
  58. package/dist/procedures/lib/audit.d.ts.map +1 -0
  59. package/dist/procedures/lib/audit.js +236 -0
  60. package/dist/procedures/lib/audit.js.map +1 -0
  61. package/dist/procedures/lib/index.d.ts +8 -0
  62. package/dist/procedures/lib/index.d.ts.map +1 -0
  63. package/dist/procedures/lib/index.js +8 -0
  64. package/dist/procedures/lib/index.js.map +1 -0
  65. package/dist/procedures/lib/new.d.ts +20 -0
  66. package/dist/procedures/lib/new.d.ts.map +1 -0
  67. package/dist/procedures/lib/new.js +205 -0
  68. package/dist/procedures/lib/new.js.map +1 -0
  69. package/dist/procedures/lib/rename.d.ts +28 -0
  70. package/dist/procedures/lib/rename.d.ts.map +1 -0
  71. package/dist/procedures/lib/rename.js +267 -0
  72. package/dist/procedures/lib/rename.js.map +1 -0
  73. package/dist/procedures/lib/scan.d.ts +14 -0
  74. package/dist/procedures/lib/scan.d.ts.map +1 -0
  75. package/dist/procedures/lib/scan.js +102 -0
  76. package/dist/procedures/lib/scan.js.map +1 -0
  77. package/dist/register.d.ts +7 -0
  78. package/dist/register.d.ts.map +1 -0
  79. package/dist/register.js +9 -0
  80. package/dist/register.js.map +1 -0
  81. package/dist/scoped-input.d.ts +17 -0
  82. package/dist/scoped-input.d.ts.map +1 -0
  83. package/dist/scoped-input.js +23 -0
  84. package/dist/scoped-input.js.map +1 -0
  85. package/dist/types.d.ts +248 -0
  86. package/dist/types.d.ts.map +1 -0
  87. package/dist/types.js +231 -0
  88. package/dist/types.js.map +1 -0
  89. package/dist/workspace.d.ts +46 -0
  90. package/dist/workspace.d.ts.map +1 -0
  91. package/dist/workspace.js +98 -0
  92. package/dist/workspace.js.map +1 -0
  93. package/package.json +87 -3
  94. package/src/cli-flags.ts +49 -0
  95. package/src/dag/builder.ts +44 -0
  96. package/src/dag/index.ts +23 -0
  97. package/src/git/index.ts +11 -0
  98. package/src/git/ref-parser.ts +83 -0
  99. package/src/index.ts +56 -0
  100. package/src/module.ts +232 -0
  101. package/src/procedures/core/catch.ts +142 -0
  102. package/src/procedures/core/index.ts +9 -0
  103. package/src/procedures/dag/index.ts +5 -0
  104. package/src/procedures/dag/traverse.ts +281 -0
  105. package/src/procedures/ecosystem/index.ts +12 -0
  106. package/src/procedures/ecosystem/procedures.ts +317 -0
  107. package/src/procedures/lib/audit.ts +307 -0
  108. package/src/procedures/lib/index.ts +8 -0
  109. package/src/procedures/lib/new.ts +253 -0
  110. package/src/procedures/lib/rename.ts +355 -0
  111. package/src/procedures/lib/scan.ts +143 -0
  112. package/src/register.ts +11 -0
  113. package/src/types.ts +526 -0
  114. package/src/workspace.ts +110 -0
@@ -0,0 +1,143 @@
1
+ /**
2
+ * lib.scan procedure
3
+ *
4
+ * Scans the packages of the workspace (`<root>/packages/*`). The root is the pnpm
5
+ * workspace that contains client-lib, unless the input gives `rootPath`.
6
+ * All packages share one git repository, so the branch and the remote are read once.
7
+ */
8
+
9
+ import { join } from "node:path";
10
+ import type { ProcedureContext } from "@procedures/client";
11
+ import type { LibScanInput, LibScanOutput, PackageInfo } from "../../types.js";
12
+ import { extractEcosystemDeps, packagesDir, resolveWorkspaceRoot } from "../../workspace.js";
13
+
14
+ interface FsReadJsonOutput {
15
+ path: string;
16
+ data: unknown;
17
+ }
18
+
19
+ interface FsReaddirOutput {
20
+ path: string;
21
+ entries: Array<{ name: string; path: string; type: string }>;
22
+ }
23
+
24
+ interface GitStatusOutput {
25
+ branch: string;
26
+ }
27
+
28
+ interface GitRemoteOutput {
29
+ name: string;
30
+ url: string;
31
+ }
32
+
33
+ interface PackageJson {
34
+ name?: string;
35
+ dependencies?: Record<string, string>;
36
+ devDependencies?: Record<string, string>;
37
+ peerDependencies?: Record<string, string>;
38
+ }
39
+
40
+ /**
41
+ * Read and parse package.json from a directory using fs.read.json
42
+ */
43
+ async function readPackageJson(dirPath: string, ctx: ProcedureContext): Promise<PackageJson | null> {
44
+ try {
45
+ const result = await ctx.client.call<{ path: string }, FsReadJsonOutput>(
46
+ ["fs", "read.json"],
47
+ { path: join(dirPath, "package.json") }
48
+ );
49
+ return result.data as PackageJson;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Read the branch and the origin remote of the repository that holds the workspace
57
+ */
58
+ async function readRepositoryInfo(
59
+ rootPath: string,
60
+ ctx: ProcedureContext
61
+ ): Promise<{ branch?: string; remote?: string; warning?: string }> {
62
+ try {
63
+ const status = await ctx.client.call<{ cwd?: string }, GitStatusOutput>(["git", "status"], { cwd: rootPath });
64
+ let remote: string | undefined;
65
+ try {
66
+ const result = await ctx.client.call<{ cwd?: string; name?: string }, GitRemoteOutput>(
67
+ ["git", "remote"],
68
+ { cwd: rootPath, name: "origin" }
69
+ );
70
+ remote = result.url;
71
+ } catch {
72
+ // No remote configured - that's fine
73
+ }
74
+ return remote === undefined ? { branch: status.branch } : { branch: status.branch, remote };
75
+ } catch (error) {
76
+ return { warning: `Git not initialized: ${error instanceof Error ? error.message : String(error)}` };
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Scan the workspace packages
82
+ */
83
+ export async function libScan(input: LibScanInput, ctx: ProcedureContext): Promise<LibScanOutput> {
84
+ const packages: Record<string, PackageInfo> = {};
85
+ const warnings: Array<{ path: string; issue: string }> = [];
86
+
87
+ let rootPath: string;
88
+ try {
89
+ rootPath = resolveWorkspaceRoot(input.rootPath);
90
+ } catch (error) {
91
+ warnings.push({
92
+ path: input.rootPath ?? process.cwd(),
93
+ issue: error instanceof Error ? error.message : String(error),
94
+ });
95
+ return { packages, warnings };
96
+ }
97
+
98
+ const dir = packagesDir(rootPath);
99
+ let entries: FsReaddirOutput["entries"];
100
+ try {
101
+ const result = await ctx.client.call<{ path: string }, FsReaddirOutput>(["fs", "readdir"], { path: dir });
102
+ entries = result.entries.filter((entry) => entry.type === "directory");
103
+ } catch (error) {
104
+ warnings.push({
105
+ path: dir,
106
+ issue: `Failed to list workspace packages: ${error instanceof Error ? error.message : String(error)}`,
107
+ });
108
+ return { packages, warnings };
109
+ }
110
+
111
+ const repository = await readRepositoryInfo(rootPath, ctx);
112
+ if (repository.warning) {
113
+ warnings.push({ path: rootPath, issue: repository.warning });
114
+ }
115
+
116
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
117
+ const pkgPath = join(dir, entry.name);
118
+ const pkg = await readPackageJson(pkgPath, ctx);
119
+ if (!pkg) {
120
+ warnings.push({ path: pkgPath, issue: "Failed to read package.json" });
121
+ continue;
122
+ }
123
+ if (!pkg.name) {
124
+ warnings.push({ path: pkgPath, issue: "package.json has no name" });
125
+ continue;
126
+ }
127
+
128
+ const info: PackageInfo = {
129
+ name: pkg.name,
130
+ repoPath: pkgPath,
131
+ mark1russell7Deps: extractEcosystemDeps(pkg),
132
+ };
133
+ if (repository.branch !== undefined) {
134
+ info.currentBranch = repository.branch;
135
+ }
136
+ if (repository.remote !== undefined) {
137
+ info.gitRemote = repository.remote;
138
+ }
139
+ packages[pkg.name] = info;
140
+ }
141
+
142
+ return { packages, warnings };
143
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Importing this file registers the procedures of @procedures/lib, and of the modules that it
3
+ * requires, in the global registry: `import "@procedures/lib/register"`. The package root and the
4
+ * `./module` entry register nothing (ARCHITECTURE-PROPOSALS-2026-10, P9).
5
+ */
6
+
7
+ import { registerLibProcedures } from "./module.js";
8
+
9
+ export * from "./module.js";
10
+
11
+ registerLibProcedures();
package/src/types.ts ADDED
@@ -0,0 +1,526 @@
1
+ /**
2
+ * Type definitions for client-lib procedures
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import type {
7
+ DAGNode as DagBaseNode,
8
+ DependencyDAG as DagDependencyDAG,
9
+ DAGExecutionOptions as DagExecutionOptions,
10
+ NodeResult as DagNodeResult,
11
+ DAGResult as DagResult,
12
+ } from "@procedures/dag";
13
+
14
+ // =============================================================================
15
+ // lib.scan Types
16
+ // =============================================================================
17
+
18
+ export const LibScanInputSchema: z.ZodObject<{
19
+ rootPath: z.ZodOptional<z.ZodString>;
20
+ }> = z.object({
21
+ /** Workspace root (defaults to the pnpm workspace that contains client-lib) */
22
+ rootPath: z.string().optional().describe("Workspace root (defaults to the pnpm workspace that contains client-lib)"),
23
+ });
24
+
25
+ export type LibScanInput = z.infer<typeof LibScanInputSchema>;
26
+
27
+ export interface PackageInfo {
28
+ /** Package name from package.json */
29
+ name: string;
30
+ /** Absolute path to the package folder */
31
+ repoPath: string;
32
+ /** Git remote URL of the workspace repository, if available */
33
+ gitRemote?: string | undefined;
34
+ /** Current branch of the workspace repository */
35
+ currentBranch?: string | undefined;
36
+ /** The ecosystem dependencies (@procedures/* and @mark1russell7/*): workspace links and github: references */
37
+ mark1russell7Deps: string[];
38
+ }
39
+
40
+ export interface LibScanOutput {
41
+ /** Map of package name to package info */
42
+ packages: Record<string, PackageInfo>;
43
+ /** Warnings for any issues found */
44
+ warnings: Array<{ path: string; issue: string }>;
45
+ }
46
+
47
+ // =============================================================================
48
+ // DAG Types (ecosystem-specific)
49
+ // =============================================================================
50
+
51
+ // DAGNode extends @procedures/dag's base node (which supplies `id`, `dependencies`, and
52
+ // `level`, and keys nodes by id), adding the ecosystem-specific fields. The generic DAG algorithms
53
+ // (buildLeveledDAG/executeDAG/...) come from client-dag; only buildDAGNodes is local, and it sets
54
+ // `id` = `name`. This deduplicates a previously vendored copy. See documentation/BUGS-2026-07.md.
55
+ export interface DAGNode extends DagBaseNode {
56
+ /** Package name (e.g., "@mark1russell7/logger"). Mirrors `id`. */
57
+ name: string;
58
+ /** Path to the repo */
59
+ repoPath: string;
60
+ /** Git ref from package.json (e.g., "github:mark1russell7/logger#main") */
61
+ gitRef: string;
62
+ /** Required branch from git ref */
63
+ requiredBranch: string;
64
+ }
65
+
66
+ // Aggregate DAG types are client-dag's generics parameterized by the ecosystem node, so they line
67
+ // up structurally with what the re-exported client-dag functions accept and return.
68
+ export type DependencyDAG = DagDependencyDAG<DAGNode>;
69
+ export type DAGExecutionOptions = DagExecutionOptions<DAGNode>;
70
+ export type NodeResult = DagNodeResult<DAGNode>;
71
+ export type DAGResult = DagResult<DAGNode>;
72
+
73
+ // =============================================================================
74
+ // Git Types
75
+ // =============================================================================
76
+
77
+ export interface GitRef {
78
+ /** Full ref string (e.g., "github:mark1russell7/logger#main") */
79
+ raw: string;
80
+ /** Host (github, gitlab, etc.) */
81
+ host: string;
82
+ /** Owner/org */
83
+ owner: string;
84
+ /** Repo name */
85
+ repo: string;
86
+ /** Branch or tag */
87
+ ref: string;
88
+ }
89
+
90
+ export interface GitStatus {
91
+ /** Whether the repo has uncommitted changes */
92
+ hasUncommittedChanges: boolean;
93
+ /** Whether there are staged changes */
94
+ hasStagedChanges: boolean;
95
+ /** Current branch */
96
+ currentBranch: string;
97
+ /** Whether the repo is clean */
98
+ isClean: boolean;
99
+ }
100
+
101
+ // =============================================================================
102
+ // lib.rename Types
103
+ // =============================================================================
104
+
105
+ export const LibRenameInputSchema: z.ZodObject<{
106
+ oldName: z.ZodString;
107
+ newName: z.ZodString;
108
+ rootPath: z.ZodOptional<z.ZodString>;
109
+ dryRun: z.ZodDefault<z.ZodBoolean>;
110
+ }> = z.object({
111
+ /** Current package name to rename from */
112
+ oldName: z.string().describe("Current package name to rename from"),
113
+ /** New package name to rename to */
114
+ newName: z.string().describe("New package name to rename to"),
115
+ /** Workspace root (defaults to the pnpm workspace that contains client-lib) */
116
+ rootPath: z.string().optional().describe("Workspace root (defaults to the pnpm workspace that contains client-lib)"),
117
+ /** Preview changes without applying (default: false) */
118
+ dryRun: z.boolean().default(false).describe("Preview changes without applying (default: false)"),
119
+ });
120
+
121
+ export type LibRenameInput = z.infer<typeof LibRenameInputSchema>;
122
+
123
+ export interface RenameChange {
124
+ /** Type of change */
125
+ type: "package-name" | "dependency" | "import" | "dynamic-import";
126
+ /** File that was changed */
127
+ file: string;
128
+ /** Field name (for dependency changes) */
129
+ field?: string | undefined;
130
+ /** Line number (for imports) */
131
+ line?: number | undefined;
132
+ /** Old value */
133
+ oldValue: string;
134
+ /** New value */
135
+ newValue: string;
136
+ }
137
+
138
+ export interface LibRenameOutput {
139
+ /** Whether all changes succeeded */
140
+ success: boolean;
141
+ /** List of changes made (or would be made if dryRun) */
142
+ changes: RenameChange[];
143
+ /** Any errors encountered */
144
+ errors: string[];
145
+ /** Summary counts */
146
+ summary: {
147
+ packageNames: number;
148
+ dependencies: number;
149
+ imports: number;
150
+ total: number;
151
+ };
152
+ }
153
+
154
+ // =============================================================================
155
+ // lib.new Types
156
+ // =============================================================================
157
+
158
+ export const LibNewInputSchema: z.ZodObject<{
159
+ name: z.ZodString;
160
+ preset: z.ZodDefault<z.ZodString>;
161
+ rootPath: z.ZodOptional<z.ZodString>;
162
+ dryRun: z.ZodDefault<z.ZodBoolean>;
163
+ }> = z.object({
164
+ /** Package name (without the @procedures/ scope) */
165
+ name: z.string().regex(/^[a-z][a-z0-9-]*$/, "Name must be lowercase alphanumeric with hyphens").describe("Package name (without the @procedures/ scope)"),
166
+ /** Feature preset to use */
167
+ preset: z.string().regex(/^[a-z][a-z0-9-]*$/, "Preset must be lowercase alphanumeric with hyphens").default("lib").describe("Feature preset to use"),
168
+ /** Workspace root (defaults to the pnpm workspace that contains client-lib) */
169
+ rootPath: z.string().optional().describe("Workspace root (defaults to the pnpm workspace that contains client-lib)"),
170
+ /** Preview changes without creating */
171
+ dryRun: z.boolean().default(false).describe("Preview changes without creating"),
172
+ });
173
+
174
+ export type LibNewInput = z.infer<typeof LibNewInputSchema>;
175
+
176
+ export interface LibNewOutput {
177
+ /** Whether creation succeeded */
178
+ success: boolean;
179
+ /** Full package name (@procedures/...) */
180
+ packageName: string;
181
+ /** Path to created package */
182
+ packagePath: string;
183
+ /** Files created */
184
+ created: string[];
185
+ /** Operations performed */
186
+ operations: string[];
187
+ /** Any errors encountered */
188
+ errors: string[];
189
+ }
190
+
191
+ // =============================================================================
192
+ // lib.audit Types
193
+ // =============================================================================
194
+
195
+ export const LibAuditInputSchema: z.ZodObject<{
196
+ rootPath: z.ZodOptional<z.ZodString>;
197
+ fix: z.ZodDefault<z.ZodBoolean>;
198
+ }> = z.object({
199
+ /** Workspace root (defaults to the pnpm workspace that contains client-lib) */
200
+ rootPath: z.string().optional().describe("Workspace root (defaults to the pnpm workspace that contains client-lib)"),
201
+ /** Attempt to fix issues (create missing files/dirs) */
202
+ fix: z.boolean().default(false).describe("Attempt to fix issues (create missing files/dirs)"),
203
+ });
204
+
205
+ export type LibAuditInput = z.infer<typeof LibAuditInputSchema>;
206
+
207
+ export interface PnpmIssue {
208
+ /** Type of pnpm issue */
209
+ type: "npm-lockfile" | "package-lockfile" | "ignored-pnpm-field" | "not-workspace-link";
210
+ /** Description of the issue */
211
+ message: string;
212
+ /** The dependency the issue is about, if any */
213
+ package?: string | undefined;
214
+ }
215
+
216
+ export interface PackageAuditResult {
217
+ /** Package name */
218
+ name: string;
219
+ /** Package path */
220
+ path: string;
221
+ /** Whether package passes audit */
222
+ valid: boolean;
223
+ /** Missing required files */
224
+ missingFiles: string[];
225
+ /** Missing required directories */
226
+ missingDirs: string[];
227
+ /** pnpm configuration issues */
228
+ pnpmIssues: PnpmIssue[];
229
+ /** Files that were fixed (if fix=true) */
230
+ fixedFiles?: string[] | undefined;
231
+ /** Dirs that were fixed (if fix=true) */
232
+ fixedDirs?: string[] | undefined;
233
+ }
234
+
235
+ /**
236
+ * A procedure flag that hides a global flag of `mark` after the command path. It is not an
237
+ * error: the user gives the global flag before the command.
238
+ */
239
+ export interface FlagNotice {
240
+ /** The procedure path, for example "docker compose down" */
241
+ procedure: string;
242
+ /** The flag of the procedure, for example "-v" */
243
+ flag: string;
244
+ /** The global flag that it hides, for example "-v (--version)" */
245
+ global: string;
246
+ }
247
+
248
+ export interface LibAuditOutput {
249
+ /** Overall success (all packages valid) */
250
+ success: boolean;
251
+ /** Procedure flags that hide a global flag of mark after the command path (information only) */
252
+ flagNotices: FlagNotice[];
253
+ /** Project template used for validation */
254
+ template: {
255
+ files: string[];
256
+ dirs: string[];
257
+ };
258
+ /** Results per package */
259
+ results: PackageAuditResult[];
260
+ /** Summary counts */
261
+ summary: {
262
+ total: number;
263
+ valid: number;
264
+ invalid: number;
265
+ };
266
+ }
267
+
268
+ // =============================================================================
269
+ // dag.traverse Types
270
+ // =============================================================================
271
+
272
+ /**
273
+ * Schema for $proc references with $when control.
274
+ */
275
+ const ProcRefSchema: z.ZodObject<{
276
+ $proc: z.ZodArray<z.ZodString>;
277
+ input: z.ZodOptional<z.ZodUnknown>;
278
+ $when: z.ZodOptional<z.ZodString>;
279
+ $name: z.ZodOptional<z.ZodString>;
280
+ }> = z.object({
281
+ $proc: z.array(z.string()),
282
+ input: z.unknown().optional(),
283
+ $when: z.string().optional(),
284
+ $name: z.string().optional(),
285
+ });
286
+
287
+ export const DagTraverseInputSchema: z.ZodObject<{
288
+ visit: z.ZodUnion<readonly [z.ZodArray<z.ZodString>, typeof ProcRefSchema]>;
289
+ filter: z.ZodOptional<z.ZodArray<z.ZodString>>;
290
+ root: z.ZodOptional<z.ZodString>;
291
+ rootPath: z.ZodOptional<z.ZodString>;
292
+ concurrency: z.ZodDefault<z.ZodNumber>;
293
+ serialize: z.ZodDefault<z.ZodEnum<{ auto: "auto"; repository: "repository"; none: "none" }>>;
294
+ continueOnError: z.ZodDefault<z.ZodBoolean>;
295
+ dryRun: z.ZodDefault<z.ZodBoolean>;
296
+ }> = z.object({
297
+ /**
298
+ * Procedure to execute for each node.
299
+ * - Array: procedure path, e.g., ["git", "add"]
300
+ * - $proc with $when: "$never" or "$parent": deferred, executed per-node
301
+ *
302
+ * Example:
303
+ * ```json
304
+ * { "$proc": ["git", "add"], "input": { "all": true }, "$when": "$parent" }
305
+ * ```
306
+ */
307
+ visit: z.union([
308
+ z.array(z.string()),
309
+ ProcRefSchema,
310
+ ]).describe("Procedure to execute for each node. - Array: procedure path, e.g., [\"git\", \"add\"] - $proc with $when: \"$never\" or \"$parent\": deferred, executed per-node Example: ```json { \"$proc\": [\"git\", \"add\"], \"input\": { \"all\": true }, \"$when\": \"$parent\" } ```"),
311
+ /** Filter to specific package names */
312
+ filter: z.array(z.string()).optional().describe("Filter to specific package names"),
313
+ /** Start from specific root package */
314
+ root: z.string().optional().describe("Start from specific root package"),
315
+ /** Workspace root (defaults to the pnpm workspace that contains client-lib) */
316
+ rootPath: z.string().optional().describe("Workspace root (defaults to the pnpm workspace that contains client-lib)"),
317
+ /** Max parallel operations (default: 4) */
318
+ concurrency: z.number().default(4).describe("Max parallel operations (default: 4)"),
319
+ /**
320
+ * When the visits of nodes in one git repository run one at a time (default: "auto").
321
+ * - "auto": when the visit calls a `git` procedure (git locks the index of the repository)
322
+ * - "repository": always
323
+ * - "none": never
324
+ */
325
+ serialize: z.enum(["auto", "repository", "none"]).default("auto").describe("When the visits of nodes in one git repository run one at a time (default: \"auto\"). - \"auto\": when the visit calls a `git` procedure (git locks the index of the repository) - \"repository\": always - \"none\": never"),
326
+ /** Continue on error (default: false) */
327
+ continueOnError: z.boolean().default(false).describe("Continue on error (default: false)"),
328
+ /** Preview without executing (default: false) */
329
+ dryRun: z.boolean().default(false).describe("Preview without executing (default: false)"),
330
+ });
331
+
332
+ export type DagTraverseInput = z.infer<typeof DagTraverseInputSchema>;
333
+
334
+ export interface TraverseNodeResult {
335
+ name: string;
336
+ path: string;
337
+ success: boolean;
338
+ duration: number;
339
+ error?: string | undefined;
340
+ output?: unknown | undefined;
341
+ }
342
+
343
+ export interface DagTraverseOutput {
344
+ success: boolean;
345
+ results: TraverseNodeResult[];
346
+ totalDuration: number;
347
+ visited: number;
348
+ failed: number;
349
+ }
350
+
351
+ // =============================================================================
352
+ // core.catch Types
353
+ // =============================================================================
354
+
355
+ export const CoreCatchInputSchema: z.ZodObject<{
356
+ try: z.ZodUnknown;
357
+ handler: z.ZodOptional<z.ZodUnknown>;
358
+ cwd: z.ZodOptional<z.ZodString>;
359
+ }> = z.object({
360
+ /**
361
+ * Procedure ref to try executing.
362
+ * Should use $when: "catch" so it's not auto-executed during hydration.
363
+ *
364
+ * Example:
365
+ * ```json
366
+ * { "$proc": ["git", "commit"], "input": { "message": "auto" }, "$when": "catch" }
367
+ * ```
368
+ */
369
+ try: z.unknown().describe("Procedure ref to try executing. Should use $when: \"catch\" so it's not auto-executed during hydration. Example: ```json { \"$proc\": [\"git\", \"commit\"], \"input\": { \"message\": \"auto\" }, \"$when\": \"catch\" } ```"),
370
+ /**
371
+ * Handler procedure ref called on error.
372
+ * Receives StepResultInfo (success, error, proc) merged with its input.
373
+ * Should return ContinueDecision { continue: boolean }.
374
+ */
375
+ handler: z.unknown().optional().describe("Handler procedure ref called on error. Receives StepResultInfo (success, error, proc) merged with its input. Should return ContinueDecision { continue: boolean }."),
376
+ /** Working directory for procedure execution */
377
+ cwd: z.string().optional().describe("Working directory for procedure execution"),
378
+ });
379
+
380
+ export type CoreCatchInput = z.infer<typeof CoreCatchInputSchema>;
381
+
382
+ export interface CoreCatchOutput {
383
+ /** Whether the try succeeded */
384
+ success: boolean;
385
+ /** Result from try (if success) or handler decision */
386
+ result?: unknown | undefined;
387
+ /** Error message if failed */
388
+ error?: string | undefined;
389
+ /** Whether execution should continue (from handler) */
390
+ continue: boolean;
391
+ }
392
+
393
+ // =============================================================================
394
+ // Output schemas (roadmap 2.1)
395
+ // =============================================================================
396
+
397
+ // Loose objects: a field that a schema does not name stays in the result (a strict schema would
398
+ // remove it when the output is validated).
399
+
400
+ export const LibScanOutputSchema: z.ZodType<LibScanOutput> = z.looseObject({
401
+ packages: z
402
+ .record(
403
+ z.string(),
404
+ z.looseObject({
405
+ name: z.string().describe("The package name"),
406
+ repoPath: z.string().describe("The absolute path of the package folder"),
407
+ gitRemote: z.string().optional().describe("The git remote URL of the workspace repository"),
408
+ currentBranch: z.string().optional().describe("The current branch of the workspace repository"),
409
+ mark1russell7Deps: z.array(z.string()).describe("The ecosystem dependencies of the package (@procedures/* and @mark1russell7/*)"),
410
+ })
411
+ )
412
+ .describe("The workspace packages, by name"),
413
+ warnings: z
414
+ .array(z.looseObject({ path: z.string().describe("The folder"), issue: z.string().describe("The problem") }))
415
+ .describe("The problems that the scan found"),
416
+ });
417
+
418
+ export const LibRenameOutputSchema: z.ZodType<LibRenameOutput> = z.looseObject({
419
+ success: z.boolean().describe("True when every change succeeded"),
420
+ changes: z
421
+ .array(
422
+ z.looseObject({
423
+ type: z.enum(["package-name", "dependency", "import", "dynamic-import"]).describe("The kind of change"),
424
+ file: z.string().describe("The file that changes"),
425
+ field: z.string().optional().describe("The dependency field, for a dependency change"),
426
+ line: z.number().optional().describe("The line, for an import change"),
427
+ oldValue: z.string().describe("The old text"),
428
+ newValue: z.string().describe("The new text"),
429
+ })
430
+ )
431
+ .describe("The changes that the call made, or would make with dryRun"),
432
+ errors: z.array(z.string()).describe("The errors"),
433
+ summary: z
434
+ .looseObject({
435
+ packageNames: z.number().describe("The number of package name changes"),
436
+ dependencies: z.number().describe("The number of dependency changes"),
437
+ imports: z.number().describe("The number of import changes"),
438
+ total: z.number().describe("The number of changes"),
439
+ })
440
+ .describe("The counts"),
441
+ });
442
+
443
+ export const LibNewOutputSchema: z.ZodType<LibNewOutput> = z.looseObject({
444
+ success: z.boolean().describe("True when the package was made"),
445
+ packageName: z.string().describe("The full package name (@procedures/...)"),
446
+ packagePath: z.string().describe("The folder of the new package"),
447
+ created: z.array(z.string()).describe("The files that the call made"),
448
+ operations: z.array(z.string()).describe("The steps that the call ran"),
449
+ errors: z.array(z.string()).describe("The errors"),
450
+ });
451
+
452
+ export const LibAuditOutputSchema: z.ZodType<LibAuditOutput> = z.looseObject({
453
+ success: z.boolean().describe("True when every package passes the audit"),
454
+ flagNotices: z
455
+ .array(
456
+ z.looseObject({
457
+ procedure: z.string().describe('The procedure path, for example "docker compose down"'),
458
+ flag: z.string().describe('The flag of the procedure, for example "-v"'),
459
+ global: z.string().describe('The global flag of mark that it hides, for example "-v (--version)"'),
460
+ })
461
+ )
462
+ .describe("Procedure flags that hide a global flag of mark after the command path (information only)"),
463
+ template: z
464
+ .looseObject({
465
+ files: z.array(z.string()).describe("The files that each package needs"),
466
+ dirs: z.array(z.string()).describe("The folders that each package needs"),
467
+ })
468
+ .describe("The template of the audit"),
469
+ results: z
470
+ .array(
471
+ z.looseObject({
472
+ name: z.string().describe("The package name"),
473
+ path: z.string().describe("The package folder"),
474
+ valid: z.boolean().describe("True when the package passes"),
475
+ missingFiles: z.array(z.string()).describe("The required files that are missing"),
476
+ missingDirs: z.array(z.string()).describe("The required folders that are missing"),
477
+ pnpmIssues: z
478
+ .array(
479
+ z.looseObject({
480
+ type: z
481
+ .enum(["npm-lockfile", "package-lockfile", "ignored-pnpm-field", "not-workspace-link"])
482
+ .describe("The kind of pnpm problem"),
483
+ message: z.string().describe("The problem"),
484
+ package: z.string().optional().describe("The dependency that the problem is about"),
485
+ })
486
+ )
487
+ .describe("The pnpm configuration problems"),
488
+ fixedFiles: z.array(z.string()).optional().describe("The files that fix made"),
489
+ fixedDirs: z.array(z.string()).optional().describe("The folders that fix made"),
490
+ })
491
+ )
492
+ .describe("The result of each package"),
493
+ summary: z
494
+ .looseObject({
495
+ total: z.number().describe("The number of packages"),
496
+ valid: z.number().describe("The number of packages that pass"),
497
+ invalid: z.number().describe("The number of packages that fail"),
498
+ })
499
+ .describe("The counts"),
500
+ });
501
+
502
+ export const DagTraverseOutputSchema: z.ZodType<DagTraverseOutput> = z.looseObject({
503
+ success: z.boolean().describe("True when every visit succeeded"),
504
+ results: z
505
+ .array(
506
+ z.looseObject({
507
+ name: z.string().describe("The package name of the node"),
508
+ path: z.string().describe("The folder of the node"),
509
+ success: z.boolean().describe("True when the visit succeeded"),
510
+ duration: z.number().describe("The time of the visit, in milliseconds"),
511
+ error: z.string().optional().describe("The error of a failed visit"),
512
+ output: z.unknown().optional().describe("The result of the visit"),
513
+ })
514
+ )
515
+ .describe("The result of each visited node"),
516
+ totalDuration: z.number().describe("The time of the traversal, in milliseconds"),
517
+ visited: z.number().describe("The number of visited nodes"),
518
+ failed: z.number().describe("The number of failed visits"),
519
+ });
520
+
521
+ export const CoreCatchOutputSchema: z.ZodType<CoreCatchOutput> = z.looseObject({
522
+ success: z.boolean().describe("True when the try operand succeeded"),
523
+ result: z.unknown().optional().describe("The result of try, or the decision of the handler"),
524
+ error: z.string().optional().describe("The error of try, when it failed"),
525
+ continue: z.boolean().describe("True when the caller can continue"),
526
+ });