<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rulesets/claude-templates/bin, branch main</title>
<subtitle>Claude Code skills, rules, and language bundles
</subtitle>
<id>https://git.cjennings.net/rulesets/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/rulesets/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/'/>
<updated>2026-06-12T20:26:22+00:00</updated>
<entry>
<title>chore: delete the page-signal pager wrapper</title>
<updated>2026-06-12T20:26:22+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-12T20:26:22+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=13256aad033f84c0854f2d562685ea808a5ec619'/>
<id>urn:sha1:13256aad033f84c0854f2d562685ea808a5ec619</id>
<content type='text'>
Remove the page-signal CLI wrapper, its workflow, and the references in INDEX.org, broadcast.org, and mcp/README.org. The signal MCP server stays. It's the two-way path and a separate capability. The pager number had deregistered and the send-only wrapper isn't worth re-registering.
</content>
</entry>
<entry>
<title>feat(page-signal): route pages through a dedicated Signal pager account</title>
<updated>2026-06-02T23:03:28+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-02T23:03:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=cfaff12c2425c0e28953981e5b06b54fd60e25cf'/>
<id>urn:sha1:cfaff12c2425c0e28953981e5b06b54fd60e25cf</id>
<content type='text'>
Paging never actually reached the phone before. signal-cli was registered as my primary number, so a page was that account messaging itself, and Signal mobile doesn't push-notify a self-message. I registered signal-cli with a separate Google Voice number (profile "Claude Pager") and pointed everything at it. page-signal now sends from that account to my Signal account by default, so a page lands as a normal third-party message and rings the phone.

The old --note-to-self default is gone, since note-to-self on the pager account wouldn't reach me. Sender and default recipient now come from PAGE_SIGNAL_ACCOUNT and PAGE_SIGNAL_TO with baked-in defaults. The send command pins the sender with -a, and --to also accepts a Signal account UUID, since my account hides its phone number. servers.json points signal-mcp at the new number. Verified end-to-end: live sends from the pager account notified the phone, and signal-mcp shows connected.
</content>
</entry>
<entry>
<title>feat(signal): page-signal CLI wrapper + workflows + cross-project broadcast helper</title>
<updated>2026-05-29T19:51:53+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-29T19:51:53+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=664bf01ceaccf730cb636463cc8587cd1d966192'/>
<id>urn:sha1:664bf01ceaccf730cb636463cc8587cd1d966192</id>
<content type='text'>
Three coupled additions ship together.

claude-templates/bin/page-signal is a bash wrapper around signal-cli
send. It defaults to --note-to-self for safety. The wrapper supports
--file for attachments, --to &lt;+number&gt; for outbound (explicit per
call, no defaults, no batch), --quiet, and --json. Exit codes: 0
sent, 1 signal-cli failure, 2 usage error, 3 signal-cli not
installed.

claude-templates/.ai/workflows/page-signal.org carries the
discrimination rules and safety rails. When desktop notify covers it,
don't reach for Signal. Long-running task completion is the canonical
case. Outbound to other contacts requires explicit Craig instruction
per send. A known-limitation note covers the current notification
gap. signal-cli registered on Craig's primary number means messages
don't fire notifications until the pending Google Voice registration
lands.

claude-templates/.ai/workflows/cross-project-broadcast.org and its
helper cross-project-broadcast.py fan out a single message file to
every AI project's inbox in one operation. Discovery is
fingerprint-based: any directory under ~/code, ~/projects, ~/.emacs.d
with both .ai/protocols.org and a top-level inbox/ is broadcastable.
Senders are auto-excluded. Verified discovery against 23
broadcastable targets.

Makefile's install target gains a general bin/ loop. The previous
version hardcoded bin/ai. The new version iterates over every
executable under claude-templates/bin/ and symlinks each into
~/.local/bin/. install-hooks (existing Claude hook installer) is
unchanged. install-githooks (sync-check pre-commit hook setup, added
earlier today) is unchanged. The bin/ loop now picks up bin/page-signal
automatically.

INDEX entries for both new workflows landed under Tools and meta.

No bats tests on the new scripts. page-signal was smoke-tested with a
live send. The send succeeded. The notification gap is covered by the
workflow's known-limitation note. cross-project-broadcast.py was
smoke-tested via --list against the live project set. Tests can be
added when the broadcast pattern proves out across multiple use cases.
</content>
</entry>
<entry>
<title>fix(ai): explicit end-of-session placement for new windows</title>
<updated>2026-05-19T06:45:01+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T06:45:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=68569726f4ca1e550a3e4e288a8f861703cdeb8e'/>
<id>urn:sha1:68569726f4ca1e550a3e4e288a8f861703cdeb8e</id>
<content type='text'>
tmux's default new-window placement chooses the lowest free index, which can land between existing windows when the session's window indexes have gaps. The -a flag plus the :{end} target makes it explicit: insert after the existing last window, every time.

The change only touches create_window. The two tmux new-session sites (single_mode and multi_mode first window) create fresh sessions; the first window's position is whatever base-index dictates, and there's nothing to append after.
</content>
</entry>
<entry>
<title>feat(ai): per-project opening line with host and project name</title>
<updated>2026-05-19T02:53:37+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T02:53:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=9d804cdb4cdb14787e928a1d45c3f2023d739e41'/>
<id>urn:sha1:9d804cdb4cdb14787e928a1d45c3f2023d739e41</id>
<content type='text'>
The ai launcher used to send claude a fixed instruction string ('Read .ai/protocols.org and follow all instructions.'). Every window opened the same way, with no signal in the conversation about which machine or project it belonged to. That's fine when there's one window, less fine when an ai-session has 5+ windows across two machines.

The new build_instructions helper formats the opener per project: "This is &lt;host&gt; &lt;name&gt; project. Follow all instructions in .ai/protocols.org." Host comes from uname -n (POSIX, no dependency on the hostname binary which isn't installed by default on Arch). The project name distinguishes windows; the .ai/ prefix on the path stays so claude resolves the file reliably on first read.

The three send-keys call sites all use the helper now (create_window, single_mode new-session, multi_mode first-window). Smoke-tested with rulesets, .emacs.d, jr-estate basenames.

Also fixes the stale Source/Install header comments. They still pointed at ~/projects/claude-templates/bin/ai, the pre-fold path the subtree merge replaced.
</content>
</entry>
<entry>
<title>Merge commit '69c5e4ace81586c05dea6a9a3afd54dafa61a73b' as 'claude-templates'</title>
<updated>2026-05-15T21:56:39+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-15T21:56:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=c1d4e3c4a42abd01bc7ef83b1d6ae036ee32ef1d'/>
<id>urn:sha1:c1d4e3c4a42abd01bc7ef83b1d6ae036ee32ef1d</id>
<content type='text'>
</content>
</entry>
</feed>
