Skip to content

Commit a345149

Browse files
pratiksahuclaude
andcommitted
Role-tailored questions, capped at 10 (v0.4.0)
Add per-question `roles` + `priority` to Question. Both quick and full modes now filter to the user's role and ask at most MAX_QUESTIONS (10), keeping the highest-priority questions — roughly 5 shared fundamentals + 5 role-specialty per role. Sections become purely thematic grouping; role logic lives on the questions. - session.ts: filterByRole + selectSections (role filter then top-10 cap) - checklists.ts: review/retag every quick + full question with roles + priority - stack.ts: priority on each candidate so stack questions compete for slots - cli.ts: roleSections (uncapped, for --list) + buildSections (capped, for sessions) - index.ts: export selectSections, filterByRole, MAX_QUESTIONS - README/CLAUDE: document the model; bump 0.3.0 -> 0.4.0 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5374fd4 commit a345149

10 files changed

Lines changed: 305 additions & 141 deletions

File tree

CLAUDE.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ Guidance for working in this repository.
66

77
`think-before` — a small, **zero-runtime-dependency** TypeScript CLI published to npm. It walks a developer through a fast yes/no checklist *before* they make a change, so the side-effects of "simple" changes surface on the whiteboard instead of in production.
88

9-
- **`quick` mode (default)**~15 blast-radius questions for the "I'm about to touch the big app" moment.
10-
- **`full` mode** — a full pre-development discovery checklist, with `--role designer|engineer|developer` to filter (shared sections always shown).
9+
- **`quick` mode (default)** — blast-radius questions for the "I'm about to touch the big app" moment.
10+
- **`full` mode** — pre-development discovery questions.
11+
12+
Both modes are **role-tailored and capped at 10 questions** (`MAX_QUESTIONS` in `src/session.ts`). Every `Question` declares the `roles` it applies to (omit = everyone) and a `priority`; `--role designer|engineer|developer` filters to the matching questions, and when more than 10 are eligible the highest-priority ones win. The aim is ~5 shared fundamentals + ~5 role-specialty per role.
1113

1214
Answers are single keypresses (`y`/`n`/`s`/`?`/`q`). On a real terminal it runs **full-screen** (alternate screen buffer, classic-installer style, one boxed question at a time); piped/non-TTY input auto-falls back to scrolling **line mode**. Risky answers get flagged; the user gets a summary and an optional Markdown report.
1315

@@ -33,13 +35,13 @@ ESM, `module: NodeNext`. Source in `src/`, compiled to `dist/`. Relative imports
3335
| File | Responsibility |
3436
| --- | --- |
3537
| `src/types.ts` | All shared types (`Question`, `Section`, `Checklist`, `RecordedAnswer`, `SessionResult`, `Role`, `Detection`, `ProjectKind`, `Config`, `ProjectRecord`). |
36-
| `src/checklists.ts` | **The content.** The `quick` and `full` checklist data. This is where base questions are added/edited. |
38+
| `src/checklists.ts` | **The content.** The `quick` and `full` checklist data. This is where base questions are added/edited. Each question carries `roles` (omit = everyone) + `priority` (higher = asked first). Sections are now purely thematic grouping (`roles: ["shared"]`); role logic lives on the questions. |
3739
| `src/detect.ts` | Read-only project scan. `detectProject`/`safeDetect` classify the cwd as frontend/backend/fullstack/unknown by matching framework deps + marker files; `kindLabel` formats it. |
3840
| `src/stack.ts` | The stack-specific question bank. `stackSection(detection, role)` builds an extra `Section` of questions relevant to the detected stack, filtered by role. |
3941
| `src/config.ts` | User-level persistence at `~/.config/think-before/config.json` (honors `$XDG_CONFIG_HOME`). Stores per-project role + detection. |
4042
| `src/prompt.ts` | Single-keypress prompts: `askYesNo` and `askChoice` (role picker), with a shared non-TTY line-reader fallback for piped/CI input. Used by **line mode**. |
4143
| `src/tui.ts` | The full-screen (alternate-screen-buffer) installer-style UI. `runTui` drives role → welcome → one boxed question per screen → final, and **always restores the terminal**. `tuiAvailable()` gates it on a real TTY. |
42-
| `src/session.ts` | `runSession` (drives the **line-mode** prompts, records + flags answers) and `filterSections` (role filtering). |
44+
| `src/session.ts` | `runSession` (drives the **line-mode** prompts, records + flags answers); `filterSections` (section-level role filter); `filterByRole` (per-question role filter) + `selectSections` (role filter then cap to the top `MAX_QUESTIONS` by priority). |
4345
| `src/report.ts` | Terminal summary (`printSummary`), Markdown report (`buildMarkdown`/`saveReport`), and `printList`. |
4446
| `src/cli.ts` | Entry point (`bin`). Arg parsing, help/version, detection, role resolution + persistence, `config` mode, orchestration, save logic. |
4547
| `src/index.ts` | Public library API (re-exports for programmatic use). |
@@ -48,14 +50,14 @@ Data flow: `cli` parses args → `safeDetect(cwd)` → resolve role (flag > save
4850
- **TUI** (default on a real TTY): `runTui` asks for role if unknown, shows a welcome/detection screen, runs the questions full-screen, returns the `SessionResult` + chosen role. `cli` then persists, and prints the detection banner + `printSummary` into the *restored* terminal.
4951
- **Line mode** (piped/non-TTY, or `--no-tui`): `askRole` if needed → banner → `runSession`.
5052

51-
Both then share `printSummary` → optional `buildMarkdown`/`saveReport`. Sections are built by a shared `buildSections(role)` closure (`filterSections` + append `stackSection`) so both modes stay in sync. `--list` always uses line mode.
53+
Both then share `printSummary` → optional `buildMarkdown`/`saveReport`. Sections are built by two shared closures in `cli.ts`: `roleSections(role)` (`filterSections` + append `stackSection` + `filterByRole`) is the full role-relevant set, and `buildSections(role)` wraps it in `selectSections` to cap at 10. Sessions (TUI + line) use `buildSections`; `--list` uses `roleSections` (uncapped, the full reference for a role) and always uses line mode.
5254

5355
## Conventions & gotchas
5456

55-
- **Editing questions:** each `Question` declares `risky: "yes" | "no"` (which answer raises a flag) and an optional `why` (shown on `?` and beside flags). Keep questions phrased so a yes/no answer is meaningful. Most `full` questions are "Have you / do you know…" so a `no` is the gap.
56-
- **Stack questions** live in `src/stack.ts` as `CANDIDATES`, each with `roles` (who it applies to) and a `when(detection)` predicate. To support a new framework, add its dep/marker to the maps in `src/detect.ts`; to add a tailored question, add a `CANDIDATES` entry. Detection is **read-only and must never throw**`safeDetect` swallows errors, and `detectProject` only reads a handful of manifest files + checks marker folders (it peeks one level into `apps/`/`packages/`/`services/` for monorepos).
57+
- **Editing questions:** each `Question` declares `risky: "yes" | "no"` (which answer raises a flag), an optional `why` (shown on `?` and beside flags), the `roles` it applies to (omit = everyone), and a `priority`. Keep questions phrased so a yes/no answer is meaningful. Most `full` questions are "Have you / do you know…" so a `no` is the gap. **Mind the cap:** only the top `MAX_QUESTIONS` (10) by priority per role are asked — when adding a question, set a priority that reflects whether it should displace an existing one for that role. Aim to keep each role at roughly 5 shared + 5 specialty in the top 10 (eyeball it with `--role X --list` for the full set and a piped session for the capped set).
58+
- **Stack questions** live in `src/stack.ts` as `CANDIDATES`, each with `roles` (who it applies to), a `when(detection)` predicate, and a `priority` (last arg to the `q()` helper) so they compete with base questions for the 10 slots. To support a new framework, add its dep/marker to the maps in `src/detect.ts`; to add a tailored question, add a `CANDIDATES` entry. Detection is **read-only and must never throw**`safeDetect` swallows errors, and `detectProject` only reads a handful of manifest files + checks marker folders (it peeks one level into `apps/`/`packages/`/`services/` for monorepos).
5759
- **Config persistence is best-effort:** `saveConfig` may fail on a read-only home; `cli.ts` wraps it in try/catch and continues. Don't make a run depend on config being writable. Tests should set `XDG_CONFIG_HOME` to a temp dir to avoid touching the real `~/.config`.
58-
- **Role** is resolved as: `--role` flag > saved per-project role > `defaultRole` > interactive `askChoice` (TTY only). In non-TTY with nothing saved, role stays `undefined` (all sections shown). Role now affects `quick` mode too (it filters the stack section), so don't reintroduce the old "role only affects full mode" guard.
60+
- **Role** is resolved as: `--role` flag > saved per-project role > `defaultRole` > interactive `askChoice` (TTY only). In non-TTY with nothing saved, role stays `undefined` — then **all** questions are eligible and the cap returns a cross-role top 10 (a sensible "pick a role for a tailored set" fallback). Role now drives **both** modes via per-question `roles` (not just the stack section), so don't reintroduce the old "role only affects full mode" guard.
5961
- **Zero runtime dependencies — keep it that way.** Colors are hand-rolled ANSI in `colors.ts`; prompts use Node's `readline` directly. Don't add runtime deps without a strong reason.
6062
- **Non-TTY input** uses ONE shared `readline` interface (`src/prompt.ts`). Do not create a `readline` interface per question — that drops buffered piped input. Call `closeInput()` at the end so the process can exit.
6163
- **TUI terminal restoration is sacred.** `src/tui.ts` enters the alternate screen + raw mode + hides the cursor; `restore()` must undo all of it. It's idempotent and wired to `finally`, `process.on("exit")`, and SIGINT/SIGTERM handlers — keep all those paths. In raw mode `Ctrl-C` arrives as a `ctrl+c` *keypress* (ISIG is off), normalized to `"quit"` like `q`; the signal handlers are backstops. If you add a new early-return/throw inside `runTui`, the `finally` already covers it — don't bypass it. Testing the TUI needs a PTY (`expect`); piped input can't exercise it (it falls back to line mode by design).
@@ -81,7 +83,18 @@ Published as **`@geekyants/think-before`** (scoped). The installed binary is sti
8183

8284
`prepublishOnly` runs the build. `files` ships only `dist/`, `README.md`, `LICENSE`. `publishConfig.access` is `public` so the scoped package publishes publicly (scoped packages default to private/paid).
8385

86+
**Publish via GitHub Actions, not from your laptop.** `.github/workflows/release.yml` publishes to npm when a `vX.Y.Z` tag is pushed. It uses an org **Automation** npm token (repo secret `NPM_TOKEN`) which bypasses 2FA/OTP — a local `npm publish` will fail on an OTP prompt. The workflow verifies the tag matches `package.json` version, builds, publishes with `--provenance`, and creates a GitHub Release.
87+
88+
To cut a release:
89+
90+
```bash
91+
npm version patch # (or minor/major) bumps package.json + makes a vX.Y.Z commit & tag
92+
git push --follow-tags # pushing the tag triggers the Release workflow
93+
```
94+
95+
If the version was already bumped by hand (package.json edited, committed), just tag and push the tag to match:
96+
8497
```bash
85-
npm login # must be a member of the @geekyants npm org
86-
npm publish # access:public is already set in package.json
98+
git tag v$(node -p "require('./package.json').version")
99+
git push --follow-tags
87100
```

README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ think-before # the installed command is just `think-be
3838

3939
| Mode | What it's for |
4040
| --- | --- |
41-
| `quick` *(default)* | The "I'm about to touch the big app" moment. ~16 sharp questions about blast radius, data, contracts, your safety net, and timing. |
42-
| `full` | A team-level pre-development discovery sweep: problem, users, scope, risks, plus role sections for designers, engineers, and developers. |
41+
| `quick` *(default)* | The "I'm about to touch the big app" moment. Blast radius, data, contracts, your safety net, and timing. |
42+
| `full` | A team-level pre-development discovery sweep: problem, users, scope, risks, plus the questions specific to your role. |
4343

44-
In `full` mode you can focus on one role (shared sections are always included):
44+
Both modes ask **at most 10 questions** — the ones most relevant to *you*. Every question is tagged with the roles it applies to and a priority; pick a role and you get your fundamentals plus your own specialty, never a wall of questions meant for someone else:
4545

4646
```bash
47-
think-before full --role designer
48-
think-before full --role engineer
49-
think-before full --role developer
47+
think-before --role designer # quick check, design-focused
48+
think-before full --role engineer # discovery, architecture-focused
49+
think-before full --role developer # discovery, implementation-focused
5050
```
5151

5252
## Smart, stack-aware questions
@@ -72,7 +72,7 @@ Skip the scan anytime with `--no-detect`.
7272

7373
## Your role & saved config
7474

75-
The first time you run it in a project, it asks **what your role is** (designer / engineer / developer) and tailors the stack questions to you. Your answer — plus the detected stack — is remembered per project in:
75+
The first time you run it in a project, it asks **what your role is** (designer / engineer / developer) and tailors the whole question set to you. Your answer — plus the detected stack — is remembered per project in:
7676

7777
```
7878
~/.config/think-before/config.json # honors $XDG_CONFIG_HOME
@@ -93,7 +93,7 @@ By default, in an interactive terminal `think-before` takes over the screen like
9393
║ think-before · quick ║
9494
╠══════════════════════════════════════════════════╣
9595
║ ║
96-
║ Question 5 / 15 · Blast radius ║
96+
║ Question 5 / 10 · Blast radius ║
9797
║ ║
9898
║ Could this change behavior in flows you are NOT ║
9999
║ going to manually test? ║
@@ -104,7 +104,7 @@ By default, in an interactive terminal `think-before` takes over the screen like
104104
║ ‹ Yes › ‹ No › ‹ Skip › ║
105105
║ ║
106106
╚══════════════════════════════════════════════════╝
107-
y yes n no s skip ? why q quit [███░░░░░░░] 5/15
107+
y yes n no s skip ? why q quit [█████░░░░░] 5/10
108108
```
109109

110110
It uses the terminal's alternate screen buffer (like `vim` or `less`), so when you're done — or quit with `q`/`Ctrl-C` — your terminal is restored exactly as it was, and the summary + report prompt print into your normal scrollback.
@@ -151,14 +151,15 @@ The report is plain Markdown — drop it into a PR description, a ticket, or you
151151

152152
```ts
153153
import {
154-
quick, full, filterSections, runSession, buildMarkdown,
154+
quick, full, filterSections, selectSections, runSession, buildMarkdown,
155155
detectProject, stackSection,
156156
} from "@geekyants/think-before";
157157

158158
const detection = detectProject(process.cwd()); // { kind: "fullstack", frameworks: [...] }
159159
const extra = stackSection(detection, "engineer");
160160

161-
const sections = [...filterSections(full, "engineer"), ...(extra ? [extra] : [])];
161+
const all = [...filterSections(full, "engineer"), ...(extra ? [extra] : [])];
162+
const sections = selectSections(all, "engineer"); // role-tailored, capped at 10
162163
const result = await runSession(full, sections);
163164
const markdown = buildMarkdown(full, result, new Date());
164165
```
@@ -172,7 +173,7 @@ npm run build # compile to dist/
172173
npm start # run the built CLI
173174
```
174175

175-
Want to add or tweak questions? They live in [`src/checklists.ts`](src/checklists.ts) — each question declares which answer (`yes`/`no`) is the risky one, plus an optional `why`. PRs welcome.
176+
Want to add or tweak questions? They live in [`src/checklists.ts`](src/checklists.ts) — each question declares which answer (`yes`/`no`) is the risky one, an optional `why`, the `roles` it applies to, and a `priority` (higher = asked first when the 10-question cap kicks in). PRs welcome.
176177

177178
## License
178179

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@geekyants/think-before",
3-
"version": "0.3.0",
3+
"version": "0.4.0",
44
"description": "A fast yes/no reflection CLI to surface the side-effects of a change before you make it. Scans your project (frontend/backend/full-stack) and asks stack-relevant questions. Think before you touch the big app.",
55
"type": "module",
66
"bin": {

0 commit comments

Comments
 (0)