The CLI was rendering real lesson titles and "TBD" summaries from
unreleased modules. Now renderModuleDetail returns lessons: [] in JSON
mode and a single "available after unlock" line in human mode when a
module is locked, regardless of what the API returns.
This is a client-side mask; the API still emits the data on the wire.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The first-poll diagnostic dumped the 5 most recent emails from the
shared Resend account, including their `to` field, leaking unrelated
customer addresses into CI logs. Replace with a counts-only summary
and drop the recipient from the timeout error message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
list now returns module 0 (prework) first, and the shared test account
is only enrolled in module 0 — switch the get fixtures from m1l1 to
m0l1 and update the list assertion to expect module 0 at index 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The API ships {error: "<code>"} envelopes (e.g. "course_not_found"); we
were displaying that raw, or worse, "[object Object]" when callers tried
to print the payload. Map the 16 known codes (catalogued from the
delivery API routes) to human strings, prefer the optional `message`
field on auth routes, and fall back to a mentor-facing message for
unknown codes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop MIN_MODULE from 1 to 0 so parseLessonRef and parseModuleRef accept
m0/m0l1 (the 10xdevs3 prework module). Lesson numbers remain positive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The notify-slack job checked only check, e2e, and publish-npm but
missed version and github-release from its needs list. A failure in
either would show a green notification and skip #alerting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the backend rate-limits magic-link requests (e.g. from CI re-runs),
tests now skip with a clear message rather than hard-failing the entire
suite. AuthRateLimitedError is caught in beforeAll and sets authSkipped
flag that each test checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
auth-flow.test.ts now uses ensureSharedAuth() (single login per run)
and verifies the resulting auth.json, rather than triggering a
duplicate login that gets rate-limited by the backend.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In --json mode the error envelope goes to stdout, not stderr. The
earlyExit handler now reports both streams so CI failures show the
actual API error message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npm pack --dry-run --json consistently exceeds 30s on windows-latest
runners. Since package contents are platform-independent, this test
only needs to run on Ubuntu (check job).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npm pack --dry-run --json takes >15s on windows-latest runners.
Bump timeout from 15s to 30s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- auto-version.mjs: use os.tmpdir() instead of hardcoded /tmp/ for
release-notes.md (Windows has no /tmp)
- binary.test.ts: raise startup budget to 150ms on Windows (CI runners
have slower process spawn overhead)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shell commands like `echo '...' > file` and `cp` don't work on Windows.
Replace with writeFileSync, appendFileSync, and copyFileSync so the
auto-version smoke tests pass on both unix and Windows CI runners.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- .github/workflows/ci.yml: add e2e + e2e-windows jobs with Resend secrets,
extend check-windows with build:binary + smoke tests
- docs/reference/platform-support.md: update CI testing table to match reality
- context/changes/e2e/plan.md: mark phase 5 automated steps complete
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add PowerShell Set-Clipboard to clipboard table, expand CI testing
section with unit+e2e matrix for both Ubuntu and Windows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch Windows CI job to native PowerShell with Get-ChildItem for test
file enumeration (no glob expansion needed). Add platform-support
reference doc for learners.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use path.join() instead of hardcoded forward slashes for path assertions
that compare absolute paths on Windows (backslash vs forward slash).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On Windows, configDir() reads APPDATA, not XDG_CONFIG_HOME. Tests that
only redirected XDG_CONFIG_HOME leaked config state across test files,
causing failures on windows-latest CI. Extract redirectConfigDir/
restoreConfigDir helper that sets both env vars, apply across all 10
test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PowerShell does not expand glob patterns — use Git Bash (available on
all GitHub Actions Windows runners) so tests/*.test.ts resolves.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PowerShell does not expand bare globs — bun test needs ./tests/*.test.ts
to resolve the path on Windows runners.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Windsurf tool profile with detection (.windsurfrules, .windsurf/).
Make doctor check the configured tool's directory instead of hardcoded
.claude/. Add 10x-cli-guide skill for OS- and tool-aware daily usage
guidance. Add Cursor and Windsurf MCP config templates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass the resolved tool profile id through fetchLesson's options bag
so the API can substitute universalContent for non-Claude learners.
Mirrors the existing fetchArtifact precedent in the same file.
- src/lib/api-content.ts: add optional tool? to fetchLesson options;
emit ?tool= when present
- src/commands/get.ts: pass profile.toolId at both fetchLesson call
sites (install flow + print/filter flow)
- tests/helpers/api-content-mock.ts: widen fetchLessonImpl signature
- tests/get-command.test.ts: add three propagation tests
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bump version 0.5.0 → 1.0.0 and add CHANGELOG entry for the directory-shaped
skill bundle. The CLI is functionally complete on master (commits 09a60c5,
afb9c48); this commit only stages the release metadata. `npm publish` is a
follow-up action — not run from this commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In --print human mode, a skill's SKILL.md still goes to stdout, but every
non-SKILL.md file under the skill directory is now surfaced on stderr as a
"Note: skill X has N additional files not shown in --print: ..." line so
students don't assume the print output is the entire skill. JSON mode is
unchanged (the full files[] array is already in the response).
Touched:
- src/commands/get.ts: emitMultiFileSkillNotice() called from both single-
artifact (--name) and multi-skill (no --name) print paths.
- tests/print-mode.test.ts: 4 new cases covering single-file no-notice,
multi-file notice content, JSON-mode silence, and per-skill iteration
in the no-name flow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wave 2.1 PR2 closes the UX gap where students who switch tools would
silently accumulate orphan artifacts from their old profile. When
resolveToolProfile settles on a new tool and a prior tool's manifest
exists under a different manifestDir, the CLI now prompts once per
orphan: migrate (recommended), remove 10x artifacts, or keep both
(don't ask again).
- findOrphanedManifests in writer.ts returns structured OrphanInfo
(profile, manifestPath, parsed manifest). Corrupt manifests are
skipped so migration never moves files without a file list.
- ToolConfig gains acknowledgedOrphans: string[] with shape validation
on read; readToolConfig drops the field when it isn't a string array.
- tool-switch.ts implements migrateArtifacts and deleteArtifacts:
- isSafeName gate on every manifest entry (skills, prompts, configs)
before any filesystem operation.
- moveIfSafe refuses symlinked sources, compares existing destinations
byte-for-byte (readFileSync + Buffer.equals) so two distinct binary
payloads that decode to U+FFFD never collapse as equal.
- EXDEV cross-device fallback writes to <to>.tmp then renames into
place; post-copy rmSync failure is reported via summary.skipped
while still counting the file as moved. Non-EXDEV rename errors
propagate.
- deleteArtifacts scopes removal to the 10x-written surface — a
Copilot orphan no longer wipes .github/workflows; only manifest-
listed files plus the now-empty skills/prompts/config-templates
subdirs are removed. Partial-failure contract documented.
- removeRulesBlockWithMarkers in sentinel-migration.ts strips the 10x
block from the old rules file, collapsing the splice to at most one
blank line and handling CRLF endings. Symmetric to
applyRulesBlockWithMarkers; apply + remove round-trips cleanly.
- tool-prompt.ts wires handleToolSwitch into resolveToolProfile for TTY
flows; the "keep" branch spreads the existing config so unknown
fields (future CLI versions, hand-edits) survive. The first-run
prompt's saveToolConfig also spreads existing config.
- commands/get.ts keeps the legacy verbose orphan warning for non-TTY
paths (CI, Docker); TTY flows get the interactive prompt instead.
- config.ts: writeJsonAtomic shared tmp+rename helper used by both
saveAuth and saveToolConfig — saveToolConfig is now atomic too, so a
crash mid-write can no longer corrupt acknowledgedOrphans state.
- fs-utils.ts: readFileOrNull hoisted out of tool-switch for reuse.
Plans:
- thoughts/shared/plans/2026-04-17-wave2.1-cli-polish.md (Phases 3-4)
- thoughts/shared/plans/2026-04-19-wave2.1-cli-polish-review-fixes.md
- thoughts/shared/plans/2026-04-20-wave2.1-cli-polish-review-fixes-review-fixes.md
bun test: 328 pass, 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wave 2.1 PR1 lands two low-risk CLI polish items ahead of the prework
release so first-run students benefit from a smarter default and the
profile layer gains a drift-safety net.
- Auto-detection: scan the project root for tool-native markers
(.cursor/rules, .claude/, CLAUDE.md, .github/copilot-instructions.md,
AGENTS.md, .agents/, .ai/) and rank signals by confidence. The
interactive first-run prompt pre-fills initialValue with the detected
tool and prints a "Detected: <tool> (<reason>)" hint. User still
confirms — no silent selection. Non-TTY paths are unchanged.
- Coherence invariants: SENTINEL_BEGIN/END hoisted to shared constants;
every profile now references them. New describe block asserts 7
invariants (paths rooted under manifestDir, safe leaf names, non-empty
rulesFile, identical sentinels, unique manifestDir, unique toolId,
non-empty displayName) so refactors fail fast on CI.
Plan: thoughts/shared/plans/2026-04-17-wave2.1-cli-polish.md (Phases 1-2)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: add 10x-cli-setup skill for README-driven CLI configuration
Add a skill that fetches the latest README from GitHub and walks users
through installing, authenticating, and configuring 10x-cli. Include
skills/ directory in npm package files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add Agentic Installation section to README
Document how to install the 10x-cli-setup skill via skills.sh,
enabling AI agents to handle CLI setup automatically.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement v1 signing verification (v1:<keyId>:<hash> canonical string) and
add loadEffectiveKeyset() with localhost-gated env var override for e2e testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>