@probelabs/visor 0.1.107 → 0.1.112
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/README.md +6 -0
- package/defaults/task-refinement.yaml +7 -3
- package/defaults/visor.tests.yaml +13 -2
- package/defaults/visor.yaml +1 -0
- package/dist/663.index.js +3 -2
- package/dist/80.index.js +3 -2
- package/dist/ai-review-service.d.ts +13 -9
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/debug-visualizer/ws-server.d.ts +7 -1
- package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
- package/dist/defaults/task-refinement.yaml +7 -3
- package/dist/defaults/visor.tests.yaml +13 -2
- package/dist/defaults/visor.yaml +1 -0
- package/dist/docs/advanced-ai.md +60 -1
- package/dist/docs/ai-configuration.md +67 -0
- package/dist/docs/ai-custom-tools-usage.md +261 -0
- package/dist/docs/ai-custom-tools.md +392 -0
- package/dist/docs/bot-transports-rfc.md +23 -0
- package/dist/docs/configuration.md +21 -0
- package/dist/docs/engine-pause-resume-rfc.md +192 -0
- package/dist/docs/lifecycle-hooks.md +253 -0
- package/dist/docs/liquid-templates.md +143 -0
- package/dist/docs/providers/git-checkout.md +589 -0
- package/dist/docs/recipes.md +458 -5
- package/dist/docs/rfc/git-checkout-step.md +601 -0
- package/dist/docs/rfc/on_init-hook.md +1294 -0
- package/dist/docs/rfc/workspace-isolation.md +216 -0
- package/dist/docs/router-patterns.md +339 -0
- package/dist/event-bus/types.d.ts +14 -0
- package/dist/event-bus/types.d.ts.map +1 -1
- package/dist/examples/ai-custom-tools-example.yaml +206 -0
- package/dist/examples/ai-custom-tools-simple.yaml +76 -0
- package/dist/examples/git-checkout-basic.yaml +32 -0
- package/dist/examples/git-checkout-compare.yaml +59 -0
- package/dist/examples/git-checkout-cross-repo.yaml +76 -0
- package/dist/examples/on-init-import-demo.yaml +179 -0
- package/dist/examples/reusable-tools.yaml +92 -0
- package/dist/examples/reusable-workflows.yaml +88 -0
- package/dist/examples/session-reuse-self.yaml +81 -0
- package/dist/examples/slack-simple-chat.yaml +775 -0
- package/dist/failure-condition-evaluator.d.ts +2 -0
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/frontends/github-frontend.d.ts +20 -0
- package/dist/frontends/github-frontend.d.ts.map +1 -1
- package/dist/frontends/host.d.ts +4 -0
- package/dist/frontends/host.d.ts.map +1 -1
- package/dist/frontends/slack-frontend.d.ts +58 -0
- package/dist/frontends/slack-frontend.d.ts.map +1 -0
- package/dist/generated/config-schema.d.ts +409 -41
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +436 -47
- package/dist/github-comments.d.ts +2 -0
- package/dist/github-comments.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +83587 -56085
- package/dist/liquid-extensions.d.ts.map +1 -1
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/output/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
- package/dist/output/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
- package/dist/output-formatters.d.ts.map +1 -1
- package/dist/providers/ai-check-provider.d.ts +12 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/check-provider-registry.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +9 -0
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/custom-tool-executor.d.ts.map +1 -1
- package/dist/providers/git-checkout-provider.d.ts +25 -0
- package/dist/providers/git-checkout-provider.d.ts.map +1 -0
- package/dist/providers/http-client-provider.d.ts +3 -0
- package/dist/providers/http-client-provider.d.ts.map +1 -1
- package/dist/providers/human-input-check-provider.d.ts +2 -0
- package/dist/providers/human-input-check-provider.d.ts.map +1 -1
- package/dist/providers/log-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts +1 -1
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
- package/dist/providers/memory-check-provider.d.ts.map +1 -1
- package/dist/providers/script-check-provider.d.ts.map +1 -1
- package/dist/providers/workflow-check-provider.d.ts.map +1 -1
- package/dist/reviewer.d.ts.map +1 -1
- package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
- package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
- package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
- package/dist/sdk/{chunk-OOZITMRU.mjs → chunk-3OMWVM6J.mjs} +11 -1
- package/dist/sdk/{chunk-OOZITMRU.mjs.map → chunk-3OMWVM6J.mjs.map} +1 -1
- package/dist/sdk/{chunk-37ZSCMFC.mjs → chunk-7UK3NIIT.mjs} +2 -2
- package/dist/sdk/{chunk-VMPLF6FT.mjs → chunk-AGIZJ4UZ.mjs} +50 -4
- package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
- package/dist/sdk/{chunk-IEO6CFLG.mjs → chunk-AIVFBIS4.mjs} +161 -5
- package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
- package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
- package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
- package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
- package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
- package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
- package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
- package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
- package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
- package/dist/sdk/{chunk-6Y4YTKCF.mjs → chunk-NAW3DB3I.mjs} +2 -2
- package/dist/sdk/{chunk-OWUVOILT.mjs → chunk-QR7MOMJH.mjs} +4 -3
- package/dist/sdk/{chunk-OWUVOILT.mjs.map → chunk-QR7MOMJH.mjs.map} +1 -1
- package/dist/sdk/{chunk-PTL3K3PN.mjs → chunk-QY2XYPEV.mjs} +488 -60
- package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
- package/dist/sdk/{chunk-OZJ263FM.mjs → chunk-SIWNBRTK.mjs} +29 -215
- package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
- package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
- package/dist/sdk/{config-M4ZNO6NU.mjs → config-YNC2EOOT.mjs} +5 -3
- package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs → failure-condition-evaluator-YGTF2GHG.mjs} +6 -5
- package/dist/sdk/{github-frontend-4AWRJT7D.mjs → github-frontend-SIAEOCON.mjs} +190 -12
- package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
- package/dist/sdk/{host-7GBC3S7L.mjs → host-DXUYTNMU.mjs} +5 -2
- package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
- package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs → liquid-extensions-PKWCKK7E.mjs} +5 -4
- package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
- package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
- package/dist/sdk/{renderer-schema-6RF26VUS.mjs → renderer-schema-LPKN5UJS.mjs} +3 -2
- package/dist/sdk/{renderer-schema-6RF26VUS.mjs.map → renderer-schema-LPKN5UJS.mjs.map} +1 -1
- package/dist/sdk/{routing-RP56JTV2.mjs → routing-6N45MJ4F.mjs} +7 -6
- package/dist/sdk/sdk.d.mts +219 -5
- package/dist/sdk/sdk.d.ts +219 -5
- package/dist/sdk/sdk.js +21329 -14908
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +407 -12874
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/{session-registry-N5FFYFTM.mjs → session-registry-4E6YRQ77.mjs} +2 -2
- package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
- package/dist/sdk/{tracer-init-WP4X46IF.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
- package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
- package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
- package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
- package/dist/slack/adapter.d.ts +36 -0
- package/dist/slack/adapter.d.ts.map +1 -0
- package/dist/slack/cache-prewarmer.d.ts +31 -0
- package/dist/slack/cache-prewarmer.d.ts.map +1 -0
- package/dist/slack/client.d.ts +77 -0
- package/dist/slack/client.d.ts.map +1 -0
- package/dist/slack/markdown.d.ts +45 -0
- package/dist/slack/markdown.d.ts.map +1 -0
- package/dist/slack/prompt-state.d.ts +33 -0
- package/dist/slack/prompt-state.d.ts.map +1 -0
- package/dist/slack/rate-limiter.d.ts +56 -0
- package/dist/slack/rate-limiter.d.ts.map +1 -0
- package/dist/slack/signature.d.ts +2 -0
- package/dist/slack/signature.d.ts.map +1 -0
- package/dist/slack/socket-runner.d.ts +42 -0
- package/dist/slack/socket-runner.d.ts.map +1 -0
- package/dist/slack/thread-cache.d.ts +51 -0
- package/dist/slack/thread-cache.d.ts.map +1 -0
- package/dist/state-machine/context/build-engine-context.d.ts +8 -0
- package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
- package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
- package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
- package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
- package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
- package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -1
- package/dist/state-machine/runner.d.ts +6 -0
- package/dist/state-machine/runner.d.ts.map +1 -1
- package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
- package/dist/state-machine/states/plan-ready.d.ts.map +1 -1
- package/dist/state-machine/states/routing.d.ts.map +1 -1
- package/dist/state-machine/states/wave-planning.d.ts.map +1 -1
- package/dist/state-machine/workflow-projection.d.ts.map +1 -1
- package/dist/state-machine-execution-engine.d.ts +21 -9
- package/dist/state-machine-execution-engine.d.ts.map +1 -1
- package/dist/telemetry/state-capture.d.ts +5 -0
- package/dist/telemetry/state-capture.d.ts.map +1 -1
- package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
- package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -1
- package/dist/test-runner/evaluators.d.ts +37 -4
- package/dist/test-runner/evaluators.d.ts.map +1 -1
- package/dist/test-runner/index.d.ts +7 -0
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
- package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
- package/dist/test-runner/validator.d.ts.map +1 -1
- package/dist/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
- package/dist/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
- package/dist/types/bot.d.ts +109 -0
- package/dist/types/bot.d.ts.map +1 -0
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +182 -5
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/engine.d.ts +5 -0
- package/dist/types/engine.d.ts.map +1 -1
- package/dist/types/git-checkout.d.ts +76 -0
- package/dist/types/git-checkout.d.ts.map +1 -0
- package/dist/utils/json-text-extractor.d.ts +17 -0
- package/dist/utils/json-text-extractor.d.ts.map +1 -0
- package/dist/utils/sandbox.d.ts +10 -0
- package/dist/utils/sandbox.d.ts.map +1 -1
- package/dist/utils/template-context.d.ts +1 -0
- package/dist/utils/template-context.d.ts.map +1 -1
- package/dist/utils/tracer-init.d.ts.map +1 -1
- package/dist/utils/workspace-manager.d.ts +118 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -0
- package/dist/utils/worktree-cleanup.d.ts +33 -0
- package/dist/utils/worktree-cleanup.d.ts.map +1 -0
- package/dist/utils/worktree-manager.d.ts +153 -0
- package/dist/utils/worktree-manager.d.ts.map +1 -0
- package/dist/webhook-server.d.ts.map +1 -1
- package/dist/workflow-executor.d.ts.map +1 -1
- package/dist/workflow-registry.d.ts.map +1 -1
- package/package.json +5 -4
- package/dist/output/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
- package/dist/sdk/chunk-IEO6CFLG.mjs.map +0 -1
- package/dist/sdk/chunk-JEHPDJIF.mjs +0 -223
- package/dist/sdk/chunk-JEHPDJIF.mjs.map +0 -1
- package/dist/sdk/chunk-OZJ263FM.mjs.map +0 -1
- package/dist/sdk/chunk-PTL3K3PN.mjs.map +0 -1
- package/dist/sdk/chunk-VMPLF6FT.mjs.map +0 -1
- package/dist/sdk/github-frontend-4AWRJT7D.mjs.map +0 -1
- package/dist/sdk/host-7GBC3S7L.mjs.map +0 -1
- package/dist/sdk/memory-store-GJACZC2A.mjs +0 -11
- package/dist/sdk/workflow-registry-2YIIXQCK.mjs +0 -11
- package/dist/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
- /package/dist/sdk/{config-M4ZNO6NU.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
- /package/dist/sdk/{chunk-37ZSCMFC.mjs.map → chunk-7UK3NIIT.mjs.map} +0 -0
- /package/dist/sdk/{chunk-6Y4YTKCF.mjs.map → chunk-NAW3DB3I.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
- /package/dist/sdk/{memory-store-GJACZC2A.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
- /package/dist/sdk/{routing-RP56JTV2.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
- /package/dist/sdk/{session-registry-N5FFYFTM.mjs.map → memory-store-XGBB7LX7.mjs.map} +0 -0
- /package/dist/sdk/{tracer-init-WP4X46IF.mjs.map → prompt-state-YRJY6QAL.mjs.map} +0 -0
- /package/dist/sdk/{workflow-registry-2YIIXQCK.mjs.map → routing-6N45MJ4F.mjs.map} +0 -0
|
@@ -0,0 +1,1294 @@
|
|
|
1
|
+
# RFC: `on_init` Lifecycle Hook for Context Preprocessing
|
|
2
|
+
|
|
3
|
+
**Status:** Proposal
|
|
4
|
+
**Author:** Visor Team
|
|
5
|
+
**Created:** 2024-01-XX
|
|
6
|
+
**Updated:** 2024-01-XX
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Abstract
|
|
11
|
+
|
|
12
|
+
This RFC proposes adding an `on_init` lifecycle hook to enable automatic context enrichment before step execution. This solves the link preprocessing problem (JIRA, Linear, etc.) and provides a general-purpose mechanism for setup, data fetching, and context preparation.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Table of Contents
|
|
17
|
+
|
|
18
|
+
1. [Motivation](#motivation)
|
|
19
|
+
2. [Proposed Solution](#proposed-solution)
|
|
20
|
+
3. [Detailed Design](#detailed-design)
|
|
21
|
+
4. [Type Definitions](#type-definitions)
|
|
22
|
+
5. [Syntax Specifications](#syntax-specifications)
|
|
23
|
+
6. [Execution Semantics](#execution-semantics)
|
|
24
|
+
7. [Implementation Plan](#implementation-plan)
|
|
25
|
+
8. [Examples](#examples)
|
|
26
|
+
9. [Alternatives Considered](#alternatives-considered)
|
|
27
|
+
10. [Migration Path](#migration-path)
|
|
28
|
+
11. [Open Questions](#open-questions)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. Motivation
|
|
33
|
+
|
|
34
|
+
### 1.1 Problem Statement
|
|
35
|
+
|
|
36
|
+
**Use Case:** Automatically enrich AI prompts with external context (JIRA tickets, Linear issues, etc.)
|
|
37
|
+
|
|
38
|
+
**Current Approach:** Use `depends_on` for preprocessing
|
|
39
|
+
```yaml
|
|
40
|
+
# Verbose and clunky
|
|
41
|
+
steps:
|
|
42
|
+
enrich-jira:
|
|
43
|
+
type: mcp
|
|
44
|
+
method: fetch-jira
|
|
45
|
+
args:
|
|
46
|
+
issue_key: "{{ pr.description | regex_search: '[A-Z]+-[0-9]+' }}"
|
|
47
|
+
|
|
48
|
+
ai-review:
|
|
49
|
+
depends_on: [enrich-jira] # Explicit dependency
|
|
50
|
+
prompt: |
|
|
51
|
+
JIRA: {{ outputs["enrich-jira"] }}
|
|
52
|
+
Review...
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Problems:**
|
|
56
|
+
- ❌ Verbose: Requires separate step definition
|
|
57
|
+
- ❌ Clutters logs: Preprocessor appears as separate step
|
|
58
|
+
- ❌ Not reusable: Hard to share preprocessing logic
|
|
59
|
+
- ❌ Unclear intent: Looks like regular dependency, not preprocessing
|
|
60
|
+
- ❌ Tight coupling: Parent knows about preprocessor step name
|
|
61
|
+
|
|
62
|
+
### 1.2 Goals
|
|
63
|
+
|
|
64
|
+
1. **Clean preprocessing** - Make preprocessing intent explicit
|
|
65
|
+
2. **Reusability** - One helper, many callers with different arguments
|
|
66
|
+
3. **Composability** - Mix tools, steps, and workflows
|
|
67
|
+
4. **Consistency** - Follow existing `on_success`/`on_fail`/`on_finish` patterns
|
|
68
|
+
5. **Flexibility** - Support conditional and dynamic preprocessing
|
|
69
|
+
|
|
70
|
+
### 1.3 Non-Goals
|
|
71
|
+
|
|
72
|
+
- Replacing `depends_on` for actual dependencies
|
|
73
|
+
- Adding new parameter passing mechanisms (reuse templates)
|
|
74
|
+
- Supporting parallel execution within `on_init`
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 2. Proposed Solution
|
|
79
|
+
|
|
80
|
+
### 2.1 Overview
|
|
81
|
+
|
|
82
|
+
Add an `on_init` lifecycle hook that runs **before** a step executes:
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
steps:
|
|
86
|
+
ai-review:
|
|
87
|
+
type: ai
|
|
88
|
+
on_init:
|
|
89
|
+
run:
|
|
90
|
+
- tool: fetch-jira
|
|
91
|
+
with:
|
|
92
|
+
issue_key: "{{ pr.description | regex_search: '[A-Z]+-[0-9]+' }}"
|
|
93
|
+
as: jira-context
|
|
94
|
+
prompt: |
|
|
95
|
+
JIRA: {{ outputs["jira-context"] }}
|
|
96
|
+
Review the PR...
|
|
97
|
+
|
|
98
|
+
# Reusable helper (doesn't run independently)
|
|
99
|
+
fetch-jira:
|
|
100
|
+
type: mcp
|
|
101
|
+
method: fetch-jira-ticket
|
|
102
|
+
args:
|
|
103
|
+
issue_key: "{{ args.issue_key }}"
|
|
104
|
+
on: []
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 2.2 Key Features
|
|
108
|
+
|
|
109
|
+
1. **`on_init` hook** - Runs before step execution
|
|
110
|
+
2. **`with` directive** - Pass custom arguments to invoked items
|
|
111
|
+
3. **`as` directive** - Custom output naming
|
|
112
|
+
4. **Unified invocations** - Call tools, steps, or workflows
|
|
113
|
+
5. **`run_js` support** - Dynamic/conditional preprocessing
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 3. Detailed Design
|
|
118
|
+
|
|
119
|
+
### 3.1 Execution Flow
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
┌─────────────────────┐
|
|
123
|
+
│ Step Scheduled │
|
|
124
|
+
│ (e.g., ai-review) │
|
|
125
|
+
└──────────┬──────────┘
|
|
126
|
+
│
|
|
127
|
+
▼
|
|
128
|
+
┌─────────────────────┐
|
|
129
|
+
│ Check on_init │
|
|
130
|
+
│ directive │
|
|
131
|
+
└──────────┬──────────┘
|
|
132
|
+
│
|
|
133
|
+
▼
|
|
134
|
+
┌────┴────┐
|
|
135
|
+
│ Has │
|
|
136
|
+
│ on_init?│
|
|
137
|
+
└────┬────┘
|
|
138
|
+
│
|
|
139
|
+
Yes │ No
|
|
140
|
+
┌────┴────┐
|
|
141
|
+
▼ ▼
|
|
142
|
+
┌─────────┐ ┌──────────────┐
|
|
143
|
+
│ Execute │ │ Execute Step │
|
|
144
|
+
│ on_init │ │ Normally │
|
|
145
|
+
│ Items │ └──────────────┘
|
|
146
|
+
└────┬────┘
|
|
147
|
+
│
|
|
148
|
+
▼
|
|
149
|
+
┌─────────────────────┐
|
|
150
|
+
│ For each item: │
|
|
151
|
+
│ 1. Resolve type │
|
|
152
|
+
│ (tool/step/wflow)│
|
|
153
|
+
│ 2. Inject 'args' │
|
|
154
|
+
│ 3. Execute │
|
|
155
|
+
│ 4. Store output │
|
|
156
|
+
└──────────┬──────────┘
|
|
157
|
+
│
|
|
158
|
+
▼
|
|
159
|
+
┌─────────────────────┐
|
|
160
|
+
│ Execute Main Step │
|
|
161
|
+
│ (outputs available) │
|
|
162
|
+
└─────────────────────┘
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 3.2 Invocation Types
|
|
166
|
+
|
|
167
|
+
The `on_init.run` array supports three invocation types:
|
|
168
|
+
|
|
169
|
+
#### 3.2.1 Tool Invocation
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
- tool: fetch-jira-ticket
|
|
173
|
+
with:
|
|
174
|
+
issue_key: PROJ-123
|
|
175
|
+
as: jira-data
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Execution:**
|
|
179
|
+
1. Looks up tool in `tools:` section
|
|
180
|
+
2. Creates temporary MCP check
|
|
181
|
+
3. Injects `with` as `args` context
|
|
182
|
+
4. Executes via MCP provider
|
|
183
|
+
5. Stores output as `outputs["jira-data"]`
|
|
184
|
+
|
|
185
|
+
#### 3.2.2 Step Invocation
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
- step: extract-metadata
|
|
189
|
+
with:
|
|
190
|
+
pr_number: "{{ pr.number }}"
|
|
191
|
+
as: metadata
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Execution:**
|
|
195
|
+
1. Looks up step in `steps:` section
|
|
196
|
+
2. Injects `with` as `args` context
|
|
197
|
+
3. Executes step normally
|
|
198
|
+
4. Stores output as `outputs["metadata"]`
|
|
199
|
+
|
|
200
|
+
#### 3.2.3 Workflow Invocation
|
|
201
|
+
|
|
202
|
+
```yaml
|
|
203
|
+
- workflow: security-validation
|
|
204
|
+
with:
|
|
205
|
+
files: "{{ files | map: 'filename' }}"
|
|
206
|
+
severity: high
|
|
207
|
+
as: security-results
|
|
208
|
+
overrides:
|
|
209
|
+
scan-step:
|
|
210
|
+
timeout: 120
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Execution:**
|
|
214
|
+
1. Looks up workflow (registry or file)
|
|
215
|
+
2. Passes `with` as workflow `inputs`
|
|
216
|
+
3. Executes workflow via state machine
|
|
217
|
+
4. Stores output as `outputs["security-results"]`
|
|
218
|
+
|
|
219
|
+
### 3.3 Argument Passing
|
|
220
|
+
|
|
221
|
+
Arguments flow via the existing template context:
|
|
222
|
+
|
|
223
|
+
```yaml
|
|
224
|
+
# Caller
|
|
225
|
+
on_init:
|
|
226
|
+
run:
|
|
227
|
+
- tool: fetch-jira
|
|
228
|
+
with:
|
|
229
|
+
issue_key: PROJ-123
|
|
230
|
+
include_comments: true
|
|
231
|
+
|
|
232
|
+
# Tool definition
|
|
233
|
+
tools:
|
|
234
|
+
fetch-jira:
|
|
235
|
+
exec: |
|
|
236
|
+
# Access via args.*
|
|
237
|
+
KEY="{{ args.issue_key }}"
|
|
238
|
+
COMMENTS="{{ args.include_comments | default: false }}"
|
|
239
|
+
curl https://jira.../issue/$KEY
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Template Context Enhancement:**
|
|
243
|
+
```typescript
|
|
244
|
+
const templateContext = {
|
|
245
|
+
// Standard context
|
|
246
|
+
pr: { ... },
|
|
247
|
+
files: [ ... ],
|
|
248
|
+
outputs: { ... },
|
|
249
|
+
env: { ... },
|
|
250
|
+
|
|
251
|
+
// NEW: Custom arguments from caller
|
|
252
|
+
args: {
|
|
253
|
+
issue_key: "PROJ-123",
|
|
254
|
+
include_comments: true,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 4. Type Definitions
|
|
262
|
+
|
|
263
|
+
### 4.1 Core Types
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
/**
|
|
267
|
+
* Init hook configuration - runs BEFORE check execution
|
|
268
|
+
*/
|
|
269
|
+
export interface OnInitConfig {
|
|
270
|
+
/** Items to run before this check executes */
|
|
271
|
+
run?: OnInitRunItem[];
|
|
272
|
+
|
|
273
|
+
/** Dynamic init items: JS expression returning OnInitRunItem[] */
|
|
274
|
+
run_js?: string;
|
|
275
|
+
|
|
276
|
+
/** Declarative transitions (optional, for advanced use cases) */
|
|
277
|
+
transitions?: TransitionRule[];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Unified run item - can be tool, step, workflow, or plain string
|
|
282
|
+
*/
|
|
283
|
+
export type OnInitRunItem =
|
|
284
|
+
| OnInitToolInvocation
|
|
285
|
+
| OnInitStepInvocation
|
|
286
|
+
| OnInitWorkflowInvocation
|
|
287
|
+
| string; // Backward compatible: plain step name
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Invoke a custom tool (from tools: section)
|
|
291
|
+
*/
|
|
292
|
+
export interface OnInitToolInvocation {
|
|
293
|
+
/** Tool name (must exist in tools: section) */
|
|
294
|
+
tool: string;
|
|
295
|
+
|
|
296
|
+
/** Arguments to pass to the tool (Liquid templates supported) */
|
|
297
|
+
with?: Record<string, unknown>;
|
|
298
|
+
|
|
299
|
+
/** Custom output name (defaults to tool name) */
|
|
300
|
+
as?: string;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Invoke a helper step (regular check)
|
|
305
|
+
*/
|
|
306
|
+
export interface OnInitStepInvocation {
|
|
307
|
+
/** Step name (must exist in steps: section) */
|
|
308
|
+
step: string;
|
|
309
|
+
|
|
310
|
+
/** Arguments to pass to the step (Liquid templates supported) */
|
|
311
|
+
with?: Record<string, unknown>;
|
|
312
|
+
|
|
313
|
+
/** Custom output name (defaults to step name) */
|
|
314
|
+
as?: string;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Invoke a reusable workflow
|
|
319
|
+
*/
|
|
320
|
+
export interface OnInitWorkflowInvocation {
|
|
321
|
+
/** Workflow ID or path */
|
|
322
|
+
workflow: string;
|
|
323
|
+
|
|
324
|
+
/** Workflow inputs (Liquid templates supported) */
|
|
325
|
+
with?: Record<string, unknown>;
|
|
326
|
+
|
|
327
|
+
/** Custom output name (defaults to workflow name) */
|
|
328
|
+
as?: string;
|
|
329
|
+
|
|
330
|
+
/** Step overrides */
|
|
331
|
+
overrides?: Record<string, Partial<CheckConfig>>;
|
|
332
|
+
|
|
333
|
+
/** Output mapping */
|
|
334
|
+
output_mapping?: Record<string, string>;
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### 4.2 CheckConfig Extension
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
export interface CheckConfig {
|
|
342
|
+
// ... existing fields ...
|
|
343
|
+
|
|
344
|
+
/** Init routing configuration for this check (runs before execution) */
|
|
345
|
+
on_init?: OnInitConfig;
|
|
346
|
+
|
|
347
|
+
/** Success routing configuration */
|
|
348
|
+
on_success?: OnSuccessConfig;
|
|
349
|
+
|
|
350
|
+
/** Failure routing configuration */
|
|
351
|
+
on_fail?: OnFailConfig;
|
|
352
|
+
|
|
353
|
+
/** Finish routing configuration (forEach only) */
|
|
354
|
+
on_finish?: OnFinishConfig;
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## 5. Syntax Specifications
|
|
361
|
+
|
|
362
|
+
### 5.1 Basic Syntax
|
|
363
|
+
|
|
364
|
+
```yaml
|
|
365
|
+
# Simple tool invocation
|
|
366
|
+
on_init:
|
|
367
|
+
run:
|
|
368
|
+
- tool: fetch-jira
|
|
369
|
+
with:
|
|
370
|
+
issue_key: PROJ-123
|
|
371
|
+
|
|
372
|
+
# Simple step invocation
|
|
373
|
+
on_init:
|
|
374
|
+
run:
|
|
375
|
+
- step: extract-data
|
|
376
|
+
with:
|
|
377
|
+
text: "{{ pr.description }}"
|
|
378
|
+
|
|
379
|
+
# Simple workflow invocation
|
|
380
|
+
on_init:
|
|
381
|
+
run:
|
|
382
|
+
- workflow: validate
|
|
383
|
+
with:
|
|
384
|
+
env: production
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### 5.2 Mixed Invocations
|
|
388
|
+
|
|
389
|
+
```yaml
|
|
390
|
+
on_init:
|
|
391
|
+
run:
|
|
392
|
+
# Tool
|
|
393
|
+
- tool: fetch-jira
|
|
394
|
+
with:
|
|
395
|
+
issue_key: PROJ-123
|
|
396
|
+
as: jira
|
|
397
|
+
|
|
398
|
+
# Step
|
|
399
|
+
- step: analyze
|
|
400
|
+
with:
|
|
401
|
+
files: "{{ files }}"
|
|
402
|
+
as: analysis
|
|
403
|
+
|
|
404
|
+
# Workflow
|
|
405
|
+
- workflow: security-scan
|
|
406
|
+
with:
|
|
407
|
+
severity: high
|
|
408
|
+
as: security
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### 5.3 Custom Output Names
|
|
412
|
+
|
|
413
|
+
```yaml
|
|
414
|
+
on_init:
|
|
415
|
+
run:
|
|
416
|
+
- tool: fetch-jira
|
|
417
|
+
with:
|
|
418
|
+
issue_key: PROJ-100
|
|
419
|
+
as: jira-100
|
|
420
|
+
|
|
421
|
+
- tool: fetch-jira
|
|
422
|
+
with:
|
|
423
|
+
issue_key: PROJ-200
|
|
424
|
+
as: jira-200
|
|
425
|
+
|
|
426
|
+
prompt: |
|
|
427
|
+
JIRA 100: {{ outputs["jira-100"] }}
|
|
428
|
+
JIRA 200: {{ outputs["jira-200"] }}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### 5.4 Dynamic Invocations
|
|
432
|
+
|
|
433
|
+
```yaml
|
|
434
|
+
on_init:
|
|
435
|
+
run_js: |
|
|
436
|
+
const items = [];
|
|
437
|
+
|
|
438
|
+
// Conditional tool invocation
|
|
439
|
+
const jiraKey = pr.description?.match(/([A-Z]+-[0-9]+)/)?.[1];
|
|
440
|
+
if (jiraKey) {
|
|
441
|
+
items.push({
|
|
442
|
+
tool: 'fetch-jira',
|
|
443
|
+
with: { issue_key: jiraKey },
|
|
444
|
+
as: 'jira',
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Conditional workflow invocation
|
|
449
|
+
if (pr.base === 'main') {
|
|
450
|
+
items.push({
|
|
451
|
+
workflow: 'prod-checks',
|
|
452
|
+
with: { pr_number: pr.number },
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return items;
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### 5.5 Backward Compatibility
|
|
460
|
+
|
|
461
|
+
```yaml
|
|
462
|
+
# Old style (still supported)
|
|
463
|
+
on_init:
|
|
464
|
+
run: [step1, step2] # Plain strings
|
|
465
|
+
|
|
466
|
+
# New style
|
|
467
|
+
on_init:
|
|
468
|
+
run:
|
|
469
|
+
- step: step1
|
|
470
|
+
- tool: my-tool
|
|
471
|
+
with:
|
|
472
|
+
key: value
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### 5.6 Template Expressions in Arguments
|
|
476
|
+
|
|
477
|
+
```yaml
|
|
478
|
+
on_init:
|
|
479
|
+
run:
|
|
480
|
+
- tool: fetch-jira
|
|
481
|
+
with:
|
|
482
|
+
# Liquid template expressions
|
|
483
|
+
issue_key: "{{ pr.description | regex_search: '[A-Z]+-[0-9]+' }}"
|
|
484
|
+
pr_number: "{{ pr.number }}"
|
|
485
|
+
files: "{{ files | map: 'filename' }}"
|
|
486
|
+
has_tests: "{{ files | map: 'filename' | join: ',' | contains: 'test' }}"
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## 6. Execution Semantics
|
|
492
|
+
|
|
493
|
+
### 6.1 Execution Order
|
|
494
|
+
|
|
495
|
+
`on_init` items execute **sequentially** in the order specified:
|
|
496
|
+
|
|
497
|
+
```yaml
|
|
498
|
+
on_init:
|
|
499
|
+
run:
|
|
500
|
+
- tool: step-1 # Executes first
|
|
501
|
+
- step: step-2 # Executes second (can use outputs["step-1"])
|
|
502
|
+
- workflow: step-3 # Executes third (can use outputs["step-1"] and outputs["step-2"])
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### 6.2 Output Availability
|
|
506
|
+
|
|
507
|
+
Each `on_init` item's output is immediately available to subsequent items:
|
|
508
|
+
|
|
509
|
+
```yaml
|
|
510
|
+
on_init:
|
|
511
|
+
run:
|
|
512
|
+
- tool: extract-keys
|
|
513
|
+
as: keys
|
|
514
|
+
|
|
515
|
+
- tool: fetch-data
|
|
516
|
+
with:
|
|
517
|
+
keys: "{{ outputs['keys'] }}" # Uses previous output
|
|
518
|
+
as: data
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### 6.3 Error Handling
|
|
522
|
+
|
|
523
|
+
**Default behavior:** If any `on_init` item fails, the parent step fails.
|
|
524
|
+
|
|
525
|
+
```yaml
|
|
526
|
+
on_init:
|
|
527
|
+
run:
|
|
528
|
+
- tool: fetch-jira
|
|
529
|
+
# If this fails, ai-review doesn't run
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**Optional:** Support `continue_on_failure` for specific items:
|
|
533
|
+
|
|
534
|
+
```yaml
|
|
535
|
+
on_init:
|
|
536
|
+
run:
|
|
537
|
+
- tool: fetch-jira
|
|
538
|
+
with:
|
|
539
|
+
issue_key: "{{ pr.description | regex_search: '[A-Z]+-[0-9]+' }}"
|
|
540
|
+
continue_on_failure: true # Optional enhancement
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### 6.4 Scope Inheritance
|
|
544
|
+
|
|
545
|
+
`on_init` items inherit the parent's forEach scope:
|
|
546
|
+
|
|
547
|
+
```yaml
|
|
548
|
+
steps:
|
|
549
|
+
validate-files:
|
|
550
|
+
forEach: true
|
|
551
|
+
exec: echo {{ files | map: 'filename' | json }}
|
|
552
|
+
|
|
553
|
+
process-file:
|
|
554
|
+
depends_on: [validate-files]
|
|
555
|
+
on_init:
|
|
556
|
+
run:
|
|
557
|
+
- step: setup-processor
|
|
558
|
+
# Runs once per forEach item, with same scope
|
|
559
|
+
exec: process {{ outputs['validate-files'] }}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### 6.5 Conditional Execution
|
|
563
|
+
|
|
564
|
+
The parent's `if` condition is evaluated **before** `on_init`:
|
|
565
|
+
|
|
566
|
+
```yaml
|
|
567
|
+
steps:
|
|
568
|
+
conditional-step:
|
|
569
|
+
if: pr.base === 'main' # Evaluated first
|
|
570
|
+
on_init:
|
|
571
|
+
run: [setup] # Only runs if condition true
|
|
572
|
+
exec: deploy
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## 7. Implementation Plan
|
|
578
|
+
|
|
579
|
+
### 7.1 Phase 1: Core Infrastructure
|
|
580
|
+
|
|
581
|
+
#### File: `src/types/config.ts`
|
|
582
|
+
|
|
583
|
+
**Tasks:**
|
|
584
|
+
- [ ] Add `OnInitConfig` interface
|
|
585
|
+
- [ ] Add `OnInitRunItem` type union
|
|
586
|
+
- [ ] Add `OnInitToolInvocation` interface
|
|
587
|
+
- [ ] Add `OnInitStepInvocation` interface
|
|
588
|
+
- [ ] Add `OnInitWorkflowInvocation` interface
|
|
589
|
+
- [ ] Add `on_init?: OnInitConfig` to `CheckConfig`
|
|
590
|
+
|
|
591
|
+
**Lines:** ~100 new lines
|
|
592
|
+
|
|
593
|
+
#### File: `src/state-machine/dispatch/execution-invoker.ts`
|
|
594
|
+
|
|
595
|
+
**Tasks:**
|
|
596
|
+
- [ ] Add `handleOnInit()` function
|
|
597
|
+
- [ ] Add `executeOnInitItem()` function
|
|
598
|
+
- [ ] Add `detectInvocationType()` function
|
|
599
|
+
- [ ] Add `normalizeRunItems()` function
|
|
600
|
+
- [ ] Call `handleOnInit()` before step execution
|
|
601
|
+
- [ ] Pass enriched context with `args` to helpers
|
|
602
|
+
|
|
603
|
+
**Lines:** ~200-300 new lines
|
|
604
|
+
|
|
605
|
+
**Pseudocode:**
|
|
606
|
+
```typescript
|
|
607
|
+
async function handleOnInit(
|
|
608
|
+
checkId: string,
|
|
609
|
+
onInit: OnInitConfig,
|
|
610
|
+
context: EngineContext,
|
|
611
|
+
parentScope: Scope
|
|
612
|
+
): Promise<void> {
|
|
613
|
+
// Process on_init.run
|
|
614
|
+
if (onInit.run && onInit.run.length > 0) {
|
|
615
|
+
const items = normalizeRunItems(onInit.run);
|
|
616
|
+
|
|
617
|
+
for (const item of items) {
|
|
618
|
+
await executeOnInitItem(item, context, parentScope);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Process on_init.run_js
|
|
623
|
+
if (onInit.run_js) {
|
|
624
|
+
const dynamicItems = evaluateRunJs(onInit.run_js, context);
|
|
625
|
+
for (const item of dynamicItems) {
|
|
626
|
+
await executeOnInitItem(item, context, parentScope);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
async function executeOnInitItem(
|
|
632
|
+
item: OnInitRunItem,
|
|
633
|
+
context: EngineContext,
|
|
634
|
+
scope: Scope
|
|
635
|
+
): Promise<void> {
|
|
636
|
+
const type = detectInvocationType(item);
|
|
637
|
+
const outputName = item.as || item.tool || item.step || item.workflow;
|
|
638
|
+
|
|
639
|
+
let result: unknown;
|
|
640
|
+
|
|
641
|
+
switch (type) {
|
|
642
|
+
case 'tool':
|
|
643
|
+
result = await executeToolInvocation(item, context, scope);
|
|
644
|
+
break;
|
|
645
|
+
case 'step':
|
|
646
|
+
result = await executeStepInvocation(item, context, scope);
|
|
647
|
+
break;
|
|
648
|
+
case 'workflow':
|
|
649
|
+
result = await executeWorkflowInvocation(item, context, scope);
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Store result for parent and subsequent on_init items
|
|
654
|
+
context.outputs[outputName] = result;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function normalizeRunItems(run: OnInitRunItem[]): OnInitRunItem[] {
|
|
658
|
+
return run.map(item => {
|
|
659
|
+
if (typeof item === 'string') {
|
|
660
|
+
return { step: item }; // Backward compat
|
|
661
|
+
}
|
|
662
|
+
return item;
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function detectInvocationType(item: OnInitRunItem): 'tool' | 'step' | 'workflow' {
|
|
667
|
+
if (typeof item === 'string') return 'step';
|
|
668
|
+
if ('tool' in item) return 'tool';
|
|
669
|
+
if ('workflow' in item) return 'workflow';
|
|
670
|
+
if ('step' in item) return 'step';
|
|
671
|
+
throw new Error('Unknown on_init item type');
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### 7.2 Phase 2: Invocation Handlers
|
|
676
|
+
|
|
677
|
+
#### Tool Invocation
|
|
678
|
+
|
|
679
|
+
```typescript
|
|
680
|
+
async function executeToolInvocation(
|
|
681
|
+
item: OnInitToolInvocation,
|
|
682
|
+
context: EngineContext,
|
|
683
|
+
scope: Scope
|
|
684
|
+
): Promise<unknown> {
|
|
685
|
+
const toolDef = context.config.tools?.[item.tool];
|
|
686
|
+
if (!toolDef) {
|
|
687
|
+
throw new Error(`Tool '${item.tool}' not found`);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Create temporary MCP check
|
|
691
|
+
const tempCheck: CheckConfig = {
|
|
692
|
+
type: 'mcp',
|
|
693
|
+
method: item.tool,
|
|
694
|
+
transport: 'custom',
|
|
695
|
+
args: item.with || {},
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
// Build context with args
|
|
699
|
+
const enrichedContext = {
|
|
700
|
+
...context,
|
|
701
|
+
args: item.with || {},
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
// Execute via MCP provider
|
|
705
|
+
const provider = registry.getProvider('mcp');
|
|
706
|
+
const result = await provider.execute(
|
|
707
|
+
context.prInfo,
|
|
708
|
+
tempCheck,
|
|
709
|
+
buildDependencyResultsWithScope(checkId, tempCheck, context, scope),
|
|
710
|
+
enrichedContext
|
|
711
|
+
);
|
|
712
|
+
|
|
713
|
+
return result.output;
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
#### Step Invocation
|
|
718
|
+
|
|
719
|
+
```typescript
|
|
720
|
+
async function executeStepInvocation(
|
|
721
|
+
item: OnInitStepInvocation,
|
|
722
|
+
context: EngineContext,
|
|
723
|
+
scope: Scope
|
|
724
|
+
): Promise<unknown> {
|
|
725
|
+
const stepConfig = context.config.steps?.[item.step];
|
|
726
|
+
if (!stepConfig) {
|
|
727
|
+
throw new Error(`Step '${item.step}' not found`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Build context with args
|
|
731
|
+
const enrichedContext = {
|
|
732
|
+
...context,
|
|
733
|
+
args: item.with || {},
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
// Execute step
|
|
737
|
+
const provider = registry.getProvider(stepConfig.type || 'ai');
|
|
738
|
+
const result = await provider.execute(
|
|
739
|
+
context.prInfo,
|
|
740
|
+
stepConfig,
|
|
741
|
+
buildDependencyResultsWithScope(checkId, stepConfig, context, scope),
|
|
742
|
+
enrichedContext
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
return result.output;
|
|
746
|
+
}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
#### Workflow Invocation
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
async function executeWorkflowInvocation(
|
|
753
|
+
item: OnInitWorkflowInvocation,
|
|
754
|
+
context: EngineContext,
|
|
755
|
+
scope: Scope
|
|
756
|
+
): Promise<unknown> {
|
|
757
|
+
// Create workflow check
|
|
758
|
+
const workflowCheck: CheckConfig = {
|
|
759
|
+
type: 'workflow',
|
|
760
|
+
workflow: item.workflow,
|
|
761
|
+
args: item.with || {},
|
|
762
|
+
overrides: item.overrides,
|
|
763
|
+
output_mapping: item.output_mapping,
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
// Execute via workflow provider
|
|
767
|
+
const provider = registry.getProvider('workflow');
|
|
768
|
+
const result = await provider.execute(
|
|
769
|
+
context.prInfo,
|
|
770
|
+
workflowCheck,
|
|
771
|
+
buildDependencyResultsWithScope(checkId, workflowCheck, context, scope),
|
|
772
|
+
context
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
return result.output;
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### 7.3 Phase 3: Context Enhancement
|
|
780
|
+
|
|
781
|
+
#### File: `src/providers/command-check-provider.ts`
|
|
782
|
+
|
|
783
|
+
**Tasks:**
|
|
784
|
+
- [ ] Add `args` to template context
|
|
785
|
+
- [ ] Pass `args` from `context?.args`
|
|
786
|
+
|
|
787
|
+
**Changes:**
|
|
788
|
+
```typescript
|
|
789
|
+
const templateContext = {
|
|
790
|
+
pr: { ... },
|
|
791
|
+
files: [ ... ],
|
|
792
|
+
outputs: { ... },
|
|
793
|
+
env: { ... },
|
|
794
|
+
|
|
795
|
+
// NEW: Custom arguments
|
|
796
|
+
args: context?.args || {},
|
|
797
|
+
};
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
**Files to update:**
|
|
801
|
+
- `src/providers/ai-check-provider.ts`
|
|
802
|
+
- `src/providers/command-check-provider.ts`
|
|
803
|
+
- `src/providers/mcp-check-provider.ts`
|
|
804
|
+
- `src/providers/http-check-provider.ts`
|
|
805
|
+
- All providers that render templates
|
|
806
|
+
|
|
807
|
+
### 7.4 Phase 4: Routing & Loop Budget
|
|
808
|
+
|
|
809
|
+
#### File: `src/state-machine/states/routing.ts`
|
|
810
|
+
|
|
811
|
+
**Tasks:**
|
|
812
|
+
- [ ] Add loop budget checking for `on_init`
|
|
813
|
+
- [ ] Increment routing loop counter
|
|
814
|
+
- [ ] Add `on_init` to routing metrics
|
|
815
|
+
|
|
816
|
+
**Changes:**
|
|
817
|
+
```typescript
|
|
818
|
+
// Check loop budget
|
|
819
|
+
if (checkLoopBudget(context, state, 'on_init', 'run')) {
|
|
820
|
+
throw new Error(`Loop budget exceeded during on_init`);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Increment counter
|
|
824
|
+
incrementRoutingLoopCount(context, state, 'on_init', 'run');
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### 7.5 Phase 5: Testing
|
|
828
|
+
|
|
829
|
+
#### Unit Tests
|
|
830
|
+
|
|
831
|
+
**File:** `tests/unit/on-init-handler.test.ts`
|
|
832
|
+
- [ ] Test `normalizeRunItems()`
|
|
833
|
+
- [ ] Test `detectInvocationType()`
|
|
834
|
+
- [ ] Test `handleOnInit()` with tools
|
|
835
|
+
- [ ] Test `handleOnInit()` with steps
|
|
836
|
+
- [ ] Test `handleOnInit()` with workflows
|
|
837
|
+
- [ ] Test mixed invocations
|
|
838
|
+
- [ ] Test `with` argument passing
|
|
839
|
+
- [ ] Test `as` output naming
|
|
840
|
+
- [ ] Test error handling
|
|
841
|
+
- [ ] Test loop budget enforcement
|
|
842
|
+
|
|
843
|
+
#### Integration Tests
|
|
844
|
+
|
|
845
|
+
**File:** `tests/integration/on-init.test.ts`
|
|
846
|
+
- [ ] Test tool invocation end-to-end
|
|
847
|
+
- [ ] Test step invocation end-to-end
|
|
848
|
+
- [ ] Test workflow invocation end-to-end
|
|
849
|
+
- [ ] Test argument flow (with → args)
|
|
850
|
+
- [ ] Test output availability
|
|
851
|
+
- [ ] Test chaining (tool → step → workflow)
|
|
852
|
+
- [ ] Test conditional execution (run_js)
|
|
853
|
+
- [ ] Test forEach scope inheritance
|
|
854
|
+
- [ ] Test backward compatibility (string arrays)
|
|
855
|
+
|
|
856
|
+
#### E2E Tests
|
|
857
|
+
|
|
858
|
+
**File:** `tests/e2e/on-init-jira.test.ts`
|
|
859
|
+
- [ ] Test JIRA link preprocessing
|
|
860
|
+
- [ ] Test multiple link types (JIRA + Linear)
|
|
861
|
+
- [ ] Test dynamic preprocessing
|
|
862
|
+
- [ ] Test with real workflows
|
|
863
|
+
|
|
864
|
+
### 7.6 Phase 6: Documentation
|
|
865
|
+
|
|
866
|
+
**Tasks:**
|
|
867
|
+
- [ ] Update `CLAUDE.md` with `on_init` pattern
|
|
868
|
+
- [ ] Add examples to `examples/` directory
|
|
869
|
+
- [ ] Update API documentation
|
|
870
|
+
- [ ] Add troubleshooting guide
|
|
871
|
+
- [ ] Create migration guide from `depends_on`
|
|
872
|
+
|
|
873
|
+
**Files:**
|
|
874
|
+
- `docs/on_init-guide.md` - User guide
|
|
875
|
+
- `docs/on_init-api.md` - API reference
|
|
876
|
+
- `examples/on_init-*.yaml` - Examples
|
|
877
|
+
- `CLAUDE.md` - Update with on_init pattern
|
|
878
|
+
|
|
879
|
+
### 7.7 Phase 7: Schema Generation
|
|
880
|
+
|
|
881
|
+
**File:** `src/generated/config-schema.ts`
|
|
882
|
+
|
|
883
|
+
**Tasks:**
|
|
884
|
+
- [ ] Run schema generator
|
|
885
|
+
- [ ] Validate generated types
|
|
886
|
+
- [ ] Update JSON schema
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
## 8. Examples
|
|
891
|
+
|
|
892
|
+
### 8.1 Basic JIRA Preprocessing
|
|
893
|
+
|
|
894
|
+
```yaml
|
|
895
|
+
tools:
|
|
896
|
+
fetch-jira:
|
|
897
|
+
exec: curl https://jira.../issue/{{ args.issue_key }}
|
|
898
|
+
parseJson: true
|
|
899
|
+
transform_js: |
|
|
900
|
+
return `<jira>${output.fields.summary}</jira>`;
|
|
901
|
+
|
|
902
|
+
steps:
|
|
903
|
+
ai-review:
|
|
904
|
+
type: ai
|
|
905
|
+
on_init:
|
|
906
|
+
run:
|
|
907
|
+
- tool: fetch-jira
|
|
908
|
+
with:
|
|
909
|
+
issue_key: "{{ pr.description | regex_search: '[A-Z]+-[0-9]+' }}"
|
|
910
|
+
as: jira-context
|
|
911
|
+
prompt: |
|
|
912
|
+
JIRA: {{ outputs["jira-context"] }}
|
|
913
|
+
Review the PR...
|
|
914
|
+
|
|
915
|
+
fetch-jira:
|
|
916
|
+
type: mcp
|
|
917
|
+
method: fetch-jira
|
|
918
|
+
transport: custom
|
|
919
|
+
args:
|
|
920
|
+
issue_key: "{{ args.issue_key }}"
|
|
921
|
+
on: []
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
### 8.2 Multi-Source Enrichment
|
|
925
|
+
|
|
926
|
+
```yaml
|
|
927
|
+
steps:
|
|
928
|
+
comprehensive-review:
|
|
929
|
+
type: ai
|
|
930
|
+
on_init:
|
|
931
|
+
run_js: |
|
|
932
|
+
const items = [];
|
|
933
|
+
|
|
934
|
+
// JIRA
|
|
935
|
+
const jiraKey = pr.description?.match(/([A-Z]+-[0-9]+)/)?.[1];
|
|
936
|
+
if (jiraKey) {
|
|
937
|
+
items.push({
|
|
938
|
+
tool: 'fetch-jira',
|
|
939
|
+
with: { issue_key: jiraKey },
|
|
940
|
+
as: 'jira',
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Linear
|
|
945
|
+
const linearUrl = pr.description?.match(/(https:\/\/linear\.app\/[^\s]+)/)?.[1];
|
|
946
|
+
if (linearUrl) {
|
|
947
|
+
items.push({
|
|
948
|
+
tool: 'fetch-linear',
|
|
949
|
+
with: { url: linearUrl },
|
|
950
|
+
as: 'linear',
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// Security scan (workflow)
|
|
955
|
+
if (pr.base === 'main') {
|
|
956
|
+
items.push({
|
|
957
|
+
workflow: 'security-scan',
|
|
958
|
+
with: { files: files.map(f => f.filename) },
|
|
959
|
+
as: 'security',
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
return items;
|
|
964
|
+
|
|
965
|
+
prompt: |
|
|
966
|
+
{% if outputs["jira"] %}
|
|
967
|
+
JIRA: {{ outputs["jira"] }}
|
|
968
|
+
{% endif %}
|
|
969
|
+
|
|
970
|
+
{% if outputs["linear"] %}
|
|
971
|
+
Linear: {{ outputs["linear"] }}
|
|
972
|
+
{% endif %}
|
|
973
|
+
|
|
974
|
+
{% if outputs["security"] %}
|
|
975
|
+
Security: {{ outputs["security"] }}
|
|
976
|
+
{% endif %}
|
|
977
|
+
|
|
978
|
+
Review...
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
### 8.3 Setup/Teardown Pattern
|
|
982
|
+
|
|
983
|
+
```yaml
|
|
984
|
+
steps:
|
|
985
|
+
integration-test:
|
|
986
|
+
type: command
|
|
987
|
+
on_init:
|
|
988
|
+
run:
|
|
989
|
+
- step: setup-database
|
|
990
|
+
with:
|
|
991
|
+
name: test-db
|
|
992
|
+
on_finish:
|
|
993
|
+
run:
|
|
994
|
+
- step: cleanup-database
|
|
995
|
+
with:
|
|
996
|
+
name: test-db
|
|
997
|
+
exec: npm run test:integration
|
|
998
|
+
|
|
999
|
+
setup-database:
|
|
1000
|
+
type: command
|
|
1001
|
+
exec: docker run -d --name {{ args.name }} postgres
|
|
1002
|
+
on: []
|
|
1003
|
+
|
|
1004
|
+
cleanup-database:
|
|
1005
|
+
type: command
|
|
1006
|
+
exec: docker rm -f {{ args.name }}
|
|
1007
|
+
on: []
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
---
|
|
1011
|
+
|
|
1012
|
+
## 9. Alternatives Considered
|
|
1013
|
+
|
|
1014
|
+
### 9.1 Alternative 1: `preprocess` Directive
|
|
1015
|
+
|
|
1016
|
+
```yaml
|
|
1017
|
+
steps:
|
|
1018
|
+
ai-review:
|
|
1019
|
+
preprocess: [fetch-jira]
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
**Rejected:** Less consistent with existing `on_*` pattern.
|
|
1023
|
+
|
|
1024
|
+
### 9.2 Alternative 2: `setup` Hook
|
|
1025
|
+
|
|
1026
|
+
```yaml
|
|
1027
|
+
steps:
|
|
1028
|
+
ai-review:
|
|
1029
|
+
setup:
|
|
1030
|
+
run: [fetch-jira]
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
**Rejected:** "setup" implies infrastructure, not data enrichment.
|
|
1034
|
+
|
|
1035
|
+
### 9.3 Alternative 3: Automatic Detection
|
|
1036
|
+
|
|
1037
|
+
```yaml
|
|
1038
|
+
steps:
|
|
1039
|
+
ai-review:
|
|
1040
|
+
prompt: |
|
|
1041
|
+
{{ enrich("jira", pr.description) }}
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
**Rejected:** Too magical, unclear execution order, hard to debug.
|
|
1045
|
+
|
|
1046
|
+
### 9.4 Alternative 4: Context Enrichers (Global)
|
|
1047
|
+
|
|
1048
|
+
```yaml
|
|
1049
|
+
context_enrichers:
|
|
1050
|
+
jira:
|
|
1051
|
+
pattern: 'https://.*atlassian.net/browse/([A-Z]+-[0-9]+)'
|
|
1052
|
+
tool: fetch-jira
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
**Rejected:** Less flexible, no per-step control, new top-level concept.
|
|
1056
|
+
|
|
1057
|
+
### 9.5 Alternative 5: Inline Syntax
|
|
1058
|
+
|
|
1059
|
+
```yaml
|
|
1060
|
+
on_init:
|
|
1061
|
+
run: [fetch-jira(issue_key=PROJ-123)]
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
**Rejected:** Non-standard YAML, harder to parse, limited.
|
|
1065
|
+
|
|
1066
|
+
---
|
|
1067
|
+
|
|
1068
|
+
## 10. Migration Path
|
|
1069
|
+
|
|
1070
|
+
### 10.1 From `depends_on` to `on_init`
|
|
1071
|
+
|
|
1072
|
+
**Before:**
|
|
1073
|
+
```yaml
|
|
1074
|
+
steps:
|
|
1075
|
+
enrich-jira:
|
|
1076
|
+
type: mcp
|
|
1077
|
+
method: fetch-jira
|
|
1078
|
+
|
|
1079
|
+
ai-review:
|
|
1080
|
+
depends_on: [enrich-jira]
|
|
1081
|
+
prompt: "JIRA: {{ outputs['enrich-jira'] }}"
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
**After:**
|
|
1085
|
+
```yaml
|
|
1086
|
+
steps:
|
|
1087
|
+
ai-review:
|
|
1088
|
+
on_init:
|
|
1089
|
+
run:
|
|
1090
|
+
- step: enrich-jira
|
|
1091
|
+
prompt: "JIRA: {{ outputs['enrich-jira'] }}"
|
|
1092
|
+
|
|
1093
|
+
enrich-jira:
|
|
1094
|
+
type: mcp
|
|
1095
|
+
method: fetch-jira
|
|
1096
|
+
on: [] # Mark as helper only
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
### 10.2 Backward Compatibility
|
|
1100
|
+
|
|
1101
|
+
**Existing configs continue to work:**
|
|
1102
|
+
```yaml
|
|
1103
|
+
on_init:
|
|
1104
|
+
run: [step1, step2] # String array (backward compat)
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
**New syntax is opt-in:**
|
|
1108
|
+
```yaml
|
|
1109
|
+
on_init:
|
|
1110
|
+
run:
|
|
1111
|
+
- tool: my-tool
|
|
1112
|
+
with: { ... }
|
|
1113
|
+
```
|
|
1114
|
+
|
|
1115
|
+
---
|
|
1116
|
+
|
|
1117
|
+
## 11. Open Questions
|
|
1118
|
+
|
|
1119
|
+
### 11.1 Parallel Execution
|
|
1120
|
+
|
|
1121
|
+
**Question:** Should `on_init` items execute in parallel or sequentially?
|
|
1122
|
+
|
|
1123
|
+
**Proposal:** Sequential (preserves output ordering, simpler)
|
|
1124
|
+
|
|
1125
|
+
**Future:** Add `parallel: true` option if needed:
|
|
1126
|
+
```yaml
|
|
1127
|
+
on_init:
|
|
1128
|
+
run:
|
|
1129
|
+
- tool: fetch-jira
|
|
1130
|
+
- tool: fetch-linear
|
|
1131
|
+
parallel: true # Future enhancement
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
### 11.2 Nested on_init
|
|
1135
|
+
|
|
1136
|
+
**Question:** Can `on_init` steps have their own `on_init`?
|
|
1137
|
+
|
|
1138
|
+
**Proposal:** Yes, but enforce max depth (e.g., 3 levels) to prevent infinite loops.
|
|
1139
|
+
|
|
1140
|
+
### 11.3 Continue on Failure
|
|
1141
|
+
|
|
1142
|
+
**Question:** Should individual `on_init` items support `continue_on_failure`?
|
|
1143
|
+
|
|
1144
|
+
**Proposal:** Future enhancement:
|
|
1145
|
+
```yaml
|
|
1146
|
+
on_init:
|
|
1147
|
+
run:
|
|
1148
|
+
- tool: fetch-jira
|
|
1149
|
+
continue_on_failure: true
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
### 11.4 Output Scoping
|
|
1153
|
+
|
|
1154
|
+
**Question:** Should `on_init` outputs be scoped differently from regular outputs?
|
|
1155
|
+
|
|
1156
|
+
**Proposal:** No, use the same `outputs` map for consistency.
|
|
1157
|
+
|
|
1158
|
+
### 11.5 Timeout
|
|
1159
|
+
|
|
1160
|
+
**Question:** Should `on_init` have a global timeout?
|
|
1161
|
+
|
|
1162
|
+
**Proposal:** Use sum of individual item timeouts. Add `timeout` if needed:
|
|
1163
|
+
```yaml
|
|
1164
|
+
on_init:
|
|
1165
|
+
timeout: 60000 # ms (future enhancement)
|
|
1166
|
+
run: [ ... ]
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
---
|
|
1170
|
+
|
|
1171
|
+
## 12. Summary
|
|
1172
|
+
|
|
1173
|
+
### 12.1 Benefits
|
|
1174
|
+
|
|
1175
|
+
1. **✅ Clean preprocessing** - Explicit intent, doesn't clutter logs
|
|
1176
|
+
2. **✅ Reusable** - One helper, many callers with `with` args
|
|
1177
|
+
3. **✅ Unified** - Tools, steps, workflows with same syntax
|
|
1178
|
+
4. **✅ Consistent** - Follows existing `on_*` patterns
|
|
1179
|
+
5. **✅ Flexible** - Conditional, dynamic, composable
|
|
1180
|
+
6. **✅ Backward compatible** - Existing code works
|
|
1181
|
+
|
|
1182
|
+
### 12.2 Implementation Effort
|
|
1183
|
+
|
|
1184
|
+
| Phase | Effort | Lines of Code |
|
|
1185
|
+
|-------|--------|---------------|
|
|
1186
|
+
| Type definitions | Low | ~100 |
|
|
1187
|
+
| Core handler | Medium | ~300 |
|
|
1188
|
+
| Invocation handlers | Medium | ~200 |
|
|
1189
|
+
| Context enhancement | Low | ~50 |
|
|
1190
|
+
| Testing | High | ~500 |
|
|
1191
|
+
| Documentation | Medium | ~1000 |
|
|
1192
|
+
| **Total** | **Medium-High** | **~2150** |
|
|
1193
|
+
|
|
1194
|
+
### 12.3 Rollout Plan
|
|
1195
|
+
|
|
1196
|
+
1. **Week 1:** Type definitions + schema
|
|
1197
|
+
2. **Week 2:** Core implementation
|
|
1198
|
+
3. **Week 3:** Testing
|
|
1199
|
+
4. **Week 4:** Documentation + examples
|
|
1200
|
+
5. **Week 5:** Beta testing with real workflows
|
|
1201
|
+
6. **Week 6:** GA release
|
|
1202
|
+
|
|
1203
|
+
---
|
|
1204
|
+
|
|
1205
|
+
## 13. References
|
|
1206
|
+
|
|
1207
|
+
- [Existing `on_success` implementation](src/state-machine/states/routing.ts#599)
|
|
1208
|
+
- [Existing `on_fail` implementation](src/state-machine/states/routing.ts#816)
|
|
1209
|
+
- [Existing `on_finish` implementation](src/state-machine/states/routing.ts#231)
|
|
1210
|
+
- [Workflow provider](src/providers/workflow-check-provider.ts)
|
|
1211
|
+
- [Custom tool executor](src/providers/custom-tool-executor.ts)
|
|
1212
|
+
|
|
1213
|
+
---
|
|
1214
|
+
|
|
1215
|
+
## Appendix A: Complete Type Definitions
|
|
1216
|
+
|
|
1217
|
+
```typescript
|
|
1218
|
+
// src/types/config.ts
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Init hook configuration - runs BEFORE check execution
|
|
1222
|
+
*/
|
|
1223
|
+
export interface OnInitConfig {
|
|
1224
|
+
/** Items to run before this check executes */
|
|
1225
|
+
run?: OnInitRunItem[];
|
|
1226
|
+
|
|
1227
|
+
/** Dynamic init items: JS expression returning OnInitRunItem[] */
|
|
1228
|
+
run_js?: string;
|
|
1229
|
+
|
|
1230
|
+
/** Declarative transitions (optional) */
|
|
1231
|
+
transitions?: TransitionRule[];
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* Unified run item
|
|
1236
|
+
*/
|
|
1237
|
+
export type OnInitRunItem =
|
|
1238
|
+
| OnInitToolInvocation
|
|
1239
|
+
| OnInitStepInvocation
|
|
1240
|
+
| OnInitWorkflowInvocation
|
|
1241
|
+
| string;
|
|
1242
|
+
|
|
1243
|
+
/**
|
|
1244
|
+
* Tool invocation
|
|
1245
|
+
*/
|
|
1246
|
+
export interface OnInitToolInvocation {
|
|
1247
|
+
tool: string;
|
|
1248
|
+
with?: Record<string, unknown>;
|
|
1249
|
+
as?: string;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Step invocation
|
|
1254
|
+
*/
|
|
1255
|
+
export interface OnInitStepInvocation {
|
|
1256
|
+
step: string;
|
|
1257
|
+
with?: Record<string, unknown>;
|
|
1258
|
+
as?: string;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/**
|
|
1262
|
+
* Workflow invocation
|
|
1263
|
+
*/
|
|
1264
|
+
export interface OnInitWorkflowInvocation {
|
|
1265
|
+
workflow: string;
|
|
1266
|
+
with?: Record<string, unknown>;
|
|
1267
|
+
as?: string;
|
|
1268
|
+
overrides?: Record<string, Partial<CheckConfig>>;
|
|
1269
|
+
output_mapping?: Record<string, string>;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
/**
|
|
1273
|
+
* Check configuration (extended)
|
|
1274
|
+
*/
|
|
1275
|
+
export interface CheckConfig {
|
|
1276
|
+
// ... existing fields ...
|
|
1277
|
+
|
|
1278
|
+
/** Init hook - runs before check execution */
|
|
1279
|
+
on_init?: OnInitConfig;
|
|
1280
|
+
|
|
1281
|
+
/** Success hook */
|
|
1282
|
+
on_success?: OnSuccessConfig;
|
|
1283
|
+
|
|
1284
|
+
/** Failure hook */
|
|
1285
|
+
on_fail?: OnFailConfig;
|
|
1286
|
+
|
|
1287
|
+
/** Finish hook (forEach only) */
|
|
1288
|
+
on_finish?: OnFinishConfig;
|
|
1289
|
+
}
|
|
1290
|
+
```
|
|
1291
|
+
|
|
1292
|
+
---
|
|
1293
|
+
|
|
1294
|
+
**End of RFC**
|