diff --git a/CHANGELOG.md b/CHANGELOG.md index 65e443b..7975773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ All notable changes to atom land here. Format: [Keep a Changelog](https://keepac No changes pending. v0.3 candidate list at `docs/planning/v0.3.md`. +## [0.3.0] — 2026-06-05 + +Opens the v0.3 line with the first candidate to ship ahead of scope-lock: a global Claude skill that runs atom's bootstrap from inside any session. The five CLIs align to 0.3.0; `bin/atom-update-check` stays at 0.1.0. + +### Added + +- **`/atom-new` — bootstrap a project from inside Claude** (v0.3 #23). A global Claude Code skill that drives the Mode 1 bootstrap conversationally from any directory or session — no `cd ~/.atom/atom`, no terminal round-trip. New top-level `skills/` directory holds **global user-facing** skills (distinct from `scaffold/.claude/skills/`, which are copied *into* bootstrapped projects). The skill is a **thin launcher**: it locates the installed atom (`$ATOM_SOURCE_DIR` → `$ATOM_INSTALL` → `~/.atom/atom/`), reads `AGENTS.md` Mode 1 + the docs it references at invoke time, and runs that flow against a target directory it asks the user to confirm. It deliberately does *not* restate the bootstrap steps, so it can't drift from `AGENTS.md` — the single source of truth. Scoped to Mode 1 only; Modes 2 (maintain atom) and 3 (build atom features) are dev-facing and stay out. Fails gracefully with the `install.sh` one-liner when no atom checkout is found. +- **Global-skill linking in `install.sh` and `atom upgrade`.** Both symlink each subdirectory of `skills/` into `~/.claude/skills/` (e.g. `~/.claude/skills/atom-new` → `~/.atom/atom/skills/atom-new`). Because it's a symlink into the git checkout, `atom upgrade`'s `git pull` refreshes skill content with no copy step — no new distribution channel, consistent with git-not-npm. Idempotent: re-runs repoint stale symlinks. Safe: a real (non-symlink) file or directory the user owns at the target name is left untouched and the skip is reported, never silently clobbered. `atom upgrade` gained an exported `linkGlobalSkills(installDir)` that runs after the CLI re-install (covers users who installed before global skills existed). Best-effort in both paths — a link failure never fails the install or upgrade. + +### Tests + +- **Test 21 in `scripts/test-atom-setup.sh`** (14 assertions): the skill ships and is shaped as a discoverable thin launcher (frontmatter `name`/`description`, references `AGENTS.md` Mode 1, has the not-installed fallback); both install paths wire the symlink and protect user-owned dirs; and a hermetic functional pass exercising `linkGlobalSkills` directly across fresh-link / idempotent-repoint / user-owned-dir-skip, using a scratch `HOME` so the runner's real `~/.claude` is never touched. Suite total 164 → 178. Added a `bin/atom` dependency pre-flight (Test 20 and 21 both load `picocolors` via `upgrade.js`). +- **Fixed two CI-only test failures** (pre-existing, environment-specific). `14.15` asserted on `name is ready`, but picocolors auto-enables ANSI codes when `$CI` is set (GitHub Actions), splitting the string — pinned `NO_COLOR=1` in the harness so output is plain text everywhere. `20.7` double-quoted backticks in its grep pattern, producing `` \` `` which GNU grep (Ubuntu CI) treats as a buffer-start anchor that never matches mid-pattern, while BSD grep (macOS) took it literally — single-quoted the pattern so backticks stay literal. Verified against GNU grep 3.12. The wizard-e2e CI job, red since 2026-05-14, is green again. + ## [0.2.1] — 2026-05-14 Bundled release for Wave 2 (user-facing features) + Wave 3 (distribution architecture). All five CLIs aligned to 0.2.1; new `bin/atom-update-check` ships at 0.1.0. diff --git a/README.md b/README.md index e902444..53261a5 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,9 @@ To keep atom itself up to date later, run `atom upgrade`. > [!TIP] > Want zero questions? Run `atom-setup new my-project --bare` and you're done in under 5 seconds. All flags pass through (`--minimal`, `--full`, `--dry-run`, `--resume`, etc.). +> [!TIP] +> Live in Claude Code? Install puts an **`/atom-new`** skill on your machine. Type `/atom-new` in any session — Claude walks you through the same bootstrap conversationally, no trip back to the terminal. The wizard above is still there when you want the fast, no-questions path. + ### Don't trust curl-pipe-bash? Install manually. ```bash diff --git a/VERSION b/VERSION index 0c62199..0d91a54 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.1 +0.3.0 diff --git a/bin/atom-setup/package.json b/bin/atom-setup/package.json index 118132b..e0bac6a 100644 --- a/bin/atom-setup/package.json +++ b/bin/atom-setup/package.json @@ -1,6 +1,6 @@ { "name": "@atom/atom-setup", - "version": "0.2.1", + "version": "0.3.0", "description": "Interactive wizard that transforms a cloned atom checkout into a personalized project.", "type": "module", "bin": { diff --git a/bin/atom/package.json b/bin/atom/package.json index 8291923..1b56828 100644 --- a/bin/atom/package.json +++ b/bin/atom/package.json @@ -1,6 +1,6 @@ { "name": "@atom/atom", - "version": "0.2.1", + "version": "0.3.0", "description": "Top-level help dispatcher for atom's tooling. Lists every CLI in one place.", "type": "module", "bin": { diff --git a/bin/atom/src/upgrade.js b/bin/atom/src/upgrade.js index 46d5e55..74e8fa2 100644 --- a/bin/atom/src/upgrade.js +++ b/bin/atom/src/upgrade.js @@ -13,7 +13,17 @@ // Refuses to upgrade if the install dir's git tree is dirty — the // user has been editing it and a pull might lose work. -import { existsSync, readFileSync, realpathSync, statSync } from 'node:fs'; +import { + existsSync, + lstatSync, + mkdirSync, + readFileSync, + readdirSync, + realpathSync, + rmSync, + statSync, + symlinkSync, +} from 'node:fs'; import { homedir } from 'node:os'; import { dirname, join } from 'node:path'; import { spawnSync } from 'node:child_process'; @@ -130,9 +140,66 @@ export async function runUpgrade(args) { } } + linkGlobalSkills(installDir); + console.log(`\n ${color.green('✓')} now on atom ${color.bold(upstreamVersion)}`); } +// Symlink each skill in /skills/ into ~/.claude/skills/ so it's +// invocable from any Claude session. Mirrors the install.sh block; this +// path covers users who installed before global skills existed and +// repoints stale links after the git pull. Best-effort: a failure here +// never fails the upgrade (CLIs are already updated by this point). +export function linkGlobalSkills(installDir) { + const skillsSrc = join(installDir, 'skills'); + if (!existsSync(skillsSrc)) return; + + const claudeSkillsDir = join(homedir(), '.claude', 'skills'); + let entries; + try { + entries = readdirSync(skillsSrc, { withFileTypes: true }); + } catch { + return; + } + + for (const entry of entries) { + if (!entry.isDirectory()) continue; + const name = entry.name; + const target = join(skillsSrc, name); + const dest = join(claudeSkillsDir, name); + try { + mkdirSync(claudeSkillsDir, { recursive: true }); + const link = lstatExisting(dest); + if (link === 'symlink') { + rmSync(dest); + symlinkSync(target, dest); + console.log(` ${color.dim('→')} skill ${name} ${color.dim('linked')}`); + } else if (link === 'other') { + // A real file/dir the user owns — never clobber it. + console.log( + ` ${color.dim('→')} skill ${name} ${color.dim(`skipped (~/.claude/skills/${name} exists, not a link)`)}`, + ); + } else { + symlinkSync(target, dest); + console.log(` ${color.dim('→')} skill ${name} ${color.dim('linked')}`); + } + } catch { + // Non-fatal: the CLI upgrade already succeeded. + console.log(` ${color.dim('→')} skill ${name} ${color.dim('(link skipped)')}`); + } + } +} + +// Returns 'symlink' if dest is a symlink, 'other' if it's a real +// file/dir, null if it doesn't exist. +function lstatExisting(dest) { + try { + return lstatSync(dest).isSymbolicLink() ? 'symlink' : 'other'; + } catch { + return null; + } +} + function findInstallDir() { // 1. env override const env = process.env.ATOM_INSTALL; diff --git a/bin/learnings/package.json b/bin/learnings/package.json index 831b32f..0f00b1c 100644 --- a/bin/learnings/package.json +++ b/bin/learnings/package.json @@ -1,6 +1,6 @@ { "name": "@atom/learnings", - "version": "0.2.1", + "version": "0.3.0", "description": "Your local playbook of generalized patterns. Lives at ~/.atom/learnings, optionally synced to a private GitHub repo. Auto-copied into every new atom-bootstrapped project.", "type": "module", "bin": { diff --git a/bin/model-race/package.json b/bin/model-race/package.json index 90a0c20..4b14030 100644 --- a/bin/model-race/package.json +++ b/bin/model-race/package.json @@ -1,6 +1,6 @@ { "name": "@atom/model-race", - "version": "0.2.1", + "version": "0.3.0", "description": "Run the same feature spec through multiple AI models in parallel via Git worktrees. Compare, score, merge the winner.", "type": "module", "bin": { diff --git a/bin/nucleus/package.json b/bin/nucleus/package.json index 8ad7109..a494900 100644 --- a/bin/nucleus/package.json +++ b/bin/nucleus/package.json @@ -1,6 +1,6 @@ { "name": "@atom/nucleus", - "version": "0.2.1", + "version": "0.3.0", "description": "Cross-project learning store. Captures session learnings into ~/.nucleus, syncs across machines via GitHub.", "type": "module", "bin": { diff --git a/docs/planning/v0.3.md b/docs/planning/v0.3.md index 19bdc8e..bc98f34 100644 --- a/docs/planning/v0.3.md +++ b/docs/planning/v0.3.md @@ -167,6 +167,73 @@ doctor command answers them in one paste. --- +### #23 `/atom-new` bootstrap skill + +> **Status: built** (shipped ahead of v0.3 scope-lock, see `[Unreleased]` +> in `CHANGELOG.md`). Lives in `skills/atom-new/`; linked into +> `~/.claude/skills/` by `install.sh` + `atom upgrade`. Locked decisions +> below held: conversational, Mode 1 only, thin launcher, symlink +> distribution. Open questions resolved during build: named `atom-new`; +> target dir is confirmed per-invocation; not-installed path points at +> `install.sh`. **Released in 0.3.0 (2026-06-05)** — opened the v0.3 +> line ahead of full scope-lock. + +**What**: ship a global Claude Code skill (`/atom-new`) that drives the +Mode 1 bootstrap flow conversationally from **any** directory or +session — no `cd ~/.atom/atom`, no terminal round-trip. The skill is a +**thin launcher**: it locates the installed atom (`~/.atom/atom/`), +reads `AGENTS.md` Mode 1 + `docs/` from there, and executes the +conversational bootstrap against the current working dir. It does *not* +restate the steps — `AGENTS.md` stays the single source of truth. + +**Why**: today, to bootstrap you have to *be inside atom* — either +`cd ~/.atom/atom` so `CLAUDE.md → AGENTS.md` auto-loads, or run the +`atom-setup` wizard in a terminal. Both mean leaving wherever you +actually are. A global skill removes that friction, and — the real +unlock — its description makes Claude *proactively offer it* when a user +says "let's start a new project" in any session. You stop having to +remember atom exists. The `atom-setup` wizard already serves the +terminal-first path; this is the in-Claude conversational counterpart. + +**Scope (locked)**: **Mode 1 only.** Modes 2 (maintain atom) and 3 +(build atom features) are dev-facing — they only make sense inside the +`atom-dev` repo. Folding them into a global skill makes it ambiguous +("start a project... or maintain the template?") for zero benefit. One +focused skill = the bootstrap flow. + +**Flow (locked)**: **conversational**, not a wizard shell-out. The +guided dialogue is atom's differentiator *inside* Claude; the +deterministic 10-section wizard already exists for terminal lovers. + +**Distribution**: piggyback on existing machinery, no new channel. The +skill installs to `~/.claude/skills/atom-new/` as one more global +symlink that `install.sh` creates and `atom upgrade` refreshes — +consistent with the dev/use globals separation +(`atom_dev_use_separation_state` memory) and the git-not-npm decision +(`atom_distribution_decision` memory). + +**Open questions**: +- Name: `/atom-new` vs. `/new-project` vs. `/atom`. `/atom` is + ambiguous against the `atom` CLI; lean `/atom-new`. +- Where does the new project get created? The wizard handles target + dir; the skill must ask (cwd vs. a named subdir) since it can fire + from anywhere. +- Thin-launcher contract: the skill reads `AGENTS.md` Mode 1 from the + install at invoke time. Confirm it fails gracefully (points to + `install.sh`) when atom isn't installed. +- Discovery tuning: the skill `description:` is what makes Claude + proactively offer it. Get the trigger phrasing right ("start a new + project", "bootstrap from a template") without over-firing. +- Compatibility: Claude Code only. Other AI tools keep the + `cd` + AGENTS.md path and the `atom-setup` wizard. + +**Effort**: small. A thin `SKILL.md` (~20–40 lines pointing at the +installed `AGENTS.md`/`docs`) + one symlink wired into `install.sh` and +`atom upgrade`. The risk is *not* effort — it's the fork-the-instructions +trap, which the thin-launcher design kills. + +--- + ## Tier 2 — interesting, requires deliberate scoping These could fit atom but only if v0.3 explicitly decides atom is @@ -347,16 +414,20 @@ it — the embedding column belongs in the DB. material already exists; the work is curation + voice rewrite. Almost certainly yes if the maintainer is comfortable shipping the distilled patterns as opinionated defaults. -4. **Decide on #18 (cost tracking)**. The §1 wizard question forces +4. **Decide on #23 (`/atom-new` skill)**. Standalone, cheap (thin + launcher + one symlink), reinforces proactive discovery. Almost + certainly yes — the only real risk is the fork-the-instructions + trap, which the thin-launcher design already mitigates. +5. **Decide on #18 (cost tracking)**. The §1 wizard question forces the issue: ship the tracker, or drop the question. -5. **Decide on #16 (SQLite)**. Architectural; gate on whether #20 +6. **Decide on #16 (SQLite)**. Architectural; gate on whether #20 (semantic) is in scope and on whether scale demands it yet. -6. **Decide on #17 (research)**. Lowest urgency; defer to v0.4 if +7. **Decide on #17 (research)**. Lowest urgency; defer to v0.4 if v0.3 already fills up. -7. **Ship #22 Phase A now (out-of-band v0.2.x)**. WSL2 callout is a +8. **Ship #22 Phase A now (out-of-band v0.2.x)**. WSL2 callout is a ~1hr stopgap independent of the v0.3 wave. Phase B (native port) is demand-gated — re-evaluate after Phase A surfaces signal. -8. **#19, #20** ride along with whichever wave fits. +9. **#19, #20** ride along with whichever wave fits. --- diff --git a/install.sh b/install.sh index bb76134..971099a 100755 --- a/install.sh +++ b/install.sh @@ -140,6 +140,33 @@ if [ ${#MISSING[@]} -gt 0 ]; then exit 1 fi +# ─── global Claude skills ─────────────────────────────────────────── +# Symlink each skill in $TARGET/skills/ into ~/.claude/skills/ so it's +# invocable from any Claude session. Symlinks (not copies) into the git +# checkout mean `atom upgrade`'s git pull refreshes them for free. +# Best-effort: a skill link failing must not fail the whole install. +SKILLS_SRC="$TARGET/skills" +CLAUDE_SKILLS_DIR="$HOME/.claude/skills" +if [ -d "$SKILLS_SRC" ]; then + for skill_path in "$SKILLS_SRC"/*/; do + [ -d "$skill_path" ] || continue + skill_name="$(basename "$skill_path")" + dest="$CLAUDE_SKILLS_DIR/$skill_name" + mkdir -p "$CLAUDE_SKILLS_DIR" + if [ -L "$dest" ]; then + # Existing symlink — repoint it (idempotent across reinstalls). + ln -sfn "${skill_path%/}" "$dest" + printf " → skill %-12s linked\n" "$skill_name" + elif [ -e "$dest" ]; then + # A real file/dir the user owns — never clobber it. + printf " → skill %-12s skipped (\$HOME/.claude/skills/%s exists, not a link)\n" "$skill_name" "$skill_name" + else + ln -sfn "${skill_path%/}" "$dest" + printf " → skill %-12s linked\n" "$skill_name" + fi + done +fi + # ─── success ──────────────────────────────────────────────────────── echo "" echo "✓ atom installed. Run \`atom-setup new \` to start your first project." @@ -147,6 +174,7 @@ echo "" echo "Quick reference:" echo " atom --help see every atom command" echo " atom-setup new my-project scaffold a new project" +echo " /atom-new bootstrap a project from inside Claude" echo " atom upgrade update atom in place" echo " nucleus init one-time setup for your session memory" echo "" diff --git a/scripts/test-atom-setup.sh b/scripts/test-atom-setup.sh index 2397609..7500cde 100755 --- a/scripts/test-atom-setup.sh +++ b/scripts/test-atom-setup.sh @@ -16,6 +16,14 @@ set -u +# Pin color OFF so captured output is deterministic across environments. +# picocolors auto-enables ANSI codes when the `CI` env var is present +# (GitHub Actions sets it), but not on a local non-TTY run. That made +# assertions on human-readable strings — e.g. `name is ready` +# — pass locally and fail in CI, where the escape codes split the string. +# NO_COLOR makes both paths emit plain text. Children inherit it. +export NO_COLOR=1 + SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) ATOM=$(cd "$SCRIPT_DIR/.." && pwd) SETUP="node $ATOM/bin/atom-setup/bin/atom-setup.js" @@ -105,13 +113,26 @@ if [ ! -d "$ATOM/bin/atom-setup/node_modules" ]; then } fi +# Pre-flight: atom (dispatcher) deps must be installed — Test 20 runs +# `atom upgrade`, Test 21 imports upgrade.js; both load picocolors. +if [ ! -d "$ATOM/bin/atom/node_modules" ]; then + echo "atom dependencies missing. Installing..." + (cd "$ATOM/bin/atom" && npm install --silent) || { + echo "Failed to install atom deps; aborting." + exit 99 + } +fi + # ============================================================= section "Test 1: pre-flight detection runs and --version works" # ============================================================= $SETUP --version > "$LOG_DIR/t1-version.log" 2>&1 assert "1.1 atom-setup --version exits 0" test $? -eq 0 -assert_grep "1.2 prints 0.2.1" "0.2.1" "$LOG_DIR/t1-version.log" +# Assert against the repo's VERSION file, not a hardcoded string, so a +# release bump doesn't break this test. CLIs align to the root VERSION. +ATOM_VERSION=$(cat "$ATOM/VERSION") +assert_grep "1.2 prints version $ATOM_VERSION" "$ATOM_VERSION" "$LOG_DIR/t1-version.log" $SETUP --help > "$LOG_DIR/t1-help.log" 2>&1 assert_grep "1.3 --help mentions --bare" "\\-\\-bare" "$LOG_DIR/t1-help.log" @@ -857,7 +878,11 @@ ATOM_STATE_DIR="$T20_HOME/state" \ ATOM_INSTALL="$T20_INSTALL" \ node "$ATOM/bin/atom/bin/atom.js" --version > "$LOG_DIR/t20c-notice.log" 2>&1 assert_grep "20.6 inlined client prints upgrade notice (atom CLI)" "0.2.5 is available" "$LOG_DIR/t20c-notice.log" -assert_grep "20.7 notice mentions \`atom upgrade --snooze\`" "snooze: \\\`atom upgrade --snooze 7d\\\`" "$LOG_DIR/t20c-notice.log" +# Pattern is single-quoted: backticks must stay literal. Double-quoting +# forced `\`` escaping, which GNU grep (CI) reads as a buffer-start +# anchor mid-pattern and never matches; BSD grep (macOS) took it as a +# literal, hiding the bug locally. Backtick is not a regex metachar. +assert_grep "20.7 notice mentions \`atom upgrade --snooze\`" 'snooze: `atom upgrade --snooze 7d`' "$LOG_DIR/t20c-notice.log" # 20d: second invocation within 24h does NOT re-print the notice. ATOM_STATE_DIR="$T20_HOME/state" \ @@ -951,6 +976,55 @@ assert_grep "20.19 missing-duration error mentions valid tiers" "24h, 48h, 7d" " node "$ATOM/bin/atom/bin/atom.js" --help > "$LOG_DIR/t20k-help.log" 2>&1 assert_grep "20.20 atom --help advertises --snooze" "atom upgrade --snooze" "$LOG_DIR/t20k-help.log" +# ============================================================= +section "Test 21: global /atom-new skill + skill-linking" +# ============================================================= +# +# Item #23: skills/ holds global Claude skills. install.sh and +# atom upgrade symlink each into ~/.claude/skills/ so they're invocable +# from any session. The skill itself is a thin launcher over AGENTS.md +# Mode 1 (it must NOT restate the bootstrap steps). Symlink logic must +# repoint stale links and never clobber a real dir the user owns. + +# 21a: the skill ships and is shaped like a discoverable thin launcher. +assert "21.1 skills/atom-new/SKILL.md exists" test -f "$ATOM/skills/atom-new/SKILL.md" +assert_grep "21.2 skill frontmatter names it atom-new" "^name: atom-new" "$ATOM/skills/atom-new/SKILL.md" +assert_grep "21.3 skill has a description (drives discovery)" "^description: " "$ATOM/skills/atom-new/SKILL.md" +assert_grep "21.4 skill points at AGENTS.md Mode 1 (thin launcher)" "Mode 1" "$ATOM/skills/atom-new/SKILL.md" +assert_grep "21.5 skill fails gracefully when atom not installed" "install.sh" "$ATOM/skills/atom-new/SKILL.md" +assert "21.6 skills/README.md exists" test -f "$ATOM/skills/README.md" + +# 21b: both install paths wire the symlink and protect user-owned dirs. +assert_grep "21.7 install.sh links skills into ~/.claude/skills" '.claude/skills' "$ATOM/install.sh" +assert_grep "21.8 install.sh won't clobber a real dir" "not a link" "$ATOM/install.sh" +assert_grep "21.9 atom upgrade re-links global skills" "linkGlobalSkills" "$ATOM/bin/atom/src/upgrade.js" + +# 21c: functional — real symlink creation, hermetic (scratch HOME, no +# pollution of the runner's ~/.claude). Exercises linkGlobalSkills directly. +T21_HOME=$SCRATCH/test-21-home +T21_INST=$SCRATCH/test-21-install +rm -rf "$T21_HOME" "$T21_INST" +mkdir -p "$T21_INST/skills/atom-new" "$T21_HOME/.claude/skills" +echo "stub" > "$T21_INST/skills/atom-new/SKILL.md" +T21_RUN='import { linkGlobalSkills } from "file://'"$ATOM"'/bin/atom/src/upgrade.js"; linkGlobalSkills(process.env.INST);' + +# fresh link +INST="$T21_INST" HOME="$T21_HOME" node --input-type=module -e "$T21_RUN" > "$LOG_DIR/t21-fresh.log" 2>&1 +assert "21.10 fresh install creates ~/.claude/skills/atom-new symlink" test -L "$T21_HOME/.claude/skills/atom-new" + +# idempotent re-run repoints, stays a symlink +INST="$T21_INST" HOME="$T21_HOME" node --input-type=module -e "$T21_RUN" > "$LOG_DIR/t21-rerun.log" 2>&1 +assert "21.11 idempotent re-run keeps it a symlink" test -L "$T21_HOME/.claude/skills/atom-new" + +# user-owned real dir is never clobbered +rm "$T21_HOME/.claude/skills/atom-new" +mkdir "$T21_HOME/.claude/skills/atom-new" +echo "USER DATA" > "$T21_HOME/.claude/skills/atom-new/SKILL.md" +INST="$T21_INST" HOME="$T21_HOME" node --input-type=module -e "$T21_RUN" > "$LOG_DIR/t21-skip.log" 2>&1 +assert_not "21.12 user-owned dir not converted to a symlink" test -L "$T21_HOME/.claude/skills/atom-new" +assert_grep "21.13 user-owned skill file left intact" "USER DATA" "$T21_HOME/.claude/skills/atom-new/SKILL.md" +assert_grep "21.14 skip is reported, not silent" "not a link" "$LOG_DIR/t21-skip.log" + # ============================================================= # Report # ============================================================= diff --git a/skills/README.md b/skills/README.md new file mode 100644 index 0000000..1d4df66 --- /dev/null +++ b/skills/README.md @@ -0,0 +1,30 @@ +# skills/ — global Claude skills + +Skills in this directory are **global, user-facing** Claude Code skills. +They install once per machine (symlinked into `~/.claude/skills/` by +`install.sh`, kept fresh by `atom upgrade`) and are invocable from +**any** directory or session. + +Do not confuse this with `scaffold/.claude/skills/`: + +| Directory | Audience | Scope | +| --------------------------- | ------------------- | --------------------------------------- | +| `skills/` (here) | the atom **user** | global — works anywhere `atom` is installed | +| `scaffold/.claude/skills/` | a **new project** | project-local — copied into each bootstrapped repo | + +## What's here + +- **`atom-new/`** — bootstrap a new project from atom, conversationally, + without returning to the terminal. A thin launcher over Mode 1 of the + installed `AGENTS.md` (it reads the real instructions at invoke time; + it does not copy them). See [`docs/planning/v0.3.md`](../docs/planning/v0.3.md) + item #23 for the design rationale. + +## How they install + +`install.sh` symlinks each subdirectory here into `~/.claude/skills/` +(e.g. `~/.claude/skills/atom-new` → `~/.atom/atom/skills/atom-new`). +Because it's a symlink into the git checkout, `atom upgrade`'s `git +pull` refreshes the skill content with no copy step. If a user already +has a non-symlink file or directory at the target name, the installer +leaves it alone and warns rather than clobbering it. diff --git a/skills/atom-new/SKILL.md b/skills/atom-new/SKILL.md new file mode 100644 index 0000000..d135f21 --- /dev/null +++ b/skills/atom-new/SKILL.md @@ -0,0 +1,89 @@ +--- +name: atom-new +description: Bootstrap a new project from atom — the project-starter template — without leaving Claude. Use when the user says "start a new project", "new project from atom", "bootstrap from a template", "scaffold a new repo from atom", or similar. Drives the conversational atom bootstrap (project context → scaffold → constitution → first phase) from any directory. Only for STARTING a project; not for maintaining atom itself or building atom's features. +--- + +# atom-new — bootstrap a new project from atom + +This skill is a **thin launcher**. It does not contain the bootstrap +steps. The single source of truth is **Mode 1 of `AGENTS.md` in the +installed atom checkout**. Your job is to find that file, read it, and +run its flow conversationally against the user's chosen directory. + +Why thin: atom is distributed as a git checkout that the user upgrades +in place (`atom upgrade`). If this skill restated the bootstrap steps, +the two would drift. Instead it points at the live instructions so the +flow is always whatever the installed atom says it is. + +## Step 1 — locate the installed atom + +Resolve the atom source directory, in this order: + +1. `$ATOM_SOURCE_DIR` (if set) +2. `$ATOM_INSTALL` (if set) +3. `~/.atom/atom/` (the canonical install location) + +A directory is a valid atom checkout if it contains **both** a +`VERSION` file and `AGENTS.md`. Verify before proceeding. + +**If no atom checkout is found**, stop and tell the user — do not +improvise a bootstrap from memory: + +> I couldn't find an atom install (looked in `$ATOM_SOURCE_DIR`, +> `$ATOM_INSTALL`, and `~/.atom/atom/`). Install it once with: +> +> curl -fsSL https://raw.githubusercontent.com/machbuilds/atom/main/install.sh | bash +> +> Then re-run `/atom-new`. + +## Step 2 — read the real instructions + +Read these from the resolved atom directory (not from this skill, not +from memory — they evolve between releases): + +- `AGENTS.md` → **Mode 1: Bootstrap a new project**. This is the + canonical, ordered flow. Follow it exactly. +- The docs `AGENTS.md` tells you to read, in the order it lists them + (`docs/VOICE.md`, `docs/WORKFLOW.md`, `docs/PATTERNS.md`, + `docs/LESSONS_LEARNED.md`, `docs/HOW_TO_WRITE_CONSTITUTION.md`, and + the conditional ones). + +Whatever Mode 1 says wins over anything summarized here. + +## Step 3 — ask where the project goes + +This skill can fire from **any** directory, so you cannot assume the +current working directory is where the new project should live. Before +scaffolding, confirm the target: + +- A new subdirectory of the cwd (e.g. `./my-project/`)? — the default, + and what `atom-setup new ` does. +- The current directory itself (only if it's empty)? +- Some other path the user names? + +Get an explicit answer. Do not write into a non-empty directory. + +## Step 4 — run the bootstrap, conversationally + +Drive Mode 1 as a **conversation**, not a form. Ask the project-context +questions from Mode 1 step 1 (name, stack, deploy target, cost +envelope, solo vs multi-agent, public vs internal), then proceed +through scaffold copy, Docker tier, placeholder fill, constitution, +tooling install, and the GSD/Spec Kit kickoff — in the order `AGENTS.md` +specifies. + +The deterministic terminal wizard (`atom-setup new `) is the +fast, non-conversational path and already exists for users who want it. +This skill is the in-Claude, guided counterpart — lean into the +dialogue: explain tradeoffs when a choice matters, suggest sensible +defaults, and let the user steer. + +## Scope — Mode 1 only + +This skill handles **bootstrapping a new project** and nothing else. + +`AGENTS.md` also defines Mode 2 (maintain atom itself) and Mode 3 +(build atom features). Those are **dev-facing** — they only make sense +when working inside the `atom-dev` repo, not from an arbitrary session. +If the user actually wants one of those, point them at the `atom-dev` +checkout and stop; do not try to run Mode 2 or 3 from here.