@holdpoint/yaml-core 0.1.0-alpha.1 → 0.1.0-alpha.11

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.
package/dist/index.d.ts CHANGED
@@ -24,264 +24,107 @@ declare function generateYaml(config: HoldpointConfig): string;
24
24
  */
25
25
  declare function matchesWhen(when: string | undefined, changedFiles: string[], userPatterns?: Record<string, string>): boolean;
26
26
 
27
- declare const HookEventSchema: z.ZodEnum<["before_done"]>;
27
+ declare const HookEventSchema: z.ZodEnum<{
28
+ session_start: "session_start";
29
+ message_submit: "message_submit";
30
+ before_tool: "before_tool";
31
+ after_tool: "after_tool";
32
+ session_end: "session_end";
33
+ before_done: "before_done";
34
+ }>;
35
+ declare const InjectSpecSchema: z.ZodObject<{
36
+ text: z.ZodOptional<z.ZodString>;
37
+ files: z.ZodOptional<z.ZodArray<z.ZodString>>;
38
+ datetime: z.ZodOptional<z.ZodBoolean>;
39
+ }, z.core.$strip>;
28
40
  declare const ConditionDefSchema: z.ZodObject<{
29
41
  id: z.ZodString;
30
- operator: z.ZodEnum<["file_exists", "file_contains", "env_var_set", "shell_returns_0"]>;
42
+ operator: z.ZodEnum<{
43
+ file_exists: "file_exists";
44
+ file_contains: "file_contains";
45
+ env_var_set: "env_var_set";
46
+ shell_returns_0: "shell_returns_0";
47
+ }>;
31
48
  path: z.ZodOptional<z.ZodString>;
32
49
  contains: z.ZodOptional<z.ZodString>;
33
50
  envVar: z.ZodOptional<z.ZodString>;
34
51
  cmd: z.ZodOptional<z.ZodString>;
35
- }, "strip", z.ZodTypeAny, {
36
- id: string;
37
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
38
- path?: string | undefined;
39
- contains?: string | undefined;
40
- envVar?: string | undefined;
41
- cmd?: string | undefined;
42
- }, {
43
- id: string;
44
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
45
- path?: string | undefined;
46
- contains?: string | undefined;
47
- envVar?: string | undefined;
48
- cmd?: string | undefined;
49
- }>;
50
- declare const CheckDefSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
52
+ }, z.core.$strip>;
53
+ declare const CheckDefSchema: z.ZodPreprocess<z.ZodObject<{
51
54
  id: z.ZodString;
52
55
  label: z.ZodString;
53
- on: z.ZodOptional<z.ZodEnum<["before_done"]>>;
56
+ on: z.ZodOptional<z.ZodEnum<{
57
+ session_start: "session_start";
58
+ message_submit: "message_submit";
59
+ before_tool: "before_tool";
60
+ after_tool: "after_tool";
61
+ session_end: "session_end";
62
+ before_done: "before_done";
63
+ }>>;
54
64
  when: z.ZodOptional<z.ZodString>;
55
65
  cmd: z.ZodOptional<z.ZodString>;
56
66
  prompt: z.ZodOptional<z.ZodString>;
67
+ inject: z.ZodOptional<z.ZodObject<{
68
+ text: z.ZodOptional<z.ZodString>;
69
+ files: z.ZodOptional<z.ZodArray<z.ZodString>>;
70
+ datetime: z.ZodOptional<z.ZodBoolean>;
71
+ }, z.core.$strip>>;
57
72
  conditionId: z.ZodOptional<z.ZodString>;
58
- }, "strip", z.ZodTypeAny, {
59
- id: string;
60
- label: string;
61
- cmd?: string | undefined;
62
- on?: "before_done" | undefined;
63
- when?: string | undefined;
64
- prompt?: string | undefined;
65
- conditionId?: string | undefined;
66
- }, {
67
- id: string;
68
- label: string;
69
- cmd?: string | undefined;
70
- on?: "before_done" | undefined;
71
- when?: string | undefined;
72
- prompt?: string | undefined;
73
- conditionId?: string | undefined;
74
- }>, {
75
- id: string;
76
- label: string;
77
- cmd?: string | undefined;
78
- on?: "before_done" | undefined;
79
- when?: string | undefined;
80
- prompt?: string | undefined;
81
- conditionId?: string | undefined;
82
- }, {
83
- id: string;
84
- label: string;
85
- cmd?: string | undefined;
86
- on?: "before_done" | undefined;
87
- when?: string | undefined;
88
- prompt?: string | undefined;
89
- conditionId?: string | undefined;
90
- }>, {
91
- id: string;
92
- label: string;
93
- cmd?: string | undefined;
94
- on?: "before_done" | undefined;
95
- when?: string | undefined;
96
- prompt?: string | undefined;
97
- conditionId?: string | undefined;
98
- }, unknown>;
99
- declare const HoldpointConfigSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
73
+ }, z.core.$strip>>;
74
+ declare const HoldpointConfigSchema: z.ZodPreprocess<z.ZodObject<{
100
75
  version: z.ZodDefault<z.ZodNumber>;
101
76
  context: z.ZodDefault<z.ZodObject<{
102
77
  guides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
103
- }, "strip", z.ZodTypeAny, {
104
- guides: Record<string, string>;
105
- }, {
106
- guides?: Record<string, string> | undefined;
107
- }>>;
78
+ }, z.core.$strip>>;
108
79
  conditions: z.ZodDefault<z.ZodArray<z.ZodObject<{
109
80
  id: z.ZodString;
110
- operator: z.ZodEnum<["file_exists", "file_contains", "env_var_set", "shell_returns_0"]>;
81
+ operator: z.ZodEnum<{
82
+ file_exists: "file_exists";
83
+ file_contains: "file_contains";
84
+ env_var_set: "env_var_set";
85
+ shell_returns_0: "shell_returns_0";
86
+ }>;
111
87
  path: z.ZodOptional<z.ZodString>;
112
88
  contains: z.ZodOptional<z.ZodString>;
113
89
  envVar: z.ZodOptional<z.ZodString>;
114
90
  cmd: z.ZodOptional<z.ZodString>;
115
- }, "strip", z.ZodTypeAny, {
116
- id: string;
117
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
118
- path?: string | undefined;
119
- contains?: string | undefined;
120
- envVar?: string | undefined;
121
- cmd?: string | undefined;
122
- }, {
123
- id: string;
124
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
125
- path?: string | undefined;
126
- contains?: string | undefined;
127
- envVar?: string | undefined;
128
- cmd?: string | undefined;
129
- }>, "many">>;
130
- checks: z.ZodDefault<z.ZodArray<z.ZodEffects<z.ZodEffects<z.ZodObject<{
91
+ }, z.core.$strip>>>;
92
+ checks: z.ZodDefault<z.ZodArray<z.ZodPreprocess<z.ZodObject<{
131
93
  id: z.ZodString;
132
94
  label: z.ZodString;
133
- on: z.ZodOptional<z.ZodEnum<["before_done"]>>;
95
+ on: z.ZodOptional<z.ZodEnum<{
96
+ session_start: "session_start";
97
+ message_submit: "message_submit";
98
+ before_tool: "before_tool";
99
+ after_tool: "after_tool";
100
+ session_end: "session_end";
101
+ before_done: "before_done";
102
+ }>>;
134
103
  when: z.ZodOptional<z.ZodString>;
135
104
  cmd: z.ZodOptional<z.ZodString>;
136
105
  prompt: z.ZodOptional<z.ZodString>;
106
+ inject: z.ZodOptional<z.ZodObject<{
107
+ text: z.ZodOptional<z.ZodString>;
108
+ files: z.ZodOptional<z.ZodArray<z.ZodString>>;
109
+ datetime: z.ZodOptional<z.ZodBoolean>;
110
+ }, z.core.$strip>>;
137
111
  conditionId: z.ZodOptional<z.ZodString>;
138
- }, "strip", z.ZodTypeAny, {
139
- id: string;
140
- label: string;
141
- cmd?: string | undefined;
142
- on?: "before_done" | undefined;
143
- when?: string | undefined;
144
- prompt?: string | undefined;
145
- conditionId?: string | undefined;
146
- }, {
147
- id: string;
148
- label: string;
149
- cmd?: string | undefined;
150
- on?: "before_done" | undefined;
151
- when?: string | undefined;
152
- prompt?: string | undefined;
153
- conditionId?: string | undefined;
154
- }>, {
155
- id: string;
156
- label: string;
157
- cmd?: string | undefined;
158
- on?: "before_done" | undefined;
159
- when?: string | undefined;
160
- prompt?: string | undefined;
161
- conditionId?: string | undefined;
162
- }, {
163
- id: string;
164
- label: string;
165
- cmd?: string | undefined;
166
- on?: "before_done" | undefined;
167
- when?: string | undefined;
168
- prompt?: string | undefined;
169
- conditionId?: string | undefined;
170
- }>, {
171
- id: string;
172
- label: string;
173
- cmd?: string | undefined;
174
- on?: "before_done" | undefined;
175
- when?: string | undefined;
176
- prompt?: string | undefined;
177
- conditionId?: string | undefined;
178
- }, unknown>, "many">>;
112
+ }, z.core.$strip>>>>;
179
113
  patterns: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
180
- session_context_files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
181
- }, "strip", z.ZodTypeAny, {
182
- checks: {
183
- id: string;
184
- label: string;
185
- cmd?: string | undefined;
186
- on?: "before_done" | undefined;
187
- when?: string | undefined;
188
- prompt?: string | undefined;
189
- conditionId?: string | undefined;
190
- }[];
191
- version: number;
192
- context: {
193
- guides: Record<string, string>;
194
- };
195
- conditions: {
196
- id: string;
197
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
198
- path?: string | undefined;
199
- contains?: string | undefined;
200
- envVar?: string | undefined;
201
- cmd?: string | undefined;
202
- }[];
203
- patterns?: Record<string, string> | undefined;
204
- session_context_files?: string[] | undefined;
205
- }, {
206
- checks?: unknown[] | undefined;
207
- version?: number | undefined;
208
- context?: {
209
- guides?: Record<string, string> | undefined;
210
- } | undefined;
211
- conditions?: {
212
- id: string;
213
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
214
- path?: string | undefined;
215
- contains?: string | undefined;
216
- envVar?: string | undefined;
217
- cmd?: string | undefined;
218
- }[] | undefined;
219
- patterns?: Record<string, string> | undefined;
220
- session_context_files?: string[] | undefined;
221
- }>, {
222
- checks: {
223
- id: string;
224
- label: string;
225
- cmd?: string | undefined;
226
- on?: "before_done" | undefined;
227
- when?: string | undefined;
228
- prompt?: string | undefined;
229
- conditionId?: string | undefined;
230
- }[];
231
- version: number;
232
- context: {
233
- guides: Record<string, string>;
234
- };
235
- conditions: {
236
- id: string;
237
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
238
- path?: string | undefined;
239
- contains?: string | undefined;
240
- envVar?: string | undefined;
241
- cmd?: string | undefined;
242
- }[];
243
- patterns?: Record<string, string> | undefined;
244
- session_context_files?: string[] | undefined;
245
- }, {
246
- checks?: unknown[] | undefined;
247
- version?: number | undefined;
248
- context?: {
249
- guides?: Record<string, string> | undefined;
250
- } | undefined;
251
- conditions?: {
252
- id: string;
253
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
254
- path?: string | undefined;
255
- contains?: string | undefined;
256
- envVar?: string | undefined;
257
- cmd?: string | undefined;
258
- }[] | undefined;
259
- patterns?: Record<string, string> | undefined;
260
- session_context_files?: string[] | undefined;
261
- }>, {
262
- checks: {
263
- id: string;
264
- label: string;
265
- cmd?: string | undefined;
266
- on?: "before_done" | undefined;
267
- when?: string | undefined;
268
- prompt?: string | undefined;
269
- conditionId?: string | undefined;
270
- }[];
271
- version: number;
272
- context: {
273
- guides: Record<string, string>;
274
- };
275
- conditions: {
276
- id: string;
277
- operator: "file_exists" | "file_contains" | "env_var_set" | "shell_returns_0";
278
- path?: string | undefined;
279
- contains?: string | undefined;
280
- envVar?: string | undefined;
281
- cmd?: string | undefined;
282
- }[];
283
- patterns?: Record<string, string> | undefined;
284
- session_context_files?: string[] | undefined;
285
- }, unknown>;
114
+ session_context_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
115
+ inject_datetime: z.ZodOptional<z.ZodBoolean>;
116
+ engines: z.ZodOptional<z.ZodObject<{
117
+ claude: z.ZodOptional<z.ZodObject<{
118
+ stop_command: z.ZodOptional<z.ZodString>;
119
+ live_command: z.ZodOptional<z.ZodString>;
120
+ }, z.core.$strip>>;
121
+ codex: z.ZodOptional<z.ZodObject<{
122
+ stop_command: z.ZodOptional<z.ZodString>;
123
+ }, z.core.$strip>>;
124
+ copilot: z.ZodOptional<z.ZodObject<{
125
+ check_command: z.ZodOptional<z.ZodString>;
126
+ }, z.core.$strip>>;
127
+ }, z.core.$strip>>;
128
+ }, z.core.$strip>>;
286
129
 
287
- export { CheckDefSchema, ConditionDefSchema, HoldpointConfigSchema, HookEventSchema, generateYaml, matchesWhen, parseHoldpointYaml, validateConfig };
130
+ export { CheckDefSchema, ConditionDefSchema, HoldpointConfigSchema, HookEventSchema, InjectSpecSchema, generateYaml, matchesWhen, parseHoldpointYaml, validateConfig };
package/dist/index.js CHANGED
@@ -7,7 +7,21 @@ import * as yaml from "js-yaml";
7
7
 
8
8
  // src/schema.ts
9
9
  import { z } from "zod";
10
- var HookEventSchema = z.enum(["before_done"]);
10
+ var HookEventSchema = z.enum([
11
+ "session_start",
12
+ "message_submit",
13
+ "before_tool",
14
+ "after_tool",
15
+ "session_end",
16
+ "before_done"
17
+ ]);
18
+ var InjectSpecSchema = z.object({
19
+ text: z.string().optional(),
20
+ files: z.array(z.string()).optional(),
21
+ datetime: z.boolean().optional()
22
+ }).refine((i) => i.text !== void 0 || (i.files?.length ?? 0) > 0 || i.datetime === true, {
23
+ message: "inject must set at least one of text, files, or datetime"
24
+ });
11
25
  var ConditionOperatorSchema = z.enum([
12
26
  "file_exists",
13
27
  "file_contains",
@@ -70,13 +84,17 @@ var CheckDefSchema = z.preprocess(
70
84
  when: z.string().optional(),
71
85
  cmd: z.string().optional(),
72
86
  prompt: z.string().optional(),
87
+ inject: InjectSpecSchema.optional(),
73
88
  conditionId: z.string().optional()
74
- }).refine((c) => c.cmd !== void 0 || c.prompt !== void 0, {
75
- message: "A check must have either cmd (task) or prompt (agent instruction)"
76
- })
89
+ }).refine(
90
+ (c) => [c.cmd !== void 0, c.prompt !== void 0, c.inject !== void 0].filter(Boolean).length === 1,
91
+ {
92
+ message: "A check must have exactly one of cmd (command), prompt (agent instruction), or inject (context)"
93
+ }
94
+ )
77
95
  );
78
96
  var HoldpointContextSchema = z.object({
79
- guides: z.record(z.string()).default({})
97
+ guides: z.record(z.string(), z.string()).default({})
80
98
  });
81
99
  var BUILTIN_SCOPES = /* @__PURE__ */ new Set([
82
100
  "frontend",
@@ -96,6 +114,14 @@ var BUILTIN_SCOPES = /* @__PURE__ */ new Set([
96
114
  "docs",
97
115
  "structural"
98
116
  ]);
117
+ var EnginesConfigSchema = z.object({
118
+ claude: z.object({
119
+ stop_command: z.string().optional(),
120
+ live_command: z.string().optional()
121
+ }).optional(),
122
+ codex: z.object({ stop_command: z.string().optional() }).optional(),
123
+ copilot: z.object({ check_command: z.string().optional() }).optional()
124
+ }).optional();
99
125
  var HoldpointConfigSchema = z.preprocess(
100
126
  migrateLegacyConfig,
101
127
  z.object({
@@ -103,8 +129,10 @@ var HoldpointConfigSchema = z.preprocess(
103
129
  context: HoldpointContextSchema.default({ guides: {} }),
104
130
  conditions: z.array(ConditionDefSchema).default([]),
105
131
  checks: z.array(CheckDefSchema).default([]),
106
- patterns: z.record(z.string()).optional(),
107
- session_context_files: z.array(z.string()).optional()
132
+ patterns: z.record(z.string(), z.string()).optional(),
133
+ session_context_files: z.array(z.string()).optional(),
134
+ inject_datetime: z.boolean().optional(),
135
+ engines: EnginesConfigSchema
108
136
  }).superRefine((data, ctx) => {
109
137
  if (!data.patterns) return;
110
138
  for (const [key, value] of Object.entries(data.patterns)) {
@@ -133,7 +161,7 @@ function parseHoldpointYaml(text) {
133
161
  const raw = yaml.load(text);
134
162
  const result = HoldpointConfigSchema.safeParse(raw);
135
163
  if (!result.success) {
136
- const messages = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("\n");
164
+ const messages = result.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join("\n");
137
165
  throw new Error(`Invalid checks.yaml:
138
166
  ${messages}`);
139
167
  }
@@ -146,7 +174,7 @@ function validateConfig(config) {
146
174
  }
147
175
  return {
148
176
  valid: false,
149
- errors: result.error.errors.map((e) => ({
177
+ errors: result.error.issues.map((e) => ({
150
178
  path: e.path.join("."),
151
179
  message: e.message
152
180
  }))
@@ -166,6 +194,7 @@ export {
166
194
  ConditionDefSchema,
167
195
  HoldpointConfigSchema,
168
196
  HookEventSchema,
197
+ InjectSpecSchema,
169
198
  generateYaml,
170
199
  matchesWhen,
171
200
  parseHoldpointYaml,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/parser.ts","../src/schema.ts"],"sourcesContent":["import * as yaml from \"js-yaml\";\nimport type { HoldpointConfig, ValidationResult } from \"@holdpoint/types\";\nimport { HoldpointConfigSchema } from \"./schema.js\";\n\n/**\n * Parse a checks.yaml text into a HoldpointConfig.\n * Throws on invalid YAML or schema violations.\n */\nexport function parseHoldpointYaml(text: string): HoldpointConfig {\n const raw = yaml.load(text);\n const result = HoldpointConfigSchema.safeParse(raw);\n if (!result.success) {\n const messages = result.error.errors.map((e) => `${e.path.join(\".\")}: ${e.message}`).join(\"\\n\");\n throw new Error(`Invalid checks.yaml:\\n${messages}`);\n }\n return result.data as HoldpointConfig;\n}\n\n/**\n * Validate a parsed HoldpointConfig, returning structured errors.\n */\nexport function validateConfig(config: HoldpointConfig): ValidationResult {\n const result = HoldpointConfigSchema.safeParse(config);\n if (result.success) {\n return { valid: true, errors: [] };\n }\n return {\n valid: false,\n errors: result.error.errors.map((e) => ({\n path: e.path.join(\".\"),\n message: e.message,\n })),\n };\n}\n\n/**\n * Serialize a HoldpointConfig back to YAML text.\n */\nexport function generateYaml(config: HoldpointConfig): string {\n return yaml.dump(config, {\n indent: 2,\n lineWidth: 120,\n quotingType: '\"',\n forceQuotes: false,\n noRefs: true,\n });\n}\n","import { z } from \"zod\";\n\n// ─── Zod schemas ─────────────────────────────────────────────────────────────\n\nexport const HookEventSchema = z.enum([\"before_done\"]);\n\nexport const ConditionOperatorSchema = z.enum([\n \"file_exists\",\n \"file_contains\",\n \"env_var_set\",\n \"shell_returns_0\",\n]);\n\nexport const ConditionDefSchema = z.object({\n id: z.string().min(1),\n operator: ConditionOperatorSchema,\n path: z.string().optional(),\n contains: z.string().optional(),\n envVar: z.string().optional(),\n cmd: z.string().optional(),\n});\n\n/**\n * Per-item migration: handles legacy `trigger: { type, pattern }` → on/when\n * and legacy `manual:` field → `prompt:`.\n */\nfunction migrateLegacyCheckDef(raw: unknown): unknown {\n if (raw == null || typeof raw !== \"object\") return raw;\n const obj = raw as Record<string, unknown>;\n const result: Record<string, unknown> = { ...obj };\n\n // migrate trigger: { type, pattern } → on/when\n if (\"trigger\" in result && !(\"on\" in result) && !(\"when\" in result)) {\n const { trigger } = result;\n delete result.trigger;\n const t = trigger as Record<string, unknown>;\n if (t.type !== \"always\") {\n result.when = t.type === \"custom\" ? t.pattern : t.type;\n }\n }\n\n // migrate manual: → prompt:\n if (\"manual\" in result && !(\"prompt\" in result)) {\n result.prompt = result.manual;\n delete result.manual;\n }\n\n return result;\n}\n\n/**\n * Top-level migration: collapses ALL legacy array names into `checks:`.\n * Handles: deterministic, manual, task, prompt (any combination).\n * Existing `checks:` entries are preserved and come first.\n */\nfunction migrateLegacyConfig(raw: unknown): unknown {\n if (raw == null || typeof raw !== \"object\") return raw;\n const obj = raw as Record<string, unknown>;\n const result: Record<string, unknown> = { ...obj };\n\n const toArray = (key: string) => (Array.isArray(result[key]) ? (result[key] as unknown[]) : []);\n\n const merged = [\n ...toArray(\"checks\"),\n ...toArray(\"task\"),\n ...toArray(\"prompt\"),\n ...toArray(\"deterministic\"),\n ...toArray(\"manual\"),\n ].map(migrateLegacyCheckDef);\n\n if (\n \"checks\" in result ||\n \"task\" in result ||\n \"prompt\" in result ||\n \"deterministic\" in result ||\n \"manual\" in result\n ) {\n result.checks = merged;\n delete result.task;\n delete result.prompt;\n delete result.deterministic;\n delete result.manual;\n }\n\n return result;\n}\n\nexport const CheckDefSchema = z.preprocess(\n migrateLegacyCheckDef,\n z\n .object({\n id: z.string().min(1),\n label: z.string().min(1),\n on: HookEventSchema.optional(),\n when: z.string().optional(),\n cmd: z.string().optional(),\n prompt: z.string().optional(),\n conditionId: z.string().optional(),\n })\n .refine((c) => c.cmd !== undefined || c.prompt !== undefined, {\n message: \"A check must have either cmd (task) or prompt (agent instruction)\",\n }),\n);\n\nexport const HoldpointContextSchema = z.object({\n guides: z.record(z.string()).default({}),\n});\n\n/** Built-in scope names that cannot be overridden by user-defined patterns. */\nconst BUILTIN_SCOPES = new Set([\n \"frontend\",\n \"backend\",\n \"socket\",\n \"visual\",\n \"python\",\n \"go\",\n \"rust\",\n \"java\",\n \"ruby\",\n \"database\",\n \"prisma\",\n \"testing\",\n \"infra\",\n \"ci\",\n \"docs\",\n \"structural\",\n]);\n\nexport const HoldpointConfigSchema = z.preprocess(\n migrateLegacyConfig,\n z\n .object({\n version: z.number().int().positive().default(1),\n context: HoldpointContextSchema.default({ guides: {} }),\n conditions: z.array(ConditionDefSchema).default([]),\n checks: z.array(CheckDefSchema).default([]),\n patterns: z.record(z.string()).optional(),\n session_context_files: z.array(z.string()).optional(),\n })\n .superRefine((data, ctx) => {\n if (!data.patterns) return;\n for (const [key, value] of Object.entries(data.patterns)) {\n if (BUILTIN_SCOPES.has(key)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `'${key}' is a built-in scope name and cannot be redefined in patterns`,\n path: [\"patterns\", key],\n });\n }\n try {\n new RegExp(value);\n } catch {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid regex in patterns.${key}: '${value}'`,\n path: [\"patterns\", key],\n });\n }\n }\n }),\n);\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/parser.ts","../src/schema.ts"],"sourcesContent":["import * as yaml from \"js-yaml\";\nimport type { HoldpointConfig, ValidationResult } from \"@holdpoint/types\";\nimport { HoldpointConfigSchema } from \"./schema.js\";\n\n/**\n * Parse a checks.yaml text into a HoldpointConfig.\n * Throws on invalid YAML or schema violations.\n */\nexport function parseHoldpointYaml(text: string): HoldpointConfig {\n const raw = yaml.load(text);\n const result = HoldpointConfigSchema.safeParse(raw);\n if (!result.success) {\n const messages = result.error.issues.map((e) => `${e.path.join(\".\")}: ${e.message}`).join(\"\\n\");\n throw new Error(`Invalid checks.yaml:\\n${messages}`);\n }\n return result.data as HoldpointConfig;\n}\n\n/**\n * Validate a parsed HoldpointConfig, returning structured errors.\n */\nexport function validateConfig(config: HoldpointConfig): ValidationResult {\n const result = HoldpointConfigSchema.safeParse(config);\n if (result.success) {\n return { valid: true, errors: [] };\n }\n return {\n valid: false,\n errors: result.error.issues.map((e) => ({\n path: e.path.join(\".\"),\n message: e.message,\n })),\n };\n}\n\n/**\n * Serialize a HoldpointConfig back to YAML text.\n */\nexport function generateYaml(config: HoldpointConfig): string {\n return yaml.dump(config, {\n indent: 2,\n lineWidth: 120,\n quotingType: '\"',\n forceQuotes: false,\n noRefs: true,\n });\n}\n","import { z } from \"zod\";\n\n// ─── Zod schemas ─────────────────────────────────────────────────────────────\n\nexport const HookEventSchema = z.enum([\n \"session_start\",\n \"message_submit\",\n \"before_tool\",\n \"after_tool\",\n \"session_end\",\n \"before_done\",\n]);\n\nexport const InjectSpecSchema = z\n .object({\n text: z.string().optional(),\n files: z.array(z.string()).optional(),\n datetime: z.boolean().optional(),\n })\n .refine((i) => i.text !== undefined || (i.files?.length ?? 0) > 0 || i.datetime === true, {\n message: \"inject must set at least one of text, files, or datetime\",\n });\n\nexport const ConditionOperatorSchema = z.enum([\n \"file_exists\",\n \"file_contains\",\n \"env_var_set\",\n \"shell_returns_0\",\n]);\n\nexport const ConditionDefSchema = z.object({\n id: z.string().min(1),\n operator: ConditionOperatorSchema,\n path: z.string().optional(),\n contains: z.string().optional(),\n envVar: z.string().optional(),\n cmd: z.string().optional(),\n});\n\n/**\n * Per-item migration: handles legacy `trigger: { type, pattern }` → on/when\n * and legacy `manual:` field → `prompt:`.\n */\nfunction migrateLegacyCheckDef(raw: unknown): unknown {\n if (raw == null || typeof raw !== \"object\") return raw;\n const obj = raw as Record<string, unknown>;\n const result: Record<string, unknown> = { ...obj };\n\n // migrate trigger: { type, pattern } → on/when\n if (\"trigger\" in result && !(\"on\" in result) && !(\"when\" in result)) {\n const { trigger } = result;\n delete result.trigger;\n const t = trigger as Record<string, unknown>;\n if (t.type !== \"always\") {\n result.when = t.type === \"custom\" ? t.pattern : t.type;\n }\n }\n\n // migrate manual: → prompt:\n if (\"manual\" in result && !(\"prompt\" in result)) {\n result.prompt = result.manual;\n delete result.manual;\n }\n\n return result;\n}\n\n/**\n * Top-level migration: collapses ALL legacy array names into `checks:`.\n * Handles: deterministic, manual, task, prompt (any combination).\n * Existing `checks:` entries are preserved and come first.\n */\nfunction migrateLegacyConfig(raw: unknown): unknown {\n if (raw == null || typeof raw !== \"object\") return raw;\n const obj = raw as Record<string, unknown>;\n const result: Record<string, unknown> = { ...obj };\n\n const toArray = (key: string) => (Array.isArray(result[key]) ? (result[key] as unknown[]) : []);\n\n const merged = [\n ...toArray(\"checks\"),\n ...toArray(\"task\"),\n ...toArray(\"prompt\"),\n ...toArray(\"deterministic\"),\n ...toArray(\"manual\"),\n ].map(migrateLegacyCheckDef);\n\n if (\n \"checks\" in result ||\n \"task\" in result ||\n \"prompt\" in result ||\n \"deterministic\" in result ||\n \"manual\" in result\n ) {\n result.checks = merged;\n delete result.task;\n delete result.prompt;\n delete result.deterministic;\n delete result.manual;\n }\n\n return result;\n}\n\nexport const CheckDefSchema = z.preprocess(\n migrateLegacyCheckDef,\n z\n .object({\n id: z.string().min(1),\n label: z.string().min(1),\n on: HookEventSchema.optional(),\n when: z.string().optional(),\n cmd: z.string().optional(),\n prompt: z.string().optional(),\n inject: InjectSpecSchema.optional(),\n conditionId: z.string().optional(),\n })\n .refine(\n (c) =>\n [c.cmd !== undefined, c.prompt !== undefined, c.inject !== undefined].filter(Boolean)\n .length === 1,\n {\n message:\n \"A check must have exactly one of cmd (command), prompt (agent instruction), or inject (context)\",\n },\n ),\n);\n\nexport const HoldpointContextSchema = z.object({\n guides: z.record(z.string(), z.string()).default({}),\n});\n\n/** Built-in scope names that cannot be overridden by user-defined patterns. */\nconst BUILTIN_SCOPES = new Set([\n \"frontend\",\n \"backend\",\n \"socket\",\n \"visual\",\n \"python\",\n \"go\",\n \"rust\",\n \"java\",\n \"ruby\",\n \"database\",\n \"prisma\",\n \"testing\",\n \"infra\",\n \"ci\",\n \"docs\",\n \"structural\",\n]);\n\nconst EnginesConfigSchema = z\n .object({\n claude: z\n .object({\n stop_command: z.string().optional(),\n live_command: z.string().optional(),\n })\n .optional(),\n codex: z.object({ stop_command: z.string().optional() }).optional(),\n copilot: z.object({ check_command: z.string().optional() }).optional(),\n })\n .optional();\n\nexport const HoldpointConfigSchema = z.preprocess(\n migrateLegacyConfig,\n z\n .object({\n version: z.number().int().positive().default(1),\n context: HoldpointContextSchema.default({ guides: {} }),\n conditions: z.array(ConditionDefSchema).default([]),\n checks: z.array(CheckDefSchema).default([]),\n patterns: z.record(z.string(), z.string()).optional(),\n session_context_files: z.array(z.string()).optional(),\n inject_datetime: z.boolean().optional(),\n engines: EnginesConfigSchema,\n })\n .superRefine((data, ctx) => {\n if (!data.patterns) return;\n for (const [key, value] of Object.entries(data.patterns)) {\n if (BUILTIN_SCOPES.has(key)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `'${key}' is a built-in scope name and cannot be redefined in patterns`,\n path: [\"patterns\", key],\n });\n }\n try {\n new RegExp(value);\n } catch {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Invalid regex in patterns.${key}: '${value}'`,\n path: [\"patterns\", key],\n });\n }\n }\n }),\n);\n"],"mappings":"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","names":[]}
@@ -1,10 +1,11 @@
1
- import { HoldpointConfig, CheckResult } from '@holdpoint/types';
1
+ import { HoldpointConfig, HookEvent, CheckResult } from '@holdpoint/types';
2
2
 
3
3
  /**
4
- * Run all task checks (those with cmd) against the given changed files.
5
- * Checks whose trigger doesn't match are skipped.
6
- * Checks with a failing condition (false branch) are skipped.
4
+ * Run task checks (those with cmd) for a given hook against the changed files.
5
+ * Only checks whose effective hook matches `hook` run (default `before_done`,
6
+ * which preserves the completion-gate behavior). Checks whose trigger doesn't
7
+ * match, or whose condition is false, are skipped.
7
8
  */
8
- declare function runDeterministicChecks(config: HoldpointConfig, changedFiles: string[]): CheckResult[];
9
+ declare function runDeterministicChecks(config: HoldpointConfig, changedFiles: string[], hook?: HookEvent): CheckResult[];
9
10
 
10
11
  export { runDeterministicChecks };
@@ -5,6 +5,7 @@ import {
5
5
  // src/runner.ts
6
6
  import { execSync } from "child_process";
7
7
  import { existsSync, readFileSync } from "fs";
8
+ import { checkHook } from "@holdpoint/types";
8
9
  function evaluateCondition(condition) {
9
10
  switch (condition.operator) {
10
11
  case "file_exists":
@@ -45,9 +46,9 @@ function runCheck(check) {
45
46
  return { check, status: "fail", output, exitCode: e.status ?? 1 };
46
47
  }
47
48
  }
48
- function runDeterministicChecks(config, changedFiles) {
49
+ function runDeterministicChecks(config, changedFiles, hook = "before_done") {
49
50
  const conditionMap = new Map(config.conditions.map((c) => [c.id, c]));
50
- const taskChecks = config.checks.filter((c) => c.cmd !== void 0);
51
+ const taskChecks = config.checks.filter((c) => c.cmd !== void 0 && checkHook(c) === hook);
51
52
  return taskChecks.map((check) => {
52
53
  if (!matchesWhen(check.when, changedFiles, config.patterns)) {
53
54
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport type { CheckDef, CheckResult, ConditionDef, HoldpointConfig } from \"@holdpoint/types\";\nimport { matchesWhen } from \"./trigger.js\";\n\nfunction evaluateCondition(condition: ConditionDef): boolean {\n switch (condition.operator) {\n case \"file_exists\":\n return condition.path != null && existsSync(condition.path);\n\n case \"file_contains\": {\n if (!condition.path || !condition.contains) return false;\n if (!existsSync(condition.path)) return false;\n const content = readFileSync(condition.path, \"utf8\");\n return content.includes(condition.contains);\n }\n\n case \"env_var_set\":\n return condition.envVar != null && process.env[condition.envVar] !== undefined;\n\n case \"shell_returns_0\": {\n if (!condition.cmd) return false;\n try {\n execSync(condition.cmd, { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n }\n }\n}\n\nfunction runCheck(check: CheckDef): CheckResult {\n if (!check.cmd) {\n return { check, status: \"pending\" };\n }\n try {\n const output = execSync(check.cmd, {\n stdio: \"pipe\",\n encoding: \"utf8\",\n timeout: 60_000,\n });\n return { check, status: \"pass\", output, exitCode: 0 };\n } catch (err: unknown) {\n const e = err as { stdout?: string; stderr?: string; status?: number };\n const output = [e.stdout, e.stderr].filter(Boolean).join(\"\\n\");\n return { check, status: \"fail\", output, exitCode: e.status ?? 1 };\n }\n}\n\n/**\n * Run all task checks (those with cmd) against the given changed files.\n * Checks whose trigger doesn't match are skipped.\n * Checks with a failing condition (false branch) are skipped.\n */\nexport function runDeterministicChecks(\n config: HoldpointConfig,\n changedFiles: string[],\n): CheckResult[] {\n const conditionMap = new Map(config.conditions.map((c) => [c.id, c]));\n const taskChecks = config.checks.filter((c) => c.cmd !== undefined);\n\n return taskChecks.map((check) => {\n if (!matchesWhen(check.when, changedFiles, config.patterns)) {\n return {\n check,\n status: \"skip\",\n skipReason: `'when: ${check.when}' did not match changed files`,\n };\n }\n\n if (check.conditionId) {\n const condition = conditionMap.get(check.conditionId);\n if (!condition) {\n return {\n check,\n status: \"skip\",\n skipReason: `Condition '${check.conditionId}' not found`,\n };\n }\n if (!evaluateCondition(condition)) {\n return {\n check,\n status: \"skip\",\n skipReason: `Condition '${check.conditionId}' evaluated to false`,\n };\n }\n }\n\n return runCheck(check);\n });\n}\n"],"mappings":";;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,YAAY,oBAAoB;AAIzC,SAAS,kBAAkB,WAAkC;AAC3D,UAAQ,UAAU,UAAU;AAAA,IAC1B,KAAK;AACH,aAAO,UAAU,QAAQ,QAAQ,WAAW,UAAU,IAAI;AAAA,IAE5D,KAAK,iBAAiB;AACpB,UAAI,CAAC,UAAU,QAAQ,CAAC,UAAU,SAAU,QAAO;AACnD,UAAI,CAAC,WAAW,UAAU,IAAI,EAAG,QAAO;AACxC,YAAM,UAAU,aAAa,UAAU,MAAM,MAAM;AACnD,aAAO,QAAQ,SAAS,UAAU,QAAQ;AAAA,IAC5C;AAAA,IAEA,KAAK;AACH,aAAO,UAAU,UAAU,QAAQ,QAAQ,IAAI,UAAU,MAAM,MAAM;AAAA,IAEvE,KAAK,mBAAmB;AACtB,UAAI,CAAC,UAAU,IAAK,QAAO;AAC3B,UAAI;AACF,iBAAS,UAAU,KAAK,EAAE,OAAO,SAAS,CAAC;AAC3C,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,SAAS,OAA8B;AAC9C,MAAI,CAAC,MAAM,KAAK;AACd,WAAO,EAAE,OAAO,QAAQ,UAAU;AAAA,EACpC;AACA,MAAI;AACF,UAAM,SAAS,SAAS,MAAM,KAAK;AAAA,MACjC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,UAAU,EAAE;AAAA,EACtD,SAAS,KAAc;AACrB,UAAM,IAAI;AACV,UAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC7D,WAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,UAAU,EAAE,UAAU,EAAE;AAAA,EAClE;AACF;AAOO,SAAS,uBACd,QACA,cACe;AACf,QAAM,eAAe,IAAI,IAAI,OAAO,WAAW,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACpE,QAAM,aAAa,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,QAAQ,MAAS;AAElE,SAAO,WAAW,IAAI,CAAC,UAAU;AAC/B,QAAI,CAAC,YAAY,MAAM,MAAM,cAAc,OAAO,QAAQ,GAAG;AAC3D,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,YAAY,UAAU,MAAM,IAAI;AAAA,MAClC;AAAA,IACF;AAEA,QAAI,MAAM,aAAa;AACrB,YAAM,YAAY,aAAa,IAAI,MAAM,WAAW;AACpD,UAAI,CAAC,WAAW;AACd,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,UACR,YAAY,cAAc,MAAM,WAAW;AAAA,QAC7C;AAAA,MACF;AACA,UAAI,CAAC,kBAAkB,SAAS,GAAG;AACjC,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,UACR,YAAY,cAAc,MAAM,WAAW;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport type {\n CheckDef,\n CheckResult,\n ConditionDef,\n HoldpointConfig,\n HookEvent,\n} from \"@holdpoint/types\";\nimport { checkHook } from \"@holdpoint/types\";\nimport { matchesWhen } from \"./trigger.js\";\n\nfunction evaluateCondition(condition: ConditionDef): boolean {\n switch (condition.operator) {\n case \"file_exists\":\n return condition.path != null && existsSync(condition.path);\n\n case \"file_contains\": {\n if (!condition.path || !condition.contains) return false;\n if (!existsSync(condition.path)) return false;\n const content = readFileSync(condition.path, \"utf8\");\n return content.includes(condition.contains);\n }\n\n case \"env_var_set\":\n return condition.envVar != null && process.env[condition.envVar] !== undefined;\n\n case \"shell_returns_0\": {\n if (!condition.cmd) return false;\n try {\n execSync(condition.cmd, { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n }\n }\n}\n\nfunction runCheck(check: CheckDef): CheckResult {\n if (!check.cmd) {\n return { check, status: \"pending\" };\n }\n try {\n const output = execSync(check.cmd, {\n stdio: \"pipe\",\n encoding: \"utf8\",\n timeout: 60_000,\n });\n return { check, status: \"pass\", output, exitCode: 0 };\n } catch (err: unknown) {\n const e = err as { stdout?: string; stderr?: string; status?: number };\n const output = [e.stdout, e.stderr].filter(Boolean).join(\"\\n\");\n return { check, status: \"fail\", output, exitCode: e.status ?? 1 };\n }\n}\n\n/**\n * Run task checks (those with cmd) for a given hook against the changed files.\n * Only checks whose effective hook matches `hook` run (default `before_done`,\n * which preserves the completion-gate behavior). Checks whose trigger doesn't\n * match, or whose condition is false, are skipped.\n */\nexport function runDeterministicChecks(\n config: HoldpointConfig,\n changedFiles: string[],\n hook: HookEvent = \"before_done\",\n): CheckResult[] {\n const conditionMap = new Map(config.conditions.map((c) => [c.id, c]));\n const taskChecks = config.checks.filter((c) => c.cmd !== undefined && checkHook(c) === hook);\n\n return taskChecks.map((check) => {\n if (!matchesWhen(check.when, changedFiles, config.patterns)) {\n return {\n check,\n status: \"skip\",\n skipReason: `'when: ${check.when}' did not match changed files`,\n };\n }\n\n if (check.conditionId) {\n const condition = conditionMap.get(check.conditionId);\n if (!condition) {\n return {\n check,\n status: \"skip\",\n skipReason: `Condition '${check.conditionId}' not found`,\n };\n }\n if (!evaluateCondition(condition)) {\n return {\n check,\n status: \"skip\",\n skipReason: `Condition '${check.conditionId}' evaluated to false`,\n };\n }\n }\n\n return runCheck(check);\n });\n}\n"],"mappings":";;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,YAAY,oBAAoB;AAQzC,SAAS,iBAAiB;AAG1B,SAAS,kBAAkB,WAAkC;AAC3D,UAAQ,UAAU,UAAU;AAAA,IAC1B,KAAK;AACH,aAAO,UAAU,QAAQ,QAAQ,WAAW,UAAU,IAAI;AAAA,IAE5D,KAAK,iBAAiB;AACpB,UAAI,CAAC,UAAU,QAAQ,CAAC,UAAU,SAAU,QAAO;AACnD,UAAI,CAAC,WAAW,UAAU,IAAI,EAAG,QAAO;AACxC,YAAM,UAAU,aAAa,UAAU,MAAM,MAAM;AACnD,aAAO,QAAQ,SAAS,UAAU,QAAQ;AAAA,IAC5C;AAAA,IAEA,KAAK;AACH,aAAO,UAAU,UAAU,QAAQ,QAAQ,IAAI,UAAU,MAAM,MAAM;AAAA,IAEvE,KAAK,mBAAmB;AACtB,UAAI,CAAC,UAAU,IAAK,QAAO;AAC3B,UAAI;AACF,iBAAS,UAAU,KAAK,EAAE,OAAO,SAAS,CAAC;AAC3C,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,SAAS,OAA8B;AAC9C,MAAI,CAAC,MAAM,KAAK;AACd,WAAO,EAAE,OAAO,QAAQ,UAAU;AAAA,EACpC;AACA,MAAI;AACF,UAAM,SAAS,SAAS,MAAM,KAAK;AAAA,MACjC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,UAAU,EAAE;AAAA,EACtD,SAAS,KAAc;AACrB,UAAM,IAAI;AACV,UAAM,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC7D,WAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,UAAU,EAAE,UAAU,EAAE;AAAA,EAClE;AACF;AAQO,SAAS,uBACd,QACA,cACA,OAAkB,eACH;AACf,QAAM,eAAe,IAAI,IAAI,OAAO,WAAW,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACpE,QAAM,aAAa,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,QAAQ,UAAa,UAAU,CAAC,MAAM,IAAI;AAE3F,SAAO,WAAW,IAAI,CAAC,UAAU;AAC/B,QAAI,CAAC,YAAY,MAAM,MAAM,cAAc,OAAO,QAAQ,GAAG;AAC3D,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,YAAY,UAAU,MAAM,IAAI;AAAA,MAClC;AAAA,IACF;AAEA,QAAI,MAAM,aAAa;AACrB,YAAM,YAAY,aAAa,IAAI,MAAM,WAAW;AACpD,UAAI,CAAC,WAAW;AACd,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,UACR,YAAY,cAAc,MAAM,WAAW;AAAA,QAC7C;AAAA,MACF;AACA,UAAI,CAAC,kBAAkB,SAAS,GAAG;AACjC,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,UACR,YAAY,cAAc,MAAM,WAAW;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@holdpoint/yaml-core",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.11",
4
4
  "publishConfig": {
5
- "access": "public",
6
- "tag": "alpha"
5
+ "access": "public"
7
6
  },
8
7
  "description": "Shared checks.yaml parser, validator, trigger matcher and runner",
9
8
  "homepage": "https://holdpoint.dev",
@@ -49,15 +48,15 @@
49
48
  "dependencies": {
50
49
  "js-yaml": "^4.1.0",
51
50
  "minimatch": "^10.0.1",
52
- "zod": "^3.24.1",
53
- "@holdpoint/types": "0.1.0-alpha.1"
51
+ "zod": "^4.4.3",
52
+ "@holdpoint/types": "0.1.0-alpha.10"
54
53
  },
55
54
  "devDependencies": {
56
55
  "@types/js-yaml": "^4.0.9",
57
- "@types/node": "^22.10.2",
56
+ "@types/node": "^25.9.1",
58
57
  "tsup": "^8.3.5",
59
- "typescript": "^5.7.2",
60
- "vitest": "^2.1.8"
58
+ "typescript": "^6.0.3",
59
+ "vitest": "^4.1.7"
61
60
  },
62
61
  "scripts": {
63
62
  "build": "tsup",