@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.
Files changed (101) hide show
  1. package/core/.antigravity-plugin/plugin.json +0 -1
  2. package/core/.claude-plugin/plugin.json +1 -1
  3. package/core/.codex-plugin/plugin.json +1 -1
  4. package/core/.plugin/plugin.json +1 -1
  5. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  6. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  7. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  8. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  9. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  10. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  11. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  12. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  13. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  14. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  15. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  16. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  17. package/core/harnesses/codex/hooks/subagent-audit.sh +17 -6
  18. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  19. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +7 -18
  20. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +7 -18
  21. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +7 -19
  22. package/core/harnesses/gemini/hooks/session-start.sh +5 -3
  23. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +20 -5
  24. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +20 -5
  25. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +20 -5
  26. package/core/harnesses/kiro/hooks/session-start.sh +11 -5
  27. package/core/harnesses/kiro/hooks/stop-check.sh +21 -30
  28. package/core/harnesses/pi/extensions/doflow-guard.js +159 -0
  29. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -11
  30. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  31. package/core/harnesses/shared/hooks/bridge/bridge.sh +184 -0
  32. package/core/harnesses/shared/hooks/bridge/envelope.jq +60 -0
  33. package/core/harnesses/shared/hooks/bridge/respond.sh +62 -0
  34. package/core/harnesses/shared/hooks/policies/capture-failure.sh +4 -2
  35. package/core/harnesses/shared/hooks/policies/contract.sh +75 -0
  36. package/core/harnesses/shared/hooks/policies/deny-json.sh +39 -4
  37. package/core/harnesses/shared/hooks/policies/lib.sh +4 -2
  38. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +46 -34
  39. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +75 -25
  40. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +67 -73
  41. package/core/harnesses/shared/hooks/policies/session-context.sh +37 -7
  42. package/core/harnesses/shared/hooks/policies/stop-check.sh +42 -15
  43. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +35 -5
  44. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +40 -7
  45. package/core/harnesses/shared/hooks/stream-hook-runner.js +69 -181
  46. package/core/registry/assets.json +19 -0
  47. package/core/registry/contracts.json +2 -2
  48. package/core/registry/harnesses.json +196 -10
  49. package/core/registry/lifecycle.json +2626 -7
  50. package/core/registry/storage.json +24 -1
  51. package/core/shared/scripts/doflow/bin/doflow-run +10 -6
  52. package/core/shared/skills/do-design/SKILL.md +6 -6
  53. package/core/shared/skills/do-execute-plan/SKILL.md +11 -10
  54. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +2 -0
  55. package/core/shared/skills/do-implement/SKILL.md +3 -2
  56. package/core/shared/skills/do-plan/SKILL.md +7 -8
  57. package/package.json +1 -1
  58. package/src/adapters/antigravity/index.js +83 -47
  59. package/src/adapters/claude/index.js +15 -1
  60. package/src/adapters/codex/hooks.js +40 -5
  61. package/src/adapters/codex/index.js +52 -9
  62. package/src/adapters/copy-tree.js +4 -4
  63. package/src/adapters/gemini/hooks.js +57 -16
  64. package/src/adapters/gemini/index.js +57 -18
  65. package/src/adapters/kiro/index.js +15 -1
  66. package/src/adapters/pi/hooks.js +119 -0
  67. package/src/adapters/pi/index.js +34 -9
  68. package/src/cli/commands/install.js +1 -1
  69. package/src/cli/commands/update.js +1 -1
  70. package/src/cli/index.js +17 -5
  71. package/src/cli/runtime-commands.js +51 -12
  72. package/src/lifecycle/coverage.js +57 -0
  73. package/src/lifecycle/hook-step.js +61 -0
  74. package/src/lifecycle/index.js +28 -14
  75. package/src/registry/index.js +136 -7
  76. package/src/runtime/candidate.js +505 -0
  77. package/src/runtime/claims.js +14 -5
  78. package/src/runtime/cli-result.js +11 -4
  79. package/src/runtime/cli.js +35 -268
  80. package/src/runtime/context/budget.js +203 -0
  81. package/src/runtime/context/compose.js +1367 -0
  82. package/src/runtime/context/delivery-store.js +102 -0
  83. package/src/runtime/context/mandatory.js +594 -0
  84. package/src/runtime/context/pointer.js +142 -0
  85. package/src/runtime/context-pack.js +1029 -244
  86. package/src/runtime/evidence-fields.js +39 -0
  87. package/src/runtime/evidence-ledger.js +7 -0
  88. package/src/runtime/freshness.js +44 -7
  89. package/src/runtime/outcome.js +1 -1
  90. package/src/runtime/readiness.js +83 -13
  91. package/src/runtime/retrieval-plan.js +422 -73
  92. package/src/runtime/retrieval-record.js +223 -0
  93. package/src/runtime/scaffold/artifacts.js +3 -0
  94. package/src/runtime/source-version.js +289 -0
  95. package/src/runtime/task-attempts.js +1 -1
  96. package/src/runtime/task-scope.js +28 -3
  97. package/src/runtime/task-state.js +72 -7
  98. package/core/.antigravity-plugin/hooks.json +0 -39
  99. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  100. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  101. 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
- throw new Error(`Failed to load state file '${file}': ${error.message}. The write path is atomic `
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
- throw new Error(`State file '${file}' has schema version ${data.version}; this runtime reads version ${SCHEMA_VERSION}. `
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
- * @returns {string} the file path written
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({ fsImpl, file, build, prepare } ) {
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 = acquireLock(fsImpl, file);
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
- ...build(disk),
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 = { acquireLock, tryAcquireLock, updateTaskState, readTaskState, mergeRecords, SCHEMA_VERSION };
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
- }