agentplane 0.2.24 → 0.2.26

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 (187) hide show
  1. package/bin/agentplane.js +91 -54
  2. package/dist/.build-manifest.json +11 -0
  3. package/dist/backends/task-backend/local-backend.d.ts +2 -0
  4. package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
  5. package/dist/backends/task-backend/local-backend.js +12 -1
  6. package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
  7. package/dist/backends/task-backend/redmine/mapping.js +26 -1
  8. package/dist/backends/task-backend/redmine-backend.d.ts +4 -0
  9. package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
  10. package/dist/backends/task-backend/redmine-backend.js +92 -9
  11. package/dist/backends/task-backend/shared/types.d.ts +1 -0
  12. package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
  13. package/dist/backends/task-index.d.ts.map +1 -1
  14. package/dist/backends/task-index.js +8 -1
  15. package/dist/cli/command-guide.d.ts.map +1 -1
  16. package/dist/cli/command-guide.js +21 -8
  17. package/dist/cli/command-snippets.d.ts +24 -0
  18. package/dist/cli/command-snippets.d.ts.map +1 -0
  19. package/dist/cli/command-snippets.js +23 -0
  20. package/dist/cli/reason-codes.d.ts +9 -0
  21. package/dist/cli/reason-codes.d.ts.map +1 -0
  22. package/dist/cli/reason-codes.js +79 -0
  23. package/dist/cli/recipes-bundled.d.ts +1 -0
  24. package/dist/cli/recipes-bundled.d.ts.map +1 -1
  25. package/dist/cli/recipes-bundled.js +4 -1
  26. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  27. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  28. package/dist/cli/run-cli/command-catalog.js +40 -1
  29. package/dist/cli/run-cli/commands/config.d.ts +5 -0
  30. package/dist/cli/run-cli/commands/config.d.ts.map +1 -1
  31. package/dist/cli/run-cli/commands/config.js +86 -1
  32. package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
  33. package/dist/cli/run-cli/commands/core.js +55 -0
  34. package/dist/cli/run-cli/commands/init/recipes.d.ts +5 -1
  35. package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
  36. package/dist/cli/run-cli/commands/init/recipes.js +24 -4
  37. package/dist/cli/run-cli/commands/init/write-workflow.d.ts +7 -0
  38. package/dist/cli/run-cli/commands/init/write-workflow.d.ts.map +1 -0
  39. package/dist/cli/run-cli/commands/init/write-workflow.js +52 -0
  40. package/dist/cli/run-cli/commands/init.d.ts +2 -1
  41. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  42. package/dist/cli/run-cli/commands/init.js +104 -54
  43. package/dist/cli/run-cli.d.ts.map +1 -1
  44. package/dist/cli/run-cli.js +70 -1
  45. package/dist/commands/backend/sync.command.d.ts.map +1 -1
  46. package/dist/commands/backend/sync.command.js +7 -6
  47. package/dist/commands/backend.d.ts.map +1 -1
  48. package/dist/commands/backend.js +2 -0
  49. package/dist/commands/block.spec.d.ts.map +1 -1
  50. package/dist/commands/block.spec.js +23 -2
  51. package/dist/commands/commit.spec.d.ts.map +1 -1
  52. package/dist/commands/commit.spec.js +18 -6
  53. package/dist/commands/doctor.run.d.ts.map +1 -1
  54. package/dist/commands/doctor.run.js +96 -10
  55. package/dist/commands/finish.spec.d.ts.map +1 -1
  56. package/dist/commands/finish.spec.js +53 -4
  57. package/dist/commands/guard/commit.command.d.ts.map +1 -1
  58. package/dist/commands/guard/commit.command.js +26 -20
  59. package/dist/commands/guard/impl/allow.d.ts.map +1 -1
  60. package/dist/commands/guard/impl/allow.js +8 -1
  61. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  62. package/dist/commands/guard/impl/commands.js +19 -21
  63. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  64. package/dist/commands/guard/impl/comment-commit.js +8 -17
  65. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  66. package/dist/commands/recipes/impl/commands/install.js +36 -13
  67. package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
  68. package/dist/commands/recipes/impl/scenario.js +25 -0
  69. package/dist/commands/recipes/impl/types.d.ts +4 -0
  70. package/dist/commands/recipes/impl/types.d.ts.map +1 -1
  71. package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
  72. package/dist/commands/scenario/impl/commands.js +74 -3
  73. package/dist/commands/scenario/impl/report.d.ts +8 -0
  74. package/dist/commands/scenario/impl/report.d.ts.map +1 -1
  75. package/dist/commands/scenario/impl/report.js +1 -0
  76. package/dist/commands/shared/reconcile-check.d.ts +7 -0
  77. package/dist/commands/shared/reconcile-check.d.ts.map +1 -0
  78. package/dist/commands/shared/reconcile-check.js +60 -0
  79. package/dist/commands/start.spec.d.ts.map +1 -1
  80. package/dist/commands/start.spec.js +23 -2
  81. package/dist/commands/sync.command.d.ts.map +1 -1
  82. package/dist/commands/sync.command.js +9 -2
  83. package/dist/commands/task/finish.d.ts.map +1 -1
  84. package/dist/commands/task/finish.js +34 -10
  85. package/dist/commands/task/list.d.ts.map +1 -1
  86. package/dist/commands/task/list.js +2 -1
  87. package/dist/commands/task/list.spec.d.ts.map +1 -1
  88. package/dist/commands/task/list.spec.js +7 -0
  89. package/dist/commands/task/next.d.ts.map +1 -1
  90. package/dist/commands/task/next.js +2 -1
  91. package/dist/commands/task/next.spec.d.ts.map +1 -1
  92. package/dist/commands/task/next.spec.js +7 -0
  93. package/dist/commands/task/search.d.ts.map +1 -1
  94. package/dist/commands/task/search.js +2 -1
  95. package/dist/commands/task/search.spec.d.ts.map +1 -1
  96. package/dist/commands/task/search.spec.js +7 -0
  97. package/dist/commands/task/set-status.command.d.ts.map +1 -1
  98. package/dist/commands/task/set-status.command.js +22 -2
  99. package/dist/commands/task/shared.d.ts +7 -0
  100. package/dist/commands/task/shared.d.ts.map +1 -1
  101. package/dist/commands/task/shared.js +21 -1
  102. package/dist/commands/task/verify-record.d.ts.map +1 -1
  103. package/dist/commands/task/verify-record.js +2 -0
  104. package/dist/commands/workflow-build.command.d.ts +8 -0
  105. package/dist/commands/workflow-build.command.d.ts.map +1 -0
  106. package/dist/commands/workflow-build.command.js +96 -0
  107. package/dist/commands/workflow-playbook.command.d.ts +10 -0
  108. package/dist/commands/workflow-playbook.command.d.ts.map +1 -0
  109. package/dist/commands/workflow-playbook.command.js +174 -0
  110. package/dist/commands/workflow-restore.command.d.ts +5 -0
  111. package/dist/commands/workflow-restore.command.d.ts.map +1 -0
  112. package/dist/commands/workflow-restore.command.js +30 -0
  113. package/dist/commands/workflow.command.d.ts +6 -0
  114. package/dist/commands/workflow.command.d.ts.map +1 -0
  115. package/dist/commands/workflow.command.js +36 -0
  116. package/dist/harness/dynamic-tool-contract.d.ts +29 -0
  117. package/dist/harness/dynamic-tool-contract.d.ts.map +1 -0
  118. package/dist/harness/dynamic-tool-contract.js +86 -0
  119. package/dist/harness/hooks-lifecycle.d.ts +27 -0
  120. package/dist/harness/hooks-lifecycle.d.ts.map +1 -0
  121. package/dist/harness/hooks-lifecycle.js +67 -0
  122. package/dist/harness/index.d.ts +9 -0
  123. package/dist/harness/index.d.ts.map +1 -0
  124. package/dist/harness/index.js +8 -0
  125. package/dist/harness/reconcile.d.ts +37 -0
  126. package/dist/harness/reconcile.d.ts.map +1 -0
  127. package/dist/harness/reconcile.js +42 -0
  128. package/dist/harness/retry-policy.d.ts +31 -0
  129. package/dist/harness/retry-policy.d.ts.map +1 -0
  130. package/dist/harness/retry-policy.js +33 -0
  131. package/dist/harness/scheduler.d.ts +18 -0
  132. package/dist/harness/scheduler.d.ts.map +1 -0
  133. package/dist/harness/scheduler.js +55 -0
  134. package/dist/harness/state-machine.d.ts +17 -0
  135. package/dist/harness/state-machine.d.ts.map +1 -0
  136. package/dist/harness/state-machine.js +70 -0
  137. package/dist/harness/token-accounting.d.ts +19 -0
  138. package/dist/harness/token-accounting.d.ts.map +1 -0
  139. package/dist/harness/token-accounting.js +77 -0
  140. package/dist/harness/workspace-safety.d.ts +14 -0
  141. package/dist/harness/workspace-safety.d.ts.map +1 -0
  142. package/dist/harness/workspace-safety.js +62 -0
  143. package/dist/policy/rules/allowlist.d.ts.map +1 -1
  144. package/dist/policy/rules/allowlist.js +9 -0
  145. package/dist/recipes/bundled-recipes.d.ts +4 -0
  146. package/dist/recipes/bundled-recipes.d.ts.map +1 -1
  147. package/dist/recipes/bundled-recipes.js +11 -0
  148. package/dist/shared/allow-prefix-policy.d.ts +3 -0
  149. package/dist/shared/allow-prefix-policy.d.ts.map +1 -0
  150. package/dist/shared/allow-prefix-policy.js +8 -0
  151. package/dist/shared/errors.d.ts +6 -0
  152. package/dist/shared/errors.d.ts.map +1 -1
  153. package/dist/shared/errors.js +1 -0
  154. package/dist/workflow-runtime/build.d.ts +4 -0
  155. package/dist/workflow-runtime/build.d.ts.map +1 -0
  156. package/dist/workflow-runtime/build.js +114 -0
  157. package/dist/workflow-runtime/enforcement.d.ts +3 -0
  158. package/dist/workflow-runtime/enforcement.d.ts.map +1 -0
  159. package/dist/workflow-runtime/enforcement.js +10 -0
  160. package/dist/workflow-runtime/file-ops.d.ts +11 -0
  161. package/dist/workflow-runtime/file-ops.d.ts.map +1 -0
  162. package/dist/workflow-runtime/file-ops.js +248 -0
  163. package/dist/workflow-runtime/fix.d.ts +9 -0
  164. package/dist/workflow-runtime/fix.d.ts.map +1 -0
  165. package/dist/workflow-runtime/fix.js +107 -0
  166. package/dist/workflow-runtime/index.d.ts +11 -0
  167. package/dist/workflow-runtime/index.d.ts.map +1 -0
  168. package/dist/workflow-runtime/index.js +10 -0
  169. package/dist/workflow-runtime/markdown.d.ts +10 -0
  170. package/dist/workflow-runtime/markdown.d.ts.map +1 -0
  171. package/dist/workflow-runtime/markdown.js +147 -0
  172. package/dist/workflow-runtime/observability.d.ts +12 -0
  173. package/dist/workflow-runtime/observability.d.ts.map +1 -0
  174. package/dist/workflow-runtime/observability.js +14 -0
  175. package/dist/workflow-runtime/paths.d.ts +3 -0
  176. package/dist/workflow-runtime/paths.d.ts.map +1 -0
  177. package/dist/workflow-runtime/paths.js +11 -0
  178. package/dist/workflow-runtime/template.d.ts +7 -0
  179. package/dist/workflow-runtime/template.d.ts.map +1 -0
  180. package/dist/workflow-runtime/template.js +94 -0
  181. package/dist/workflow-runtime/types.d.ts +68 -0
  182. package/dist/workflow-runtime/types.d.ts.map +1 -0
  183. package/dist/workflow-runtime/types.js +1 -0
  184. package/dist/workflow-runtime/validate.d.ts +8 -0
  185. package/dist/workflow-runtime/validate.d.ts.map +1 -0
  186. package/dist/workflow-runtime/validate.js +331 -0
  187. package/package.json +3 -3
@@ -0,0 +1,94 @@
1
+ import { diagnosticsToValidationResult } from "./markdown.js";
2
+ function stringifyTemplateValue(value) {
3
+ if (value === undefined || value === null)
4
+ return "";
5
+ if (typeof value === "string")
6
+ return value;
7
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
8
+ return String(value);
9
+ }
10
+ if (typeof value === "symbol") {
11
+ return value.description ? `Symbol(${value.description})` : "Symbol()";
12
+ }
13
+ return JSON.stringify(value) ?? "";
14
+ }
15
+ const DEFAULT_FILTERS = {
16
+ upper: (value) => stringifyTemplateValue(value).toUpperCase(),
17
+ lower: (value) => stringifyTemplateValue(value).toLowerCase(),
18
+ trim: (value) => stringifyTemplateValue(value).trim(),
19
+ json: (value) => JSON.stringify(value),
20
+ };
21
+ const DEFAULT_OPTIONS = {
22
+ strictVariables: true,
23
+ strictFilters: true,
24
+ allowedFilters: DEFAULT_FILTERS,
25
+ };
26
+ function getPathValue(context, dottedPath) {
27
+ const segments = dottedPath.split(".");
28
+ let current = context;
29
+ for (const segment of segments) {
30
+ if (!current || typeof current !== "object")
31
+ return undefined;
32
+ const record = current;
33
+ current = record[segment];
34
+ }
35
+ return current;
36
+ }
37
+ export function validateTemplateStrict(template, context, options = {}) {
38
+ const opts = {
39
+ ...DEFAULT_OPTIONS,
40
+ ...options,
41
+ allowedFilters: { ...DEFAULT_FILTERS, ...options.allowedFilters },
42
+ };
43
+ const diagnostics = [];
44
+ const re = /{{\s*([a-zA-Z_][\w.]*)(?:\s*\|\s*([a-zA-Z_][\w-]*))?\s*}}/g;
45
+ for (const match of template.matchAll(re)) {
46
+ const variable = match[1];
47
+ const filter = match[2];
48
+ if (variable && opts.strictVariables) {
49
+ const value = getPathValue(context, variable);
50
+ if (value === undefined) {
51
+ diagnostics.push({
52
+ code: "WF_TEMPLATE_UNKNOWN_VARIABLE",
53
+ severity: "ERROR",
54
+ path: `template.${variable}`,
55
+ message: `Unknown template variable: ${variable}`,
56
+ });
57
+ }
58
+ }
59
+ if (filter && opts.strictFilters && !opts.allowedFilters[filter]) {
60
+ diagnostics.push({
61
+ code: "WF_TEMPLATE_UNKNOWN_FILTER",
62
+ severity: "ERROR",
63
+ path: `template.filter.${filter}`,
64
+ message: `Unknown template filter: ${filter}`,
65
+ });
66
+ }
67
+ }
68
+ return diagnosticsToValidationResult(diagnostics);
69
+ }
70
+ export function renderTemplateStrict(template, context, options = {}) {
71
+ const opts = {
72
+ ...DEFAULT_OPTIONS,
73
+ ...options,
74
+ allowedFilters: { ...DEFAULT_FILTERS, ...options.allowedFilters },
75
+ };
76
+ const validation = validateTemplateStrict(template, context, opts);
77
+ if (!validation.ok) {
78
+ return {
79
+ text: template,
80
+ diagnostics: validation.diagnostics,
81
+ };
82
+ }
83
+ const rendered = template.replaceAll(/{{\s*([a-zA-Z_][\w.]*)(?:\s*\|\s*([a-zA-Z_][\w-]*))?\s*}}/g, (_full, variable, filter) => {
84
+ let value = getPathValue(context, variable);
85
+ if (filter) {
86
+ value = opts.allowedFilters[filter]?.(value);
87
+ }
88
+ return stringifyTemplateValue(value);
89
+ });
90
+ return {
91
+ text: rendered,
92
+ diagnostics: [],
93
+ };
94
+ }
@@ -0,0 +1,68 @@
1
+ export type WorkflowMode = "direct" | "branch_pr";
2
+ export type WorkflowApprovals = {
3
+ require_plan: boolean;
4
+ require_verify: boolean;
5
+ require_network: boolean;
6
+ };
7
+ export type WorkflowRetryPolicy = {
8
+ normal_exit_continuation: boolean;
9
+ abnormal_backoff: "exponential";
10
+ max_attempts: number;
11
+ };
12
+ export type WorkflowTimeouts = {
13
+ stall_seconds: number;
14
+ };
15
+ export type WorkflowFrontMatter = {
16
+ version: number;
17
+ mode: WorkflowMode;
18
+ owners: {
19
+ orchestrator: string;
20
+ };
21
+ approvals: WorkflowApprovals;
22
+ retry_policy: WorkflowRetryPolicy;
23
+ timeouts: WorkflowTimeouts;
24
+ in_scope_paths: string[];
25
+ };
26
+ export type WorkflowSectionName = "Prompt Template" | "Checks" | "Fallback";
27
+ export type WorkflowSections = Record<string, string>;
28
+ export type WorkflowDocument = {
29
+ frontMatter: WorkflowFrontMatter;
30
+ frontMatterRaw: Record<string, unknown>;
31
+ body: string;
32
+ sections: WorkflowSections;
33
+ promptTemplate: string;
34
+ sourcePath?: string;
35
+ };
36
+ export type WorkflowSeverity = "ERROR" | "WARN" | "INFO";
37
+ export type WorkflowErrorCode = "WF_MISSING_FILE" | "WF_READ_FAILED" | "WF_FRONTMATTER_NOT_OBJECT" | "WF_PARSE_ERROR" | "WF_SCHEMA_MISSING" | "WF_SCHEMA_TYPE" | "WF_SCHEMA_ENUM" | "WF_SCHEMA_RANGE" | "WF_SCHEMA_UNKNOWN_KEY" | "WF_REQUIRED_SECTION_MISSING" | "WF_TEMPLATE_UNKNOWN_VARIABLE" | "WF_TEMPLATE_UNKNOWN_FILTER" | "WF_PATH_OUTSIDE_ROOT" | "WF_OWNER_NOT_FOUND" | "WF_POLICY_MISMATCH" | "WF_FIX_SKIPPED_UNSAFE";
38
+ export type WorkflowDiagnostic = {
39
+ code: WorkflowErrorCode;
40
+ severity: WorkflowSeverity;
41
+ path: string;
42
+ message: string;
43
+ };
44
+ export type WorkflowValidationResult = {
45
+ ok: boolean;
46
+ diagnostics: WorkflowDiagnostic[];
47
+ };
48
+ export type WorkflowTemplateRenderOptions = {
49
+ strictVariables: boolean;
50
+ strictFilters: boolean;
51
+ allowedFilters: Record<string, (value: unknown) => unknown>;
52
+ };
53
+ export type WorkflowPaths = {
54
+ workflowPath: string;
55
+ legacyWorkflowPath: string;
56
+ lastKnownGoodPath: string;
57
+ workflowDir: string;
58
+ };
59
+ export type WorkflowBuildInput = {
60
+ baseTemplate: string;
61
+ projectOverrideTemplate?: string;
62
+ runtimeContext: Record<string, unknown>;
63
+ };
64
+ export type WorkflowBuildOutput = {
65
+ text: string;
66
+ diagnostics: WorkflowDiagnostic[];
67
+ };
68
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workflow-runtime/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { AgentplaneConfig } from "@agentplaneorg/core";
2
+ import type { WorkflowDocument, WorkflowValidationResult } from "./types.js";
3
+ export declare function validateWorkflowDocument(document: WorkflowDocument, opts?: {
4
+ repoRoot?: string;
5
+ knownAgentIds?: Set<string>;
6
+ config?: AgentplaneConfig | null;
7
+ }): WorkflowValidationResult;
8
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/workflow-runtime/validate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAEV,gBAAgB,EAEhB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AA8VpB,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,CAAC,EAAE;IACL,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAClC,GACA,wBAAwB,CA0F1B"}
@@ -0,0 +1,331 @@
1
+ import path from "node:path";
2
+ import { diagnosticsToValidationResult } from "./markdown.js";
3
+ const ROOT_KEYS = new Set([
4
+ "version",
5
+ "mode",
6
+ "owners",
7
+ "approvals",
8
+ "retry_policy",
9
+ "timeouts",
10
+ "in_scope_paths",
11
+ ]);
12
+ function push(diags, diagnostic) {
13
+ diags.push(diagnostic);
14
+ }
15
+ function isRecord(value) {
16
+ return !!value && typeof value === "object" && !Array.isArray(value);
17
+ }
18
+ function expectBoolean(diags, value, pathName, required) {
19
+ if (value === undefined) {
20
+ if (required) {
21
+ push(diags, {
22
+ code: "WF_SCHEMA_MISSING",
23
+ severity: "ERROR",
24
+ path: pathName,
25
+ message: `${pathName} is required.`,
26
+ });
27
+ }
28
+ return undefined;
29
+ }
30
+ if (typeof value !== "boolean") {
31
+ push(diags, {
32
+ code: "WF_SCHEMA_TYPE",
33
+ severity: "ERROR",
34
+ path: pathName,
35
+ message: `${pathName} must be a boolean.`,
36
+ });
37
+ return undefined;
38
+ }
39
+ return value;
40
+ }
41
+ function expectString(diags, value, pathName, required) {
42
+ if (value === undefined) {
43
+ if (required) {
44
+ push(diags, {
45
+ code: "WF_SCHEMA_MISSING",
46
+ severity: "ERROR",
47
+ path: pathName,
48
+ message: `${pathName} is required.`,
49
+ });
50
+ }
51
+ return undefined;
52
+ }
53
+ if (typeof value !== "string") {
54
+ push(diags, {
55
+ code: "WF_SCHEMA_TYPE",
56
+ severity: "ERROR",
57
+ path: pathName,
58
+ message: `${pathName} must be a string.`,
59
+ });
60
+ return undefined;
61
+ }
62
+ if (value.trim().length === 0) {
63
+ push(diags, {
64
+ code: "WF_SCHEMA_RANGE",
65
+ severity: "ERROR",
66
+ path: pathName,
67
+ message: `${pathName} must be non-empty.`,
68
+ });
69
+ return undefined;
70
+ }
71
+ return value;
72
+ }
73
+ function expectIntegerInRange(diags, value, pathName, min, max, required) {
74
+ if (value === undefined) {
75
+ if (required) {
76
+ push(diags, {
77
+ code: "WF_SCHEMA_MISSING",
78
+ severity: "ERROR",
79
+ path: pathName,
80
+ message: `${pathName} is required.`,
81
+ });
82
+ }
83
+ return undefined;
84
+ }
85
+ if (typeof value !== "number" || !Number.isInteger(value)) {
86
+ push(diags, {
87
+ code: "WF_SCHEMA_TYPE",
88
+ severity: "ERROR",
89
+ path: pathName,
90
+ message: `${pathName} must be an integer.`,
91
+ });
92
+ return undefined;
93
+ }
94
+ if (value < min || value > max) {
95
+ push(diags, {
96
+ code: "WF_SCHEMA_RANGE",
97
+ severity: "ERROR",
98
+ path: pathName,
99
+ message: `${pathName} must be in [${min}, ${max}].`,
100
+ });
101
+ return undefined;
102
+ }
103
+ return value;
104
+ }
105
+ function validateUnknownKeys(diags, raw) {
106
+ for (const key of Object.keys(raw)) {
107
+ if (!ROOT_KEYS.has(key)) {
108
+ push(diags, {
109
+ code: "WF_SCHEMA_UNKNOWN_KEY",
110
+ severity: "ERROR",
111
+ path: `front_matter.${key}`,
112
+ message: `Unknown front matter key: ${key}`,
113
+ });
114
+ }
115
+ }
116
+ }
117
+ function validateMode(diags, value) {
118
+ const mode = expectString(diags, value, "front_matter.mode", true);
119
+ if (!mode)
120
+ return undefined;
121
+ if (mode !== "direct" && mode !== "branch_pr") {
122
+ push(diags, {
123
+ code: "WF_SCHEMA_ENUM",
124
+ severity: "ERROR",
125
+ path: "front_matter.mode",
126
+ message: "front_matter.mode must be one of: direct, branch_pr.",
127
+ });
128
+ return undefined;
129
+ }
130
+ return mode;
131
+ }
132
+ function validateOwners(diags, value, knownAgentIds) {
133
+ if (!isRecord(value)) {
134
+ push(diags, {
135
+ code: value === undefined ? "WF_SCHEMA_MISSING" : "WF_SCHEMA_TYPE",
136
+ severity: "ERROR",
137
+ path: "front_matter.owners",
138
+ message: "front_matter.owners must be an object.",
139
+ });
140
+ return undefined;
141
+ }
142
+ const orchestrator = expectString(diags, value.orchestrator, "front_matter.owners.orchestrator", true);
143
+ if (!orchestrator)
144
+ return undefined;
145
+ if (knownAgentIds && !knownAgentIds.has(orchestrator)) {
146
+ push(diags, {
147
+ code: "WF_OWNER_NOT_FOUND",
148
+ severity: "ERROR",
149
+ path: "front_matter.owners.orchestrator",
150
+ message: `Owner ${orchestrator} was not found in .agentplane/agents/*.json.`,
151
+ });
152
+ }
153
+ return { orchestrator };
154
+ }
155
+ function validateApprovals(diags, value) {
156
+ if (!isRecord(value)) {
157
+ push(diags, {
158
+ code: value === undefined ? "WF_SCHEMA_MISSING" : "WF_SCHEMA_TYPE",
159
+ severity: "ERROR",
160
+ path: "front_matter.approvals",
161
+ message: "front_matter.approvals must be an object.",
162
+ });
163
+ return undefined;
164
+ }
165
+ const require_plan = expectBoolean(diags, value.require_plan, "front_matter.approvals.require_plan", true);
166
+ const require_verify = expectBoolean(diags, value.require_verify, "front_matter.approvals.require_verify", true);
167
+ const require_network = expectBoolean(diags, value.require_network, "front_matter.approvals.require_network", true);
168
+ if (require_plan === undefined || require_verify === undefined || require_network === undefined) {
169
+ return undefined;
170
+ }
171
+ return { require_plan, require_verify, require_network };
172
+ }
173
+ function validateRetryPolicy(diags, value) {
174
+ if (!isRecord(value)) {
175
+ push(diags, {
176
+ code: value === undefined ? "WF_SCHEMA_MISSING" : "WF_SCHEMA_TYPE",
177
+ severity: "ERROR",
178
+ path: "front_matter.retry_policy",
179
+ message: "front_matter.retry_policy must be an object.",
180
+ });
181
+ return undefined;
182
+ }
183
+ const normal_exit_continuation = expectBoolean(diags, value.normal_exit_continuation, "front_matter.retry_policy.normal_exit_continuation", true);
184
+ const abnormal_backoff = expectString(diags, value.abnormal_backoff, "front_matter.retry_policy.abnormal_backoff", true);
185
+ if (abnormal_backoff && abnormal_backoff !== "exponential") {
186
+ push(diags, {
187
+ code: "WF_SCHEMA_ENUM",
188
+ severity: "ERROR",
189
+ path: "front_matter.retry_policy.abnormal_backoff",
190
+ message: "front_matter.retry_policy.abnormal_backoff must be exponential.",
191
+ });
192
+ }
193
+ const max_attempts = expectIntegerInRange(diags, value.max_attempts, "front_matter.retry_policy.max_attempts", 1, 100, true);
194
+ if (normal_exit_continuation === undefined || !abnormal_backoff || max_attempts === undefined) {
195
+ return undefined;
196
+ }
197
+ return {
198
+ normal_exit_continuation,
199
+ abnormal_backoff: "exponential",
200
+ max_attempts,
201
+ };
202
+ }
203
+ function validateTimeouts(diags, value) {
204
+ if (!isRecord(value)) {
205
+ push(diags, {
206
+ code: value === undefined ? "WF_SCHEMA_MISSING" : "WF_SCHEMA_TYPE",
207
+ severity: "ERROR",
208
+ path: "front_matter.timeouts",
209
+ message: "front_matter.timeouts must be an object.",
210
+ });
211
+ return undefined;
212
+ }
213
+ const stall_seconds = expectIntegerInRange(diags, value.stall_seconds, "front_matter.timeouts.stall_seconds", 1, 86_400, true);
214
+ if (stall_seconds === undefined)
215
+ return undefined;
216
+ return { stall_seconds };
217
+ }
218
+ function validateScopePaths(diags, value, repoRoot) {
219
+ if (!Array.isArray(value)) {
220
+ push(diags, {
221
+ code: value === undefined ? "WF_SCHEMA_MISSING" : "WF_SCHEMA_TYPE",
222
+ severity: "ERROR",
223
+ path: "front_matter.in_scope_paths",
224
+ message: "front_matter.in_scope_paths must be an array.",
225
+ });
226
+ return undefined;
227
+ }
228
+ const normalized = value
229
+ .map((item) => (typeof item === "string" ? item.trim() : ""))
230
+ .filter((item) => item.length > 0);
231
+ if (normalized.length === 0) {
232
+ push(diags, {
233
+ code: "WF_SCHEMA_RANGE",
234
+ severity: "ERROR",
235
+ path: "front_matter.in_scope_paths",
236
+ message: "front_matter.in_scope_paths must contain at least one path.",
237
+ });
238
+ return undefined;
239
+ }
240
+ if (repoRoot) {
241
+ for (const p of normalized) {
242
+ const candidate = path.resolve(repoRoot, p.replaceAll(/[*]{1,2}$/g, ""));
243
+ const rootPrefix = `${path.resolve(repoRoot)}${path.sep}`;
244
+ if (!(candidate === path.resolve(repoRoot) || candidate.startsWith(rootPrefix))) {
245
+ push(diags, {
246
+ code: "WF_PATH_OUTSIDE_ROOT",
247
+ severity: "ERROR",
248
+ path: "front_matter.in_scope_paths",
249
+ message: `Path escapes repository root: ${p}`,
250
+ });
251
+ }
252
+ }
253
+ }
254
+ return normalized;
255
+ }
256
+ export function validateWorkflowDocument(document, opts) {
257
+ const diags = [];
258
+ const raw = document.frontMatterRaw;
259
+ if (!isRecord(raw)) {
260
+ push(diags, {
261
+ code: "WF_FRONTMATTER_NOT_OBJECT",
262
+ severity: "ERROR",
263
+ path: "front_matter",
264
+ message: "Workflow front matter must decode to an object.",
265
+ });
266
+ return diagnosticsToValidationResult(diags);
267
+ }
268
+ validateUnknownKeys(diags, raw);
269
+ const version = expectIntegerInRange(diags, raw.version, "front_matter.version", 1, Number.MAX_SAFE_INTEGER, true);
270
+ const mode = validateMode(diags, raw.mode);
271
+ const owners = validateOwners(diags, raw.owners, opts?.knownAgentIds ?? null);
272
+ const approvals = validateApprovals(diags, raw.approvals);
273
+ const retry_policy = validateRetryPolicy(diags, raw.retry_policy);
274
+ const timeouts = validateTimeouts(diags, raw.timeouts);
275
+ const in_scope_paths = validateScopePaths(diags, raw.in_scope_paths, opts?.repoRoot);
276
+ if (opts?.config && mode && opts.config.workflow_mode !== mode) {
277
+ push(diags, {
278
+ code: "WF_POLICY_MISMATCH",
279
+ severity: "ERROR",
280
+ path: "front_matter.mode",
281
+ message: `workflow mode mismatch: WORKFLOW.md=${mode}, config=${opts.config.workflow_mode}`,
282
+ });
283
+ }
284
+ if (opts?.config && approvals) {
285
+ const cfgApprovals = opts.config.agents?.approvals;
286
+ if (cfgApprovals) {
287
+ if (cfgApprovals.require_plan !== approvals.require_plan) {
288
+ push(diags, {
289
+ code: "WF_POLICY_MISMATCH",
290
+ severity: "WARN",
291
+ path: "front_matter.approvals.require_plan",
292
+ message: "Approval mismatch with .agentplane/config.json (require_plan).",
293
+ });
294
+ }
295
+ if (cfgApprovals.require_verify !== approvals.require_verify) {
296
+ push(diags, {
297
+ code: "WF_POLICY_MISMATCH",
298
+ severity: "WARN",
299
+ path: "front_matter.approvals.require_verify",
300
+ message: "Approval mismatch with .agentplane/config.json (require_verify).",
301
+ });
302
+ }
303
+ if (cfgApprovals.require_network !== approvals.require_network) {
304
+ push(diags, {
305
+ code: "WF_POLICY_MISMATCH",
306
+ severity: "WARN",
307
+ path: "front_matter.approvals.require_network",
308
+ message: "Approval mismatch with .agentplane/config.json (require_network).",
309
+ });
310
+ }
311
+ }
312
+ }
313
+ if (version !== undefined &&
314
+ mode &&
315
+ owners &&
316
+ approvals &&
317
+ retry_policy &&
318
+ timeouts &&
319
+ in_scope_paths) {
320
+ document.frontMatter = {
321
+ version,
322
+ mode,
323
+ owners,
324
+ approvals,
325
+ retry_policy,
326
+ timeouts,
327
+ in_scope_paths,
328
+ };
329
+ }
330
+ return diagnosticsToValidationResult(diags);
331
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentplane",
3
- "version": "0.2.24",
3
+ "version": "0.2.26",
4
4
  "description": "Agent Plane CLI for task workflows, recipes, and project automation.",
5
5
  "keywords": [
6
6
  "agentplane",
@@ -48,14 +48,14 @@
48
48
  },
49
49
  "scripts": {
50
50
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true}); require('node:fs').rmSync('tsconfig.tsbuildinfo',{force:true});\"",
51
- "build": "tsc -b",
51
+ "build": "tsc -b && node ../../scripts/write-build-manifest.mjs .",
52
52
  "typecheck": "tsc -b",
53
53
  "prepare": "npm run build",
54
54
  "prepack": "npm run clean && npm run build",
55
55
  "prepublishOnly": "node ../../scripts/enforce-github-publish.mjs && npm run prepack"
56
56
  },
57
57
  "dependencies": {
58
- "@agentplaneorg/core": "0.2.24",
58
+ "@agentplaneorg/core": "0.2.26",
59
59
  "yauzl": "^2.10.0"
60
60
  },
61
61
  "devDependencies": {