omniconductor 0.3.0

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 (118) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +219 -0
  3. package/NOTICE +11 -0
  4. package/README.md +526 -0
  5. package/THIRD_PARTY_NOTICES.md +31 -0
  6. package/TRADEMARKS.md +26 -0
  7. package/VISION.md +106 -0
  8. package/adapters/README.md +121 -0
  9. package/adapters/claude/README.md +102 -0
  10. package/adapters/claude/SUPPORTED-FEATURES.md +66 -0
  11. package/adapters/claude/hookify-templates/.recipe-scoped +9 -0
  12. package/adapters/claude/hookify-templates/README.md +93 -0
  13. package/adapters/claude/hookify-templates/block-completion-claim-without-push.local.md.template +56 -0
  14. package/adapters/claude/hookify-templates/block-direct-push-protected-branch.local.md.template +18 -0
  15. package/adapters/claude/hookify-templates/block-force-push-protected-branch.local.md.template +42 -0
  16. package/adapters/claude/hookify-templates/block-server-secret-in-client.local.md.template +36 -0
  17. package/adapters/claude/hookify-templates/warn-any-type-added.local.md.template +44 -0
  18. package/adapters/claude/hookify-templates/warn-commit-without-pre-commit-review.local.md.template +52 -0
  19. package/adapters/claude/hookify-templates/warn-console-direct.local.md.template +55 -0
  20. package/adapters/claude/hookify-templates/warn-create-table-without-access-control.local.md.template +35 -0
  21. package/adapters/claude/hookify-templates/warn-current-work-without-remaining-tasks.local.md.template +36 -0
  22. package/adapters/claude/hookify-templates/warn-gh-pr-create-without-pre-merge-review.local.md.template +38 -0
  23. package/adapters/claude/hookify-templates/warn-hardcoded-text-without-i18n-key.local.md.template +21 -0
  24. package/adapters/claude/hookify-templates/warn-on-gh-pr-merge.local.md.template +33 -0
  25. package/adapters/claude/hookify-templates/warn-plan-spec-without-remaining-tasks.local.md.template +44 -0
  26. package/adapters/claude/hookify-templates/warn-raw-hex-instead-of-token.local.md.template +21 -0
  27. package/adapters/claude/hookify-templates/warn-security-definer-without-search-path.local.md.template +30 -0
  28. package/adapters/claude/hookify-templates/warn-stop-commit-without-current-work.local.md.template +47 -0
  29. package/adapters/claude/hookify-templates/warn-user-manual-completion.local.md.template +40 -0
  30. package/adapters/claude/transform-spec.md +140 -0
  31. package/adapters/claude/transform.sh +1169 -0
  32. package/adapters/codex/README.md +87 -0
  33. package/adapters/codex/SUPPORTED-FEATURES.md +66 -0
  34. package/adapters/codex/transform-spec.md +90 -0
  35. package/adapters/codex/transform.sh +748 -0
  36. package/adapters/copilot/README.md +88 -0
  37. package/adapters/copilot/SUPPORTED-FEATURES.md +73 -0
  38. package/adapters/copilot/transform-spec.md +111 -0
  39. package/adapters/copilot/transform.sh +757 -0
  40. package/adapters/cursor/README.md +87 -0
  41. package/adapters/cursor/SUPPORTED-FEATURES.md +90 -0
  42. package/adapters/cursor/transform-spec.md +124 -0
  43. package/adapters/cursor/transform.sh +731 -0
  44. package/adapters/gemini/README.md +93 -0
  45. package/adapters/gemini/SUPPORTED-FEATURES.md +80 -0
  46. package/adapters/gemini/transform-spec.md +102 -0
  47. package/adapters/gemini/transform.sh +844 -0
  48. package/adapters/windsurf/README.md +93 -0
  49. package/adapters/windsurf/SUPPORTED-FEATURES.md +67 -0
  50. package/adapters/windsurf/transform-spec.md +99 -0
  51. package/adapters/windsurf/transform.sh +727 -0
  52. package/bin/omniconductor.js +130 -0
  53. package/core/README.md +64 -0
  54. package/core/anti-patterns/README.md +142 -0
  55. package/core/anti-patterns/dynamic-system-prompt.md +113 -0
  56. package/core/anti-patterns/frequent-rule-file-edit.md +83 -0
  57. package/core/anti-patterns/large-file-read-no-range.md +90 -0
  58. package/core/anti-patterns/no-sub-agent-dispatch.md +89 -0
  59. package/core/anti-patterns/single-monolithic-rule-file.md +99 -0
  60. package/core/anti-patterns/skill-eager-load.md +83 -0
  61. package/core/anti-patterns/tool-call-spam.md +93 -0
  62. package/core/docs-templates/CURRENT_WORK.md +64 -0
  63. package/core/docs-templates/INDEX.md +57 -0
  64. package/core/docs-templates/PLANS.md +44 -0
  65. package/core/docs-templates/README.md +40 -0
  66. package/core/docs-templates/REMAINING_TASKS.md +43 -0
  67. package/core/docs-templates/TASKS.md +51 -0
  68. package/core/docs-templates/specs/_example.md +121 -0
  69. package/core/hooks/README.md +62 -0
  70. package/core/hooks/pretool-agent-routing.sh.template +97 -0
  71. package/core/hooks/pretool-commit-current-work-check.sh.template +103 -0
  72. package/core/hooks/pretool-commit-test-coverage-check.sh.template +116 -0
  73. package/core/hooks/pretool-large-file-read-guard.sh.template +117 -0
  74. package/core/hooks/stop-cache-hit-baseline-check.sh.template +133 -0
  75. package/core/hooks/stop-r6-review-check.sh.template +80 -0
  76. package/core/hooks/stop-session-log-check.sh.template +101 -0
  77. package/core/hooks/stop-trajectory-log.sh.template +96 -0
  78. package/core/memory-pattern/EXAMPLES.md +114 -0
  79. package/core/memory-pattern/README.md +133 -0
  80. package/core/recipes/README.md +46 -0
  81. package/core/recipes/auto-mock-data.md +82 -0
  82. package/core/recipes/branch-strategy.md +100 -0
  83. package/core/recipes/coding-conventions.md +123 -0
  84. package/core/recipes/database-discipline.md +65 -0
  85. package/core/recipes/debugging.md +143 -0
  86. package/core/recipes/design-system.md +30 -0
  87. package/core/recipes/i18n.md +118 -0
  88. package/core/recipes/monorepo.md +101 -0
  89. package/core/recipes/self-improvement.md +61 -0
  90. package/core/recipes/tdd.md +128 -0
  91. package/core/recipes/web-mobile-parity.md +87 -0
  92. package/core/reflector/SCHEDULING.md +83 -0
  93. package/core/reflector/prune-lessons.sh +105 -0
  94. package/core/reflector/reflect-brief.md +11 -0
  95. package/core/reflector/reflect.command.md +13 -0
  96. package/core/reflector/run-weekly.sh +54 -0
  97. package/core/reflector/trajectory-log.sh +55 -0
  98. package/core/roles/README.md +61 -0
  99. package/core/roles/builder.md +75 -0
  100. package/core/roles/designer.md +93 -0
  101. package/core/roles/helper.md +70 -0
  102. package/core/roles/planner.md +123 -0
  103. package/core/roles/reflector.md +72 -0
  104. package/core/roles/reviewer.md +104 -0
  105. package/core/roles/scribe.md +89 -0
  106. package/core/universal-rules/README.md +71 -0
  107. package/core/universal-rules/meta-discipline.md +292 -0
  108. package/core/universal-rules/operations.md +162 -0
  109. package/core/universal-rules/quality-gates.md +191 -0
  110. package/core/universal-rules/spec-as-you-go.md +123 -0
  111. package/core/universal-rules/workflow.md +153 -0
  112. package/core/workflow/PHASES.md +134 -0
  113. package/core/workflow/README.md +56 -0
  114. package/docs/MANUAL-INSTALL.md +456 -0
  115. package/package.json +52 -0
  116. package/tools/check-framework-purity.sh +91 -0
  117. package/tools/measure-tokens.sh +169 -0
  118. package/tools/validate-adapter-output.sh +611 -0
@@ -0,0 +1,121 @@
1
+ ---
2
+ area: <your-area> # e.g., auth, billing, email
3
+ owner: <you-or-team>
4
+ status: draft|active|archived
5
+ last_synced: YYYY-MM-DD
6
+ api_routes: # list of API route paths owned by this area
7
+ - /api/<your-area>/<route>
8
+ pages: # list of pages owned by this area
9
+ - /<your-area>
10
+ components: # list of components owned by this area
11
+ - <YourComponent>
12
+ services: # list of services owned by this area
13
+ - <yourAreaService>
14
+ hooks: # list of hooks owned by this area
15
+ - useYourArea
16
+ db_tables: # list of DB tables owned by this area
17
+ - <table_name>
18
+ ---
19
+
20
+ # Spec — `<your-area>` (PLACEHOLDER — copy + rename per area)
21
+
22
+ > **What this is**: single source-of-truth for everything in the `<your-area>` domain. Updated EVERY time code in this area changes (spec-as-you-go ABSOLUTE rule).
23
+
24
+ > **Status (P0 placeholder)**: copy this file, rename to your area, replace contents. P1 will provide a richer example.
25
+
26
+ ---
27
+
28
+ ## Overview
29
+
30
+ One paragraph: what is this area, what problem does it solve, what are the boundaries.
31
+
32
+ ---
33
+
34
+ ## User-facing flows
35
+
36
+ For each user flow:
37
+
38
+ ### Flow: <flow name>
39
+
40
+ 1. User does X.
41
+ 2. System responds Y.
42
+ 3. User sees Z.
43
+
44
+ **Edge cases**:
45
+ - What happens on error.
46
+ - What happens on empty state.
47
+ - What happens on permission denied.
48
+
49
+ ---
50
+
51
+ ## API
52
+
53
+ For each route:
54
+
55
+ ### `<METHOD> /api/<your-area>/<route>`
56
+
57
+ - **Auth**: required / optional / none.
58
+ - **Body**: schema.
59
+ - **Response**: schema (Result pattern: `{ data: T; error: null } | { data: null; error: string }`).
60
+ - **Errors**: list of error codes + meanings.
61
+
62
+ ---
63
+
64
+ ## UI
65
+
66
+ For each page or major component:
67
+
68
+ ### Page: `/<your-area>`
69
+
70
+ - **Layout**: brief description.
71
+ - **Components used**: list.
72
+ - **State**: what state the page manages.
73
+ - **Permissions**: who can access.
74
+
75
+ ---
76
+
77
+ ## Services
78
+
79
+ For each service function:
80
+
81
+ ### `<yourAreaService>.<method>()`
82
+
83
+ - **Input**: parameters.
84
+ - **Output**: Result-pattern return.
85
+ - **Side effects**: DB writes, external API calls, etc.
86
+
87
+ ---
88
+
89
+ ## DB Schema
90
+
91
+ For each table:
92
+
93
+ ### `<table_name>`
94
+
95
+ | Column | Type | Notes |
96
+ |---|---|---|
97
+ | id | uuid | PK |
98
+ | ... | ... | ... |
99
+
100
+ **Indexes**:
101
+ - ...
102
+
103
+ **RLS policies**:
104
+ - ...
105
+
106
+ ---
107
+
108
+ ## Known constraints / gotchas
109
+
110
+ - Limits, quotas, rate limits.
111
+ - Browser-specific behavior.
112
+ - Things future contributors must know.
113
+
114
+ ---
115
+
116
+ ## Recent changes
117
+
118
+ - YYYY-MM-DD: <what changed and why>.
119
+ - YYYY-MM-DD: ...
120
+
121
+ (Append-only; oldest at bottom; archive once it grows past 20 entries.)
@@ -0,0 +1,62 @@
1
+ # `core/hooks/` — Universal hook templates
2
+
3
+ CONDUCTOR's universal hook spec. Templates here are compiled into native shell scripts by adapters that natively support hooks (Claude Code as of v0.2). Adapters without native hook support transform these into rule-text reminders embedded in the tool's primary rule file.
4
+
5
+ ## The templates
6
+
7
+ | Template | Trigger | Action | Compile target |
8
+ |---|---|---|---|
9
+ | `pretool-agent-routing.sh.template` | Agent / sub-agent dispatch about to fire | Validate routing (no `general-purpose`, explicit model) | Claude `.claude/hooks/pretool-agent-routing.sh` |
10
+ | `pretool-commit-current-work-check.sh.template` | PreToolUse on Bash `git commit` | Soft `ask` warn — surfaces a reason at commit time when 3+ source files are staged but `CURRENT_WORK.md` is not part of the commit; non-blocking, orchestrator approves to proceed. Override env disables: `CONDUCTOR_SKIP_CURRENT_WORK_HOOK=1`. | Claude `.claude/hooks/pretool-commit-current-work-check.sh` |
11
+ | `pretool-commit-test-coverage-check.sh.template` | PreToolUse on Bash `git commit` | Soft `ask` warn (quality-gates Q3) — surfaces a reason when a newly-added feature-shaped source file has no new test (path with `test`/`spec`/`__tests__`/`e2e`) in the same commit; non-blocking, orchestrator approves to proceed. Override env disables: `CONDUCTOR_SKIP_TEST_COVERAGE_HOOK=1`. | Claude `.claude/hooks/pretool-commit-test-coverage-check.sh` |
12
+ | `pretool-large-file-read-guard.sh.template` | PreToolUse on the Read tool | **Block** a Read of files ≥ `${CONDUCTOR_LARGE_FILE_LINE_THRESHOLD}` (default 500) lines when no `offset`/`limit` is supplied; recommends range-read or Grep (token-economy / anti-pattern `large-file-read-no-range`). Fail-open. Override env disables: `CONDUCTOR_ALLOW_LARGE_READ=1`. | Claude `.claude/hooks/pretool-large-file-read-guard.sh` |
13
+ | `stop-session-log-check.sh.template` | Session stop event | Block stop if recent commits exist + CURRENT_WORK.md or specs are stale | Claude `.claude/hooks/stop-session-log-check.sh` |
14
+ | `stop-r6-review-check.sh.template` | Session stop event | Remind to run pre-merge review on open PR | Claude `.claude/hooks/stop-r6-review-check.sh` |
15
+ | `stop-cache-hit-baseline-check.sh.template` | Session stop event | Non-blocking diagnostic — reads the latest session JSONL, computes cache hit rate, reminds when below baseline (token-economy). Fail-open. Override env disables: `CONDUCTOR_SKIP_CACHE_CHECK=1`. | Claude `.claude/hooks/stop-cache-hit-baseline-check.sh` |
16
+ | `stop-trajectory-log.sh.template` | Session stop event | Non-blocking — reads `transcript_path` + `session_id` from the Stop hook's **stdin** (exact provenance; no `~/.claude/projects` dir-scan) and **upserts** one pointer record per session (session id, transcript path, git HEAD, cwd) into `.conductor/trajectories/index.jsonl` for the Reflector (recipes/self-improvement.md). Same stdin approach as the non-Claude portable logger `core/reflector/trajectory-log.sh`. **Opt-in gated: no-ops unless `.conductor/reflect/` exists** (created only by the self-improvement recipe). Anchors to the project root; fail-open. Override: `CONDUCTOR_SKIP_TRAJLOG=1`. | Claude `.claude/hooks/stop-trajectory-log.sh` |
17
+
18
+ The two `pretool-commit-*` templates are **soft `ask` warns**: they emit
19
+ `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":...}}`,
20
+ which surfaces the reason and routes to the permission flow — the orchestrator approves and
21
+ proceeds (or justifies). This is non-blocking and does NOT auto-approve (unlike
22
+ `permissionDecision: allow`, which would silently bypass the permission prompt). It mirrors
23
+ `pretool-agent-routing.sh.template`'s `deny`+reason shape (here `ask`+reason). They also fail
24
+ soft — any internal error exits 0. Each honors a per-commit override env var (above) for the
25
+ rare intentional exception.
26
+
27
+ ## Frontmatter convention
28
+
29
+ Templates use a comment-block frontmatter (because shell scripts don't read YAML at runtime — the adapter parses the comment block):
30
+
31
+ ```bash
32
+ #!/usr/bin/env bash
33
+ #
34
+ # CONDUCTOR universal hook template
35
+ # hook_id: <id>
36
+ # trigger: <pretool | stop>
37
+ # action: <description>
38
+ # compile_targets: claude
39
+ # fallback: rule-text-reminder
40
+ ```
41
+
42
+ ## Per-tool compilation
43
+
44
+ | Adapter | Compile behavior |
45
+ |---|---|
46
+ | Claude | `transform.sh` substitutes placeholders (paths, branch names, cool-down windows), writes to `.claude/hooks/<hook>.sh`, runs `chmod +x`. |
47
+ | Cursor / Copilot / Gemini / Codex / Windsurf | Hook compiles to a rule-text reminder added to the appropriate rule file. The reminder includes the trigger condition, the action, and the rationale. |
48
+
49
+ ## Placeholders
50
+
51
+ Templates use `${PLACEHOLDER}` syntax for values that the adapter substitutes at compile time:
52
+
53
+ | Placeholder | Replaced with |
54
+ |---|---|
55
+ | `${CONDUCTOR_PROJECT_DIR}` | Absolute path to the target project |
56
+ | `${CONDUCTOR_PROTECTED_BRANCHES}` | Pipe-separated list (e.g., `main\|release\|develop`) |
57
+ | `${CONDUCTOR_COOLDOWN_SECONDS}` | Cool-down between reminders (typical: 1800 = 30 min) |
58
+ | `${CONDUCTOR_STALE_MINUTES}` | Staleness threshold (typical: 30) |
59
+ | `${CONDUCTOR_SOURCE_GLOB}` | Alternation of source extensions (e.g., `ts\|tsx`) |
60
+ | `${CONDUCTOR_CURRENT_WORK_PATH}` | Session-log path (default `docs/CURRENT_WORK.md`) |
61
+
62
+ Placeholders that the adapter does not know how to fill are passed through unchanged (with a warning) so the user can edit them post-install.
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # CONDUCTOR universal hook template
4
+ # hook_id: pretool-agent-routing
5
+ # trigger: pretool
6
+ # action: Validate sub-agent dispatch — block forbidden subagent_type, require explicit model.
7
+ # compile_targets: claude
8
+ # fallback: rule-text-reminder
9
+ #
10
+ # Generated by adapters/<tool>/transform.sh.
11
+ # Tool-native semantics:
12
+ # - Claude Code: PreToolUse hook reads the tool-call payload from stdin (JSON)
13
+ # and validates Agent tool calls.
14
+ # - Other tools: this template is converted to rule-text by their adapter.
15
+ #
16
+ # Validations:
17
+ # V1. Agent tool MUST NOT use a forbidden subagent_type (e.g., "general-purpose"
18
+ # which defaults to a generic model and ignores project rules).
19
+ # Route to one of the 6 universal roles + project-specific roles instead.
20
+ # V2. Agent tool calls MUST include an explicit `model` parameter. The
21
+ # orchestrator is responsible for sizing tasks per
22
+ # universal-rules/meta-discipline.md section 6.
23
+ #
24
+ # Exit code meaning in PreToolUse hooks (Claude Code):
25
+ # 0 with no JSON output → allow
26
+ # 0 with {"permissionDecision":"deny"} → block with reason (shown to LLM)
27
+ # non-zero → allow (fail-open)
28
+
29
+ set -u
30
+
31
+ # ${CONDUCTOR_PROJECT_DIR} — absolute path to project root (filled by transform.sh).
32
+ # ${CONDUCTOR_FORBIDDEN_SUBAGENT_TYPES} — pipe-separated list (default: general-purpose).
33
+
34
+ # Read payload from stdin into env var so python can parse it safely.
35
+ export PAYLOAD_RAW="$(/bin/cat 2>/dev/null || true)"
36
+ [ -n "${PAYLOAD_RAW:-}" ] || exit 0
37
+
38
+ if ! command -v /usr/bin/python3 >/dev/null 2>&1; then
39
+ exit 0
40
+ fi
41
+
42
+ PARSED=$(/usr/bin/python3 -c "
43
+ import json, os, sys
44
+ try:
45
+ d = json.loads(os.environ.get('PAYLOAD_RAW', '{}'))
46
+ if d.get('tool_name', '') != 'Agent':
47
+ sys.exit(0)
48
+ inp = d.get('tool_input', {}) or {}
49
+ print('SUB=' + inp.get('subagent_type', ''))
50
+ print('MODEL=' + inp.get('model', ''))
51
+ except Exception:
52
+ sys.exit(0)
53
+ " 2>/dev/null)
54
+
55
+ [ -z "$PARSED" ] && exit 0
56
+
57
+ while IFS='=' read -r key val; do
58
+ case "$key" in
59
+ SUB) SUB="$val" ;;
60
+ MODEL) MODEL="$val" ;;
61
+ esac
62
+ done <<< "$PARSED"
63
+
64
+ REASONS=""
65
+
66
+ # V1 — block forbidden subagent_type.
67
+ # Default forbidden list: general-purpose. Override at compile time.
68
+ FORBIDDEN_RE="${CONDUCTOR_FORBIDDEN_SUBAGENT_TYPES:-general-purpose}"
69
+ if [ -n "${SUB:-}" ] && echo "${SUB}" | /usr/bin/grep -qE "^(${FORBIDDEN_RE})$"; then
70
+ REASONS="[ROUTING VIOLATION] subagent_type='${SUB}' is FORBIDDEN. Route to one of the registered roles in core/roles/ (planner, builder, reviewer, helper, designer, scribe) or a project-specific role. If no role fits, do the work in the orchestrator thread."
71
+ fi
72
+
73
+ # V2 — require explicit model parameter.
74
+ if [ -z "${MODEL:-}" ]; then
75
+ MSG="[ROUTING VIOLATION] Agent tool call is missing the 'model' parameter. Orchestrator must classify the task and pass model='opus' | 'sonnet' | 'haiku' explicitly per universal-rules/meta-discipline.md section 6."
76
+ if [ -n "$REASONS" ]; then
77
+ REASONS="$REASONS $MSG"
78
+ else
79
+ REASONS="$MSG"
80
+ fi
81
+ fi
82
+
83
+ [ -z "$REASONS" ] && exit 0
84
+
85
+ REASONS_JSON=$(REASONS="$REASONS" /usr/bin/python3 -c "
86
+ import json, os
87
+ print(json.dumps({
88
+ 'hookSpecificOutput': {
89
+ 'hookEventName': 'PreToolUse',
90
+ 'permissionDecision': 'deny',
91
+ 'permissionDecisionReason': os.environ.get('REASONS', ''),
92
+ }
93
+ }))
94
+ ")
95
+
96
+ echo "$REASONS_JSON"
97
+ exit 0
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # CONDUCTOR universal hook template
4
+ # hook_id: pretool-commit-current-work-check
5
+ # trigger: pretool
6
+ # action: Soft-warn at commit time when 3+ source files are staged but
7
+ # ${CONDUCTOR_CURRENT_WORK_PATH} is not part of the same commit.
8
+ # compile_targets: claude
9
+ # fallback: rule-text-reminder
10
+ #
11
+ # Generated by adapters/<tool>/transform.sh.
12
+ #
13
+ # Commit-time companion to stop-session-log-check.sh.template. Soft warn, never blocks.
14
+ #
15
+ # Enforces:
16
+ # - operations.md O2 (CURRENT_WORK.md must update when work happens) — caught at
17
+ # commit time instead of (only) at session stop.
18
+ #
19
+ # Behavior:
20
+ # 1. Parse PreToolUse stdin payload (same method as pretool-agent-routing).
21
+ # 2. If tool_name != Bash → allow (silent exit 0).
22
+ # 3. If the command is not a `git commit` → allow (silent exit 0).
23
+ # 4. Count staged source files matching ${CONDUCTOR_SOURCE_GLOB}.
24
+ # 5. If 3+ staged source files AND ${CONDUCTOR_CURRENT_WORK_PATH} is NOT staged
25
+ # → emit a soft confirmation (permissionDecision: ask + permissionDecisionReason).
26
+ # 6. Override env / any error → allow (silent exit 0).
27
+ #
28
+ # Override (skip this check for a commit):
29
+ # CONDUCTOR_SKIP_CURRENT_WORK_HOOK=1
30
+ #
31
+ # Exit code meaning in PreToolUse hooks (Claude Code):
32
+ # 0 with no JSON output → allow (silent)
33
+ # 0 with {"permissionDecision":"ask","permissionDecisionReason":...}
34
+ # → surface the reason and route to the permission flow; the orchestrator
35
+ # approves and proceeds (or justifies). Does NOT block and does NOT
36
+ # auto-approve. This is the documented non-blocking soft-warn and matches
37
+ # pretool-agent-routing.sh.template's deny+reason shape (here: ask+reason).
38
+ # 0 with {"permissionDecision":"deny"} → block with reason
39
+ # non-zero → allow (fail-open)
40
+
41
+ set -u
42
+
43
+ # Placeholders filled by adapters/<tool>/transform.sh:
44
+ # ${CONDUCTOR_SOURCE_GLOB} — alternation of source extensions (default ts|tsx)
45
+ # ${CONDUCTOR_CURRENT_WORK_PATH} — session-log path (default docs/CURRENT_WORK.md)
46
+
47
+ # Honor opt-out before any work.
48
+ [ "${CONDUCTOR_SKIP_CURRENT_WORK_HOOK:-}" = "1" ] && exit 0
49
+
50
+ SRC_GLOB="${CONDUCTOR_SOURCE_GLOB:-ts|tsx}"
51
+ CURRENT_WORK="${CONDUCTOR_CURRENT_WORK_PATH:-docs/CURRENT_WORK.md}"
52
+
53
+ command -v /usr/bin/python3 >/dev/null 2>&1 || exit 0
54
+
55
+ # Read payload from stdin into env var so python can parse it safely.
56
+ export PAYLOAD_RAW="$(/bin/cat 2>/dev/null || true)"
57
+ [ -n "${PAYLOAD_RAW:-}" ] || exit 0
58
+
59
+ # Extract the Bash command (same extraction shape the existing hooks use).
60
+ CMD=$(/usr/bin/python3 -c "
61
+ import json, os, sys
62
+ try:
63
+ d = json.loads(os.environ.get('PAYLOAD_RAW', '{}'))
64
+ if d.get('tool_name', '') != 'Bash':
65
+ sys.exit(0)
66
+ inp = d.get('tool_input', {}) or {}
67
+ sys.stdout.write(inp.get('command', '') or '')
68
+ except Exception:
69
+ sys.exit(0)
70
+ " 2>/dev/null)
71
+
72
+ # Only act on git commit commands.
73
+ case "$CMD" in
74
+ *"git commit"*) : ;;
75
+ *) exit 0 ;;
76
+ esac
77
+
78
+ # Count staged source files (additions, modifications, etc.).
79
+ STAGED_SRC=$(git diff --cached --name-only 2>/dev/null | /usr/bin/grep -cE "\.(${SRC_GLOB})$" || true)
80
+ case "$STAGED_SRC" in
81
+ ''|*[!0-9]*) exit 0 ;;
82
+ esac
83
+
84
+ [ "$STAGED_SRC" -ge 3 ] || exit 0
85
+
86
+ # Is the session log itself part of this commit?
87
+ if git diff --cached --name-only 2>/dev/null | /usr/bin/grep -qF "$CURRENT_WORK"; then
88
+ exit 0
89
+ fi
90
+
91
+ REASON="[current-work-check] ${STAGED_SRC} source file(s) are staged but ${CURRENT_WORK} is not in this commit. Per operations.md O2, append the planned change to ${CURRENT_WORK} before committing so the session log stays in sync. (Skip once: set CONDUCTOR_SKIP_CURRENT_WORK_HOOK=1.)"
92
+
93
+ REASON="$REASON" /usr/bin/python3 -c "
94
+ import json, os
95
+ print(json.dumps({
96
+ 'hookSpecificOutput': {
97
+ 'hookEventName': 'PreToolUse',
98
+ 'permissionDecision': 'ask',
99
+ 'permissionDecisionReason': os.environ.get('REASON', ''),
100
+ }
101
+ }))
102
+ "
103
+ exit 0
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # CONDUCTOR universal hook template
4
+ # hook_id: pretool-commit-test-coverage-check
5
+ # trigger: pretool
6
+ # action: Soft-warn at commit time when a new feature-shaped source file is added
7
+ # with no accompanying test file in the same commit.
8
+ # compile_targets: claude
9
+ # fallback: rule-text-reminder
10
+ #
11
+ # Generated by adapters/<tool>/transform.sh.
12
+ #
13
+ # Script form of quality-gates Q3. Soft warn, never blocks.
14
+ #
15
+ # Enforces:
16
+ # - quality-gates.md Q3 (test coverage sync) — a new feature/route/page/endpoint/
17
+ # service should land with its test in the same commit.
18
+ #
19
+ # Behavior:
20
+ # 1. Parse PreToolUse stdin payload (same method as pretool-agent-routing).
21
+ # 2. If tool_name != Bash → allow. If command is not `git commit` → allow.
22
+ # 3. From `git diff --cached --name-only --diff-filter=A`, collect newly-ADDED
23
+ # source files matching ${CONDUCTOR_SOURCE_GLOB}.
24
+ # 4. Heuristic (kept generic on purpose): treat an added source file as
25
+ # "feature-shaped" if its path contains one of: route, page, endpoint,
26
+ # service, api, handler, controller, feature (case-insensitive). Pure type
27
+ # files / barrels / configs rarely contain those tokens, so they don't trip it.
28
+ # 5. If a feature-shaped added file exists AND no NEW test file is staged
29
+ # (path containing test, spec, __tests__, or e2e) → emit a soft confirmation
30
+ # (permissionDecision: ask + permissionDecisionReason) citing Q3.
31
+ # 6. Override env / any error → allow (silent exit 0).
32
+ #
33
+ # Override (skip this check for a commit):
34
+ # CONDUCTOR_SKIP_TEST_COVERAGE_HOOK=1
35
+ #
36
+ # Exit code meaning in PreToolUse hooks (Claude Code):
37
+ # 0 with no JSON output → allow (silent)
38
+ # 0 with {"permissionDecision":"ask","permissionDecisionReason":...}
39
+ # → surface the reason and route to the permission flow; the orchestrator
40
+ # approves and proceeds (or justifies). Does NOT block and does NOT
41
+ # auto-approve. This is the documented non-blocking soft-warn and matches
42
+ # pretool-agent-routing.sh.template's deny+reason shape (here: ask+reason).
43
+ # non-zero → allow (fail-open)
44
+
45
+ set -u
46
+
47
+ # Placeholders filled by adapters/<tool>/transform.sh:
48
+ # ${CONDUCTOR_SOURCE_GLOB} — alternation of source extensions (default ts|tsx)
49
+
50
+ # Honor opt-out before any work.
51
+ [ "${CONDUCTOR_SKIP_TEST_COVERAGE_HOOK:-}" = "1" ] && exit 0
52
+
53
+ SRC_GLOB="${CONDUCTOR_SOURCE_GLOB:-ts|tsx}"
54
+
55
+ command -v /usr/bin/python3 >/dev/null 2>&1 || exit 0
56
+
57
+ export PAYLOAD_RAW="$(/bin/cat 2>/dev/null || true)"
58
+ [ -n "${PAYLOAD_RAW:-}" ] || exit 0
59
+
60
+ CMD=$(/usr/bin/python3 -c "
61
+ import json, os, sys
62
+ try:
63
+ d = json.loads(os.environ.get('PAYLOAD_RAW', '{}'))
64
+ if d.get('tool_name', '') != 'Bash':
65
+ sys.exit(0)
66
+ inp = d.get('tool_input', {}) or {}
67
+ sys.stdout.write(inp.get('command', '') or '')
68
+ except Exception:
69
+ sys.exit(0)
70
+ " 2>/dev/null)
71
+
72
+ case "$CMD" in
73
+ *"git commit"*) : ;;
74
+ *) exit 0 ;;
75
+ esac
76
+
77
+ # Newly-added source files in this commit (added files only).
78
+ ADDED_SRC=$(git diff --cached --name-only --diff-filter=A 2>/dev/null | /usr/bin/grep -E "\.(${SRC_GLOB})$" || true)
79
+ [ -n "$ADDED_SRC" ] || exit 0
80
+
81
+ # Feature-shaped heuristic: path segment hints at a feature/route/page/endpoint/service.
82
+ # Kept deliberately generic so it works across stacks; excludes test files themselves.
83
+ # The substring match is intentionally loose (e.g. `pageant.ts` can trip `page`) — acceptable
84
+ # because this is a soft `ask` warn the orchestrator can wave through, not a hard block.
85
+ FEATURE_RE='(route|page|endpoint|service|api|handler|controller|feature)'
86
+ TEST_RE='(test|spec|__tests__|e2e)'
87
+
88
+ FEATURE_NEW=$(printf '%s\n' "$ADDED_SRC" \
89
+ | /usr/bin/grep -ivE "$TEST_RE" \
90
+ | /usr/bin/grep -icE "$FEATURE_RE" || true)
91
+ case "$FEATURE_NEW" in
92
+ ''|*[!0-9]*) exit 0 ;;
93
+ esac
94
+ [ "$FEATURE_NEW" -ge 1 ] || exit 0
95
+
96
+ # Any NEW test file staged in this commit (added; covers the common case of a new feature).
97
+ NEW_TESTS=$(git diff --cached --name-only --diff-filter=A 2>/dev/null | /usr/bin/grep -icE "$TEST_RE" || true)
98
+ case "$NEW_TESTS" in
99
+ ''|*[!0-9]*) NEW_TESTS=0 ;;
100
+ esac
101
+
102
+ [ "$NEW_TESTS" -ge 1 ] && exit 0
103
+
104
+ REASON="[test-coverage-check] A new feature-shaped source file is staged but no new test file (path with test/spec/__tests__/e2e) is in this commit. Per quality-gates.md Q3, add or update the covering test in the same commit. (Skip once: set CONDUCTOR_SKIP_TEST_COVERAGE_HOOK=1.)"
105
+
106
+ REASON="$REASON" /usr/bin/python3 -c "
107
+ import json, os
108
+ print(json.dumps({
109
+ 'hookSpecificOutput': {
110
+ 'hookEventName': 'PreToolUse',
111
+ 'permissionDecision': 'ask',
112
+ 'permissionDecisionReason': os.environ.get('REASON', ''),
113
+ }
114
+ }))
115
+ "
116
+ exit 0
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # CONDUCTOR universal hook template
4
+ # hook_id: pretool-large-file-read-guard
5
+ # trigger: pretool
6
+ # action: Block Read of files >= ${CONDUCTOR_LARGE_FILE_LINE_THRESHOLD}
7
+ # lines unless offset/limit is supplied (or override env set).
8
+ # compile_targets: claude
9
+ # fallback: rule-text-reminder
10
+ #
11
+ # Generated by adapters/<tool>/transform.sh.
12
+ #
13
+ # Enforces:
14
+ # - meta-discipline.md token-economy section: range reads mandatory for
15
+ # files exceeding the line threshold.
16
+ #
17
+ # Behavior:
18
+ # 1. Parse PreToolUse stdin payload.
19
+ # 2. If tool_name != Read → allow (silent exit 0).
20
+ # 3. Resolve file_path; if missing or unreadable → allow (let Read tool error).
21
+ # 4. wc -l file. If lines < threshold → allow.
22
+ # 5. If offset OR limit present → allow.
23
+ # 6. If override env set → allow.
24
+ # 7. Else → deny with diagnostic message recommending offset/limit or Grep.
25
+ #
26
+ # Override (rare, e.g. small JSON config that happens to be wide):
27
+ # ${CONDUCTOR_LARGE_READ_OVERRIDE_ENV}=1
28
+ #
29
+ # Exit code meaning in PreToolUse hooks (Claude Code):
30
+ # 0 with no JSON output → allow
31
+ # 0 with {"permissionDecision":"deny"} → block with reason (shown to LLM)
32
+ # non-zero → allow (fail-open)
33
+
34
+ set -u
35
+
36
+ # Placeholders filled by adapters/<tool>/transform.sh:
37
+ # ${CONDUCTOR_LARGE_FILE_LINE_THRESHOLD} — line threshold (default 500)
38
+ # ${CONDUCTOR_LARGE_READ_OVERRIDE_ENV} — env var to bypass (default CONDUCTOR_ALLOW_LARGE_READ)
39
+
40
+ THRESHOLD="${CONDUCTOR_LARGE_FILE_LINE_THRESHOLD:-500}"
41
+ OVERRIDE_ENV_NAME="${CONDUCTOR_LARGE_READ_OVERRIDE_ENV:-CONDUCTOR_ALLOW_LARGE_READ}"
42
+
43
+ # Honor opt-out before any work.
44
+ OVERRIDE_VAL="$(/usr/bin/printenv "$OVERRIDE_ENV_NAME" 2>/dev/null || true)"
45
+ [ "$OVERRIDE_VAL" = "1" ] && exit 0
46
+
47
+ command -v python3 >/dev/null 2>&1 || exit 0
48
+
49
+ PAYLOAD_RAW="$(/bin/cat 2>/dev/null || true)"
50
+ [ -n "$PAYLOAD_RAW" ] || exit 0
51
+
52
+ # Single python pass extracts tool_name + file_path + offset/limit presence.
53
+ # Output format: TOOL=<name>\nPATH=<file>\nHAS_OFFSET=<0|1>\nHAS_LIMIT=<0|1>
54
+ PARSED=$(PAYLOAD_RAW="$PAYLOAD_RAW" python3 -c "
55
+ import json, os, sys
56
+ try:
57
+ d = json.loads(os.environ.get('PAYLOAD_RAW', '{}'))
58
+ except Exception:
59
+ sys.exit(0)
60
+ if d.get('tool_name', '') != 'Read':
61
+ sys.exit(0)
62
+ inp = d.get('tool_input', {}) or {}
63
+ fp = inp.get('file_path', '') or ''
64
+ has_off = '1' if (inp.get('offset') is not None) else '0'
65
+ has_lim = '1' if (inp.get('limit') is not None) else '0'
66
+ print('TOOL=Read')
67
+ print('PATH=' + fp)
68
+ print('HAS_OFFSET=' + has_off)
69
+ print('HAS_LIMIT=' + has_lim)
70
+ " 2>/dev/null)
71
+
72
+ [ -n "$PARSED" ] || exit 0
73
+
74
+ FILE_PATH=""
75
+ HAS_OFFSET="0"
76
+ HAS_LIMIT="0"
77
+ while IFS='=' read -r key val; do
78
+ case "$key" in
79
+ PATH) FILE_PATH="$val" ;;
80
+ HAS_OFFSET) HAS_OFFSET="$val" ;;
81
+ HAS_LIMIT) HAS_LIMIT="$val" ;;
82
+ esac
83
+ done <<EOF
84
+ $PARSED
85
+ EOF
86
+
87
+ # Allow if range read.
88
+ if [ "$HAS_OFFSET" = "1" ] || [ "$HAS_LIMIT" = "1" ]; then
89
+ exit 0
90
+ fi
91
+
92
+ # File must exist + be readable for size check; else let Read tool handle errors.
93
+ [ -n "$FILE_PATH" ] || exit 0
94
+ [ -r "$FILE_PATH" ] || exit 0
95
+ [ -f "$FILE_PATH" ] || exit 0
96
+
97
+ LINES=$(/usr/bin/wc -l < "$FILE_PATH" 2>/dev/null | /usr/bin/tr -d ' ')
98
+ case "$LINES" in
99
+ ''|*[!0-9]*) exit 0 ;;
100
+ esac
101
+
102
+ [ "$LINES" -lt "$THRESHOLD" ] && exit 0
103
+
104
+ # Build deny payload.
105
+ REASON="[large-file-read-guard] File '${FILE_PATH}' has ${LINES} lines (threshold ${THRESHOLD}). Pass offset/limit (e.g. limit:100, offset:0) to range-read, or use Grep to find the specific symbol/section first. Override (rare, e.g. wide JSON config): set ${OVERRIDE_ENV_NAME}=1 in environment."
106
+
107
+ REASON="$REASON" python3 -c "
108
+ import json, os
109
+ print(json.dumps({
110
+ 'hookSpecificOutput': {
111
+ 'hookEventName': 'PreToolUse',
112
+ 'permissionDecision': 'deny',
113
+ 'permissionDecisionReason': os.environ.get('REASON', ''),
114
+ }
115
+ }))
116
+ "
117
+ exit 0