Commit Graph

54 Commits

Author SHA1 Message Date
ejlewis b318747840 feat(parser): emit verb-target-prep on 'with'/'on'/'in'/'to' separators
Enables `light lamp with matches`, `use shears on vines`, and similar
multi-noun forms. Both the target and indirect noun must resolve;
otherwise the command falls back to unknown-noun.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:56:19 -05:00
ejlewis 46f851bc3a feat(parser): return ambiguous variant when noun matches multiple aliases
Replaces the previous behavior of returning unknown-noun. The dispatcher
will use this in the next commit to prompt the player to disambiguate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:53:38 -05:00
ejlewis b325f04b02 feat(parser): strip leading stop-words (at, the, a, an) from noun phrase
Allows `look at lamp`, `examine the letter`, `take a key`, `take an oil lamp`.
Stop-words are only removed from the head of the noun phrase, not from
anywhere in the middle.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:36:34 -05:00
ejlewis 14a58481b1 refactor(engine): theme is a UI preference; remove it from GameState
Previously, clicking the theme button updated localStorage and the DOM but
not state.theme, so the engine's `theme` meta-verb toggled from stale state.
Theme is now exclusively UI/storage concern. Old saves with the field still
load; the field is silently ignored.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:33:57 -05:00
ejlewis 657ed22b48 refactor(engine): drop redundant string[] casts now that RoomState includes arrays
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:30:35 -05:00
ejlewis 6cffb87a63 feat(engine): widen state value unions and add 'ending' transcript kind
Drops redundant string[] casts in dispatcher paths and prepares the
TranscriptLine kind for the ending-screen render path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:28:14 -05:00
ejlewis e21a308e9d docs(mystery): implementation plan for engine prereqs
17 tasks covering type widening, theme removal, parser stop-words +
ambiguous + verb-target-prep variants, dispatcher handlers for
read/light/extinguish/use, ending detection, and UI ending screen.
Includes self-contained locked-exit fixture and manual playthrough.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:21:13 -05:00
ejlewis bcff8a42f9 docs(mystery): spec for engine prereqs (verbs, disambiguation, ending UI)
Hard prereqs from halfstreet-followon-notes plus should-fix items.
Polish items deferred. Phase 2 (full bible content draft) follows after
this lands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:07:24 -05:00
ejlewis 2b3a18f208 ci: add Cloudflare Pages deploy + split success/failure notify
ci/woodpecker/push/woodpecker Pipeline was successful
Mirrors the ethanjlewis.com pipeline structure:

- single combined build step (npm ci + test + build)
- deploy via npx wrangler to project halfstreet-io
- notify-success at priority 0, notify-failure at priority 1
- triggers on push to main + manual

Secret names are lowercase (cloudflare_api_token, cloudflare_account_id,
pushover_token, pushover_user) to match what's configured on the
halfstreet repo in Woodpecker.
2026-05-09 11:56:38 -05:00
ejlewis 72f99295ca build: re-add wrangler as devDependency
Needed by the deploy step (npx wrangler pages deploy). Keeping it as a
local devDep means CI doesn't re-download wrangler on every run, matching
how ethanjlewis.com is set up.
2026-05-09 11:56:38 -05:00
ejlewis e044141043 ci: pushover notification on success too
ci/woodpecker/push/woodpecker Pipeline was successful
Single notify step now runs for both success and failure
(when.status: [success, failure]), branching on
CI_PIPELINE_STATUS for the title. Success uses Pushover
priority -1 (quiet) so passing-build pings don't alert at
night; failures stay at priority 0 (default).
2026-05-09 11:50:28 -05:00
ejlewis c0061491ab ci: pushover notification on pipeline failure
ci/woodpecker/push/woodpecker Pipeline was successful
Uses pushover_token and pushover_user secrets configured in Woodpecker.
Step runs only on failure (when.status: failure) so successful runs stay
quiet. Message includes branch, commit message, and a link back to the
pipeline.
2026-05-09 11:47:50 -05:00
ejlewis b80e4c32a5 fix(ci): commit package-lock.json so npm ci works
The Woodpecker pipeline runs `npm ci` which requires a lockfile;
without it the install step errors with EUSAGE.
2026-05-09 11:47:50 -05:00
ejlewis e31bf0fbff chore: standalone Halfstreet repo scaffolding
ci/woodpecker/manual/woodpecker Pipeline failed
- package.json: drop @astrojs/sitemap, fontsource fonts, wrangler,
  @cloudflare/workers-types, tsx, spotify scripts. Keep astro/yaml/zod
  + vitest/typescript/@astrojs/check/@types/node. Add GPL-3.0-or-later.
- astro.config.mjs: drop sitemap integration, sharp image config; set
  site to halfstreet.io.
- tsconfig.json: drop cloudflare-workers-types, drop functions/scripts
  from include.
- .gitignore: cloudflare-free; Obsidian workspace cache rules updated
  to post-rename src/world/.obsidian/ paths.
- README.md: stack, layout, design-doc index, license note.
- .woodpecker.yml: install + npm test + npm run build pipeline.
2026-05-09 11:36:19 -05:00
ejlewis 86e1aeb973 feat: rename mystery.astro -> index.astro, fix imports for src/ root
Halfstreet now lives at halfstreet.io as the entire site, so the game
serves at / instead of /mystery. After git-filter-repo lifted
src/mystery/ to src/, the page's css/ts imports need to drop the
mystery/ segment.
2026-05-09 11:33:06 -05:00
ejlewis 78b749dac4 chore(halfstreet): bible Obsidian-formatted edits + vault config
Substantial bible expansion (frontmatter, Obsidian list/heading formatting,
expanded core themes section, target length 25–28 rooms).

Adds .obsidian/ vault config (app/appearance/core-plugins) and gitignore
rules for the per-machine workspace cache, mirroring the world vault setup.

Done before extracting Halfstreet to its own repo so this work travels with
the filter-repo extraction.
2026-05-09 11:29:18 -05:00
ejlewis bc21a88786 Merge feature: mystery markdown content migration
Move Halfstreet game content (rooms, items, encounter narration, endings)
from TypeScript object literals into markdown files editable in Obsidian.
Engine, UI, and the public World type are unchanged. Three-room prototype
verified end-to-end via tests and manual playthrough.

Includes code-review followups: locked-exit requires validation,
endings-completeness check, and clear errors for malformed section headers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 11:13:38 -05:00
ejlewis 1f472402fd fix(mystery): code-review followups (locked-exit, endings, headers)
- Validate lockedExits[*].requires resolves to a known item or flag
- Throw if any of true/wrong/bad ending markdown files are missing
- Detect malformed ## headers (spaces, dots, etc.) and throw a clear
  error rather than silently dropping the section

Tests: 86 passing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 11:12:26 -05:00
ejlewis 4b8ebafe6f fix(mystery): swap gray-matter for yaml package (browser-safe)
gray-matter eagerly loads Node's Buffer API path even when only
matter(rawString) is called, crashing browser bundles. Replace it with
an inline frontmatter parser backed by the browser-safe yaml package.
All 84 mystery tests pass; build is clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:52:21 -05:00
ejlewis 20619cec09 chore(mystery): commit minimal Obsidian vault config; ignore workspace cache 2026-05-09 09:43:53 -05:00
ejlewis 506e36b801 refactor(mystery): remove story.ts; endings live in markdown 2026-05-09 09:36:28 -05:00
ejlewis 1b992642ec feat(mystery): encounters.ts uses narration() helper for prose 2026-05-09 09:35:41 -05:00
ejlewis c0c1a7e930 feat(mystery): assemble World from markdown via import.meta.glob
Rooms, items, and endings now come from .md files under world/{rooms,items,endings}/.
Encounters still come from encounters.ts (Tasks 11–12 will complete that leg).
Cross-reference validation at module init ensures exits, item refs, and encounter
refs are all consistent. Deletes rooms.ts, items.ts, roundtrip.test.ts, and the
one-shot migration script (whose output is already committed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:34:27 -05:00
ejlewis 0523158e61 test(mystery): round-trip verification of migrated markdown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:27:39 -05:00
ejlewis bbea3f4473 feat(mystery): migration script and produced markdown content
Adds scripts/migrate-mystery-content.ts which reads rooms, items,
encounters, and endings from TypeScript source and emits byte-identical
markdown files under src/mystery/world/{rooms,items,encounters,endings}/.
Installs tsx as a devDep to support .ts imports across src/ during the
one-shot run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:26:02 -05:00
ejlewis d3a2f4e1d7 feat(mystery): narration() helper and encounter narration registry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:23:37 -05:00
ejlewis bf8a693949 feat(mystery): parseEncounterNarration — phase and transition prose
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:21:29 -05:00
ejlewis e60844a937 feat(mystery): parseEnding — markdown to typed Ending
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:17:11 -05:00
ejlewis e108ca16e0 feat(mystery): parseItem — markdown to typed Item
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:13:51 -05:00
ejlewis cf257c040a fix(mystery): handle aliased wikilinks; symmetric locked-exit validation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:09:38 -05:00
ejlewis 5f3356ffb5 feat(mystery): parseRoom — markdown to typed Room
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 09:00:35 -05:00
ejlewis da7b6fac83 feat(mystery): add Zod schemas for markdown frontmatter
Defines runtime validation schemas (roomFrontmatterSchema,
itemFrontmatterSchema, endingFrontmatterSchema, encounterFrontmatterSchema)
and their inferred TypeScript types. All 8 TDD tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 08:56:44 -05:00
ejlewis 2ad81f356a docs(mystery): implementation plan for markdown content migration
14-task plan covering: gray-matter+zod setup, schemas, four pure
parsers (room/item/ending/encounter), narration() helper with
auto-registration, one-shot migration script, round-trip
verification, world assembly cutover, encounters.ts refactor,
manual playthrough, and Obsidian vault config.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 08:50:58 -05:00
ejlewis baed9dd2f7 docs(mystery): camelCase frontmatter, prototype goal, content collections rationale
Address spec review feedback: switch frontmatter convention from snake_case
to camelCase to match existing TS field names; surface the working
three-room prototype as the explicit deliverable; rephrase the Astro
content collections out-of-scope item to clarify the import.meta.glob
choice is a tooling decision, not a feature exclusion.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 08:42:12 -05:00
ejlewis 1a283076ac docs(mystery): spec for markdown content migration
Move rooms, items, encounter narration, and endings from TypeScript
object literals to markdown files editable in Obsidian. Engine code,
tests, and the World shape are unchanged. Tonal refinement is a
separate spec that follows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 08:36:38 -05:00
ejlewis 157dd07c9c fix(mystery): letter doesn't open in foyer 2026-05-09 01:03:47 -05:00
ejlewis f75030e7ee fix(mystery): persist player input lines so they survive reload
appendLines() only updated the DOM, never state.transcript, so any
UI-originated line (player input, restart/undo/quit messages) vanished
on page reload. Engine narration was unaffected because dispatch()
already adds its lines to state.transcript.

Fix: appendLines() now pushes into state.transcript (capped at
TRANSCRIPT_CAP) and renders. Engine output uses renderAll() directly
since dispatch already added its lines to state.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 00:54:54 -05:00
ejlewis d3e2bd72a5 Merge feature: mystery text adventure (Halfstreet) — engine + UI + bible
14 implementation tasks + 1 critical-fix commit + 1 follow-on notes commit.

Adds:
- Pure-function text-adventure engine (parser, dispatcher, encounters,
  save) with 53 vitest tests passing
- /mystery route with fullscreen CRT terminal (amber + ANSI themes)
- Mobile tap-chip input system above the soft keyboard
- 3-room sample world for engine validation and UI scaffolding
- Halfstreet content bible (gating artifact for room-prose follow-on plan)
- Launch script for the future MysteryCard click on the homepage

Bundle: 6.2 KB gzipped (target <80 KB).
No runtime LLM calls.

Spec: docs/superpowers/specs/2026-05-08-mystery-text-adventure-design.md
Plan: docs/superpowers/plans/2026-05-08-mystery-text-adventure.md
Bible: docs/superpowers/specs/halfstreet-bible.md
Follow-on notes: docs/superpowers/specs/halfstreet-followon-notes.md
2026-05-09 00:41:23 -05:00
ejlewis 731e51c6b9 docs(mystery): notes for follow-on room-prose plan
Captures the final code reviewer's recommendations: hard prerequisites
(read/light/extinguish/use verbs; disambiguation; endedWith ending screen),
should-fix items (look-at parser polish, theme-state divergence, roomState
type lie), and a polish backlog. Read this before generating the next plan.
2026-05-09 00:40:13 -05:00
ejlewis 33bc84e30b fix(mystery): wait verb routes to encounters; chip list reflects dynamic items
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 00:37:45 -05:00
ejlewis 1b7d2ce4e7 docs(mystery): author Halfstreet content bible — gating artifact for room prose 2026-05-09 00:22:57 -05:00
ejlewis 9e49f1f519 feat(mystery): mobile chip computation and rendering
Pure computeChips function (TDD, 4 tests) generates context-aware direction/item/encounter/meta chips from game state; chip-render.ts wires chips to DOM; terminal.ts calls refreshChips on init, each Enter dispatch, restart, and undo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 00:18:25 -05:00
ejlewis 3c0c386bbe feat(mystery): theme toggle wiring with localStorage persistence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 00:14:15 -05:00
ejlewis 5f5dc6071b feat(mystery): terminal — input handling, dispatch wiring, autosave 2026-05-08 23:45:10 -05:00
ejlewis 96d3036c4d feat(mystery): /mystery route + CRT bezel + theme scaffold 2026-05-08 23:39:58 -05:00
ejlewis 460626aad9 test(mystery): end-to-end playthrough against sample world 2026-05-08 23:32:33 -05:00
ejlewis d8c9b44058 feat(mystery): sample 3-room world for engine validation 2026-05-08 23:26:45 -05:00
ejlewis 49fc5a1015 feat(mystery): encounter phase machine wired into dispatcher 2026-05-08 23:18:22 -05:00
ejlewis 00f44ce817 feat(mystery): dispatcher — go, look, take, drop, examine, inventory
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 22:56:11 -05:00
ejlewis bd6b421ce9 feat(mystery): save layer — round-trip, schema versioning, transcript cap 2026-05-08 22:50:47 -05:00