@swifty.js/swifty 0.0.1-canary
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anthropic-IS6JB2DP.js +4 -0
- package/dist/builtin/commit/SKILL.md +41 -0
- package/dist/builtin/fullstack-interview/SKILL.md +105 -0
- package/dist/builtin/teach-me/SKILL.md +399 -0
- package/dist/builtin/teach-me/references/pedagogy.md +244 -0
- package/dist/builtin/test/SKILL.md +42 -0
- package/dist/chunk-DNCLVUXL.js +10 -0
- package/dist/chunk-H7VXSMJR.js +44 -0
- package/dist/chunk-NCPOPA4A.js +4 -0
- package/dist/chunk-X3UA5OZL.js +4 -0
- package/dist/chunk-XWWKIJ5J.js +121 -0
- package/dist/cleanup-4R3534Z3.js +4 -0
- package/dist/devtools-X2EGZM6L.js +7 -0
- package/dist/glob_addon.node +0 -0
- package/dist/main.js +1455 -0
- package/dist/node-OES65R33.js +15 -0
- package/dist/openai-4CKFFKCY.js +27 -0
- package/dist/release-JP26O6LN.js +4 -0
- package/dist/release.wasm +0 -0
- package/docs/ch1.md +25 -0
- package/docs/ch10.md +122 -0
- package/docs/ch11.md +163 -0
- package/docs/ch12.md +288 -0
- package/docs/ch13.md +320 -0
- package/docs/ch14.md +146 -0
- package/docs/ch15.md +62 -0
- package/docs/ch2.md +273 -0
- package/docs/ch3.md +206 -0
- package/docs/ch4.md +125 -0
- package/docs/ch5.md +165 -0
- package/docs/ch6.md +201 -0
- package/docs/ch7.md +448 -0
- package/docs/ch8.md +218 -0
- package/docs/ch9.md +351 -0
- package/docs/index.css +23 -0
- package/docs/index.md +21 -0
- package/docs/swifty.mdx +7 -0
- package/package.json +116 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __swiftyCreateRequire } from "node:module";
|
|
3
|
+
const require = __swiftyCreateRequire(import.meta.url);
|
|
4
|
+
import{g as w}from"./chunk-H7VXSMJR.js";import{$ as K,L as x,N as P,O,P as v,Q as f,R as S,X as j,Y as I,Z as U,_ as $,aa as g,ba as W,da as B,e as L,ea as M,p as N}from"./chunk-XWWKIJ5J.js";import"./chunk-NCPOPA4A.js";import"./chunk-X3UA5OZL.js";var d=L({module:"llm"});var F=3e3,G=x.object({max_input_tokens:x.preprocess(n=>{if(n==null||n===""||n===!1)return g;let t=Number(n);return Number.isFinite(t)&&t>0?t:g},x.number().positive())});async function et(n){if(n.protocol!=="anthropic")return 0;let t=M(n),o=`${n.base_url.replace(/\/+$/,"")}/v1/models/${encodeURIComponent(n.model)}`,r=new AbortController,i=setTimeout(()=>{r.abort()},F);try{let l=await fetch(o,{method:"GET",headers:{"anthropic-version":"2023-06-01",...t?{"x-api-key":t}:{}},signal:r.signal});if(!l.ok)return g;let y=await l.json(),{success:h,data:k}=await N(G,y);return h?k.max_input_tokens:g}catch(l){return d.error({err:l},"llm operation failed"),g}finally{clearTimeout(i)}}function H(){return!0}function Y(n){let t=[];for(let e of n)if(e.role==="assistant"){let o=[];if(e.thinkingBlocks)for(let r of e.thinkingBlocks)o.push({type:"thinking",thinking:r.thinking,signature:r.signature});if(e.content&&o.push({type:"text",text:e.content}),e.toolUses)for(let r of e.toolUses)o.push({type:"tool_use",id:r.toolUseId,name:r.toolName,input:r.arguments});o.length===0&&o.push({type:"text",text:""}),t.push({role:"assistant",content:o})}else if(e.toolResults&&e.toolResults.length>0){let o=[];for(let r of e.toolResults)o.push({type:"tool_result",tool_use_id:r.toolUseId,is_error:r.isError,content:r.content});t.push({role:"user",content:o})}else{if(t.length===0){t.push({role:"user",content:[{type:"text",text:e.content}]});continue}let o=!1,r=t[t.length-1],i=r.content;r.role==="user"&&(typeof i=="string"||Array.isArray(i)&&i.length>0&&i[0].type==="text")&&(o=!0),o?(typeof i=="string"&&(i=r.content=i.trim().length>0?[{type:"text",text:i}]:[]),i.push({type:"text",text:e.content})):t.push({role:"user",content:[{type:"text",text:e.content}]})}return t}var D=class{client;model;thinking;systemPrompt;maxOutputTokens;contextWindow;constructor(t,e){let o=M(t);if(!o)throw new I("Anthropic API key not found, set ANTHROPIC_API_KEY in .swifty/config.y(a)ml, or via ANTHROPIC_API_KEY env variable.");this.client=new w({apiKey:o,baseURL:t.base_url}),this.model=t.model,this.thinking=t.thinking??!0,this.systemPrompt=e,this.maxOutputTokens=B(t),this.contextWindow=W(t)}setMaxOutputTokens(t){this.maxOutputTokens=t}async*stream(t,e,o){let r=Y(t.getMessages()),i=e.map(c=>{let p=c.input_schema;return{name:c.name,description:c.description,input_schema:{type:"object",properties:p.properties,required:p.required??[]}}});i.length>0&&(i[i.length-1].cache_control={type:"ephemeral"}),q(r);let l={model:this.model,max_tokens:this.maxOutputTokens,stream:!0,system:[{type:"text",text:this.systemPrompt,cache_control:{type:"ephemeral"}}],messages:r,...i.length>0?{tools:i}:{}};this.thinking?H()&&(l.thinking={type:"enabled",budget_tokens:this.maxOutputTokens-1}):l.thinking={type:"enabled",budget_tokens:this.maxOutputTokens-1};let y=0,h=0,k=0,R=0,C="end_turn",b="",T="",A=!1,E=0;try{let c=this.client.messages.stream(l,{...o?{signal:o}:{}}),p="",_="",u="";for await(let a of c)switch(a.type){case"content_block_start":{let s=a.content_block;s.type==="thinking"?(A=!0,b="",T=""):s.type==="tool_use"&&(_=s.id,p=s.name,u="",yield{type:"tool_call_start",toolName:p,toolId:_});break}case"content_block_delta":{let s=a.delta;s.type==="thinking_delta"?(b+=s.thinking,yield{type:"thinking_delta",text:s.thinking}):s.type==="signature_delta"?(d.debug({signature:s.signature},"thinking signature received"),T=s.signature):s.type==="text_delta"?yield{type:"text_delta",text:s.text}:s.type==="input_json_delta"&&(u+=s.partial_json,yield{type:"tool_call_delta",text:s.partial_json});break}case"content_block_stop":{if(A&&(yield{type:"thinking_complete",thinking:b,signature:T},A=!1),p){let s={};if(u)try{let m=JSON.parse(u);s=O(m)?v(m):{[P]:u}}catch(m){d.error({err:m},"llm operation failed"),s={[P]:u}}yield{type:"tool_call_complete",toolId:_,toolName:p,arguments:s},p="",_="",u=""}break}case"message_delta":{a.delta.stop_reason&&(C=a.delta.stop_reason),a.usage.output_tokens&&(h=a.usage.output_tokens);break}case"message_start":{E=performance.now(),y=a.message.usage.input_tokens,h=a.message.usage.output_tokens,k=a.message.usage.cache_read_input_tokens??0,R=a.message.usage.cache_creation_input_tokens??0;break}case"message_stop":{let m=performance.now()-E;d.debug({elapsedMs:m},"message stream complete");break}}yield{type:"stream_end",stopReason:C,usage:{inputTokens:y,outputTokens:h,cacheReadInputTokens:k,cacheCreationInputTokens:R}}}catch(c){throw d.error({err:c},"llm operation failed"),J(c)}}};function q(n){for(let t=n.length-1;t>=0;t--){if(n[t].role!=="user")continue;let e=n[t].content;if(typeof e=="string"&&e.length===0||Array.isArray(e)&&e.length===0)return;typeof e=="string"&&(e=n[t].content=[{type:"text",text:e}]);let o=e[e.length-1];Reflect.set(o,"cache_control",{type:"ephemeral"})}}function J(n){if(n instanceof w.APIError){if(n.status===413||/prompts?\s+too\s+long/i.test(n.message))return new K(`Prompt too long: ${n.message}`);if(n.status===401)return new I(`Invalid API key: ${n.message}`);if(n.status===429){let t=v(n.headers)["retry-after"],e="Rate Limited";if(t){let o=Number.parseInt(f(t));Number.isNaN(o)&&(e+=", please wait."),e+=`, retry after ${f(o)}s.`}else e+=", please wait.";return new U(e,t?f(t):void 0)}return new j(`Anthropic API error (${f(n.status)}): ${n.message}`)}return new $(`Network error: ${S(n)}`)}export{D as AnthropicClient,Y as buildAnthropicMessages,et as fetchModelContextWindow};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: commit
|
|
3
|
+
description: Analyze git diff and produce a conventional commit
|
|
4
|
+
mode: inline
|
|
5
|
+
allowed_tools:
|
|
6
|
+
- Bash
|
|
7
|
+
- ReadFile
|
|
8
|
+
- Grep
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Task
|
|
12
|
+
|
|
13
|
+
You are creating a git commit for the user.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
1. Run `git status` to see what has changed.
|
|
18
|
+
2. Run `git diff` and `git diff --staged` to inspect the actual changes. Read both staged and unstaged.
|
|
19
|
+
3. Decide the commit type from the diff content:
|
|
20
|
+
- `feat`: new user-facing capability
|
|
21
|
+
- `fix`: bug fix
|
|
22
|
+
- `docs`: documentation only
|
|
23
|
+
- `refactor`: code restructuring without behavior change
|
|
24
|
+
- `test`: tests only
|
|
25
|
+
- `chore`: build / tooling / non-source changes
|
|
26
|
+
4. Compose a message in conventional-commit format: `type(scope): description`.
|
|
27
|
+
- Description in English, no trailing period, ≤ 72 chars.
|
|
28
|
+
- Scope optional; use the package or module touched (e.g. `feat(skills): ...`).
|
|
29
|
+
5. Stage files one by one with `git add <path>` — never `git add -A` or `git add .`.
|
|
30
|
+
- Skip `.env`, credentials, secrets, large binaries, build artifacts.
|
|
31
|
+
- If more than 10 files changed, ask the user whether to split into multiple commits before staging.
|
|
32
|
+
6. Create the commit with `git commit -m "<message>"`.
|
|
33
|
+
7. Report the commit hash and the message.
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
|
|
37
|
+
- If the user provided extra context in `$ARGUMENTS`, fold it into the message body (after the title line, blank line, then prose).
|
|
38
|
+
- Do not push.
|
|
39
|
+
- If the working tree is clean, report that and stop.
|
|
40
|
+
|
|
41
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fullstack-interview
|
|
3
|
+
description: Run a focused fullstack interview (TS+React frontend, Go backend) based on the candidate's real repository docs
|
|
4
|
+
mode: inline
|
|
5
|
+
allowed_tools:
|
|
6
|
+
- Bash
|
|
7
|
+
- Glob
|
|
8
|
+
- ReadFile
|
|
9
|
+
- Grep
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Task
|
|
13
|
+
|
|
14
|
+
You are conducting a fullstack engineering interview. The candidate's stack is TypeScript + React on the frontend and Golang on the backend. Instead of a resume, you will clone the candidate's repository, read its docs, and ask questions grounded in the actual project.
|
|
15
|
+
|
|
16
|
+
Run in four rounds, one at a time, waiting for the candidate's answer before moving on.
|
|
17
|
+
|
|
18
|
+
## Setup — clone and read the candidate's repo
|
|
19
|
+
|
|
20
|
+
1. Create a temp directory and clone the repo:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
tmp=$(mktemp -d)
|
|
24
|
+
git clone https://github.com/hangtiancheng/h.git "$tmp/h"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
2. Use Glob to understand the docs directory structure:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Glob: **/*
|
|
31
|
+
Path: $tmp/h/docs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
3. Move the docs directory out of the repo into a clean location so it's easy to reference:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
mv "$tmp/h/docs" "$tmp/docs"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
4. Use ReadFile to read every document under `$tmp/docs/`. Use Grep if you need to search across docs for specific keywords (e.g., "architecture", "API", "component", "handler").
|
|
41
|
+
|
|
42
|
+
5. Build a mental model of the project from the docs:
|
|
43
|
+
- What is this project? What problem does it solve?
|
|
44
|
+
- Frontend: What React patterns / TS types / state management are mentioned?
|
|
45
|
+
- Backend: What Go packages / patterns / concurrency model are mentioned?
|
|
46
|
+
- How do the frontend and backend communicate?
|
|
47
|
+
|
|
48
|
+
Use this understanding to tailor every question — never ask about something the docs don't cover.
|
|
49
|
+
|
|
50
|
+
## Round 1 — frontend fundamentals (3 questions)
|
|
51
|
+
|
|
52
|
+
Pick 3 concepts from the candidate's frontend stack as described in the docs (TypeScript, React, state management, etc.). For each:
|
|
53
|
+
|
|
54
|
+
- One concept question grounded in the project's actual code (e.g., "I see you use `useCallback` in the component at `src/foo.tsx` — when would you NOT use it?")
|
|
55
|
+
- Probe one follow-up if the answer is shallow
|
|
56
|
+
|
|
57
|
+
## Round 2 — backend fundamentals (3 questions)
|
|
58
|
+
|
|
59
|
+
Pick 3 concepts from the candidate's Go/backend stack as described in the docs. For each:
|
|
60
|
+
|
|
61
|
+
- One concept question grounded in the project's actual code (e.g., "I see you use a worker pool in `internal/pool.go` — why channels instead of a mutex+slice?")
|
|
62
|
+
- Probe one follow-up if the answer is shallow
|
|
63
|
+
|
|
64
|
+
## Round 3 — project deep-dive (1 topic)
|
|
65
|
+
|
|
66
|
+
Pick the most interesting architectural decision visible in the docs:
|
|
67
|
+
|
|
68
|
+
- Ask the candidate to walk through the fullstack architecture: React frontend ↔ API layer ↔ Go backend ↔ data store
|
|
69
|
+
- Drill into one specific decision on each side:
|
|
70
|
+
- Frontend: component composition, state management choice, rendering strategy (SSR/CSR/streaming)
|
|
71
|
+
- Backend: API style (REST/gRPC/GraphQL), concurrency model, error handling, data layer
|
|
72
|
+
- Find the part where the candidate had to compromise — pressure-test that trade-off
|
|
73
|
+
|
|
74
|
+
## Round 4 — system design (1 prompt)
|
|
75
|
+
|
|
76
|
+
Pick a design prompt sized to their YoE, scoped to extending the project they already have:
|
|
77
|
+
|
|
78
|
+
- 1-3 YoE: Add a real-time feature (e.g., live updates, collaborative editing) to the existing fullstack app
|
|
79
|
+
- 3-7 YoE: Scale an existing component (e.g., add a notification system, file upload pipeline) using the project's current architecture
|
|
80
|
+
- 7+ YoE: Redesign a subsystem for multi-tenant / high-scale (e.g., split the monolith into Go microservices + React SPA)
|
|
81
|
+
|
|
82
|
+
Give them 10 minutes of "interview thinking time" via the prompt; they describe the fullstack architecture out loud. Push back on missing concerns:
|
|
83
|
+
|
|
84
|
+
- Frontend: bundle size, re-render performance, accessibility, state synchronization
|
|
85
|
+
- Backend: latency, failure mode, concurrency, graceful degradation
|
|
86
|
+
- Integration: API contract design, error propagation, type safety across the boundary
|
|
87
|
+
|
|
88
|
+
## Output
|
|
89
|
+
|
|
90
|
+
After all four rounds, produce a short report:
|
|
91
|
+
|
|
92
|
+
- Frontend strengths (2-3 bullets)
|
|
93
|
+
- Backend strengths (2-3 bullets)
|
|
94
|
+
- Gaps (2-4 bullets)
|
|
95
|
+
- Hire signal: strong / lean-hire / lean-no-hire / no-hire — with one-line rationale
|
|
96
|
+
|
|
97
|
+
## Notes
|
|
98
|
+
|
|
99
|
+
- Do not give answers away during the interview.
|
|
100
|
+
- One question per turn. Wait for response.
|
|
101
|
+
- If the candidate goes off-script ("can we skip this?"), pick a different angle in the same round, don't abandon the round.
|
|
102
|
+
- If the candidate is stronger on one side (frontend or backend), spend more time on the weaker side to calibrate accurately.
|
|
103
|
+
- Clean up the temp directory when the interview is over: `rm -rf "$tmp"`.
|
|
104
|
+
|
|
105
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: teach-me
|
|
3
|
+
description: "Personalized 1-on-1 AI tutor. Diagnoses level, builds learning path, teaches via guided questions, tracks misconceptions. Use when user wants to learn/study/understand a topic, says 'teach me', 'help me understand', or invokes /teach-me."
|
|
4
|
+
mode: inline
|
|
5
|
+
allowed_tools:
|
|
6
|
+
- AskUserQuestion
|
|
7
|
+
- ReadFile
|
|
8
|
+
- WriteFile
|
|
9
|
+
- Glob
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Teach Me
|
|
13
|
+
|
|
14
|
+
Personalized mastery tutor. Diagnose, question, advance on understanding.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
/teach-me JavaScript event loop
|
|
20
|
+
/teach-me TypeScript --level beginner
|
|
21
|
+
/teach-me React hooks --resume
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Arguments
|
|
25
|
+
|
|
26
|
+
| Argument | Description |
|
|
27
|
+
| ----------------- | ---------------------------------------------------------------------------- |
|
|
28
|
+
| `<topic>` | Subject to learn (required, or prompted) |
|
|
29
|
+
| `--level <level>` | Starting level: beginner, intermediate, advanced (default: diagnose) |
|
|
30
|
+
| `--resume` | Resume previous session from `.swifty/skills/teach-me/records/{topic-slug}/` |
|
|
31
|
+
|
|
32
|
+
## Core Rules
|
|
33
|
+
|
|
34
|
+
1. **Minimize lecturing, but don't be dogmatic.** Prefer questions that lead to discovery. For complete beginners with zero context, a brief 1-2 sentence framing is acceptable before asking.
|
|
35
|
+
2. **Diagnose first.** Always probe current understanding before teaching.
|
|
36
|
+
3. **Mastery gate.** Advance to next concept only when the learner can explain it clearly and apply it.
|
|
37
|
+
4. **1-2 questions per round.** No more.
|
|
38
|
+
5. **Patience + rigor.** Encouraging tone, but never hand-wave past gaps.
|
|
39
|
+
6. **Language follows user.** Match the user's language. Technical terms can stay in English.
|
|
40
|
+
7. **Always use AskUserQuestion.** Every question to the learner MUST use AskUserQuestion with predefined options. Never ask open-ended plain-text questions — users need options to anchor their thinking. Even conceptual/deep questions should offer 3-4 options plus let the user pick "Other" for free-form input. Options serve as scaffolding, not just convenience.
|
|
41
|
+
|
|
42
|
+
## Output Directory
|
|
43
|
+
|
|
44
|
+
All teach-me data is stored under `.swifty/skills/teach-me/records/`:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
.swifty/skills/teach-me/records/
|
|
48
|
+
├── learner-profile.md # Cross-topic notes (created on first session)
|
|
49
|
+
└── {topic-slug}/
|
|
50
|
+
├── session.md # Learning state: concepts, status, notes
|
|
51
|
+
└── {topic-slug}-notes.md # Learner-facing summary notes (generated at session end)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Slug**: Topic in kebab-case, 2-5 words. Example: "Python decorators" → `python-decorators`
|
|
55
|
+
|
|
56
|
+
## Workflow
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
Input → [Load Profile] → [Diagnose] → [Build Concept List] → [Tutor Loop] → [Session End]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 0: Parse Input
|
|
63
|
+
|
|
64
|
+
1. Extract topic. If none, use AskUserQuestion to ask what they want to learn (provide common categories as options).
|
|
65
|
+
2. Detect language from user input.
|
|
66
|
+
3. Load learner profile if `.swifty/skills/teach-me/records/learner-profile.md` exists.
|
|
67
|
+
4. Check for existing session:
|
|
68
|
+
- If `--resume`: read `session.md`, restore state, continue.
|
|
69
|
+
- If exists without `--resume`: use AskUserQuestion to ask whether to resume or start fresh.
|
|
70
|
+
5. Create output directory: `.swifty/skills/teach-me/records/{topic-slug}/`
|
|
71
|
+
|
|
72
|
+
### Step 1: Diagnose Level
|
|
73
|
+
|
|
74
|
+
Ask 2-3 questions to calibrate understanding, all via AskUserQuestion with predefined options.
|
|
75
|
+
|
|
76
|
+
If learner profile exists, use it to skip known strengths and probe known weak areas.
|
|
77
|
+
|
|
78
|
+
If `--level` provided, use as hint but still ask 1-2 probing questions.
|
|
79
|
+
|
|
80
|
+
**Example for "Python decorators"**:
|
|
81
|
+
|
|
82
|
+
Round 1 (AskUserQuestion):
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
header: "Level check"
|
|
86
|
+
question: "Which of these Python concepts are you comfortable with?"
|
|
87
|
+
multiSelect: true
|
|
88
|
+
options:
|
|
89
|
+
- label: "Functions as values"
|
|
90
|
+
- label: "Closures"
|
|
91
|
+
- label: "The @ syntax"
|
|
92
|
+
- label: "Writing custom decorators"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Round 2 (AskUserQuestion — conceptual question with options as scaffolding):
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
header: "Understanding"
|
|
99
|
+
question: "When Python sees @my_decorator above a function, what do you think happens?"
|
|
100
|
+
multiSelect: false
|
|
101
|
+
options:
|
|
102
|
+
- label: "It replaces the function with a new one"
|
|
103
|
+
description: "The decorator wraps or replaces the original function"
|
|
104
|
+
- label: "It's just syntax sugar for calling the decorator"
|
|
105
|
+
description: "@decorator is equivalent to func = decorator(func)"
|
|
106
|
+
- label: "It modifies the function in-place"
|
|
107
|
+
description: "The original function object is changed directly"
|
|
108
|
+
- label: "I'm not sure"
|
|
109
|
+
description: "No worries, we'll figure it out together"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Step 2: Build Concept List
|
|
113
|
+
|
|
114
|
+
Decompose topic into 5-15 atomic concepts, ordered by dependency. Save to `session.md`:
|
|
115
|
+
|
|
116
|
+
```markdown
|
|
117
|
+
# Session: {topic}
|
|
118
|
+
|
|
119
|
+
- Level: {diagnosed}
|
|
120
|
+
- Started: {timestamp}
|
|
121
|
+
|
|
122
|
+
## Concepts
|
|
123
|
+
|
|
124
|
+
1. ✓ Functions as first-class objects (mastered)
|
|
125
|
+
2. ○ Higher-order functions (in progress)
|
|
126
|
+
3. ⬜ Closures
|
|
127
|
+
4. ⬜ Decorator basics
|
|
128
|
+
...
|
|
129
|
+
|
|
130
|
+
## Misconceptions
|
|
131
|
+
|
|
132
|
+
- [concept]: "{what learner said}" → likely root cause: {analysis}
|
|
133
|
+
|
|
134
|
+
## Log
|
|
135
|
+
|
|
136
|
+
- [timestamp] Diagnosed: intermediate
|
|
137
|
+
- [timestamp] Concept 1: pre-existing knowledge, skipped
|
|
138
|
+
- [timestamp] Concept 2: started
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Use simple status: ✓ mastered | ○ in progress | ⬜ not started | ✗ needs review
|
|
142
|
+
|
|
143
|
+
Present the concept list to the learner as a brief text outline so they see the path ahead.
|
|
144
|
+
|
|
145
|
+
### Step 3: Tutor Loop
|
|
146
|
+
|
|
147
|
+
For each concept:
|
|
148
|
+
|
|
149
|
+
#### 3a. Introduce (Brief)
|
|
150
|
+
|
|
151
|
+
Set context with 1-2 sentences max, then ask an opening question via AskUserQuestion. Options serve as thinking scaffolds:
|
|
152
|
+
|
|
153
|
+
Example for "closures":
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
header: "Closures"
|
|
157
|
+
question: "A closure is a function that remembers variables from where it was created. Why might that be useful?"
|
|
158
|
+
multiSelect: false
|
|
159
|
+
options:
|
|
160
|
+
- label: "To create private state"
|
|
161
|
+
description: "Keep variables hidden from outside code"
|
|
162
|
+
- label: "To pass data between functions"
|
|
163
|
+
description: "Share information without global variables"
|
|
164
|
+
- label: "To cache expensive computations"
|
|
165
|
+
description: "Remember results for reuse"
|
|
166
|
+
- label: "I'm not sure yet"
|
|
167
|
+
description: "We'll explore this together"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### 3b. Question Cycle
|
|
171
|
+
|
|
172
|
+
ALL questions use AskUserQuestion. Design options that probe understanding — include a mix of correct, partially correct, and common-wrong-answer distractors. The user can always use "Other" for free-form input when they have a specific idea.
|
|
173
|
+
|
|
174
|
+
**Option design tips**:
|
|
175
|
+
|
|
176
|
+
- Include 1-2 correct answers (split nuance into separate options)
|
|
177
|
+
- Include 1 distractor based on a common misconception
|
|
178
|
+
- Include "I'm not sure" or "Let me think about it" as a safe option
|
|
179
|
+
- Use descriptions to add hints or context to each option
|
|
180
|
+
|
|
181
|
+
**Interleaving** (every 3-4 questions): Mix a previously mastered concept into the current question's options naturally. Don't announce it as review.
|
|
182
|
+
|
|
183
|
+
Example (learning closures, already mastered higher-order functions):
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
header: "Prediction"
|
|
187
|
+
question: "Here's a function that takes a callback and returns a new function. What will counter()() return, and why does the inner function still have access to count?"
|
|
188
|
+
multiSelect: false
|
|
189
|
+
options:
|
|
190
|
+
- label: "0, because count starts at 0"
|
|
191
|
+
description: "The inner function reads the initial value"
|
|
192
|
+
- label: "1, because count was incremented before returning"
|
|
193
|
+
description: "Closure captures the live variable, not a copy"
|
|
194
|
+
- label: "Error, because count is out of scope"
|
|
195
|
+
description: "The outer function already returned, so count is gone"
|
|
196
|
+
- label: "Undefined behavior"
|
|
197
|
+
description: "Depends on how the function was defined"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### 3c. Respond to Answers
|
|
201
|
+
|
|
202
|
+
| Answer Quality | Response |
|
|
203
|
+
| --------------------------- | ------------------------------------------------------------------------------------------- |
|
|
204
|
+
| Correct + good explanation | Brief acknowledgment, harder follow-up via AskUserQuestion |
|
|
205
|
+
| Correct but shallow | "Good. Can you explain _why_?" — as AskUserQuestion with why-options |
|
|
206
|
+
| Partially correct | "On the right track with [part]." — follow up with a more targeted AskUserQuestion |
|
|
207
|
+
| Incorrect | "Interesting. Let's step back." — simpler AskUserQuestion to re-anchor |
|
|
208
|
+
| "I don't know" / "Not sure" | "That's fine." — give a concrete example, then ask via AskUserQuestion with simpler options |
|
|
209
|
+
|
|
210
|
+
**Hint escalation**: rephrase → simpler question → concrete example → point to principle → walk through minimal example together.
|
|
211
|
+
|
|
212
|
+
#### 3d. Misconception Tracking
|
|
213
|
+
|
|
214
|
+
On incorrect or partially correct answers, diagnose the underlying wrong mental model:
|
|
215
|
+
|
|
216
|
+
1. Present a counter-example via AskUserQuestion — ask the learner to predict what happens, where the wrong mental model leads to a clearly wrong answer:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
header: "Check this"
|
|
220
|
+
question: "Given [counter-example], what do you think the output will be?"
|
|
221
|
+
multiSelect: false
|
|
222
|
+
options:
|
|
223
|
+
- label: "[wrong prediction from their mental model]"
|
|
224
|
+
description: "Based on what we discussed earlier"
|
|
225
|
+
- label: "[correct prediction]"
|
|
226
|
+
description: "A different perspective"
|
|
227
|
+
- label: "[another wrong prediction]"
|
|
228
|
+
description: "Yet another possibility"
|
|
229
|
+
- label: "I need to think more"
|
|
230
|
+
description: "Take your time"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
2. Record in session.md under `## Misconceptions`
|
|
234
|
+
3. When the learner sees the contradiction (their model predicts the wrong thing), guide them to articulate why.
|
|
235
|
+
4. A misconception is resolved when the learner articulates why their old thinking was wrong AND handles a new scenario correctly.
|
|
236
|
+
|
|
237
|
+
Never say "that's a misconception." Let them discover it.
|
|
238
|
+
|
|
239
|
+
#### 3e. Mastery Check
|
|
240
|
+
|
|
241
|
+
After 3-5 question rounds, assess qualitatively. The learner demonstrates mastery when they can:
|
|
242
|
+
|
|
243
|
+
- Explain the concept in their own words
|
|
244
|
+
- Apply it to a new scenario
|
|
245
|
+
- Distinguish it from similar concepts
|
|
246
|
+
- Find errors in incorrect usage
|
|
247
|
+
|
|
248
|
+
If not ready: identify the specific gap and cycle back with targeted questions.
|
|
249
|
+
|
|
250
|
+
#### 3f. Practice Phase
|
|
251
|
+
|
|
252
|
+
Before marking mastered, give a small hands-on task via AskUserQuestion. Present the task as a code/output prediction or scenario choice:
|
|
253
|
+
|
|
254
|
+
- **Programming**: Show a small code snippet and ask what it outputs or which fix is correct:
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
header: "Practice"
|
|
258
|
+
question: "Here's a buggy decorator. What's wrong with it?"
|
|
259
|
+
multiSelect: false
|
|
260
|
+
options:
|
|
261
|
+
- label: "Missing return wrapper"
|
|
262
|
+
description: "The decorator doesn't return the inner function"
|
|
263
|
+
- label: "Wrong function signature"
|
|
264
|
+
description: "The wrapper doesn't accept *args, **kwargs"
|
|
265
|
+
- label: "Missing @functools.wraps"
|
|
266
|
+
description: "Metadata from the original function is lost"
|
|
267
|
+
- label: "I'd like to try writing one from scratch"
|
|
268
|
+
description: "Use 'Other' to write your own code"
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
- **Non-programming**: Ask to identify which scenario best applies the concept:
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
header: "Apply it"
|
|
275
|
+
question: "Which real-world scenario best demonstrates [concept]?"
|
|
276
|
+
multiSelect: false
|
|
277
|
+
options:
|
|
278
|
+
- label: "[scenario A]"
|
|
279
|
+
- label: "[scenario B]"
|
|
280
|
+
- label: "[scenario C]"
|
|
281
|
+
- label: "I have my own example"
|
|
282
|
+
description: "Use 'Other' to share your own"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Keep it 2-5 minutes. Pass = mastered. Fail = diagnose gap, cycle back.
|
|
286
|
+
|
|
287
|
+
#### 3g. Sync Progress (Every Round)
|
|
288
|
+
|
|
289
|
+
Update `session.md` after each round:
|
|
290
|
+
|
|
291
|
+
- Change concept status if applicable
|
|
292
|
+
- Add new misconceptions or resolve existing ones
|
|
293
|
+
- Append to log
|
|
294
|
+
|
|
295
|
+
### Step 4: Session End
|
|
296
|
+
|
|
297
|
+
When all concepts mastered or user ends session:
|
|
298
|
+
|
|
299
|
+
1. Update `session.md` with final state.
|
|
300
|
+
2. **Generate learner-facing notes** — write `{topic-slug}-notes.md` in the topic directory. This is a standalone reference document the learner can review later. See "Notes Generation" below for format.
|
|
301
|
+
3. Update `.swifty/skills/teach-me/records/learner-profile.md` (keep under 30 lines):
|
|
302
|
+
|
|
303
|
+
```markdown
|
|
304
|
+
# Learner Profile
|
|
305
|
+
|
|
306
|
+
Updated: {timestamp}
|
|
307
|
+
|
|
308
|
+
## Style
|
|
309
|
+
|
|
310
|
+
- Learns best with: {concrete examples / abstract principles / visual ...}
|
|
311
|
+
- Pace: {fast / moderate / needs-time}
|
|
312
|
+
|
|
313
|
+
## Patterns
|
|
314
|
+
|
|
315
|
+
- Tends to confuse X with Y
|
|
316
|
+
- Recurring difficulty with: {area}
|
|
317
|
+
|
|
318
|
+
## Topics
|
|
319
|
+
|
|
320
|
+
- Python decorators (8/10 concepts, 2025-01-15)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
4. Give a brief text summary of what was covered, key insights, and areas for further study.
|
|
324
|
+
|
|
325
|
+
## Notes Generation
|
|
326
|
+
|
|
327
|
+
At session end, generate a learner-facing notes file at `{topic-slug}/{topic-slug}-notes.md`. This file is **written for the learner to review later**, not for the tutor. It should be self-contained and organized as a quick-reference.
|
|
328
|
+
|
|
329
|
+
### Notes Structure
|
|
330
|
+
|
|
331
|
+
```markdown
|
|
332
|
+
# {Topic} Notes
|
|
333
|
+
|
|
334
|
+
## 1. {Section Name}
|
|
335
|
+
|
|
336
|
+
{Key concept, mechanism, or principle}
|
|
337
|
+
|
|
338
|
+
- **One-line summary**: {what it does / why it matters}
|
|
339
|
+
- **Detail**: {brief explanation, 2-4 sentences max}
|
|
340
|
+
- **Example** (if applicable): {code snippet, command, or concrete scenario}
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## 2. {Section Name}
|
|
345
|
+
|
|
346
|
+
...
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## n. 实战参数 / Cheat Sheet (if applicable)
|
|
351
|
+
|
|
352
|
+
{Practical commands, config, or quick-reference table}
|
|
353
|
+
|
|
354
|
+
| Parameter / Concept | What it does | Tuning tip |
|
|
355
|
+
| ------------------- | ------------ | ---------- |
|
|
356
|
+
| ... | ... | ... |
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Notes Writing Rules
|
|
360
|
+
|
|
361
|
+
1. **Start with "what & why"** before "how". Each section should answer: what is this, why does it exist, what problem does it solve.
|
|
362
|
+
2. **Use analogies sparingly but effectively**. Only include an analogy if it clarifies a non-obvious mechanism (e.g., "PagedAttention is like OS virtual memory paging").
|
|
363
|
+
3. **Include trade-offs**. Every optimization or design choice has a cost. Always state it (e.g., "TP improves throughput but increases communication latency").
|
|
364
|
+
4. **Code / command examples should be minimal**. Under 10 lines, self-contained, with comments explaining the key flags.
|
|
365
|
+
5. **Organize by concept dependency**, not by chronological teaching order. Foundation concepts first, advanced ones last.
|
|
366
|
+
6. **No quiz questions, no misconceptions, no tutor-side notes**. This is a clean reference document.
|
|
367
|
+
7. **Language matches the session**. If the session was in Chinese, notes are in Chinese (technical terms can stay in English).
|
|
368
|
+
8. **Keep it under 150 lines**. If it gets too long, the learner won't review it. Be ruthless about cutting fluff.
|
|
369
|
+
|
|
370
|
+
## Resuming Sessions
|
|
371
|
+
|
|
372
|
+
On `--resume`:
|
|
373
|
+
|
|
374
|
+
1. Read `session.md` and `learner-profile.md`
|
|
375
|
+
2. Quick check on 1-2 previously mastered concepts via AskUserQuestion:
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
header: "Quick review"
|
|
379
|
+
question: "Last time you mastered [concept X]. Can you recall which of these is true about it?"
|
|
380
|
+
multiSelect: false
|
|
381
|
+
options:
|
|
382
|
+
- label: "[correct statement]"
|
|
383
|
+
- label: "[plausible distractor]"
|
|
384
|
+
- label: "[plausible distractor]"
|
|
385
|
+
- label: "I forgot this one"
|
|
386
|
+
description: "No worries, we'll revisit it"
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
3. If forgotten, mark as ✗ needs review and revisit before continuing
|
|
390
|
+
4. Recap: "Last time you mastered [X]. You were working on [Y]."
|
|
391
|
+
5. Continue from first in-progress or not-started concept
|
|
392
|
+
|
|
393
|
+
## Notes
|
|
394
|
+
|
|
395
|
+
- Keep it conversational, not mechanical
|
|
396
|
+
- Vary question types: predict, compare, debug, extend, teach-back, connect
|
|
397
|
+
- Slow down when struggling, speed up when flying
|
|
398
|
+
- Interleaving should feel natural, not like a pop quiz
|
|
399
|
+
- Wrong answers are more informative than right ones — never rush past them
|