@khoavu882/doflow 2.0.0-beta.4 → 2.0.0-beta.5
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/core/.antigravity-plugin/plugin.json +0 -1
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
- package/core/harnesses/claude/hooks/session-start.sh +5 -3
- package/core/harnesses/claude/hooks/stop-check.sh +5 -3
- package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
- package/core/harnesses/codex/hooks/session-start.sh +5 -3
- package/core/harnesses/codex/hooks/stop-check.sh +6 -5
- package/core/harnesses/codex/hooks/subagent-audit.sh +17 -6
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +7 -18
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +7 -18
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +7 -19
- package/core/harnesses/gemini/hooks/session-start.sh +5 -3
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +20 -5
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +20 -5
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +20 -5
- package/core/harnesses/kiro/hooks/session-start.sh +11 -5
- package/core/harnesses/kiro/hooks/stop-check.sh +21 -30
- package/core/harnesses/pi/extensions/doflow-guard.js +159 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -11
- package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
- package/core/harnesses/shared/hooks/bridge/bridge.sh +184 -0
- package/core/harnesses/shared/hooks/bridge/envelope.jq +60 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +62 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +4 -2
- package/core/harnesses/shared/hooks/policies/contract.sh +75 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +39 -4
- package/core/harnesses/shared/hooks/policies/lib.sh +4 -2
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +46 -34
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +75 -25
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +67 -73
- package/core/harnesses/shared/hooks/policies/session-context.sh +37 -7
- package/core/harnesses/shared/hooks/policies/stop-check.sh +42 -15
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +35 -5
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +40 -7
- package/core/harnesses/shared/hooks/stream-hook-runner.js +69 -181
- package/core/registry/assets.json +19 -0
- package/core/registry/contracts.json +2 -2
- package/core/registry/harnesses.json +196 -10
- package/core/registry/lifecycle.json +2626 -7
- package/core/registry/storage.json +24 -1
- package/core/shared/scripts/doflow/bin/doflow-run +10 -6
- package/core/shared/skills/do-design/SKILL.md +6 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +11 -10
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +2 -0
- package/core/shared/skills/do-implement/SKILL.md +3 -2
- package/core/shared/skills/do-plan/SKILL.md +7 -8
- package/package.json +1 -1
- package/src/adapters/antigravity/index.js +83 -47
- package/src/adapters/claude/index.js +15 -1
- package/src/adapters/codex/hooks.js +40 -5
- package/src/adapters/codex/index.js +52 -9
- package/src/adapters/copy-tree.js +4 -4
- package/src/adapters/gemini/hooks.js +57 -16
- package/src/adapters/gemini/index.js +57 -18
- package/src/adapters/kiro/index.js +15 -1
- package/src/adapters/pi/hooks.js +119 -0
- package/src/adapters/pi/index.js +34 -9
- package/src/cli/commands/install.js +1 -1
- package/src/cli/commands/update.js +1 -1
- package/src/cli/index.js +17 -5
- package/src/cli/runtime-commands.js +51 -12
- package/src/lifecycle/coverage.js +57 -0
- package/src/lifecycle/hook-step.js +61 -0
- package/src/lifecycle/index.js +28 -14
- package/src/registry/index.js +136 -7
- package/src/runtime/candidate.js +505 -0
- package/src/runtime/claims.js +14 -5
- package/src/runtime/cli-result.js +11 -4
- package/src/runtime/cli.js +35 -268
- package/src/runtime/context/budget.js +203 -0
- package/src/runtime/context/compose.js +1367 -0
- package/src/runtime/context/delivery-store.js +102 -0
- package/src/runtime/context/mandatory.js +594 -0
- package/src/runtime/context/pointer.js +142 -0
- package/src/runtime/context-pack.js +1029 -244
- package/src/runtime/evidence-fields.js +39 -0
- package/src/runtime/evidence-ledger.js +7 -0
- package/src/runtime/freshness.js +44 -7
- package/src/runtime/outcome.js +1 -1
- package/src/runtime/readiness.js +83 -13
- package/src/runtime/retrieval-plan.js +422 -73
- package/src/runtime/retrieval-record.js +223 -0
- package/src/runtime/scaffold/artifacts.js +3 -0
- package/src/runtime/source-version.js +289 -0
- package/src/runtime/task-attempts.js +1 -1
- package/src/runtime/task-scope.js +28 -3
- package/src/runtime/task-state.js +72 -7
- package/core/.antigravity-plugin/hooks.json +0 -39
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
- package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
- package/core/harnesses/shared/hooks/hooks.json +0 -47
|
@@ -117,6 +117,43 @@ function acquireLock(fsImpl , file ) {
|
|
|
117
117
|
+ 'If no other session is running, the lock is leftover from a crash younger than its stale timeout; retry shortly.');
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Takes the cross-process lock for one state file with a bounded wait.
|
|
122
|
+
* Makes 1 try plus `retries` retries (default 3) separated by `waitMs` (default 5).
|
|
123
|
+
* Throws an Error('store-locked') after one try plus retries.
|
|
124
|
+
* @param {Object} fsImpl
|
|
125
|
+
* @param {string} file
|
|
126
|
+
* @param {Object} [options]
|
|
127
|
+
* @param {number} [options.retries=3]
|
|
128
|
+
* @param {number} [options.waitMs=5]
|
|
129
|
+
* @returns {() => void} release function
|
|
130
|
+
*/
|
|
131
|
+
function boundedAcquire(
|
|
132
|
+
fsImpl ,
|
|
133
|
+
file ,
|
|
134
|
+
options
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
= {}
|
|
140
|
+
) {
|
|
141
|
+
const retries = options?.retries ?? 3;
|
|
142
|
+
const waitMs = options?.waitMs ?? 5;
|
|
143
|
+
const sleepFn = options?.sleepFn ?? sleep;
|
|
144
|
+
const tryAcquire = options?.tryAcquireLockFn ?? tryAcquireLock;
|
|
145
|
+
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
146
|
+
const release = tryAcquire(fsImpl, file);
|
|
147
|
+
if (release) return release;
|
|
148
|
+
if (attempt < retries && waitMs > 0) {
|
|
149
|
+
sleepFn(waitMs);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const error = new Error('store-locked');
|
|
153
|
+
(error ).code = 'store-locked';
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
|
|
120
157
|
/**
|
|
121
158
|
* Reads and validates one task-state file. Returns null when the file does not exist.
|
|
122
159
|
* @param {Object} fsImpl
|
|
@@ -129,13 +166,17 @@ function readTaskState(fsImpl , file ) {
|
|
|
129
166
|
try {
|
|
130
167
|
data = JSON.parse(fsImpl.readFileSync(file, 'utf8'));
|
|
131
168
|
} catch (error ) {
|
|
132
|
-
|
|
169
|
+
const failure = new Error(`Failed to load state file '${file}': ${error.message}. The write path is atomic `
|
|
133
170
|
+ '(tmp + rename), so a torn file means outside interference; recover it from version control '
|
|
134
171
|
+ 'or move it aside and re-record the task\'s items.');
|
|
172
|
+
failure.code = 'store-unreadable';
|
|
173
|
+
throw failure;
|
|
135
174
|
}
|
|
136
175
|
if (data && typeof data === 'object' && data.version !== undefined && data.version !== SCHEMA_VERSION) {
|
|
137
|
-
|
|
176
|
+
const failure = new Error(`State file '${file}' has schema version ${data.version}; this runtime reads version ${SCHEMA_VERSION}. `
|
|
138
177
|
+ 'Refusing to guess at its shape — upgrade the runtime or migrate the file.');
|
|
178
|
+
failure.code = 'store-unreadable';
|
|
179
|
+
throw failure;
|
|
139
180
|
}
|
|
140
181
|
return data;
|
|
141
182
|
}
|
|
@@ -151,16 +192,32 @@ function readTaskState(fsImpl , file ) {
|
|
|
151
192
|
* return the complete new payload (without `version`/`revision`, which are stamped here)
|
|
152
193
|
* @param {() => void} [options.prepare] called INSIDE the lock before the file is read, for a store
|
|
153
194
|
* that must inspect or set aside what is there under the same exclusion as the write
|
|
154
|
-
* @
|
|
195
|
+
* @param {(fsImpl: Object, file: string) => () => void} [options.acquire] optional lock acquirer
|
|
196
|
+
* defaulting to acquireLock
|
|
197
|
+
* @returns {string|null} the file path written, or null when build declines a write
|
|
155
198
|
*/
|
|
156
|
-
function updateTaskState({
|
|
199
|
+
function updateTaskState({
|
|
200
|
+
fsImpl,
|
|
201
|
+
file,
|
|
202
|
+
build,
|
|
203
|
+
prepare,
|
|
204
|
+
acquire = acquireLock,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
) {
|
|
157
212
|
fsImpl.mkdirSync(path.dirname(file), { recursive: true });
|
|
158
|
-
const release =
|
|
213
|
+
const release = acquire(fsImpl, file);
|
|
159
214
|
try {
|
|
160
215
|
if (prepare) prepare();
|
|
161
216
|
const disk = readTaskState(fsImpl, file);
|
|
217
|
+
const built = build(disk);
|
|
218
|
+
if (built === null) return null;
|
|
162
219
|
const payload = {
|
|
163
|
-
...
|
|
220
|
+
...built,
|
|
164
221
|
version: SCHEMA_VERSION,
|
|
165
222
|
revision: (disk && Number.isInteger(disk.revision) ? disk.revision : 0) + 1,
|
|
166
223
|
};
|
|
@@ -173,4 +230,12 @@ function updateTaskState({ fsImpl, file, build, prepare }
|
|
|
173
230
|
}
|
|
174
231
|
}
|
|
175
232
|
|
|
176
|
-
module.exports = {
|
|
233
|
+
module.exports = {
|
|
234
|
+
acquireLock,
|
|
235
|
+
tryAcquireLock,
|
|
236
|
+
boundedAcquire,
|
|
237
|
+
updateTaskState,
|
|
238
|
+
readTaskState,
|
|
239
|
+
mergeRecords,
|
|
240
|
+
SCHEMA_VERSION,
|
|
241
|
+
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"description": "DoFlow lifecycle hooks for Google Antigravity (IDE & CLI). Uses stdin/stdout JSON protocol for real-time gating.",
|
|
3
|
-
"hooks": {
|
|
4
|
-
"SessionStart": [
|
|
5
|
-
{
|
|
6
|
-
"type": "command",
|
|
7
|
-
"command": "bash \"$DOFLOW_PROJECT_DIR/.gemini/hooks/session-start.sh\""
|
|
8
|
-
}
|
|
9
|
-
],
|
|
10
|
-
"PreToolUse": [
|
|
11
|
-
{
|
|
12
|
-
"matcher": "^(run_command|replace_file_content|write_to_file|multi_replace_file_content)$",
|
|
13
|
-
"hooks": [
|
|
14
|
-
{
|
|
15
|
-
"type": "command",
|
|
16
|
-
"command": "node \"$DOFLOW_PROJECT_DIR/.doflow/scripts/stream-hook-runner.js\""
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
|
-
"PostToolUse": [
|
|
22
|
-
{
|
|
23
|
-
"matcher": "^(replace_file_content|write_to_file|multi_replace_file_content)$",
|
|
24
|
-
"hooks": [
|
|
25
|
-
{
|
|
26
|
-
"type": "command",
|
|
27
|
-
"command": "bash \"$DOFLOW_PROJECT_DIR/.gemini/hooks/post-edit-lint.sh\""
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
],
|
|
32
|
-
"Stop": [
|
|
33
|
-
{
|
|
34
|
-
"type": "command",
|
|
35
|
-
"command": "bash \"$DOFLOW_PROJECT_DIR/.gemini/hooks/stop-check.sh\""
|
|
36
|
-
}
|
|
37
|
-
]
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Shared toolCall.args field-name lookup for the toolCall/args envelope adapters
|
|
5
|
-
* (antigravity.js, gemini.js's fallback path) both read when mapping to canonical
|
|
6
|
-
* file_path/command. Extracted because both adapters carried an identical copy.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const FILE_PATH_ARG_FIELDS = ['AbsolutePath', 'TargetFile', 'Path', 'file_path', 'path'];
|
|
10
|
-
const COMMAND_ARG_FIELDS = ['CommandLine', 'command'];
|
|
11
|
-
|
|
12
|
-
function firstArgField(args, fields) {
|
|
13
|
-
for (const field of fields) {
|
|
14
|
-
if (args && args[field]) return args[field];
|
|
15
|
-
}
|
|
16
|
-
return '';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = { FILE_PATH_ARG_FIELDS, COMMAND_ARG_FIELDS, firstArgField };
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Gemini CLI payload/decision adapter for the Cross-Harness Hook Runner (design.md C2's
|
|
5
|
-
* Payload Adapter / Decision Adapter seam, D2).
|
|
6
|
-
*
|
|
7
|
-
* Gemini's own native contract — evidenced by core/harnesses/gemini/hooks/*.sh and
|
|
8
|
-
* core/harnesses/gemini/hooks/hooks.json, all currently invoked directly by bash rather than
|
|
9
|
-
* through this runner — already speaks field names close to Claude/Codex/Kiro's, NOT
|
|
10
|
-
* Antigravity's toolCall/args envelope:
|
|
11
|
-
*
|
|
12
|
-
* SessionStart : stdin { session_id, cwd } (session-start.sh)
|
|
13
|
-
* BeforeTool (~PreToolUse): stdin { tool_name, tool_input: { file_path, command } }
|
|
14
|
-
* stdout { decision: "allow"|"deny", reason } — TOP-LEVEL, not
|
|
15
|
-
* nested under hookSpecificOutput.permissionDecision like
|
|
16
|
-
* Claude/Codex (confirmed in gemini/hooks/pre-bash-guard.sh's header).
|
|
17
|
-
* AfterTool (~PostToolUse): stdin { tool_name, tool_input: { file_path }, session_id }
|
|
18
|
-
* (post-edit-lint.sh) — stdout {} (ack; no Phase A policy owns this
|
|
19
|
-
* event, post-edit-lint.sh's collector role is out of FR-001's scope)
|
|
20
|
-
* Stop : NOT a documented Gemini lifecycle event — gemini/hooks/
|
|
21
|
-
* post-edit-lint.sh's own comment states "Gemini has no
|
|
22
|
-
* Stop-equivalent event," and no stop-check.sh ships under
|
|
23
|
-
* gemini/hooks/ today. Handled defensively below (same field-name
|
|
24
|
-
* union stop-check.sh itself accepts) in case a future Gemini
|
|
25
|
-
* version adds one, or in case core/harnesses/shared/hooks/hooks.json's
|
|
26
|
-
* existing (currently dangling) Stop entry is corrected to a real target.
|
|
27
|
-
*
|
|
28
|
-
* NFR-002 note: core/harnesses/shared/hooks/hooks.json has historically routed PreToolUse through
|
|
29
|
-
* this runner using Antigravity's { toolCall: { name, args } } envelope — inherited from this
|
|
30
|
-
* runner's original Antigravity-only design (see the runner's own prior header comment), not
|
|
31
|
-
* verified against Gemini's own documented schema above. toCanonical() below reads the
|
|
32
|
-
* tool_name/tool_input shape first (Gemini's own evidenced contract) and falls back to the
|
|
33
|
-
* toolCall envelope only if that shape isn't present, so this adapter is correct once that
|
|
34
|
-
* wiring is corrected without silently breaking before it is.
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
const { FILE_PATH_ARG_FIELDS, COMMAND_ARG_FIELDS, firstArgField } = require('./arg-fields');
|
|
38
|
-
|
|
39
|
-
function toCanonical(event, payload) {
|
|
40
|
-
const safePayload = payload && typeof payload === 'object' ? payload : {};
|
|
41
|
-
|
|
42
|
-
switch (event) {
|
|
43
|
-
case 'SessionStart':
|
|
44
|
-
return { session_id: safePayload.session_id || '', cwd: safePayload.cwd || '' };
|
|
45
|
-
|
|
46
|
-
case 'PreToolUse': {
|
|
47
|
-
if (safePayload.tool_name !== undefined || safePayload.tool_input !== undefined) {
|
|
48
|
-
const toolInput = safePayload.tool_input || {};
|
|
49
|
-
return {
|
|
50
|
-
tool_name: safePayload.tool_name || '',
|
|
51
|
-
tool_input: {
|
|
52
|
-
file_path: toolInput.file_path || '',
|
|
53
|
-
command: toolInput.command || '',
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
// Defensive fallback: Antigravity-shaped envelope (see header note).
|
|
58
|
-
const toolCall = safePayload.toolCall || {};
|
|
59
|
-
const args = toolCall.args || {};
|
|
60
|
-
return {
|
|
61
|
-
tool_name: toolCall.name || '',
|
|
62
|
-
tool_input: {
|
|
63
|
-
file_path: firstArgField(args, FILE_PATH_ARG_FIELDS),
|
|
64
|
-
command: firstArgField(args, COMMAND_ARG_FIELDS),
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
case 'PostToolUse':
|
|
70
|
-
return {
|
|
71
|
-
tool_name: safePayload.tool_name || '',
|
|
72
|
-
session_id: safePayload.session_id || '',
|
|
73
|
-
tool_input: { file_path: (safePayload.tool_input && safePayload.tool_input.file_path) || '' },
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
case 'Stop':
|
|
77
|
-
return {
|
|
78
|
-
session_id: safePayload.session_id || '',
|
|
79
|
-
transcript_path: safePayload.transcript_path || safePayload.transcriptPath || '',
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
default:
|
|
83
|
-
return safePayload;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function toNative(event, decision) {
|
|
88
|
-
const safeDecision = decision || { decision: 'allow' };
|
|
89
|
-
|
|
90
|
-
switch (event) {
|
|
91
|
-
case 'PreToolUse':
|
|
92
|
-
if (safeDecision.decision === 'deny') {
|
|
93
|
-
return { decision: 'deny', reason: safeDecision.reason };
|
|
94
|
-
}
|
|
95
|
-
return { decision: 'allow' };
|
|
96
|
-
|
|
97
|
-
case 'PostToolUse':
|
|
98
|
-
return {};
|
|
99
|
-
|
|
100
|
-
case 'Stop':
|
|
101
|
-
if (safeDecision.decision === 'deny') {
|
|
102
|
-
return { decision: 'deny', reason: safeDecision.reason };
|
|
103
|
-
}
|
|
104
|
-
return { decision: 'allow' };
|
|
105
|
-
|
|
106
|
-
case 'SessionStart':
|
|
107
|
-
return {};
|
|
108
|
-
|
|
109
|
-
default:
|
|
110
|
-
return { decision: 'allow' };
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
module.exports = { toCanonical, toNative };
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"description": "DoFlow shared lifecycle hooks schema for Antigravity, Gemini, and Claude.",
|
|
3
|
-
"hooks": {
|
|
4
|
-
"SessionStart": [
|
|
5
|
-
{
|
|
6
|
-
"hooks": [
|
|
7
|
-
{
|
|
8
|
-
"type": "command",
|
|
9
|
-
"command": "bash \"$DOFLOW_PROJECT_DIR/.gemini/hooks/session-start.sh\""
|
|
10
|
-
}
|
|
11
|
-
]
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"PreToolUse": [
|
|
15
|
-
{
|
|
16
|
-
"matcher": "^(run_command|run_shell_command|replace_file_content|write_to_file|multi_replace_file_content|edit_file|Edit|Write)$",
|
|
17
|
-
"hooks": [
|
|
18
|
-
{
|
|
19
|
-
"type": "command",
|
|
20
|
-
"command": "node \"$DOFLOW_PROJECT_DIR/.doflow/scripts/stream-hook-runner.js\""
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"PostToolUse": [
|
|
26
|
-
{
|
|
27
|
-
"matcher": "^(replace_file_content|write_to_file|multi_replace_file_content|edit_file|Edit|Write)$",
|
|
28
|
-
"hooks": [
|
|
29
|
-
{
|
|
30
|
-
"type": "command",
|
|
31
|
-
"command": "bash \"$DOFLOW_PROJECT_DIR/.gemini/hooks/post-edit-lint.sh\""
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|
|
35
|
-
],
|
|
36
|
-
"Stop": [
|
|
37
|
-
{
|
|
38
|
-
"hooks": [
|
|
39
|
-
{
|
|
40
|
-
"type": "command",
|
|
41
|
-
"command": "bash \"$DOFLOW_PROJECT_DIR/.gemini/hooks/stop-check.sh\""
|
|
42
|
-
}
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
}
|