@holdpoint/yaml-core 0.1.0-alpha.10 → 0.1.0-alpha.12

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
@@ -25,8 +25,18 @@ declare function generateYaml(config: HoldpointConfig): string;
25
25
  declare function matchesWhen(when: string | undefined, changedFiles: string[], userPatterns?: Record<string, string>): boolean;
26
26
 
27
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";
28
33
  before_done: "before_done";
29
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>;
30
40
  declare const ConditionDefSchema: z.ZodObject<{
31
41
  id: z.ZodString;
32
42
  operator: z.ZodEnum<{
@@ -44,11 +54,21 @@ declare const CheckDefSchema: z.ZodPreprocess<z.ZodObject<{
44
54
  id: z.ZodString;
45
55
  label: z.ZodString;
46
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";
47
62
  before_done: "before_done";
48
63
  }>>;
49
64
  when: z.ZodOptional<z.ZodString>;
50
65
  cmd: z.ZodOptional<z.ZodString>;
51
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>>;
52
72
  conditionId: z.ZodOptional<z.ZodString>;
53
73
  }, z.core.$strip>>;
54
74
  declare const HoldpointConfigSchema: z.ZodPreprocess<z.ZodObject<{
@@ -73,15 +93,27 @@ declare const HoldpointConfigSchema: z.ZodPreprocess<z.ZodObject<{
73
93
  id: z.ZodString;
74
94
  label: z.ZodString;
75
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";
76
101
  before_done: "before_done";
77
102
  }>>;
78
103
  when: z.ZodOptional<z.ZodString>;
79
104
  cmd: z.ZodOptional<z.ZodString>;
80
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>>;
81
111
  conditionId: z.ZodOptional<z.ZodString>;
82
112
  }, z.core.$strip>>>>;
83
113
  patterns: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
84
114
  session_context_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
115
+ inject_datetime: z.ZodOptional<z.ZodBoolean>;
116
+ security_scan: z.ZodOptional<z.ZodBoolean>;
85
117
  engines: z.ZodOptional<z.ZodObject<{
86
118
  claude: z.ZodOptional<z.ZodObject<{
87
119
  stop_command: z.ZodOptional<z.ZodString>;
@@ -96,4 +128,4 @@ declare const HoldpointConfigSchema: z.ZodPreprocess<z.ZodObject<{
96
128
  }, z.core.$strip>>;
97
129
  }, z.core.$strip>>;
98
130
 
99
- export { CheckDefSchema, ConditionDefSchema, HoldpointConfigSchema, HookEventSchema, generateYaml, matchesWhen, parseHoldpointYaml, validateConfig };
131
+ 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,10 +84,14 @@ 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
97
  guides: z.record(z.string(), z.string()).default({})
@@ -113,6 +131,8 @@ var HoldpointConfigSchema = z.preprocess(
113
131
  checks: z.array(CheckDefSchema).default([]),
114
132
  patterns: z.record(z.string(), z.string()).optional(),
115
133
  session_context_files: z.array(z.string()).optional(),
134
+ inject_datetime: z.boolean().optional(),
135
+ security_scan: z.boolean().optional(),
116
136
  engines: EnginesConfigSchema
117
137
  }).superRefine((data, ctx) => {
118
138
  if (!data.patterns) return;
@@ -175,6 +195,7 @@ export {
175
195
  ConditionDefSchema,
176
196
  HoldpointConfigSchema,
177
197
  HookEventSchema,
198
+ InjectSpecSchema,
178
199
  generateYaml,
179
200
  matchesWhen,
180
201
  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.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([\"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(), 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 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
+ {"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 security_scan: 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,6 +1,6 @@
1
1
  {
2
2
  "name": "@holdpoint/yaml-core",
3
- "version": "0.1.0-alpha.10",
3
+ "version": "0.1.0-alpha.12",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -46,17 +46,17 @@
46
46
  "LICENSE"
47
47
  ],
48
48
  "dependencies": {
49
- "js-yaml": "^4.1.0",
49
+ "js-yaml": "^4.2.0",
50
50
  "minimatch": "^10.0.1",
51
51
  "zod": "^4.4.3",
52
- "@holdpoint/types": "0.1.0-alpha.9"
52
+ "@holdpoint/types": "0.1.0-alpha.11"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/js-yaml": "^4.0.9",
56
- "@types/node": "^25.9.1",
56
+ "@types/node": "^25.9.2",
57
57
  "tsup": "^8.3.5",
58
58
  "typescript": "^6.0.3",
59
- "vitest": "^4.1.7"
59
+ "vitest": "^4.1.8"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "tsup",