Commit d7197c385dc4
Changed files (7)
dots
agents
skills
vmt-digest
config
claude
herdr
lazyworktree
zsh
tools
pi
agent
extensions
dots/agents/skills/vmt-digest/SKILL.md
@@ -0,0 +1,83 @@
+---
+name: vmt-digest
+description: Generate a weekly VMT (Vulnerability Management Team) digest email from tektoncd security advisory data. USE WHEN user says 'vmt digest', 'weekly vmt', 'security digest', 'vmt email', or wants to generate the weekly VMT triage summary.
+---
+
+# VMT Weekly Digest
+
+Generate a weekly digest email for the `tekton-vmt` mailing list summarizing
+the current state of security advisories across the `tektoncd` GitHub org.
+
+## Workflow
+
+1. **Fetch data**: Run the fetch-advisories script from tektoncd/plumbing
+2. **Generate digest**: Format the data into a clear, actionable email
+
+## Step 1: Fetch Advisory Data
+
+Run the fetch script. The script location depends on the user's setup:
+
+```bash
+# From tektoncd/plumbing repo
+python3 vmt/fetch-advisories.py > /tmp/vmt-advisories.json
+
+# Or from anywhere if plumbing is checked out
+python3 ~/src/tektoncd/plumbing/vmt/fetch-advisories.py > /tmp/vmt-advisories.json
+```
+
+Read the output JSON from `/tmp/vmt-advisories.json`.
+
+## Step 2: Generate Digest Email
+
+Using the JSON data, generate an email with this structure:
+
+### Email Format
+
+**Subject:** `[tekton-vmt] Weekly Digest โ YYYY-MM-DD`
+
+**Body sections:**
+
+#### 1. Summary
+One-line counts: X in triage, Y drafts in progress, Z published, W closed.
+
+#### 2. ๐ด Needs Triage (state: triage)
+Reports that have been submitted but not yet assessed. For each:
+- **Repo/GHSA-ID** โ summary (severity if set)
+- Days since submitted
+- Reporter
+- โ ๏ธ Flag if > 7 days without triage
+
+#### 3. ๐ก In Progress (state: draft)
+Advisories being worked on. For each:
+- **Repo/GHSA-ID** โ summary (severity)
+- Days open, days since last update
+- Credits (who's working on it)
+- โ ๏ธ Flag if > 14 days since last update
+
+#### 4. ๐ข Recently Published (last 7 days, state: published)
+Advisories published in the past week. Brief summary for awareness.
+
+#### 5. ๐ Staleness Alerts
+Any advisory (any state) not updated in > 30 days. Highlight for attention.
+
+### Tone
+- Professional but concise
+- Action-oriented: make clear what needs doing
+- Don't include full descriptions โ just enough context to know what it is
+- Link to the GHSA URL so people can click through
+
+### Example Item
+
+```
+๐ด pipeline / GHSA-g8w8-pwp7-25vp โ bundle resolver unbounded memory allocation (medium)
+ Submitted 10 days ago by reporter-login
+ โ ๏ธ Needs triage โ no assessment yet
+ https://github.com/tektoncd/pipeline/security/advisories/GHSA-g8w8-pwp7-25vp
+```
+
+## Notes
+
+- The digest is meant to be sent manually (copy-paste to mailing list)
+- No explicit assignments for now โ that's a future discussion with the VMT team
+- Credits/collaborators are shown for awareness, not as assignments
+- Output as plain text (mailing list friendly), not HTML
dots/config/claude/hooks/herdr-agent-state.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+# installed by herdr
+# managed by herdr; reinstalling or updating the integration overwrites this file.
+# add custom hooks beside this file instead of editing it.
+# HERDR_INTEGRATION_ID=claude
+# HERDR_INTEGRATION_VERSION=7
+
+set -eu
+
+action="${1:-}"
+hook_input_file="$(mktemp "${TMPDIR:-/tmp}/herdr-claude-hook.XXXXXX")" || exit 0
+trap 'rm -f "$hook_input_file"' EXIT HUP INT TERM
+cat >"$hook_input_file" 2>/dev/null || true
+
+case "$action" in
+ session) ;;
+ *) exit 0 ;;
+esac
+
+[ "${HERDR_ENV:-}" = "1" ] || exit 0
+[ -n "${HERDR_SOCKET_PATH:-}" ] || exit 0
+[ -n "${HERDR_PANE_ID:-}" ] || exit 0
+command -v python3 >/dev/null 2>&1 || exit 0
+
+HERDR_ACTION="$action" HERDR_HOOK_INPUT_FILE="$hook_input_file" python3 - <<'PY'
+import json
+import os
+import random
+import socket
+import time
+
+source = "herdr:claude"
+action = os.environ.get("HERDR_ACTION", "")
+pane_id = os.environ.get("HERDR_PANE_ID")
+socket_path = os.environ.get("HERDR_SOCKET_PATH")
+hook_input_file = os.environ.get("HERDR_HOOK_INPUT_FILE")
+
+if not pane_id or not socket_path:
+ raise SystemExit(0)
+
+hook_input = {}
+if hook_input_file:
+ try:
+ with open(hook_input_file, encoding="utf-8") as handle:
+ content = handle.read()
+ if content.strip():
+ hook_input = json.loads(content)
+ except Exception:
+ hook_input = {}
+
+hook_event_name = str(hook_input.get("hook_event_name") or "")
+is_subagent = bool(hook_input.get("agent_id"))
+if is_subagent:
+ raise SystemExit(0)
+if hook_event_name == "SubagentStop":
+ # SubagentStop is a completion event. Older Herdr integrations mapped it
+ # to durable working, but Claude recap/away-summary can emit it after the
+ # main turn has already stopped. Never let it revive an idle pane.
+ raise SystemExit(0)
+request_id = f"{source}:{int(time.time() * 1000)}:{random.randrange(1_000_000):06d}"
+report_seq = time.time_ns()
+session_id = hook_input.get("session_id")
+agent_session_id = session_id if isinstance(session_id, str) and session_id else None
+transcript_path = hook_input.get("transcript_path")
+agent_session_path = transcript_path if isinstance(transcript_path, str) and transcript_path else None
+session_start_source = hook_input.get("source") if hook_event_name == "SessionStart" else None
+if not isinstance(session_start_source, str) or not session_start_source:
+ session_start_source = None
+if agent_session_id:
+ params = {
+ "pane_id": pane_id,
+ "source": source,
+ "agent": "claude",
+ "seq": report_seq,
+ "agent_session_id": agent_session_id,
+ }
+ if agent_session_path:
+ params["agent_session_path"] = agent_session_path
+ if session_start_source:
+ params["session_start_source"] = session_start_source
+ request = {
+ "id": request_id,
+ "method": "pane.report_agent_session",
+ "params": params,
+ }
+else:
+ raise SystemExit(0)
+
+try:
+ client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ client.settimeout(0.5)
+ client.connect(socket_path)
+ client.sendall((json.dumps(request) + "\n").encode())
+ try:
+ client.recv(4096)
+ except Exception:
+ pass
+ client.close()
+except Exception:
+ pass
+PY
dots/config/claude/settings.json
@@ -1,41 +1,28 @@
{
- "model": "claude-opus-4-6[1m]",
+ "alwaysThinkingEnabled": true,
+ "enabledPlugins": {
+ "bug-hunter@chmouel-cc-plugins": true,
+ "codereview@chmouel-cc-plugins": true,
+ "commit-commands@claude-code-plugins": true,
+ "deslop@chmouel-cc-plugins": true,
+ "feature-dev@claude-code-plugins": true,
+ "gh-tools@vdemeester-claude-code-plugins": true,
+ "git-commit@vdemeester-claude-code-plugins": true,
+ "gopls-lsp@claude-plugins-official": true,
+ "hookify@claude-code-plugins": true,
+ "jira-ticket@chmouel-cc-plugins": true,
+ "plugin-dev@claude-code-plugins": true,
+ "session-manager": true,
+ "typescript-lsp@claude-plugins-official": true,
+ "weekly-review@vdemeester-claude-code-plugins": true
+ },
"hooks": {
- "PreToolUse": [
- {
- "matcher": "Bash",
- "hooks": [
- {
- "type": "command",
- "command": "bun run ~/.config/claude/hooks/validate-git-push.ts"
- }
- ]
- },
- {
- "matcher": "Write",
- "hooks": [
- {
- "type": "command",
- "command": "bun run ~/.config/claude/hooks/validate-write-path.ts"
- }
- ]
- },
- {
- "matcher": "Edit",
- "hooks": [
- {
- "type": "command",
- "command": "bun run ~/.config/claude/hooks/validate-write-path.ts"
- }
- ]
- }
- ],
- "SessionStart": [
+ "PermissionRequest": [
{
"hooks": [
{
- "type": "command",
- "command": "bun run ~/.config/claude/hooks/initialize-session.ts"
+ "command": "printf '\\a' > /dev/tty",
+ "type": "command"
}
]
}
@@ -44,95 +31,118 @@
{
"hooks": [
{
- "type": "command",
- "command": "bun run ~/.config/claude/hooks/capture-tool-output.ts"
+ "command": "bun run ~/.config/claude/hooks/capture-tool-output.ts",
+ "type": "command"
},
{
- "type": "command",
- "command": "bun run ~/.config/claude/hooks/update-terminal-title.ts"
+ "command": "bun run ~/.config/claude/hooks/update-terminal-title.ts",
+ "type": "command"
}
]
}
],
+ "PreToolUse": [
+ {
+ "hooks": [
+ {
+ "command": "bun run ~/.config/claude/hooks/validate-git-push.ts",
+ "type": "command"
+ }
+ ],
+ "matcher": "Bash"
+ },
+ {
+ "hooks": [
+ {
+ "command": "bun run ~/.config/claude/hooks/validate-write-path.ts",
+ "type": "command"
+ }
+ ],
+ "matcher": "Write"
+ },
+ {
+ "hooks": [
+ {
+ "command": "bun run ~/.config/claude/hooks/validate-write-path.ts",
+ "type": "command"
+ }
+ ],
+ "matcher": "Edit"
+ }
+ ],
"SessionEnd": [
{
"hooks": [
{
- "type": "command",
- "command": "bun run ~/.config/claude/hooks/save-session.ts"
+ "command": "bun run ~/.config/claude/hooks/save-session.ts",
+ "type": "command"
}
]
}
],
+ "SessionStart": [
+ {
+ "hooks": [
+ {
+ "command": "bun run ~/.config/claude/hooks/initialize-session.ts",
+ "type": "command"
+ }
+ ]
+ },
+ {
+ "hooks": [
+ {
+ "command": "bash '/home/vdemeest/.claude/hooks/herdr-agent-state.sh' session",
+ "timeout": 10,
+ "type": "command"
+ }
+ ],
+ "matcher": "*"
+ }
+ ],
"Stop": [
{
"hooks": [
{
- "type": "command",
- "command": "printf '\\a' > /dev/tty"
- }
- ]
- }
- ],
- "PermissionRequest": [
- {
- "hooks": [
- {
- "type": "command",
- "command": "printf '\\a' > /dev/tty"
+ "command": "printf '\\a' > /dev/tty",
+ "type": "command"
}
]
}
]
},
- "statusLine": {
- "type": "command",
- "command": "bash ~/.config/claude/statusline.sh"
- },
- "enabledPlugins": {
- "bug-hunter@chmouel-cc-plugins": true,
- "deslop@chmouel-cc-plugins": true,
- "jira-ticket@chmouel-cc-plugins": true,
- "codereview@chmouel-cc-plugins": true,
- "git-commit@vdemeester-claude-code-plugins": true,
- "weekly-review@vdemeester-claude-code-plugins": true,
- "commit-commands@claude-code-plugins": true,
- "plugin-dev@claude-code-plugins": true,
- "feature-dev@claude-code-plugins": true,
- "hookify@claude-code-plugins": true,
- "gh-tools@vdemeester-claude-code-plugins": true,
- "session-manager": true,
- "gopls-lsp@claude-plugins-official": true,
- "typescript-lsp@claude-plugins-official": true
- },
- "alwaysThinkingEnabled": true,
- "theme": "auto",
"mcpServers": {
"github": {
- "command": "github-mcp-server",
"args": [
"stdio"
- ]
+ ],
+ "command": "github-mcp-server"
},
"playwright": {
- "command": "mcp-server-playwright",
"args": [
"--browser",
"google-chrome-stable"
- ]
+ ],
+ "command": "mcp-server-playwright"
}
},
+ "model": "claude-opus-4-6[1m]",
"skills": {
- "enabled": true,
"directories": [
"~/.config/claude/skills"
- ]
+ ],
+ "enabled": true
},
+ "skipWorkflowUsageWarning": true,
+ "statusLine": {
+ "command": "bash ~/.config/claude/statusline.sh",
+ "type": "command"
+ },
+ "theme": "auto",
"trustedWorkspaces": [
"/home/vincent",
"/home/vincent/src/home",
"/home/vincent/src/tekton-watcher",
"/home/vincent/src/go-ci"
- ],
- "skipWorkflowUsageWarning": true
-}
+ ]
+}
\ No newline at end of file
dots/config/herdr/config.toml
@@ -1,3 +1,4 @@
+onboarding = false
# herdr configuration
# Managed via dots/Makefile โ do not edit ~/.config/herdr/config.toml directly.
dots/config/lazyworktree/config.yaml
@@ -10,6 +10,13 @@ refresh_interval: 10
session_prefix: wt-
sort_mode: switched
+# Prefer upstream remote for CI/PR queries (fork workflows)
+ci_remote: auto
+
+# Agent session tuning
+agent_sessions:
+ refresh_debounce_ms: 500
+
# Worktree lifecycle
init_commands:
- link_topsymlinks
dots/config/zsh/tools/kitty.zsh
@@ -6,12 +6,3 @@ autoload -Uz -- "$KITTY_INSTALLATION_DIR"/shell-integration/zsh/kitty-integratio
kitty-integration
unfunction kitty-integration
-# SSH wrapper: use raw ssh for shpool sessions (host/session pattern)
-# Kitty SSH kitten interferes with RemoteCommand
-ssh() {
- if [[ "$1" =~ / ]]; then
- command ssh "$@"
- else
- kitty +kitten ssh "$@"
- fi
-}
dots/pi/agent/extensions/herdr-agent-state.ts
@@ -0,0 +1,287 @@
+// installed by herdr
+// managed by herdr; reinstalling or updating the integration overwrites this file.
+// add custom hooks/plugins beside this file instead of editing it.
+// HERDR_INTEGRATION_ID=pi
+// HERDR_INTEGRATION_VERSION=6
+// @ts-nocheck
+
+import net from "node:net";
+
+const HERDR_ENV = process.env.HERDR_ENV;
+const socketPath = process.env.HERDR_SOCKET_PATH;
+const socketEndpoint =
+ process.platform === "win32" && socketPath ? `\\\\.\\pipe\\${socketPath}` : socketPath;
+const paneId = process.env.HERDR_PANE_ID;
+const source = "herdr:pi";
+
+function enabled() {
+ return HERDR_ENV === "1" && !!socketPath && !!paneId;
+}
+
+function sendRequestAttempt(request: unknown, timeoutMs: number): Promise<boolean> {
+ if (!enabled()) {
+ return Promise.resolve(true);
+ }
+
+ return new Promise((resolve) => {
+ let done = false;
+ let timeout: ReturnType<typeof setTimeout> | undefined;
+ const finish = (delivered: boolean) => {
+ if (done) return;
+ done = true;
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ socket.destroy();
+ resolve(delivered);
+ };
+
+ const socket = net.createConnection(socketEndpoint!);
+ socket.on("error", () => finish(false));
+ socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`));
+ socket.on("data", () => finish(true));
+ socket.on("end", () => finish(false));
+ timeout = setTimeout(() => finish(false), timeoutMs);
+ timeout.unref?.();
+ });
+}
+
+async function sendRequest(request: unknown): Promise<void> {
+ if (await sendRequestAttempt(request, 500)) {
+ return;
+ }
+ await sendRequestAttempt(request, 1500);
+}
+
+type AgentState = "working" | "blocked" | "idle";
+
+type QueuedState = {
+ state: AgentState;
+ message?: string;
+ seq: number;
+};
+
+let reportSeq = Date.now() * 1000;
+let currentAgentSessionId: string | undefined;
+let currentAgentSessionPath: string | undefined;
+
+function nextReportSeq(): number {
+ reportSeq += 1;
+ return reportSeq;
+}
+
+function updateSessionRef(ctx: any): void {
+ try {
+ const file = ctx?.sessionManager?.getSessionFile?.();
+ currentAgentSessionPath =
+ typeof file === "string" && file.startsWith("/") ? file : undefined;
+ } catch {
+ currentAgentSessionPath = undefined;
+ }
+
+ try {
+ const id = ctx?.sessionManager?.getSessionId?.();
+ currentAgentSessionId = typeof id === "string" && id.length > 0 ? id : undefined;
+ } catch {
+ currentAgentSessionId = undefined;
+ }
+}
+
+function withSessionRef(params: Record<string, unknown>): Record<string, unknown> {
+ if (currentAgentSessionPath) {
+ return { ...params, agent_session_path: currentAgentSessionPath };
+ }
+ if (currentAgentSessionId) {
+ return { ...params, agent_session_id: currentAgentSessionId };
+ }
+ return params;
+}
+
+function currentSessionRef(): Record<string, unknown> | undefined {
+ if (currentAgentSessionPath) {
+ return { agent_session_path: currentAgentSessionPath };
+ }
+ if (currentAgentSessionId) {
+ return { agent_session_id: currentAgentSessionId };
+ }
+ return undefined;
+}
+
+function reportSession(sessionStartSource?: string): Promise<void> {
+ const sessionRef = currentSessionRef();
+ if (!sessionRef) {
+ return Promise.resolve();
+ }
+
+ return sendRequest({
+ id: `${source}:session:${Date.now()}:${Math.random().toString(36).slice(2)}`,
+ method: "pane.report_agent_session",
+ params: {
+ pane_id: paneId,
+ source,
+ agent: "pi",
+ seq: nextReportSeq(),
+ session_start_source: sessionStartSource,
+ ...sessionRef,
+ },
+ });
+}
+
+function sendState(state: AgentState, message?: string, seq = nextReportSeq()): Promise<void> {
+ return sendRequest({
+ id: `${source}:${Date.now()}:${Math.random().toString(36).slice(2)}`,
+ method: "pane.report_agent",
+ params: withSessionRef({
+ pane_id: paneId,
+ source,
+ agent: "pi",
+ state,
+ message,
+ seq,
+ }),
+ });
+}
+
+function releaseAgent(): Promise<void> {
+ return sendRequest({
+ id: `${source}:release:${Date.now()}:${Math.random().toString(36).slice(2)}`,
+ method: "pane.release_agent",
+ params: {
+ pane_id: paneId,
+ source,
+ agent: "pi",
+ seq: nextReportSeq(),
+ },
+ });
+}
+
+function shouldReleaseOnSessionShutdown(event: any): boolean {
+ // Pi tears down and rebinds extension runtimes for internal lifecycle actions
+ // such as /reload, /new, /resume, and /fork. Those do not mean the pane's
+ // agent process has exited, and releasing hook authority there can suppress
+ // legitimate reports from the replacement runtime. Only a user/process quit
+ // should release Herdr's full-lifecycle authority.
+ const reason = event?.reason;
+ return reason === "quit";
+}
+
+let sendInFlight = false;
+let queuedState: QueuedState | undefined;
+
+function queueState(state: AgentState, message?: string): void {
+ queuedState = { state, message, seq: nextReportSeq() };
+ if (!sendInFlight) {
+ void drainStateQueue();
+ }
+}
+
+async function drainStateQueue(): Promise<void> {
+ if (sendInFlight) {
+ return;
+ }
+
+ sendInFlight = true;
+ try {
+ while (queuedState) {
+ const next = queuedState;
+ queuedState = undefined;
+ await sendState(next.state, next.message, next.seq);
+ }
+ } finally {
+ sendInFlight = false;
+ if (queuedState) {
+ void drainStateQueue();
+ }
+ }
+}
+
+export default function (pi) {
+ if (!enabled()) {
+ return;
+ }
+
+ let agentActive = false;
+ let blockedCount = 0;
+ let blockedMessage: string | undefined;
+ let lastState: AgentState | undefined;
+ let lastMessage: string | undefined;
+ let rootSession = false;
+
+ function desiredState() {
+ if (blockedCount > 0) {
+ return { state: "blocked" as const, message: blockedMessage };
+ }
+ if (agentActive) {
+ return { state: "working" as const, message: undefined };
+ }
+ return { state: "idle" as const, message: undefined };
+ }
+
+ function publishState(force = false) {
+ const next = desiredState();
+ if (!force && next.state === lastState && next.message === lastMessage) {
+ return;
+ }
+ lastState = next.state;
+ lastMessage = next.message;
+ queueState(next.state, next.message);
+ }
+
+ pi.events.on("herdr:blocked", (data) => {
+ if (!rootSession) {
+ return;
+ }
+ if (!data?.active) {
+ blockedCount = Math.max(0, blockedCount - 1);
+ if (blockedCount === 0) {
+ blockedMessage = undefined;
+ }
+ publishState();
+ return;
+ }
+
+ blockedCount += 1;
+ blockedMessage = data.label;
+ publishState();
+ });
+
+ pi.on("session_start", async (event, ctx) => {
+ if (ctx?.hasUI !== true) {
+ return;
+ }
+ rootSession = true;
+ updateSessionRef(ctx);
+ await reportSession(event?.reason);
+ // A reload can replace this extension mid-run without emitting another agent_start.
+ agentActive = ctx?.isIdle?.() === false;
+ publishState(true);
+ });
+
+ pi.on("agent_start", (_event, ctx) => {
+ if (!rootSession) {
+ return;
+ }
+ updateSessionRef(ctx);
+ void reportSession();
+ agentActive = true;
+ publishState();
+ });
+
+ pi.on("agent_settled", (_event, ctx) => {
+ if (!rootSession || ctx?.isIdle?.() !== true) {
+ return;
+ }
+
+ agentActive = false;
+ publishState();
+ });
+
+ pi.on("session_shutdown", async (event) => {
+ if (!rootSession) {
+ return;
+ }
+ if (shouldReleaseOnSessionShutdown(event)) {
+ await releaseAgent();
+ }
+ });
+}