feat(ambient): first-word keyword detection in preamble hook#235
Conversation
Extends the preamble UserPromptSubmit hook with a second detection path
alongside the existing 3-marker plan detection. When a prompt's first
word (case-insensitive) is one of implement/explore/research/debug/plan,
followed by at least one more word and not ending in '?', the hook emits
a directive telling the model to announce then invoke the matching
devflow:<keyword> skill via the Skill tool.
Key implementation properties:
- Pure bash 3.2 (no ${var,,}/tr): uses shopt nocasematch + lowercase case arms
- O(1) keyword scan: HEAD="${PROMPT:0:256}" bounds the hot-path work
- No subprocesses on the keyword path: pure parameter expansion
- Question guard uses "$PROMPT =~ [?][[:space:]]*$" (avoids bash 3.2
negative-offset-empty-string bug with "${PROMPT: -N}" on short strings)
- Keyword path takes precedence; marker path fires only when keyword path
does not match; both share the single devflow ambient toggle
- No file I/O on either path; stdout is empty on no-match (zero bytes)
Tests (shell-hooks.test.ts): 4 new suites, 37 new test cases
- Suite 1 (F1–F11): functionality table — match/no-match cases
- Suite 2 (C1–C7): API contract — schema, zero-byte empty, exit codes,
no file I/O, bash-3.2 guard, injection safety
- Suite 3 (C7): security/fuzz — 8 hostile payloads assert output equals
the fixed template only (no user text reaches additionalContext)
- Suite 4 (P1–P3): performance — no-subprocess static check + wall-time
length-independence assertion (delta < 500ms, ratio < 5×)
Docs: CLAUDE.md Ambient Mode paragraph and devflow-ambient README updated
to describe both detection paths and command auto-trigger coverage.
…ualify the out variable in the C7 test to reflect immutable usage. Co-Authored-By: Claude <noreply@anthropic.com>
Line 3-5: Stale File HeaderThe file header still describes plan-detection-only behavior: After this PR, the hook also fires on first-word keywords (implement/explore/research/debug/plan), so "only fires when all three plan markers are present" is now incorrect. Suggested fix: # Devflow Preamble: UserPromptSubmit Hook
# Two coexisting detection paths: (1) first-word workflow keyword (implement/explore/research/debug/plan)
# → invoke devflow:<keyword>; (2) structured plan (## Goal + ## Steps + ## Files) → devflow:implement.
# Zero overhead for normal prompts — emits nothing when neither path fires.Confidence: 95% | Flagged by: consistency, documentation, regression reviewers |
Line 60-62: Inaccurate Comment on Regex EfficiencyThe inline comment claims the # Guard B: skip prompts ending in '?' (the $ anchor makes this efficient — bash regex
# does not scan the full string for a trailing match; no subprocess required).
if [[ -n "$SKILL" && -n "$REST" ]] && ! [[ "$PROMPT" =~ [?][[:space:]]*$ ]]; thenThis is factually incorrect. POSIX ERE matching in bash 3.2 walks the entire string to establish match positions. The Impact: The comment will mislead future maintainers and contradicts the bounding intent established at line 40 ( Suggested fix: # Guard B: skip prompts ending in '?' (uses full $PROMPT, not $HEAD, because the trailing '?'
# lives at the END of the prompt, beyond the 256-byte keyword window). Regex is O(n) in bash 3.2.
# Only runs when $SKILL matched, so normal prompts are unaffected.Confidence: 95% | Flagged by: performance reviewer | Risk: HIGH (affects understanding of the hot path) |
README Line 3: Stale TaglineThe README's one-line summary now contradicts the detailed feature description that immediately follows: Current tagline:
What the README actually documents (lines 15-58): Two coexisting detection paths — first-word keyword dispatch AND structured plan detection. A reader skimming the top of the README gets the wrong scope. This is code-doc drift in a file this PR modifies heavily. Suggested fix: Confidence: 88% | Flagged by: documentation reviewer |
Line 64 vs 67: Divergent Directive-Text StyleThe two detection paths emit stylistically inconsistent directives for the same purpose (instructing the model to invoke a Keyword path (line 64): Natural-language announcement: "The user is invoking the `$SKILL` workflow. Briefly tell the user you are invoking `devflow:$SKILL`, then invoke it..."Plan path (line 67): Terse marker token: "EXECUTION_PLAN detected. Invoke `devflow:implement` via the Skill tool to execute this plan."The keyword path announces to the user first, while the plan path leaks an internal token ( Suggested fix: json_prompt_output "The user supplied a structured implementation plan. Briefly tell the user you are invoking \`devflow:implement\`, then invoke it via the Skill tool to execute this plan."Low risk: no test asserts the exact plan-path string except Confidence: 85% | Flagged by: consistency reviewer |
PR Summary: Review CompleteOverall Recommendation: ✅ APPROVED_WITH_CONDITIONS Inline Comments CreatedFour inline comments have been posted above, all at >=80% confidence:
All four are should-fix items requiring minor documentation updates. No blocking issues across any dimension. Non-Blocking Informational Items (60-79% confidence)Note 1: ADR-013 is now stale relative to shipped code
Note 2: Test coverage gaps (non-blocking)
Scoring Summary
Key Findings: What Works Well
What Needs Fixing Before Merge
All four fixes are one-liners or small multi-line comments. No architectural changes needed. Review conducted by: devflow code-review reviewers (security, architecture, performance, complexity, consistency, regression, testing, reliability, typescript, documentation) |
…ne previously only mentioned plan auto-detection. PR #235 added first-word keyword dispatch as a coequal detection path -- tagline now reflects both the keyword -> devflow:<keyword> path and the structured plan -> devflow:implement path.
…ting design Shipped preamble hook has five keywords (implement/explore/research/debug/plan) and retains three-marker structured-plan detection as a coexisting elif fallback, not a replacement. ADR-013 body previously stated four keywords and that the keyword path "replaces" plan detection — both incorrect. Update prose to match actual behavior.
- Header: describe both detection paths (keyword dispatch + structured plan) instead of the now-stale "only fires when all three plan markers are present" - Guard B comment: retract the false claim that bash $ anchor enables tail-only scan; state plainly that the regex is O(n) over prompt length and runs against full $PROMPT (not $HEAD) because the trailing ? lives beyond the 256-byte window - Plan-path directive: align phrasing with keyword-path style, drop the internal EXECUTION_PLAN token from the user-facing text - plan case arm: add inline note distinguishing plan keyword (devflow:plan) from the structured-plan path (devflow:implement) All 163 shell-hooks tests pass. avoids PF-007 (source hook edited, not installed copy). Co-Authored-By: Claude <noreply@anthropic.com>
F12: assert that multi-char trailing punct (implement...) is NOT stripped to a
keyword — WORD="${TOKEN%[[:punct:]]}" removes exactly one char, leaving
"implement.." which does not case-match, so output is empty.
F13: assert that prompts with leading newlines/spaces (\\n\\n implement ...) DO
match — preamble:41 strips HEAD leading whitespace before extracting the first
token, so the keyword is correctly isolated.
Both cases verified against the running hook before asserting (applies ADR-014).
All 165 tests pass.
Co-Authored-By: Claude <noreply@anthropic.com>
preamble:41 pointed at a comment line and would drift on any edit. Replaced with a description of the behavior the test exercises.
…ghten preamble directives (#236) ## Summary Ambient mode gained first-word keyword detection in #235 (prompts starting with `implement`, `explore`, `research`, `debug`, or `plan` invoke the matching `devflow:<keyword>` skill). But the init flow, `devflow ambient` CLI text, plugin description, and README still described **plan-only** auto-detection — leaving the docs out of step with the two-path hook behavior. This PR aligns all user-facing copy and tightens the directives the `preamble` hook injects. ## Changes **Descriptions updated to cover both detection paths (keyword + structured plan):** - `src/cli/commands/init.ts` — ambient mode prompt note + `--ambient` flag help - `src/cli/commands/ambient.ts` — command description, usage note, enable-success message, and a stale code comment - `src/cli/plugins.ts` — ambient plugin description → "Keyword + plan auto-detection" - `README.md` — "Ambient intelligence" line now names the keyword start words and structured plans **Injected directives tightened (`scripts/hooks/preamble`):** - Symmetric structure across both paths (announce briefly → invoke via Skill tool → pass input) - Explicit input pass-through on **both** paths (the plan path previously never told the model to pass the plan) - "Immediately / do not pause to ask whether to proceed" to preserve ambient's zero-friction intent ## Notes - The `preamble` hook is copied to `~/.devflow/scripts/` at `devflow init` time, so the directive change takes effect after a re-init. - The plugin README and preamble header comments were already accurate — no change needed there. ## Verification - `npm run build:cli` — passes - `bash -n scripts/hooks/preamble` — syntax OK --------- Co-authored-by: Dean Sharon <deanshrn@gmain.com>
Summary
Implements first-word keyword detection in the
preamblehook to enable zero-overhead ambient workflow triggering. When a prompt begins withimplement,explore,research,debug, orplan(case-insensitive), the hook automatically invokes the matchingdevflow:<keyword>skill — eliminating friction for frequent workflows while incurring zero overhead for normal prompts.Changes
Hook Enhancement
?(questions don't trigger automation)Test Suite
C7test (hostile-tail) validates that keyword detection respects the question-mark edge caseBreaking Changes
None — this is a zero-opt-in enhancement. Existing behavior unchanged for normal prompts.
Testing
?correctly skip automationRelated Issues
Part of ambient mode enhancement (zero-overhead workflow acceleration)