Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
---
name: Strict
description: List-first output, fixed order, code-anchored, no filler β€” tuned for low visual load
keep-coding-instructions: true
---

## The shape of every answer

This shape governs conversational replies to the user, and nothing else. Do not
Expand Down
7 changes: 7 additions & 0 deletions dot_claude/output-styles/private_strict.md.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
name: Strict
description: List-first output, fixed order, code-anchored, no filler β€” tuned for low visual load
keep-coding-instructions: true
---

{{ template "strict-output-style.md.tmpl" . -}}
1 change: 1 addition & 0 deletions dot_codex/AGENTS.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@
unavailable.

{{ template "global-ai-workflow-guidelines.md.tmpl" . }}
{{ template "strict-output-style.md.tmpl" . }}
@~/.codex/RTK.md
12 changes: 12 additions & 0 deletions dot_codex/hooks/executable_reinforce-git-consent.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Reinforce the commit and push consent rule on every Codex user turn.
set -euo pipefail

reminder="Git consent reminder:
Commit or push only when the user asks for it as part of the current task (an ongoing piece of work, not a single agent run).
Permission lasts until that task ends and does not carry over to a later task, even in the same session. When unsure whether the task has changed, ask.
A commit request does not include pushing; a push needs its own request.
A request to create a pull request on an unpushed branch allows exactly one push to publish that branch; later pushes need a new request."

jq -cn --arg ctx "$reminder" \
'{hookSpecificOutput: {hookEventName: "UserPromptSubmit", additionalContext: $ctx}}'
16 changes: 16 additions & 0 deletions dot_codex/hooks/executable_reinforce-strict-output-style.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Reinforce the Strict output style on every Codex user turn.
set -euo pipefail

reminder="Strict output-style reminder (conversational replies only, never inside files):
Use exactly these five sections, with these headings and emojis, in this order, and no others:
1. πŸ” Findings/Context
2. ⚠️ Limits/Risks
3. βœ… Done
4. βœ‹ Recommendations
5. ➑️ Next
Findings holds only facts about the current state, never a proposed change. Recommendations holds every proposal in full (a plan or set of edits awaiting approval, even if it is the only option; which option you favor and why; optional follow-ups); Next is required actions and decisions, such as the approval request for that plan. A required decision you also have an opinion on stays as one Next item with the pick stated inline.
Omit a section only when it is empty. Do not rename, merge, or invent sections. Keep the whole block together at the end of the reply. Escape hatch: a one or two line answer may drop all five headings together, never a subset."

jq -cn --arg ctx "$reminder" \
'{hookSpecificOutput: {hookEventName: "UserPromptSubmit", additionalContext: $ctx}}'
1 change: 1 addition & 0 deletions private_dot_config/opencode/AGENTS.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
delegation.

{{ template "global-ai-workflow-guidelines.md.tmpl" . }}
{{ template "strict-output-style.md.tmpl" . -}}
25 changes: 25 additions & 0 deletions private_dot_config/opencode/plugins/git-consent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const reminder = `Git consent reminder:
Commit or push only when the user asks for it as part of the current task (an ongoing piece of work, not a single agent run).
Permission lasts until that task ends and does not carry over to a later task, even in the same session. When unsure whether the task has changed, ask.
A commit request does not include pushing; a push needs its own request.
A request to create a pull request on an unpushed branch allows exactly one push to publish that branch; later pushes need a new request.`

type ContextEvent = {
system: Array<{ type: "text"; text: string }>
}

type PluginContext = {
session: {
hook(name: "context", handler: (event: ContextEvent) => void): Promise<unknown>
}
}

export default {
id: "git-consent",

async setup(ctx: PluginContext) {
await ctx.session.hook("context", event => {
event.system.push({ type: "text", text: reminder })
})
},
}
29 changes: 29 additions & 0 deletions private_dot_config/opencode/plugins/strict-output-style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const reminder = `Strict output-style reminder (conversational replies only, never inside files):
Use exactly these five sections, with these headings and emojis, in this order, and no others:
1. πŸ” Findings/Context
2. ⚠️ Limits/Risks
3. βœ… Done
4. βœ‹ Recommendations
5. ➑️ Next
Findings holds only facts about the current state, never a proposed change. Recommendations holds every proposal in full (a plan or set of edits awaiting approval, even if it is the only option; which option you favor and why; optional follow-ups); Next is required actions and decisions, such as the approval request for that plan. A required decision you also have an opinion on stays as one Next item with the pick stated inline.
Omit a section only when it is empty. Do not rename, merge, or invent sections. Keep the whole block together at the end of the reply. Escape hatch: a one or two line answer may drop all five headings together, never a subset.`

type ContextEvent = {
system: Array<{ type: "text"; text: string }>
}

type PluginContext = {
session: {
hook(name: "context", handler: (event: ContextEvent) => void): Promise<unknown>
}
}

export default {
id: "strict-output-style",

async setup(ctx: PluginContext) {
await ctx.session.hook("context", event => {
event.system.push({ type: "text", text: reminder })
})
},
}
115 changes: 115 additions & 0 deletions run_after_ensure-codex-hooks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
set -euo pipefail

# Register global reminder hooks without owning Codex's hooks file. Existing
# hooks and unrelated configuration remain unchanged.

CONFIG_DIR="$HOME/.codex"
CONFIG_FILE="$CONFIG_DIR/hooks.json"
# Codex expands HOME when it runs the registered commands.
# shellcheck disable=SC2016
HOOK_COMMANDS=(
'"$HOME/.codex/hooks/reinforce-strict-output-style.sh"'
'"$HOME/.codex/hooks/reinforce-git-consent.sh"'
)

if ! command -v codex &>/dev/null; then
exit 0
fi

if ! command -v jq &>/dev/null; then
echo "Cannot merge Codex hooks: jq is not installed." >&2
exit 1
fi

mkdir -p "$CONFIG_DIR"

if [[ -f "$CONFIG_FILE" ]]; then
if ! jq -e '
type == "object" and
(
if has("hooks") then
(.hooks | type == "object") and
(
if (.hooks | has("UserPromptSubmit")) then
(.hooks.UserPromptSubmit | type == "array") and
all(
.hooks.UserPromptSubmit[];
type == "object" and
((has("hooks") | not) or (.hooks | type == "array"))
)
else
true
end
)
else
true
end
)
' "$CONFIG_FILE" >/dev/null; then
echo "Cannot merge Codex hooks: $CONFIG_FILE has an unsupported structure." >&2
exit 1
fi

if jq -e --args '
. as $root |
$ARGS.positional as $commands |
all(
$commands[];
. as $command |
([
$root.hooks.UserPromptSubmit[]?.hooks[]?
| select(.type == "command" and .command == $command)
] | length) == 1 and
([
$root.hooks.UserPromptSubmit[]?.hooks[]?
| select(. == {type: "command", command: $command, timeout: 5})
] | length) == 1
)
' "${HOOK_COMMANDS[@]}" <"$CONFIG_FILE" >/dev/null; then
exit 0
fi
fi

tmp="$(mktemp "${CONFIG_FILE}.tmp.XXXXXX")"
trap 'rm -f "$tmp"' EXIT

# Dollar-prefixed names in this filter are jq variables.
# shellcheck disable=SC2016
merge_filter='
def is_owned($commands):
.type == "command" and
(.command as $command | $commands | index($command) != null);

$ARGS.positional as $commands |
.hooks //= {} |
.hooks.UserPromptSubmit //= [] |
.hooks.UserPromptSubmit |= (
map(
if ((.hooks // []) | any(.[]; is_owned($commands))) then
.hooks |= map(select(is_owned($commands) | not)) |
if (.hooks | length) == 0 then empty else . end
else
.
end
) + [
$commands[] |
{hooks: [{type: "command", command: ., timeout: 5}]}
]
)
'

if [[ -f "$CONFIG_FILE" ]]; then
jq --args "$merge_filter" "${HOOK_COMMANDS[@]}" <"$CONFIG_FILE" >"$tmp"

if chmod --reference="$CONFIG_FILE" "$tmp" 2>/dev/null; then
:
else
chmod "$(stat -f '%Lp' "$CONFIG_FILE")" "$tmp"
fi
else
jq -n --args "$merge_filter" "${HOOK_COMMANDS[@]}" >"$tmp"
fi

mv "$tmp" "$CONFIG_FILE"
trap - EXIT
Loading