@khoavu882/doflow 2.0.0 → 2.0.2
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 +4 -5
- package/bin/doflow.js +1 -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 +4 -1
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/claude/hooks/session-end.sh +21 -1
- package/core/harnesses/claude/settings/settings.json +8 -9
- package/core/harnesses/codex/hooks/hooks.json +0 -10
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/codex/hooks/session-end.impl.sh +21 -1
- package/core/harnesses/copilot/hooks/doflow-lib.sh +4 -3
- package/core/harnesses/copilot/hooks/doflow.json +0 -6
- package/core/harnesses/kiro/agents/doflow.json +0 -10
- package/core/harnesses/kiro/hooks/doflow.json +0 -10
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +7 -3
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +7 -3
- package/core/harnesses/pi/extensions/doflow-guard.js +3 -2
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -1
- package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +35 -25
- package/core/harnesses/shared/hooks/bridge/envelope.jq +1 -1
- package/core/harnesses/shared/hooks/bridge/respond.sh +6 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +1 -1
- package/core/harnesses/shared/hooks/policies/compact-text.sh +8 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +23 -1
- package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +4 -6
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +20 -21
- package/core/harnesses/shared/hooks/policies/stop-check.sh +7 -6
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +26 -2
- package/core/harnesses/shared/hooks/stream-hook-runner.js +11 -12
- package/core/registry/contracts.json +1 -1
- package/core/registry/contribution-points.json +5 -2
- package/core/registry/gates.json +5 -5
- package/core/registry/harnesses.json +9 -9
- package/core/registry/intake.json +53 -0
- package/core/registry/lifecycle.json +176 -503
- package/core/registry/package-templates/intake-category/files/category.json +8 -0
- package/core/registry/package-templates/intake-category/template.json +4 -0
- package/core/registry/packages/doflow/doflow-package.json +2 -2
- package/core/registry/rules.json +8 -8
- package/core/registry/settings.json +12 -16
- package/core/registry/storage.json +9 -2
- package/core/registry/telemetry.json +1 -1
- package/core/registry/verification.json +0 -5
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +7 -0
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +4 -4
- package/core/shared/guidance/references/ORCHESTRATION.md +2 -0
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
- package/core/shared/guidance/rules/RULES.md +2 -2
- package/core/shared/scripts/doflow/bash/do-git-state.sh +12 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +86 -33
- package/core/shared/scripts/doflow/bash/do-review-package.sh +36 -2
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +1 -1
- package/core/shared/scripts/doflow/bin/doflow-run +23 -2
- package/core/shared/skills/do/SKILL.md +3 -3
- package/core/shared/skills/do-brainstorm/SKILL.md +26 -24
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +2 -1
- package/core/shared/skills/do-code-review/SKILL.md +7 -4
- package/core/shared/skills/do-design/SKILL.md +11 -5
- package/core/shared/skills/do-design/references/data_model_example.md +167 -0
- package/core/shared/skills/do-diagnose/SKILL.md +4 -2
- package/core/shared/skills/do-document/SKILL.md +1 -0
- package/core/shared/skills/do-document/references/embedded-research.md +1 -1
- package/core/shared/skills/do-execute-plan/SKILL.md +4 -2
- package/core/shared/skills/do-execute-plan/references/run_position.md +3 -1
- package/core/shared/skills/do-execute-plan/references/scaffold.md +1 -1
- package/core/shared/skills/do-flow/SKILL.md +4 -4
- package/core/shared/skills/do-flow/references/task_classes.md +5 -5
- package/core/shared/skills/do-implement/SKILL.md +9 -7
- package/core/shared/skills/do-plan/SKILL.md +9 -6
- package/core/shared/templates/doflow/data-model-template.md +136 -102
- package/core/shared/workflows/do-flow-chain.md +2 -0
- package/package.json +1 -1
- package/src/adapters/antigravity/index.js +54 -12
- package/src/adapters/claude/index.js +23 -2
- package/src/adapters/codex/agents.js +15 -1
- package/src/adapters/codex/config.js +5 -2
- package/src/adapters/codex/hooks.js +39 -3
- package/src/adapters/codex/index.js +13 -0
- package/src/adapters/copilot/index.js +11 -4
- package/src/adapters/copy-tree.js +8 -2
- package/src/adapters/created-file.js +41 -0
- package/src/adapters/mcp-entries.js +11 -1
- package/src/adapters/opencode/index.js +32 -5
- package/src/adapters/pi/index.js +15 -4
- package/src/adapters/pi/mcp.js +44 -18
- package/src/cli/commands/remove.js +3 -1
- package/src/cli/commands/status.js +1 -1
- package/src/cli/commands/update.js +7 -3
- package/src/cli/index.js +43 -19
- package/src/cli/runtime-commands.js +13 -5
- package/src/cli/shared.js +5 -3
- package/src/helper/marker-merge.js +11 -7
- package/src/install/backup-set.js +7 -3
- package/src/install/backup.js +13 -7
- package/src/install/context.js +2 -1
- package/src/install/manifest.js +35 -11
- package/src/install/paths.js +38 -0
- package/src/install/targets.js +11 -2
- package/src/lifecycle/coverage.js +1 -30
- package/src/lifecycle/hook-tier.js +2 -2
- package/src/lifecycle/index.js +80 -3
- package/src/lifecycle/retired-harnesses.js +16 -1
- package/src/lifecycle/view.js +3 -2
- package/src/registry/index.js +21 -4
- package/src/release/docs-blocks.js +37 -53
- package/src/runtime/advice/doctor.js +2 -2
- package/src/runtime/brief-tools.js +2 -1
- package/src/runtime/capability-router.js +26 -3
- package/src/runtime/context/pointer.js +84 -17
- package/src/runtime/context-pack.js +52 -9
- package/src/runtime/dispatch/integrate.js +27 -8
- package/src/runtime/dispatch/loop.js +7 -0
- package/src/runtime/dispatch/verify-run.js +9 -3
- package/src/runtime/evidence-ledger.js +18 -0
- package/src/runtime/feature-pointer.js +10 -1
- package/src/runtime/health.js +68 -5
- package/src/runtime/implementation-gate.js +14 -6
- package/src/runtime/intake/engine.js +679 -0
- package/src/runtime/intake/index.js +136 -0
- package/src/runtime/intake/manifest.js +140 -0
- package/src/runtime/intake/taxonomy.js +206 -0
- package/src/runtime/knowledge/code-retrieval.js +11 -5
- package/src/runtime/knowledge/semble-provider.js +12 -2
- package/src/runtime/lifecycle/cli.js +76 -6
- package/src/runtime/lifecycle/followup.js +32 -6
- package/src/runtime/lifecycle/overview.js +34 -3
- package/src/runtime/lifecycle/release.js +16 -3
- package/src/runtime/lifecycle/retention.js +12 -15
- package/src/runtime/lifecycle/store-upkeep.js +13 -3
- package/src/runtime/mask.js +3 -3
- package/src/runtime/mcp-consultations.js +11 -5
- package/src/runtime/packages/points.js +10 -0
- package/src/runtime/research-request.js +3 -1
- package/src/runtime/review/command.js +3 -7
- package/src/runtime/review/start.js +24 -10
- package/src/runtime/roadmap/attendance.js +137 -0
- package/src/runtime/roadmap/gates.js +6 -3
- package/src/runtime/roadmap/loop.js +127 -33
- package/src/runtime/roadmap/merge.js +17 -8
- package/src/runtime/roadmap/rows.js +15 -5
- package/src/runtime/roadmap/seed.js +153 -41
- package/src/runtime/roadmap/worktree-cleanup.js +147 -0
- package/src/runtime/settings/files.js +3 -1
- package/src/runtime/settings/prepared-run.js +12 -2
- package/src/runtime/settings/registry.js +1 -1
- package/src/runtime/settings/resolve.js +13 -1
- package/src/runtime/settings/write.js +73 -2
- package/src/runtime/slots.js +1 -1
- package/src/runtime/storage/registry.js +2 -2
- package/src/runtime/storage/roots.js +4 -18
- package/src/runtime/storage/rules.js +22 -46
- package/src/runtime/storage/work-status.js +14 -4
- package/src/runtime/task-classifier.js +39 -4
- package/src/runtime/task-scope.js +17 -2
- package/src/runtime/task-state.js +13 -7
- package/src/runtime/trace/analyze.js +4 -2
- package/src/runtime/trace/indicators.js +25 -20
- package/src/runtime/trace/spans.js +4 -5
- package/src/runtime/verb-view.js +268 -0
- package/src/runtime/verification/engine.js +0 -45
- package/src/runtime/worker/command.js +4 -2
- package/src/runtime/worker/launch.js +5 -37
- package/src/runtime/worker/readiness.js +10 -6
- package/src/runtime/worker/supervisor.js +18 -6
- package/src/runtime/workflow-orchestrator.js +39 -7
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +0 -9
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -25
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -105
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -579
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ flowchart LR
|
|
|
28
28
|
|---|---|
|
|
29
29
|
| Repeatable delivery work | 13 skills, including `/do-brainstorm`, `/do-plan`, `/do-test`, and `/do-code-review` |
|
|
30
30
|
| Specialist review | 7 agents — `spec-analyst`, `system-architect`, `delivery-lead`, `core-implementer`, `quality-guardian`, `reviewer`, `research-writer` |
|
|
31
|
-
| Safer automation |
|
|
31
|
+
| Safer automation | Hooks advise when an edit starts before the spec exists and when work looks unfinished; the runtime enforces order and readiness at the stage handoff |
|
|
32
32
|
| Shared standards | Rules for safety, workflow, quality, and clarification across supported tools |
|
|
33
33
|
| Session continuity | Claude and Codex hooks capture lightweight Git context and compact-session summaries and deliver them on a session's first prompt |
|
|
34
34
|
|
|
@@ -125,10 +125,9 @@ Kiro's hooks are not in Kiro v3's
|
|
|
125
125
|
untrusted-workspace exclusion list; an untrusted workspace asks before every shell command, and
|
|
126
126
|
whether that covers hook commands is undocumented. Copilot CLI documents hooks, settings, and a
|
|
127
127
|
plugin marketplace upstream; DoFlow still installs none of those there. OpenCode and Pi
|
|
128
|
-
standard-adopt `AGENTS.md` for seamless zero-drift rule projection. See
|
|
129
|
-
|
|
128
|
+
standard-adopt `AGENTS.md` for seamless zero-drift rule projection. See the
|
|
129
|
+
[installation matrix](docs/setup.md#what-gets-installed) for the exact mapping.
|
|
130
130
|
|
|
131
131
|
## Contributing
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
Run `npm test` and the shell suites described there before submitting changes.
|
|
133
|
+
Run `npm test` and the shell suites under `test/` before submitting changes.
|
package/bin/doflow.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
// doflow — DoFlow config installer CLI. Argument parsing, the installer command table and every
|
|
4
|
-
// command handler live in src/cli
|
|
4
|
+
// command handler live in src/cli/; this file is only the forwarder
|
|
5
5
|
// so the `doflow` binary path and the doflow-run dispatcher seam
|
|
6
6
|
// (core/shared/scripts/doflow/bin/doflow-run, which execs `node <repoRoot>/bin/doflow.js`)
|
|
7
7
|
// keep resolving to this exact location.
|
package/core/.plugin/plugin.json
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
set -uo pipefail
|
|
4
4
|
export DOFLOW_AGENT="${DOFLOW_AGENT:-claude}"
|
|
5
5
|
if ! . "$(dirname "$0")/../../.doflow/shared/hooks/bridge/bridge.sh" 2>/dev/null; then
|
|
6
|
-
|
|
6
|
+
# D1: a missing bridge allows. Only DOFLOW_FAULT_PROFILE=strict-1, a person's opt-in, denies.
|
|
7
|
+
if [ "${DOFLOW_FAULT_PROFILE:-}" = strict-1 ]; then
|
|
8
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"[doflow] shared hooks lack the bridge; run: doflow update -t claude"}}'
|
|
9
|
+
fi
|
|
7
10
|
exit 0
|
|
8
11
|
fi
|
|
9
12
|
doflow_bridge claude PreToolUse mcp-tool-guard
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
set -uo pipefail
|
|
4
4
|
export DOFLOW_AGENT="${DOFLOW_AGENT:-claude}"
|
|
5
5
|
if ! . "$(dirname "$0")/../../.doflow/shared/hooks/bridge/bridge.sh" 2>/dev/null; then
|
|
6
|
-
|
|
6
|
+
# D1: a missing bridge allows. Only DOFLOW_FAULT_PROFILE=strict-1, a person's opt-in, denies.
|
|
7
|
+
if [ "${DOFLOW_FAULT_PROFILE:-}" = strict-1 ]; then
|
|
8
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"[doflow] shared hooks lack the bridge; run: doflow update -t claude"}}'
|
|
9
|
+
fi
|
|
7
10
|
exit 0
|
|
8
11
|
fi
|
|
9
12
|
doflow_bridge claude PreToolUse pre-implementation-gate
|
|
@@ -5,7 +5,7 @@ case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; th
|
|
|
5
5
|
# Runs when Claude Code closes a session. Responsibilities:
|
|
6
6
|
# 1. Append END marker to sessions.log
|
|
7
7
|
# 2. Write uncommitted-warning.txt if the working tree is dirty
|
|
8
|
-
# 3. Delete the session-scoped state directory (cleanup)
|
|
8
|
+
# 3. Delete the session-scoped state directory (cleanup) and the main session's model observation
|
|
9
9
|
# 4. Trim sessions.log to last 500 lines (lock-guarded for parallel safety)
|
|
10
10
|
#
|
|
11
11
|
# Multi-session safe: with_file_lock (lib.sh) prevents concurrent sessions from
|
|
@@ -81,6 +81,26 @@ if [[ -n "$SESSION_ID" ]]; then
|
|
|
81
81
|
rm -rf "$SESSION_PATH" 2>/dev/null || true
|
|
82
82
|
fi
|
|
83
83
|
|
|
84
|
+
# ── 3b. Remove this main session's model observation (120 IC-005) ─────────────
|
|
85
|
+
|
|
86
|
+
# observe-model.sh keys the file by the first 16 hex of sha256(session id), under primary/<cwd_hash>/<harness>/.
|
|
87
|
+
# Only the ending main session's own file goes: other sessions, worker observations and a missing file are left
|
|
88
|
+
# alone, and a failure here changes nothing (SessionEnd is fire-and-forget).
|
|
89
|
+
forget_model_observation() {
|
|
90
|
+
local key="" cwd="${CWD:-}" file=""
|
|
91
|
+
[[ -n "$SESSION_ID" ]] || return 0
|
|
92
|
+
[[ "$(doflow_session_role || echo main)" == "main" ]] || return 0
|
|
93
|
+
if command -v sha256sum >/dev/null 2>&1; then key=$(printf '%s' "$SESSION_ID" | sha256sum | cut -c1-16)
|
|
94
|
+
elif command -v shasum >/dev/null 2>&1; then key=$(printf '%s' "$SESSION_ID" | shasum -a 256 | cut -c1-16)
|
|
95
|
+
else return 0; fi
|
|
96
|
+
[[ $key =~ ^[0-9a-f]{16}$ ]] || return 0
|
|
97
|
+
case "$cwd" in /*) ;; *) cwd=$PWD ;; esac
|
|
98
|
+
file="$STATE_DIR/primary/$(cwd_hash "$cwd")/claude/$key.json"
|
|
99
|
+
if [[ -f "$file" && ! -L "$file" ]]; then rm -f "$file"; fi
|
|
100
|
+
return 0
|
|
101
|
+
}
|
|
102
|
+
forget_model_observation 2>/dev/null || true
|
|
103
|
+
|
|
84
104
|
start_storage_upkeep
|
|
85
105
|
|
|
86
106
|
# ── 4. Trim sessions.log (lock-guarded) ───────────────────────────────────────
|
|
@@ -21,6 +21,14 @@
|
|
|
21
21
|
"Bash(git push --force*:*)",
|
|
22
22
|
"Bash(git reset --hard*:*)",
|
|
23
23
|
"Bash(git clean -fd*:*)"
|
|
24
|
+
],
|
|
25
|
+
"ask": [
|
|
26
|
+
"Bash(git push:*)",
|
|
27
|
+
"Bash(git tag:*)",
|
|
28
|
+
"Bash(git * push:*)",
|
|
29
|
+
"Bash(git * tag:*)",
|
|
30
|
+
"Bash(git * push)",
|
|
31
|
+
"Bash(git * tag)"
|
|
24
32
|
]
|
|
25
33
|
},
|
|
26
34
|
"alwaysThinkingEnabled": true,
|
|
@@ -87,15 +95,6 @@
|
|
|
87
95
|
}
|
|
88
96
|
],
|
|
89
97
|
"PreToolUse": [
|
|
90
|
-
{
|
|
91
|
-
"matcher": "Bash",
|
|
92
|
-
"hooks": [
|
|
93
|
-
{
|
|
94
|
-
"type": "command",
|
|
95
|
-
"command": "~/.claude/hooks/pre-bash-guard.sh"
|
|
96
|
-
}
|
|
97
|
-
]
|
|
98
|
-
},
|
|
99
98
|
{
|
|
100
99
|
"matcher": "mcp__.*",
|
|
101
100
|
"hooks": [
|
|
@@ -43,16 +43,6 @@
|
|
|
43
43
|
}
|
|
44
44
|
],
|
|
45
45
|
"PreToolUse": [
|
|
46
|
-
{
|
|
47
|
-
"matcher": "^Bash$",
|
|
48
|
-
"hooks": [
|
|
49
|
-
{
|
|
50
|
-
"type": "command",
|
|
51
|
-
"command": "bash \"$(root=$(git rev-parse --show-toplevel 2>/dev/null) && [ -f \"$root/.codex/hooks/pre-bash-guard.sh\" ] && printf '%s/.codex/hooks/pre-bash-guard.sh' \"$root\" || printf '%s/hooks/pre-bash-guard.sh' \"${CODEX_HOME:-$HOME/.codex}\")\"",
|
|
52
|
-
"statusMessage": "Checking command safety"
|
|
53
|
-
}
|
|
54
|
-
]
|
|
55
|
-
},
|
|
56
46
|
{
|
|
57
47
|
"matcher": "^mcp__.*",
|
|
58
48
|
"hooks": [
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
set -uo pipefail
|
|
4
4
|
export DOFLOW_AGENT="${DOFLOW_AGENT:-codex}"
|
|
5
5
|
if ! . "$(dirname "$0")/../../.doflow/shared/hooks/bridge/bridge.sh" 2>/dev/null; then
|
|
6
|
-
|
|
6
|
+
# D1: a missing bridge allows. Only DOFLOW_FAULT_PROFILE=strict-1, a person's opt-in, denies.
|
|
7
|
+
if [ "${DOFLOW_FAULT_PROFILE:-}" = strict-1 ]; then
|
|
8
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"[doflow] shared hooks lack the bridge; run: doflow update -t codex"}}'
|
|
9
|
+
fi
|
|
7
10
|
exit 0
|
|
8
11
|
fi
|
|
9
12
|
doflow_bridge codex PreToolUse mcp-tool-guard
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
set -uo pipefail
|
|
4
4
|
export DOFLOW_AGENT="${DOFLOW_AGENT:-codex}"
|
|
5
5
|
if ! . "$(dirname "$0")/../../.doflow/shared/hooks/bridge/bridge.sh" 2>/dev/null; then
|
|
6
|
-
|
|
6
|
+
# D1: a missing bridge allows. Only DOFLOW_FAULT_PROFILE=strict-1, a person's opt-in, denies.
|
|
7
|
+
if [ "${DOFLOW_FAULT_PROFILE:-}" = strict-1 ]; then
|
|
8
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"[doflow] shared hooks lack the bridge; run: doflow update -t codex"}}'
|
|
9
|
+
fi
|
|
7
10
|
exit 0
|
|
8
11
|
fi
|
|
9
12
|
doflow_bridge codex PreToolUse pre-implementation-gate
|
|
@@ -5,7 +5,7 @@ case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; th
|
|
|
5
5
|
# Runs when Claude Code closes a session. Responsibilities:
|
|
6
6
|
# 1. Append END marker to sessions.log
|
|
7
7
|
# 2. Write uncommitted-warning.txt if the working tree is dirty
|
|
8
|
-
# 3. Delete the session-scoped state directory (cleanup)
|
|
8
|
+
# 3. Delete the session-scoped state directory (cleanup) and the main session's model observation
|
|
9
9
|
# 4. Trim sessions.log to last 500 lines (lock-guarded for parallel safety,
|
|
10
10
|
# via lib.sh's portable with_file_lock — no flock dependency)
|
|
11
11
|
#
|
|
@@ -82,6 +82,26 @@ if [[ -n "$SESSION_ID" ]]; then
|
|
|
82
82
|
rm -rf "$SESSION_PATH" 2>/dev/null || true
|
|
83
83
|
fi
|
|
84
84
|
|
|
85
|
+
# ── 3b. Remove this main session's model observation (120 IC-005) ─────────────
|
|
86
|
+
|
|
87
|
+
# observe-model.sh keys the file by the first 16 hex of sha256(session id), under primary/<cwd_hash>/<harness>/.
|
|
88
|
+
# Only the ending main session's own file goes: other sessions, worker observations and a missing file are left
|
|
89
|
+
# alone, and a failure here changes nothing (SessionEnd is fire-and-forget).
|
|
90
|
+
forget_model_observation() {
|
|
91
|
+
local key="" cwd="${CWD:-}" file=""
|
|
92
|
+
[[ -n "$SESSION_ID" ]] || return 0
|
|
93
|
+
[[ "$(doflow_session_role || echo main)" == "main" ]] || return 0
|
|
94
|
+
if command -v sha256sum >/dev/null 2>&1; then key=$(printf '%s' "$SESSION_ID" | sha256sum | cut -c1-16)
|
|
95
|
+
elif command -v shasum >/dev/null 2>&1; then key=$(printf '%s' "$SESSION_ID" | shasum -a 256 | cut -c1-16)
|
|
96
|
+
else return 0; fi
|
|
97
|
+
[[ $key =~ ^[0-9a-f]{16}$ ]] || return 0
|
|
98
|
+
case "$cwd" in /*) ;; *) cwd=$PWD ;; esac
|
|
99
|
+
file="$STATE_DIR/primary/$(cwd_hash "$cwd")/codex/$key.json"
|
|
100
|
+
if [[ -f "$file" && ! -L "$file" ]]; then rm -f "$file"; fi
|
|
101
|
+
return 0
|
|
102
|
+
}
|
|
103
|
+
forget_model_observation 2>/dev/null || true
|
|
104
|
+
|
|
85
105
|
start_storage_upkeep
|
|
86
106
|
|
|
87
107
|
# ── 4. Trim sessions.log (lock-guarded) ───────────────────────────────────────
|
|
@@ -69,8 +69,8 @@ copilot_bridge_payload() {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
# copilot_front_door <script-dir> <event> <policy> <fail>: run one shared policy through the bridge.
|
|
72
|
-
# <fail> is `deny` for a pre-tool policy (a missing bridge denies the tool call
|
|
73
|
-
#
|
|
72
|
+
# <fail> is `deny` for a pre-tool policy (a missing bridge denies the tool call only under strict-1)
|
|
73
|
+
# or `open` for every other event.
|
|
74
74
|
copilot_front_door() {
|
|
75
75
|
local dir="$1" event="$2" policy="$3" fail="${4:-open}" raw payload bridge status=0
|
|
76
76
|
raw=$(cat)
|
|
@@ -83,7 +83,8 @@ copilot_front_door() {
|
|
|
83
83
|
# User hooks under a custom $COPILOT_HOME sit beside no .doflow; the bridge is then in $HOME/.doflow.
|
|
84
84
|
[ -f "$bridge" ] || bridge="${HOME:-}/.doflow/shared/hooks/bridge/bridge.sh"
|
|
85
85
|
if [ ! -f "$bridge" ] || ! . "$bridge" 2>/dev/null; then
|
|
86
|
-
|
|
86
|
+
# D1: a missing bridge allows. Only DOFLOW_FAULT_PROFILE=strict-1, a person's opt-in, denies a pre-tool call.
|
|
87
|
+
if [ "$fail" = deny ] && [ "${DOFLOW_FAULT_PROFILE:-}" = strict-1 ]; then
|
|
87
88
|
printf '%s\n' '{"permissionDecision":"deny","permissionDecisionReason":"[doflow] shared hooks lack the bridge; run: doflow update -t copilot"}'
|
|
88
89
|
fi
|
|
89
90
|
return 0
|
|
@@ -16,12 +16,6 @@
|
|
|
16
16
|
"cwd": ".",
|
|
17
17
|
"timeoutSec": 20
|
|
18
18
|
},
|
|
19
|
-
{
|
|
20
|
-
"type": "command",
|
|
21
|
-
"bash": "bash \"$(root=$(git rev-parse --show-toplevel 2>/dev/null) && [ -f \"$root/.github/hooks/doflow-pre-bash-guard.sh\" ] && printf '%s/.github/hooks/doflow-pre-bash-guard.sh' \"$root\" || printf '%s/hooks/doflow-pre-bash-guard.sh' \"${COPILOT_HOME:-$HOME/.copilot}\")\"",
|
|
22
|
-
"cwd": ".",
|
|
23
|
-
"timeoutSec": 20
|
|
24
|
-
},
|
|
25
19
|
{
|
|
26
20
|
"type": "command",
|
|
27
21
|
"bash": "bash \"$(root=$(git rev-parse --show-toplevel 2>/dev/null) && [ -f \"$root/.github/hooks/doflow-mcp-tool-guard.sh\" ] && printf '%s/.github/hooks/doflow-mcp-tool-guard.sh' \"$root\" || printf '%s/hooks/doflow-mcp-tool-guard.sh' \"${COPILOT_HOME:-$HOME/.copilot}\")\"",
|
|
@@ -16,16 +16,6 @@
|
|
|
16
16
|
},
|
|
17
17
|
"timeout": 10
|
|
18
18
|
},
|
|
19
|
-
{
|
|
20
|
-
"name": "doflow-pre-bash-guard",
|
|
21
|
-
"trigger": "preToolUse",
|
|
22
|
-
"matcher": "shell",
|
|
23
|
-
"action": {
|
|
24
|
-
"type": "command",
|
|
25
|
-
"command": "bash \"$(root=$(git rev-parse --show-toplevel 2>/dev/null) && [ -f \"$root/.kiro/hooks/pre-bash-guard.sh\" ] && printf '%s/.kiro/hooks/pre-bash-guard.sh' \"$root\" || printf '%s/.kiro/hooks/pre-bash-guard.sh' \"$HOME\")\""
|
|
26
|
-
},
|
|
27
|
-
"timeout": 10
|
|
28
|
-
},
|
|
29
19
|
{
|
|
30
20
|
"name": "doflow-mcp-tool-guard",
|
|
31
21
|
"trigger": "preToolUse",
|
|
@@ -19,16 +19,6 @@
|
|
|
19
19
|
},
|
|
20
20
|
"enabled": true
|
|
21
21
|
},
|
|
22
|
-
{
|
|
23
|
-
"name": "doflow-pre-bash-guard",
|
|
24
|
-
"trigger": "PreToolUse",
|
|
25
|
-
"matcher": "shell",
|
|
26
|
-
"action": {
|
|
27
|
-
"type": "command",
|
|
28
|
-
"command": "bash \"$(root=$(git rev-parse --show-toplevel 2>/dev/null) && [ -f \"$root/.kiro/hooks/pre-bash-guard.sh\" ] && printf '%s/.kiro/hooks/pre-bash-guard.sh' \"$root\" || printf '%s/.kiro/hooks/pre-bash-guard.sh' \"$HOME\")\""
|
|
29
|
-
},
|
|
30
|
-
"enabled": true
|
|
31
|
-
},
|
|
32
22
|
{
|
|
33
23
|
"name": "doflow-mcp-tool-guard",
|
|
34
24
|
"trigger": "PreToolUse",
|
|
@@ -11,11 +11,15 @@ payload=$(kiro_bridge_payload <<<"$raw")
|
|
|
11
11
|
event="PreToolUse"
|
|
12
12
|
if command -v jq >/dev/null 2>&1; then event=$(printf '%s' "$raw" | jq -r '.hook_event_name // "PreToolUse"' 2>/dev/null) || event="PreToolUse"; fi
|
|
13
13
|
bridge="$here/../../.doflow/shared/hooks/bridge/bridge.sh"
|
|
14
|
-
|
|
14
|
+
# D1: a missing bridge allows with a warning on stderr. Only strict-1, a person's opt-in, blocks.
|
|
15
|
+
bridge_missing=0
|
|
16
|
+
[ -f "$bridge" ] || bridge_missing=1
|
|
17
|
+
if [ "$bridge_missing" -eq 0 ]; then . "$bridge" 2>/dev/null || bridge_missing=1; fi
|
|
18
|
+
if [ "$bridge_missing" -eq 1 ]; then
|
|
15
19
|
printf '%s\n' '[doflow] shared hooks lack the bridge; run: doflow update -t kiro' >&2
|
|
16
|
-
exit 2
|
|
20
|
+
if [ "${DOFLOW_FAULT_PROFILE:-}" = strict-1 ]; then exit 2; fi
|
|
21
|
+
exit 0
|
|
17
22
|
fi
|
|
18
|
-
. "$bridge" 2>/dev/null || { printf '%s\n' '[doflow] shared hooks lack the bridge; run: doflow update -t kiro' >&2; exit 2; }
|
|
19
23
|
if doflow_bridge kiro "$event" mcp-tool-guard <<<"$payload"; then
|
|
20
24
|
status=0
|
|
21
25
|
else
|
|
@@ -11,11 +11,15 @@ payload=$(kiro_bridge_payload <<<"$raw")
|
|
|
11
11
|
event="PreToolUse"
|
|
12
12
|
if command -v jq >/dev/null 2>&1; then event=$(printf '%s' "$raw" | jq -r '.hook_event_name // "PreToolUse"' 2>/dev/null) || event="PreToolUse"; fi
|
|
13
13
|
bridge="$here/../../.doflow/shared/hooks/bridge/bridge.sh"
|
|
14
|
-
|
|
14
|
+
# D1: a missing bridge allows with a warning on stderr. Only strict-1, a person's opt-in, blocks.
|
|
15
|
+
bridge_missing=0
|
|
16
|
+
[ -f "$bridge" ] || bridge_missing=1
|
|
17
|
+
if [ "$bridge_missing" -eq 0 ]; then . "$bridge" 2>/dev/null || bridge_missing=1; fi
|
|
18
|
+
if [ "$bridge_missing" -eq 1 ]; then
|
|
15
19
|
printf '%s\n' '[doflow] shared hooks lack the bridge; run: doflow update -t kiro' >&2
|
|
16
|
-
exit 2
|
|
20
|
+
if [ "${DOFLOW_FAULT_PROFILE:-}" = strict-1 ]; then exit 2; fi
|
|
21
|
+
exit 0
|
|
17
22
|
fi
|
|
18
|
-
. "$bridge" 2>/dev/null || { printf '%s\n' '[doflow] shared hooks lack the bridge; run: doflow update -t kiro' >&2; exit 2; }
|
|
19
23
|
if doflow_bridge kiro "$event" pre-implementation-gate <<<"$payload"; then
|
|
20
24
|
status=0
|
|
21
25
|
else
|
|
@@ -6,7 +6,6 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
const BRIDGE = new URL('../../shared/hooks/bridge/bridge.sh', import.meta.url);
|
|
7
7
|
const ROUTES = [
|
|
8
8
|
{ tools: ['edit', 'write'], policy: 'pre-implementation-gate' },
|
|
9
|
-
{ tools: ['bash'], policy: 'pre-bash-guard' },
|
|
10
9
|
];
|
|
11
10
|
const LIMIT = 1024 * 1024;
|
|
12
11
|
const TIMEOUT_MS = 5000;
|
|
@@ -136,7 +135,9 @@ export default function (pi) {
|
|
|
136
135
|
if (profile !== 'compat-1' && profile !== 'strict-1') captureFailure(policy, 'fault-profile-unknown');
|
|
137
136
|
if (!existsSync(BRIDGE)) {
|
|
138
137
|
captureFailure(policy, 'bridge-missing');
|
|
139
|
-
|
|
138
|
+
// D1: a missing bridge allows (the failure is recorded for doctor); only strict-1, a person's opt-in, blocks.
|
|
139
|
+
if (profile === 'strict-1') return { block: true, reason: '[doflow] shared hooks lack the bridge; run: doflow update -t pi' };
|
|
140
|
+
return undefined;
|
|
140
141
|
}
|
|
141
142
|
let model;
|
|
142
143
|
try {
|
|
@@ -36,7 +36,7 @@ them through), but sending nothing for a non-edit tool is equally correct and ch
|
|
|
36
36
|
## Version and mixed-install rules
|
|
37
37
|
|
|
38
38
|
- A missing envelope or version, empty or non-object input, or a missing `contract.sh` is out of
|
|
39
|
-
step. The gate
|
|
39
|
+
step. The gate and MCP guard deny with `DoFlow hooks out of step; run: doflow update
|
|
40
40
|
-t <harness>`; Stop and context policies exit silently, except the prompt hook emits its `{}`
|
|
41
41
|
no-op. Other envelope versions are unsupported and policies evaluate nothing.
|
|
42
42
|
- If a door cannot load `bridge/bridge.sh`, it renders its fixed native out-of-step response. The
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* SessionStart and PostToolUse have no dedicated Antigravity-native shim in
|
|
22
22
|
* src/adapters/antigravity/index.js's live projection. No Phase A canonical policy owns PostToolUse today (only
|
|
23
|
-
* session-context, pre-implementation-gate, mcp-tool-guard, stop-check
|
|
23
|
+
* session-context, pre-implementation-gate, mcp-tool-guard, stop-check exist),
|
|
24
24
|
* so its native shape is an ack passthrough either way.
|
|
25
25
|
*/
|
|
26
26
|
|
|
@@ -42,31 +42,19 @@ doflow_bridge_effect() {
|
|
|
42
42
|
profile=compat-1; doflow_bridge_failure "$policy" fault-profile-unknown
|
|
43
43
|
fi
|
|
44
44
|
DF_BRIDGE_PROFILE="$profile"
|
|
45
|
-
case "$policy" in pre-implementation-gate|
|
|
45
|
+
case "$policy" in pre-implementation-gate|mcp-tool-guard|stop-check) block=1;; esac
|
|
46
46
|
DF_BRIDGE_EFFECT=proceed
|
|
47
47
|
if [ "$outcome" = deny ]; then DF_BRIDGE_EFFECT=block
|
|
48
48
|
elif [ "$profile" = strict-1 ] && [ "$block" -eq 1 ] && [ "$policy" != stop-check ] && { [ "$outcome" = error ] || [ "$outcome" = unsupported ] || [ "$DF_BRIDGE_CODE" = bridge-spawn ] || [ "$DF_BRIDGE_CODE" = envelope-encode ]; }; then DF_BRIDGE_EFFECT=block
|
|
49
|
-
elif [ "$outcome" = error ] && [ "$DF_BRIDGE_CODE" != bridge-spawn ] && [ -n "$DF_BRIDGE_POINT" ] && [ "$profile" = compat-1 ]; then
|
|
50
|
-
case "$harness:$event" in
|
|
51
|
-
claude:PreToolUse|codex:PreToolUse|copilot:PreToolUse|pi:tool_call) DF_BRIDGE_EFFECT=block ;;
|
|
52
|
-
antigravity:PreToolUse|antigravity:Stop) case "$DF_BRIDGE_CODE" in signal-*) DF_BRIDGE_EFFECT=proceed;; *) DF_BRIDGE_EFFECT=block;; esac ;;
|
|
53
|
-
kiro:PreToolUse) [ "$DF_BRIDGE_CODE" = exit-2-unmarked ] && DF_BRIDGE_EFFECT=block ;;
|
|
54
|
-
claude:Stop) [ "$DF_BRIDGE_CODE" = exit-2-unmarked ] && DF_BRIDGE_EFFECT=block ;;
|
|
55
|
-
codex:Stop|codex:stop|copilot:Stop|kiro:Stop|kiro:stop) [ "$DF_BRIDGE_CODE" = exit-2-unmarked ] && DF_BRIDGE_EFFECT=block || DF_BRIDGE_EFFECT=proceed ;;
|
|
56
|
-
esac
|
|
57
|
-
fi
|
|
58
|
-
# Copilot CLI: a guarded preToolUse policy that errors or cannot decide (jq or the policy script
|
|
59
|
-
# missing, an edit with no decodable target) denies whatever the fault profile; only a policy that
|
|
60
|
-
# answered allow or advisory lets the call through. Copilot-only: every other harness keeps the
|
|
61
|
-
# profile rules above byte for byte.
|
|
62
|
-
if [ "$block" -eq 1 ] && [ "$DF_BRIDGE_EFFECT" = proceed ] && [ "$harness:$event" = copilot:PreToolUse ] && [ "$policy" != stop-check ]; then
|
|
63
|
-
case "$outcome" in error|unsupported) DF_BRIDGE_EFFECT=block ;; esac
|
|
64
49
|
fi
|
|
50
|
+
# D1 (advise-only hooks): under compat-1 a faulting policy never blocks, on any harness. Only a policy
|
|
51
|
+
# that answered deny (mcp-tool-guard blocked-pattern) blocks, and strict-1, a person's opt-in, blocks
|
|
52
|
+
# on a fault of a blocking policy (above).
|
|
65
53
|
}
|
|
66
54
|
|
|
67
55
|
doflow_bridge_drops_tool_streams() {
|
|
68
56
|
case "$1" in
|
|
69
|
-
pre-implementation-gate|
|
|
57
|
+
pre-implementation-gate|mcp-tool-guard) ;;
|
|
70
58
|
*) return 1 ;;
|
|
71
59
|
esac
|
|
72
60
|
case "$2:$3" in
|
|
@@ -77,7 +65,7 @@ doflow_bridge_drops_tool_streams() {
|
|
|
77
65
|
|
|
78
66
|
doflow_bridge_drops_tool_stderr() {
|
|
79
67
|
case "$1:$2:$3" in
|
|
80
|
-
pre-implementation-gate:kiro:PreToolUse|
|
|
68
|
+
pre-implementation-gate:kiro:PreToolUse|mcp-tool-guard:kiro:PreToolUse) return 1 ;;
|
|
81
69
|
esac
|
|
82
70
|
doflow_bridge_drops_tool_streams "$1" "$2" "$3"
|
|
83
71
|
}
|
|
@@ -93,7 +81,7 @@ doflow_bridge_hook_line() {
|
|
|
93
81
|
[ "${DOFLOW_TELEMETRY:-}" != off ] || return 0
|
|
94
82
|
case "$DF_TUPLE" in ''|'{"dft":1}') return 0 ;; esac
|
|
95
83
|
case "$1:$3:$2" in
|
|
96
|
-
claude:
|
|
84
|
+
claude:user-prompt-submit:UserPromptSubmit|codex:user-prompt-submit:UserPromptSubmit) ;;
|
|
97
85
|
*) return 0 ;;
|
|
98
86
|
esac
|
|
99
87
|
if [ -n "${DOFLOW_CONFIG_DIR:-}" ]; then
|
|
@@ -145,7 +133,7 @@ doflow_bridge_hook_line() {
|
|
|
145
133
|
}
|
|
146
134
|
|
|
147
135
|
doflow_bridge() {
|
|
148
|
-
local harness="${1:-}" event="${2:-}" policy="${3:-}" response_event="" emit="native" payload="" encoded="" policy_path="" captured="" err="" code=0 policy_code=0 record="" emit_record="" reason="" decision_reason="" stdout="" status=0 decision="" line="" spawn_failed=0 encode_failed=0 _df_t0=""
|
|
136
|
+
local harness="${1:-}" event="${2:-}" policy="${3:-}" response_event="" emit="native" payload="" encoded="" policy_path="" captured="" err="" code=0 policy_code=0 record="" emit_record="" reason="" decision_reason="" stdout="" status=0 decision="" advice="" out_of_step=0 line="" spawn_failed=0 encode_failed=0 _df_t0=""
|
|
149
137
|
DF_TUPLE=""
|
|
150
138
|
if [ "${BASH_VERSINFO[0]:-0}" -ge 5 ] 2>/dev/null; then _df_t0=${EPOCHREALTIME:-}; fi
|
|
151
139
|
. "$_DOFLOW_BRIDGE_DIR/respond.sh"
|
|
@@ -181,12 +169,24 @@ doflow_bridge() {
|
|
|
181
169
|
doflow_bridge_failure "$policy" dependency-missing:jq
|
|
182
170
|
fi
|
|
183
171
|
case "$policy" in
|
|
184
|
-
pre-implementation-gate|
|
|
172
|
+
pre-implementation-gate|mcp-tool-guard|stop-check|session-context|user-prompt-submit|subagent-audit) ;;
|
|
185
173
|
*) spawn_failed=1 ;;
|
|
186
174
|
esac
|
|
187
175
|
policy_path="$_DOFLOW_BRIDGE_DIR/../policies/$policy.sh"
|
|
188
|
-
if [ "$spawn_failed" -eq 1 ] || [ ! -f "$policy_path" ]; then
|
|
176
|
+
if [ "$spawn_failed" -eq 1 ] || [ ! -f "$policy_path" ] || [ ! -f "$_DOFLOW_BRIDGE_DIR/../policies/contract.sh" ]; then
|
|
189
177
|
code=127; err=""; spawn_failed=1
|
|
178
|
+
# Without contract.sh no policy can run. The gate and the tool guard still say so: the one
|
|
179
|
+
# out-of-step line goes out as advice below, and the fault is recorded as bridge-spawn.
|
|
180
|
+
case "$policy" in
|
|
181
|
+
pre-implementation-gate|mcp-tool-guard)
|
|
182
|
+
if [ -f "$policy_path" ] && [ ! -f "$_DOFLOW_BRIDGE_DIR/../policies/contract.sh" ]; then
|
|
183
|
+
out_of_step="${DOFLOW_AGENT:-$harness}"
|
|
184
|
+
if [[ "$out_of_step" =~ ^[a-z]+$ ]]; then err="[$policy] DoFlow hooks out of step; run: doflow update -t $out_of_step"$'\n'
|
|
185
|
+
else err="[$policy] DoFlow hooks out of step; run: doflow update"$'\n'; fi
|
|
186
|
+
out_of_step=1
|
|
187
|
+
fi
|
|
188
|
+
;;
|
|
189
|
+
esac
|
|
190
190
|
else
|
|
191
191
|
if [ "$emit" = decision ] || doflow_bridge_drops_tool_streams "$policy" "$harness" "$response_event" || { [ "$harness" = kiro ] && [ "$response_event" = Stop ]; }; then exec 5>/dev/null; else exec 5>&1; fi
|
|
192
192
|
captured="$( {
|
|
@@ -248,7 +248,9 @@ doflow_bridge() {
|
|
|
248
248
|
status=0
|
|
249
249
|
if [ "$DF_BRIDGE_OUTCOME" = error ]; then
|
|
250
250
|
case "$policy:$harness:$response_event" in
|
|
251
|
-
session-context:*:*|user-prompt-submit:*:*|subagent-audit
|
|
251
|
+
session-context:*:*|user-prompt-submit:*:*|subagent-audit:*:*) status="$code" ;;
|
|
252
|
+
# The blocking-set policies are advise-only (D1): a proceed effect must not let a raw exit 2 block.
|
|
253
|
+
*:kiro:PreToolUse|*:claude:Stop) case "$policy" in pre-implementation-gate|mcp-tool-guard|stop-check) status=0 ;; *) status="$code" ;; esac ;;
|
|
252
254
|
esac
|
|
253
255
|
fi
|
|
254
256
|
fi
|
|
@@ -258,14 +260,22 @@ doflow_bridge() {
|
|
|
258
260
|
local render_status=$?
|
|
259
261
|
return "$render_status"
|
|
260
262
|
fi
|
|
263
|
+
# D1: an advisory (or out-of-step) line from a blocking-set policy is shown where the harness shows it.
|
|
264
|
+
advice=""
|
|
265
|
+
if [ "$DF_BRIDGE_EFFECT" = proceed ] && { [ "$DF_BRIDGE_OUTCOME" = advisory ] || [ "$DF_BRIDGE_OUTCOME" = unsupported ] || [ "$out_of_step" = 1 ]; } && [ -n "$err" ]; then
|
|
266
|
+
case "$policy:$harness:$response_event" in
|
|
267
|
+
pre-implementation-gate:claude:PreToolUse|pre-implementation-gate:codex:PreToolUse|mcp-tool-guard:claude:PreToolUse|mcp-tool-guard:codex:PreToolUse|stop-check:claude:Stop|stop-check:codex:Stop|stop-check:codex:stop)
|
|
268
|
+
advice="${err%$'\n'}"; advice="${advice//$'\n'/ }" ;;
|
|
269
|
+
esac
|
|
270
|
+
fi
|
|
261
271
|
case "$policy:$DF_BRIDGE_OUTCOME:$harness:$response_event:$DF_BRIDGE_CODE" in
|
|
262
272
|
session-context:error:*:*:*|user-prompt-submit:error:*:*:*|subagent-audit:error:*:*:*|session-context:unsupported:*:*:*|user-prompt-submit:unsupported:*:*:*|subagent-audit:unsupported:*:*:*) ;;
|
|
263
273
|
*:error:codex:Stop:exit-*|*:error:codex:Stop:signal-*|*:error:copilot:Stop:exit-*|*:error:copilot:Stop:signal-*|*:error:codex:stop:exit-*|*:error:codex:stop:signal-*|*:error:kiro:PreToolUse:exit-*|*:error:kiro:PreToolUse:signal-*|*:error:kiro:Stop:exit-*|*:error:kiro:Stop:signal-*|*:error:claude:Stop:exit-*|*:error:claude:Stop:signal-*) ;;
|
|
264
274
|
*)
|
|
265
|
-
if ! doflow_bridge_drops_tool_stderr "$policy" "$harness" "$response_event" && [ -n "$err" ]; then printf '%s' "$err" >&2; fi
|
|
275
|
+
if [ -z "$advice" ] && ! doflow_bridge_drops_tool_stderr "$policy" "$harness" "$response_event" && [ -n "$err" ]; then printf '%s' "$err" >&2; fi
|
|
266
276
|
;;
|
|
267
277
|
esac
|
|
268
|
-
doflow_render_response "$harness" "$event" "$DF_BRIDGE_OUTCOME" "$reason" "$status"
|
|
278
|
+
doflow_render_response "$harness" "$event" "$DF_BRIDGE_OUTCOME" "$reason" "$status" "$advice"
|
|
269
279
|
local render_status=$?
|
|
270
280
|
return "$render_status"
|
|
271
281
|
}
|
|
@@ -103,7 +103,7 @@ def dft_tuple($native; $d):
|
|
|
103
103
|
if . == {dft:1} then . else
|
|
104
104
|
(if $ARGS.named.mode == "observe" then . + {duration_ms:null, tool_duration_ms:($native.duration_ms |
|
|
105
105
|
if type == "number" and . >= 0 and . <= 9007199254740991 and . == floor then . else null end)} else . + {tool_duration_ms:null} end) |
|
|
106
|
-
if
|
|
106
|
+
if $ARGS.named.mode == "observe" and $d.operation == "command"
|
|
107
107
|
then . + {test_class:classify($d.command)} else . + {test_class:null} end
|
|
108
108
|
end) catch {dft:1}] | if length == 1 then .[0] else {dft:1} end;
|
|
109
109
|
if ($ARGS.named.mode // "encode") == "minimal" then {doflow_event:{version:(1),harness:$harness,event:(event_name($event)),operation:event_operation($event)}} elif type != "object" then . else
|
|
@@ -61,6 +61,12 @@ doflow_render_response() {
|
|
|
61
61
|
*) printf '%s\n' "$reason" >&2; return 2 ;;
|
|
62
62
|
esac
|
|
63
63
|
fi
|
|
64
|
+
if [ -n "${6:-}" ]; then
|
|
65
|
+
case "$harness:$event" in
|
|
66
|
+
claude:PreToolUse|codex:PreToolUse) emit_pretooluse_advice "$6" || return $?; return 0 ;;
|
|
67
|
+
claude:Stop|codex:Stop|codex:stop) emit_stop_advice "$6" || return $?; return 0 ;;
|
|
68
|
+
esac
|
|
69
|
+
fi
|
|
64
70
|
case "$harness:$event" in
|
|
65
71
|
codex:Stop|codex:stop) printf '%s\n' '{}' ;;
|
|
66
72
|
antigravity:PreToolUse) printf '%s\n' '{"decision":"allow"}' ;;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# example its pattern file is missing). It exists only to be sourced, in a subshell with all output
|
|
7
7
|
# discarded, from inside a branch where the policy already fails open:
|
|
8
8
|
#
|
|
9
|
-
# ( . "<policy-dir>/capture-failure.sh"; doflow_capture_failure
|
|
9
|
+
# ( . "<policy-dir>/capture-failure.sh"; doflow_capture_failure mcp-tool-guard contract-missing ) >/dev/null 2>&1 || true
|
|
10
10
|
#
|
|
11
11
|
# The subshell keeps anything this file does away from the policy, which runs with `set -uo pipefail`,
|
|
12
12
|
# does not source lib.sh and would change exit codes under `set -e`. So this file:
|
|
@@ -21,6 +21,14 @@ doflow_compact_text() {
|
|
|
21
21
|
"$preserve" "${CWD:-unknown}")
|
|
22
22
|
fi
|
|
23
23
|
|
|
24
|
+
# Malformed UTF-8 (a commit subject or path can carry any byte) becomes U+FFFD here, before the cap, so the
|
|
25
|
+
# Claude (raw stdout) and Codex (jq --arg) outputs decode to the same text and the cap counts the bytes sent.
|
|
26
|
+
# jq is a hard requirement of both hooks; without it the text is left as it was (FU-535bfv).
|
|
27
|
+
if command -v jq >/dev/null 2>&1; then
|
|
28
|
+
local clean
|
|
29
|
+
if clean=$(printf '%s' "$text" | jq -Rsj '.' 2>/dev/null); then text="$clean"; fi
|
|
30
|
+
fi
|
|
31
|
+
|
|
24
32
|
# The cap is 490 BYTES in every locale: LC_ALL=C makes the substring and length below count bytes,
|
|
25
33
|
# where a UTF-8 locale would count characters. A cut can then end inside a multi-byte character, so
|
|
26
34
|
# a trailing partial UTF-8 sequence is dropped (FU-tsq77m).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
3
|
-
# deny-json.sh — shared PreToolUse deny-JSON emitters for front-door dispatchers.
|
|
3
|
+
# deny-json.sh — shared PreToolUse deny-JSON and advice-JSON emitters for front-door dispatchers.
|
|
4
4
|
#
|
|
5
5
|
# Deliberately carries no `set` directive (unlike lib.sh, which sets -euo
|
|
6
6
|
# pipefail on source): a front door needs `set -e` OFF while it captures a
|
|
@@ -53,3 +53,25 @@ emit_pretooluse_deny_nested() {
|
|
|
53
53
|
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":%s}}\n' "$(_doflow_json_reason "$reason")"
|
|
54
54
|
fi
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
# Advice emitters (D1, advise-only hooks). An advisory outcome never changes a decision; these put its
|
|
58
|
+
# one line where a harness shows it. Claude/Codex PreToolUse: additionalContext reaches the model, and
|
|
59
|
+
# carries no permissionDecision, so the user's own permission prompt still applies. Claude/Codex Stop:
|
|
60
|
+
# systemMessage is shown to the user and, unlike additionalContext, does not continue the turn.
|
|
61
|
+
emit_pretooluse_advice() {
|
|
62
|
+
local line="$1"
|
|
63
|
+
if command -v jq >/dev/null 2>&1; then
|
|
64
|
+
jq -n --arg line "$line" '{hookSpecificOutput: {hookEventName: "PreToolUse", additionalContext: $line}}'
|
|
65
|
+
else
|
|
66
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":%s}}\n' "$(_doflow_json_reason "$line")"
|
|
67
|
+
fi
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
emit_stop_advice() {
|
|
71
|
+
local line="$1"
|
|
72
|
+
if command -v jq >/dev/null 2>&1; then
|
|
73
|
+
jq -n --arg line "$line" '{systemMessage: $line}'
|
|
74
|
+
else
|
|
75
|
+
printf '{"systemMessage":%s}\n' "$(_doflow_json_reason "$line")"
|
|
76
|
+
fi
|
|
77
|
+
}
|