Commit Graph

35 Commits

Author SHA1 Message Date
shahondin1624 4968c44f2c refactor: group dark-mechanicus theme files into a folder
Seven loose root-level .ts files were the dark-mechanicus theme bundle:
indicator, banner, footer, status-line, session-names, thinking-label,
and markdown-body-color. They share visual language with
themes/dark-mechanicus.json and only make sense together.

Moved them under dark-mechanicus/ as a multi-file extension. The
pi-coding-agent loader auto-discovers <subdir>/index.ts as a single
extension entry, so dark-mechanicus/index.ts now sequences each
sub-module's default registrar.

File renames also drop the redundant 'mechanicus-' / 'dark-mechanicus-'
prefix since the directory name carries it. Symbol names, command names,
and external behavior are unchanged — disabling each piece still works
via its own /<name>-off command.

Imports inside each moved file: ./shared/ → ../shared/.

local-llama.ts stays at the root — separate stub provider, not part of
the theme.

.gitignore updated: 8 individual !/<file>.ts allowlist entries replaced
with a single !/dark-mechanicus/.

README.md: theme entries collapsed into one row pointing at the folder;
tree diagram updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 14:50:19 +02:00
shahondin1624 2a8a3e8e22 feat(scripts): add ai-complete CLI for direct llama-server router access
Minimal shell wrapper around llama.cpp router's OpenAI-compatible API
(/v1/chat/completions), gated by the same mTLS cert as the pi extension.
Single-file, runtime deps: bash + curl + jq. Useful for scripts and agents
(Claude Code, etc.) that want to delegate generation without pulling in
a full SDK.

Features:
  --list / --status / --load <model>
  --stream <model> "..." for SSE token-stream output
  --raw <model> '...'    for full openai-format json bodies (also @file)
  --prompt-file <path>   reads prompt from disk via jq --rawfile, bypassing
                         Linux's MAX_ARG_STRLEN (~128KB per argv) so prompts
                         up to the model's context window work
  --temperature / --top-p / --max-tokens / --system  sampling overrides
  Auto-retry with exponential backoff on transient empty/non-JSON
  responses (model-loading window). Short-circuits on structured 4xx
  errors (e.g. exceed_context_size).

AI_CERT_DIR / AI_ENDPOINT / AI_RETRIES env overrides.

Includes scripts/AI-COMPLETE.md with install + usage docs and a row in
the top-level README's scripts table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 14:43:34 +02:00
shahondin1624 cdae16562a fix(scripts): correct ORG default Shah*ODin → Shahondin1624
Typo in the issuer org for newly minted client certs. Existing certs are
unaffected (Caddy validates against the root CA's public key, not subject
text). Future certs issued via this script will carry the corrected
O=Shahondin1624.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 14:43:18 +02:00
shahondin1624 31300cc2a2 Include shared/ dir in repo (gitignore allowlist)
The previous commit referenced shared/ansi.ts, shared/format.ts, and
shared/ctx.ts but those files were filtered by the default-deny
.gitignore. Adding !/shared/ to the allowlist so the imports actually
resolve in a fresh clone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 00:05:38 +02:00
shahondin1624 f1ceeb4363 Refactor pass: shared utils, non-blocking discovery, safer monkey-patches
Audit produced five concrete improvements:

1) New shared/ module (zero-dep pure utilities)
   - shared/ansi.ts: hexToRgb (throws on malformed input instead of
     silently producing NaN), fgFromHex, stripAnsi, visibleWidth,
     ANSI_RESET_FG / ANSI_RESET_ALL constants.
   - shared/format.ts: formatTokens, formatElapsed.
   - shared/ctx.ts: safely() and safelyAsync() helpers for dealing with
     pi's "stale after session replacement or reload" ExtensionRunner
     semantics.

   Removes duplicate helpers from mechanicus-footer, markdown-body-color,
   dark-mechanicus-indicator.

2) ai-server: non-blocking startup + short-race timeout
   - Factory registers STATIC_MODELS immediately so pi startup isn't
     blocked on the HTTPS round-trip.
   - Races discoverModels() against a 300ms timeout. On LAN (~40ms) the
     live list wins and pi --list-models sees the real models. Past the
     timeout, fallback remains and background discovery updates the
     provider later.
   - listModelsCached() with 5s TTL for tab completions (was firing a
     round-trip on every keystroke).
   - loadModel/unloadModel invalidate the cache.

3) dark-mechanicus-indicator: stale-ctx guard
   - Wrap the setInterval ticker body in safely() so a race between
     session_shutdown and the ticker can't crash node. Same pattern as
     the earlier footer fix.

4) Safer monkey-patches in markdown-body-color and mechanicus-thinking-label
   - Feature-detect Markdown/Editor/AssistantMessageComponent's target
     method before patching. Warn-and-skip rather than silently create
     a broken prototype if a pi-tui upgrade renames the internal method.

5) Minor
   - Replaced five `as any` casts with typed Record<string, unknown>
     access in the monkey-patch sites.
   - ai-server debug log only fires when actual discovery succeeds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 00:05:18 +02:00
shahondin1624 cd94a6636d footer: survive /quit — null ctx on session_shutdown + guard render
On /quit (and /reload), pi marks the ExtensionRunner stale immediately,
but the TUI can still fire a pending render timer before tearing down.
Our footer's render() then accessed cachedCtx.sessionManager, which
throws "This extension instance is stale after session replacement or
reload" — uncaught, crashing Node with a stack trace on the way out.

Two layers of defense:

1. pi.on("session_shutdown", () => { cachedCtx = null; })
   - Runs at shutdown, before the final render timer fires in most
     cases, so render() sees null ctx and returns [] cleanly.

2. try/catch around render body
   - For the tight race where a render fires between the stale-flag
     being set and our session_shutdown handler running, swallow the
     error and return []. No more stack traces on quit.

Extracted the render body into a `renderInner` helper for readability
during the try/catch wrap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:54:05 +02:00
shahondin1624 0777b6b9e5 Re-add context filter: strip legacy cogitation-summary entries
Earlier I removed the `pi.sendMessage` call that produced
"Cogitated for Xs" custom messages, along with their renderer and
context filter. But sessions that were alive when the feature existed
retained those custom-message entries in their persistent log —
pi.sendMessage writes CustomMessageEntry records to the session file,
and removing the extension does not retroactively delete them.

Without the context filter, those entries still flow through
convertToLlm() and become role:"user" messages in every provider
request. The model started responding to them ("the user is asking...
The 'Cogitated' messages suggest they're reading/thinking...").

Re-adding the `context` event filter that skips any custom message
with customType "cogitation-summary". No-op for sessions without those
entries; retroactive cleanup for sessions that have them. Does not
bring back the feature itself — no sendMessage or renderer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:50:51 +02:00
shahondin1624 139dcbce74 messages: drop assistant entries with only thinking content
Pi's session can contain assistant entries whose AssistantMessage.content
is entirely thinking blocks (no text, no tool calls) — typical after an
aborted turn or when reasoning is edited out. Our contextToOpenAIMessages
was emitting those as { role: "assistant", content: null }.

When such a message is at the end of the context, llama.cpp's chat
template interprets the trailing assistant entry as an "assistant
response prefill" attempt. Reasoning-model templates (MiniMax M2.7,
Qwen, etc.) have enable_thinking set, and the server rejects this
combination with HTTP 400:
    "Assistant response prefill is incompatible with enable_thinking."

Fix: skip assistant entries where extractAssistantText and
extractToolCalls both return empty. Thinking blocks aren't re-fed to
the model anyway, so dropping the wrapper message loses no information.

+ two regression tests in tests/messages.test.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:48:16 +02:00
shahondin1624 24c9a92a62 Add top-level README
Lists every extension, the theme, setup scripts, tests, a quick
onboarding section, and a repo layout diagram. Links each extension
to its source file. Specifically notes the deferred stream-parsing
tests and the reason (mock HTTPS harness not worth it for single-user
deployment).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:45:47 +02:00
shahondin1624 752fdbaff1 Expand tests — unit coverage for router-utils + cache/LE contract
Refactor:
- Extracted extractCtxSize + isShardArtefact from ai-server/admin.ts to
  a new ai-server/router-utils.ts with zero relative imports. Makes them
  directly loadable in tests with Node's --experimental-strip-types
  (no jiti needed). admin.ts re-exports extractCtxSize so index.ts is
  unchanged.

tests/router-utils.test.ts (9 cases):
- extractCtxSize: present/value, missing, end-of-argv, non-numeric,
  zero (edge), missing status.
- isShardArtefact: positive cases (5-digit, numeric, no zero-padding),
  negative cases (clean preset names, non-shard numeric patterns,
  shard pattern mid-string).

tests/integration.test.ts (2 new cases):
- "server cert is publicly trusted": verifies curl without --cacert
  flag reaches /health. Catches LE regression (cert reverting to
  self-signed).
- "chat completion returns usage with prompt_tokens_details":
  sanity-checks the server contract our stream.ts now reads for
  cache-token reporting. Picks any loaded runnable model; skips
  cleanly when none loaded.

Totals: 28 tests (13 messages + 9 router-utils + 6 integration).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:44:44 +02:00
shahondin1624 19afcd5358 Remove post-turn "Cogitated for Xs" inline summary
Caused more trouble than it was worth — even with the context-event
filter stripping the custom message from LLM input, the transcript
entry could still influence the agent in subtle ways (model looking
at it on re-render, extension-interactions, export), and the visual
clutter after every turn wasn't worth the diagnostic value.

Removed:
- pi.registerMessageRenderer("cogitation-summary", ...)
- pi.sendMessage on turn_end
- pi.on("context") filter (no longer needed; nothing to strip)
- Box/Text imports from @mariozechner/pi-tui
- SUMMARY_TYPE constant

Kept:
- Live indicator: ⚙ <quote> · <elapsed> during streaming, with the
  cog pulse and quote rotation and the "Working..." suppression.
- Footer tok/s (lives in mechanicus-footer, not affected).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:41:45 +02:00
shahondin1624 423c1adc20 Fix cogitation summary leaking into LLM context + add tok/s to footer
Root cause for leak: pi's convertToLlm() turns every custom message
into a role:"user" entry with the message's content text before sending
to the provider. Our "Cogitated for 1m 24s" custom message was being
fed to the model on the next turn, and the model was responding to it
as if it were a user prompt.

Fix: dark-mechanicus-indicator now registers a `context` event handler
that returns a filtered messages array with `cogitation-summary`
entries stripped. The summary still renders inline for the user
(display:true), but the LLM never sees it.

mechanicus-footer additions:
- Track turn_start/turn_end to capture the most recent turn's wall
  duration (lastTurnDurationMs).
- Compute generation speed as lastAssistantOutput / lastTurnDuration
  and surface it in the stats line as "12.3tok/s" (shown only when a
  turn has completed and we have real output tokens to report).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:36:06 +02:00
shahondin1624 a9f1c49b2b Add mechanicus-footer extension — custom footer with clearer ctx display
Replaces pi's default footer via ctx.ui.setFooter with a near-identical
layout but two key changes:

- Context usage: "45k/131k (34%)" instead of pi's default "34%/131k".
  You see the absolute current token count next to the max, with the
  percentage in parens — no more mental math to know how many tokens are
  actually in play.
- Compaction counter: appends "C<N>" when the session has been compacted
  (reads CompactionEntry count from sessionManager.getEntries()). Hidden
  when 0.
- Preserves: ↑in ↓out R<cache> $cost · ctx · C<N> · model • thinking level,
  plus extension statuses on line 3+.
- Preserves context-percentage color thresholds (>90% error, >70% warning).

Commands: /mechanicus-footer-off (restore default), /mechanicus-footer-on
(reinstall).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:30:29 +02:00
shahondin1624 99ad3630fc stream: report cached tokens; indicator: suppress pi's "Working..."
Two small fixes:

ai-server/stream.ts
- llama.cpp reports cached prompt tokens via
    usage.prompt_tokens_details.cached_tokens
  and we were ignoring it. Populate output.usage.cacheRead so pi's
  footer can show the "R<tokens>" field. cacheRead is a subset of
  prompt_tokens (already counted in input), so totalTokens stays
  input + output — no double-counting.

dark-mechanicus-indicator.ts
- Pi appends "Working... (ESC to interrupt)" next to custom working
  indicator frames via a separate message slot. Call
  ctx.ui.setWorkingMessage("") on session_start + every turn_start to
  clear that suffix so the indicator line is just
    ⚙ <quote> · <elapsed>
  with no trailing "Working...".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:28:16 +02:00
shahondin1624 5e46c2c4c9 Consolidate timer into indicator + inline post-turn summary
Previously there were two "work indicators" running in parallel:
  1. The pulsing-cog working indicator (⚙ + rotating quote)
  2. A separate footer timer ("⚙ Cogitating... (Xm Ys)")

Merges them:

- During generation: the working indicator now shows
    "⚙ <quote> · 1m 24s"
  on one line. A 1s setInterval rebuilds 4 frames (one full pulse cycle)
  with the current quote + current elapsed time; the reset lands at
  frame 0 of the pulse so the animation looks seamless.

- After generation: a new inline message appears in the transcript right
  after the last assistant message:
    "✴ Cogitated for 1m 57s"
  Rendered via pi.registerMessageRenderer + pi.sendMessage with a
  "cogitation-summary" customType.

Removes mechanicus-cogitation-timer.ts (the old footer timer). The
mechanicus-status-line extension (HERETEK FORGE · ACTIVE flavor line)
stays — it's a separate status, not a work indicator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:16:54 +02:00
shahondin1624 e023414986 Add tests — messages unit tests + live-router integration
Runs with Node 22+ built-in test runner + --experimental-strip-types.
No external dependencies required.

tests/messages.test.ts (13 cases):
- contextToOpenAIMessages: empty, systemPrompt-only, user string content,
  user text-block array, user image-only (drops), assistant text,
  assistant tool-calls-only, assistant text + tool calls, toolResult,
  full round-trip.
- toolsToOpenAI: undefined, empty, proper OpenAI function mapping.

tests/integration.test.ts (4 cases, auto-skip if unreachable):
- GET /health returns status=ok.
- GET /models returns a non-empty data array.
- Every /models entry has a valid status.value.
- mTLS is enforced: request without client cert is rejected.

Deferred to phase 2 (needs mock HTTPS server + cert harness):
stream.ts SSE parsing — abort mid-stream, tool-call split across chunks,
reasoning deltas, usage + calculateCost, malformed events.

Run: node --experimental-strip-types --test tests/*.test.ts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:12:27 +02:00
shahondin1624 31fe302a59 Add mechanicus-cogitation-timer extension
Shows elapsed-time counters in the footer during generation and a final
"Cogitated for Xm Ys" summary after the turn ends.

- turn_start: seed "⚙ Cogitating... (0s)" and start a 1s setInterval
  that refreshes the footer with the current elapsed time.
- turn_end: clear the ticker, swap to "✴ Cogitated for Xm Ys", and
  schedule a cleanup after 30s so the footer isn't permanently cluttered.
- session_shutdown: safety net to prevent zombie timers on reload.

Uses ctx.ui.setStatus so this runs alongside the existing pulsing-cog
working-indicator without interfering with its animation pointer (which
would reset on every setWorkingIndicator call).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:09:17 +02:00
shahondin1624 b2cb1667c7 ai-server: stop pinning server cert to private CA (LE is now live)
Now that Caddy serves a Let's Encrypt cert for ai.shahondin1624.de,
passing `ca: root-ca.pem` to https.request made Node override its
default trust list with only the private CA — which no longer chains
the LE-issued server cert, so every request failed with
ECONNRESET / CERT_HAS_EXPIRED-style errors on the client side.

Dropping the `ca:` option lets Node fall back to its built-in Mozilla
CA bundle, which includes Let's Encrypt. Client cert/key still passed;
mTLS remains enforced server-side.

If the server ever reverts to a self-signed cert, re-add `ca: certs.ca`
or set NODE_EXTRA_CA_CERTS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:03:35 +02:00
shahondin1624 3015adf5d4 Add mechanicus-status-line extension
Adds a dark-mechanicum status line to pi's footer via ctx.ui.setStatus.
The default footer automatically renders registered extension statuses
on a third line below cwd + stats.

- 15 status strings (HERETEK FORGE · ACTIVE, VASHTORR NETWORK · ONLINE,
  etc.), pseudo-randomly picked.
- Seeded on session_start, rotated on every turn_end. Status persists
  between turns so it doesn't flicker during input.
- Coloured: ⚙ prefix in warning/gold, body in muted/bronze.
- Commands: /mechanicus-status-cycle (force new), /mechanicus-status-off.

Chosen as an additive approach over full footer replacement: avoids
reimplementing pi's token/cost/context-usage computation just to colour
them; pi's native stats remain intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:49:20 +02:00
shahondin1624 312107ef06 Rework indicator + session names to Dark Mechanicum flavor
dark-mechanicus-indicator:
- Rename PIOUS category to HERETEK; rewrite all 15 lines from loyalist
  cant (Omnissiah, Blessed, Sacred) to Chaos-inflected corrupted litany
  (Vashtorr, Heretek, Scrapcode, Profane, Corruption).

mechanicus-session-names:
- ADJECTIVES: Sacred/Blessed/Sanctified/Omnissian -> Profane/Damned/
  Unholy/Vashtorr-marked. Keeps the neutral descriptors
  (Ancient/Binary/Silicon/etc.) for variety.
- NOUNS: Anointment -> Desecration; Forge-Thread -> Blood-Forge;
  Cogsworn -> Scrapcode-Thread.
- setTitle(`pi · <name>`) on session_start and /mechanicus-rename so
  the terminal tab/window title mirrors the session name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:46:56 +02:00
shahondin1624 0c19e28828 mechanicus-banner: reorient text from loyalist Mechanicus to Dark Mechanicum
The previous phrasing was canonical-loyalist Adeptus Mechanicus (Lex
Mechanicus, Omnissiah Be Praised, Christian cross ✠, Binary Is Blessed).
Retheming for Chaos-aligned Dark Mechanicum:

Compact banner:
  L E X   M E C H A N I C U S             -> L E X   H E R E T E K
  OMNISSIAH BE PRAISED · DEUS EX MACHINA  -> SCRAPCODE TRIUMPHS · BLOOD-FORGE ETERNAL
  B I N A R Y   I S   B L E S S E D       -> F L E S H   I S   W E A K
  ✠ (Christian cross)                      -> ✴ (eight-pointed Chaos Star)

Full banner left block analogously (LEX HERETEK / SCRAPCODE TRIUMPHS
/ BLOOD-FORGE ETERNAL / Flesh is weak. / The cogitator hungers.).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:40:06 +02:00
shahondin1624 39d0797dc9 Gate startup logs behind PI_DEBUG + skip GGUF-shard phantom entries
- All [ai-server] / [markdown-body-color] / [mechanicus-thinking-label]
  console.log calls now fire only when PI_DEBUG is set. Default boot is
  clean.
- ai-server's discoverModels now filters out ids matching
  /-\d+-of-\d+$/ — llama.cpp's --models-autoload registers every GGUF
  shard as its own id, duplicating the preset's consolidated model.
  These shard-named phantoms are no longer surfaced to pi.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:38:09 +02:00
shahondin1624 0471518b07 Add mechanicus-session-names extension
Auto-generates a flavored session name on session_start when the
session is new and has no name yet. Format:
  "<Adj>-<Noun> · <3-digit>"
e.g. "Sacred-Cogitation · 042", "Heretek-Datalink · 717".

18 adjectives × 18 nouns × 1000 = 324000 unique names. Auto-naming is
scoped to reason === "new"; resume/fork/reload/startup are untouched.

Command: /mechanicus-rename generates a fresh name for the current
session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:34:46 +02:00
shahondin1624 4953ac7e73 Add mechanicus-thinking-label extension
Monkey-patches AssistantMessageComponent.updateContent to swap the
default "Thinking..." label for "Cogitating..." when reasoning blocks
are folded. Only replaces the default — if any code path sets its own
label, we leave it alone.

Override via PI_THINKING_LABEL env var.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:33:01 +02:00
shahondin1624 c5eeba1e13 dark-mechanicus-indicator: slow cadence to 1s pulse / 10s quote dwell
Before: 100ms/frame × 20 frames = 400ms pulse, 2s quote dwell.
After:  250ms/frame × 40 frames = 1s pulse, 10s quote dwell.

Pulse still cycles dim → normal → bright → normal (4 shades) but each
shade now holds for 250ms, making the pulse slower and the glyph more
legible. Quotes now stay long enough to read comfortably.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:26:18 +02:00
shahondin1624 6e89d1d123 Add dark-mechanicus-indicator extension — pulsing cog + rotating quotes
Replaces pi's default working spinner with a ⚙ that pulses through three
shades of gold (dim → normal → bright → normal, 100ms per frame ~ 400ms
heartbeat) next to a dark-mechanicum quote.

- 45 quotes across three categories (15 each):
    pious / liturgical: "Cogitating..." / "Praise the Omnissiah." / ...
    dark mechanicum:    "Flesh is weak. Binary is strong." / ...
    operational:        "Parsing sacred data..." / "Cache blessed..."
- Each quote dwells for 5 pulse cycles (20 cog-frames = 2s) before
  switching. Full deck runs ~90s; typical turns show 3-5 quotes.
- Pseudo-random: Fisher-Yates shuffle on session_start AND turn_start,
  so each assistant response draws from a fresh order.
- Colors: warning/gold shades for the cog pulse, muted/bronze for text.
  Hardcoded hex so pre-built frames don't bounce through theme.fg.

Commands: /dark-mechanicus-indicator-on, /dark-mechanicus-indicator-off.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:24:23 +02:00
shahondin1624 42a4d48b0b Darken warpGreen to nurgle-green + patch Editor for cursor/typing
- Theme: warpGreen #7a8a42 -> #5a6b2e (darker, more "nurgle", still
  readable for syntaxString / success / toolDiffAdded).
- markdown-body-color: extend to also monkey-patch Editor.prototype.render
  so typed-text and the cursor (reverse-video) inherit the same inkPurple
  body color instead of falling through to the terminal-default fg.
  Re-opens the color after \x1b[0m (cursor's full reset) and \x1b[39m
  (nested theme.fg close) so color survives those breaks within a line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:09:01 +02:00
shahondin1624 3f9ec6ef3c Add markdown-body-color extension — color unwrapped markdown paragraphs
pi-tui's Markdown component only colors paragraph text when the caller
passes a defaultTextStyle.color wrapper. Pi-coding-agent does this for
thinking blocks but not for regular assistant content, so paragraphs
emit uncoloured and inherit the terminal profile's foreground color
(green on many themes).

This extension monkey-patches Markdown.prototype.render to install a
fallback defaultTextStyle (inkPurple #d4c5e8 by default, overridable
via PI_MARKDOWN_BODY_COLOR) when one isn't already set. Thinking blocks
and other custom-colored markdowns are untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:02:56 +02:00
shahondin1624 471e429ed0 dark-mechanicus: stop inheriting terminal-default foreground
Setting text/userMessageText/customMessageText to "" means pi renders
with the terminal's default foreground color — which on many terminal
profiles is green, leaking through to the cursor (rendered via reverse-
video `\x1b[7m`), editor-typing text, autocomplete non-selected items,
and plain chat text.

Fix: introduce an `inkPurple` var (#d4c5e8, soft readable lavender,
~14:1 contrast on the #16101e background) and use it for every token
that was previously "":
  - text            ""  -> inkPurple
  - userMessageText ""  -> inkPurple
  - customMessageText "" -> inkPurple
  - mdCodeBlock     ""  -> parchment (distinct from body, stays warm)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:56:45 +02:00
shahondin1624 25e76678bd mechanicus-banner: add cog-and-skull art + full/compact/off modes
- Embeds a 17-line × ~38-col cog-and-skull ASCII art.
- Character→theme mapping for color shading:
    %  accent        (bloodRed — skull features)
    #  borderAccent  (rust    — dense shading)
    *  border        (brass   — main body)
    +  warning       (gold    — highlights)
    =  muted         (bronze  — mid tones)
    -  dim           (far edges)
- Full mode = art right-aligned + centered text block on the left
  (LEX MECHANICUS / OMNISSIAH… / Binary is blessed.).
- Compact mode = the prior 7-line text-only banner, used as fallback
  when terminal width < art + margin.
- Default on session_start: full (auto-falls-back to compact on
  narrow terminals).
- Commands: /mechanicus-banner-full, -compact, -on, -off.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:50:29 +02:00
shahondin1624 3e46dae17d Add mechanicus-banner extension — custom top header
Replaces pi's default header (logo + keybind hints) with a centered
banner featuring:

- Brass frame with rust ⚙ cog bookends on each horizontal rail
- Top line: rust cog-tooth hints (▲▲▲) flanking gold ✠ crosses and a
  blood-red "L E X  M E C H A N I C U S"
- Middle line: gold ☠ skulls flanking a muted "OMNISSIAH BE PRAISED ·
  DEUS EX MACHINA"
- Bottom line: dim "B I N A R Y   I S   B L E S S E D" between em dashes

Colors resolve through the active pi theme (border, borderAccent, accent,
warning, muted, dim) so the banner adapts if the theme changes, but it
was designed for the dark-mechanicus palette added earlier.

Toggle via /mechanicus-banner-on / /mechanicus-banner-off. Installed
automatically on session_start.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:42:20 +02:00
shahondin1624 f16c41a47c Rework theme: midnight-orchid -> dark-mechanicus
Heavier AdMech flair on the dark-purple undertone:
- Background: deep aubergine-black (#16101e)
- Accent: AdMech blood red (#a8232c), with burnished brass borders
  (#b8803d) and copper/ember highlights
- Syntax: crimson keywords, gilded-gold function names, brass variables,
  warp-green strings, copper numerals, cognitor-pink types, steel
  punctuation
- Thinking-level escalation: textFaint -> textDim -> brass -> copper
  -> ember -> bloodRed (cogitator overclock)
- Bash mode: copper (servitor invocation)

Keeps all 51 required color tokens. Activate with /settings or
"theme": "dark-mechanicus" in settings.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:37:43 +02:00
shahondin1624 9e70e1bf23 Add midnight-orchid theme + wire it into install-client.sh
- themes/midnight-orchid.json: custom dark purple theme. Deep aubergine
  background (#1a1525), violet/magenta accents, sage/amber/cyan syntax
  for contrast. All 51 required color tokens defined.
- scripts/install-client.sh: adds a themes sync step that rsync's the
  repo's themes/ into ~/.pi/agent/themes/ on each new machine.

Activate with /settings in pi or by setting "theme": "midnight-orchid"
in ~/.pi/agent/settings.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:35:47 +02:00
shahondin1624 e58c78d21c Add setup scripts for onboarding new machines
- scripts/install-client.sh: bootstraps a pi client — fetches certs from
  the Caddy host via scp, rsyncs the extensions into ~/.pi/agent/, sets
  up SSH key-auth to the ai-server for admin commands, probes the mTLS
  /health endpoint to verify.
- scripts/issue-client-cert.sh: run on the Caddy host to mint a new
  device identity — generates key + CSR, signs with the local root CA,
  and emits both a modern p12 (AES-256) and a -legacy p12 (3DES/RC2-40)
  for NSS-based browsers.
- scripts/install-browser-certs.sh: imports certs into Brave Flatpak's
  isolated NSS DB, ~/.pki/nssdb for packaged Chromium-family browsers,
  each Firefox profile, optionally the system trust store, and
  optionally drops a Brave AutoSelectCertificateForUrls policy so the
  cert prompt stops appearing on every page load.

All three are idempotent, --help-aware, and accept env/flag overrides
for the hardcoded defaults.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:26:08 +02:00
shahondin1624 e321f90fe9 Initial commit — ai-server and local-llama extensions
- ai-server/: multi-file pi extension that talks to a remote llama.cpp
  router over mTLS (custom streamSimple), with dynamic model discovery
  and admin slash commands for load/unload/ctx-size/restart/preset.
  Includes README.md documenting the full mTLS + systemd + Caddy setup.
- local-llama.ts: minimal extension registering a local llama.cpp server
  as an OpenAI-compatible provider.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:14:40 +02:00