learn-anything-cli 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Learn Anything
2
2
 
3
+ [English](./README.md) | [中文](./README.zh-CN.md)
4
+
3
5
  AI-powered recursive learning system — turns your AI coding assistant into an interactive tutor using the Socratic method and TDD-style exercises.
4
6
 
5
7
  Generate skill and command files for **30+ AI tools** (Claude Code, Cursor, Gemini CLI, Codex, Copilot, Windsurf, etc.), then use slash commands to systematically master any technical topic.
package/dist/cli/index.js CHANGED
@@ -43,7 +43,7 @@ program
43
43
  throw error;
44
44
  }
45
45
  else {
46
- throw new Error(mc.cannotAccess(targetPath, error.message));
46
+ throw new Error(mc.cannotAccess(targetPath, error.message), { cause: error });
47
47
  }
48
48
  }
49
49
  const { InitCommand } = await import('../core/init.js');
@@ -1,4 +1,4 @@
1
- import { claudeAdapter, cursorAdapter, codexAdapter, geminiAdapter, } from './adapters/index.js';
1
+ import { claudeAdapter, cursorAdapter, codexAdapter, geminiAdapter } from './adapters/index.js';
2
2
  export class CommandAdapterRegistry {
3
3
  static adapters = new Map();
4
4
  static {
@@ -1,33 +1,161 @@
1
1
  export const LEARN_DIR = '.learn';
2
2
  export const AI_TOOLS = [
3
- { name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer', skillsDir: '.amazonq' },
4
- { name: 'Antigravity', value: 'antigravity', available: true, successLabel: 'Antigravity', skillsDir: '.agent' },
5
- { name: 'Auggie (Augment CLI)', value: 'auggie', available: true, successLabel: 'Auggie', skillsDir: '.augment' },
6
- { name: 'Bob Shell', value: 'bob', available: true, successLabel: 'Bob Shell', skillsDir: '.bob' },
7
- { name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code', skillsDir: '.claude' },
3
+ {
4
+ name: 'Amazon Q Developer',
5
+ value: 'amazon-q',
6
+ available: true,
7
+ successLabel: 'Amazon Q Developer',
8
+ skillsDir: '.amazonq',
9
+ },
10
+ {
11
+ name: 'Antigravity',
12
+ value: 'antigravity',
13
+ available: true,
14
+ successLabel: 'Antigravity',
15
+ skillsDir: '.agent',
16
+ },
17
+ {
18
+ name: 'Auggie (Augment CLI)',
19
+ value: 'auggie',
20
+ available: true,
21
+ successLabel: 'Auggie',
22
+ skillsDir: '.augment',
23
+ },
24
+ {
25
+ name: 'Bob Shell',
26
+ value: 'bob',
27
+ available: true,
28
+ successLabel: 'Bob Shell',
29
+ skillsDir: '.bob',
30
+ },
31
+ {
32
+ name: 'Claude Code',
33
+ value: 'claude',
34
+ available: true,
35
+ successLabel: 'Claude Code',
36
+ skillsDir: '.claude',
37
+ },
8
38
  { name: 'Cline', value: 'cline', available: true, successLabel: 'Cline', skillsDir: '.cline' },
9
39
  { name: 'Codex', value: 'codex', available: true, successLabel: 'Codex', skillsDir: '.codex' },
10
- { name: 'ForgeCode', value: 'forgecode', available: true, successLabel: 'ForgeCode', skillsDir: '.forge' },
11
- { name: 'CodeBuddy Code (CLI)', value: 'codebuddy', available: true, successLabel: 'CodeBuddy Code', skillsDir: '.codebuddy' },
12
- { name: 'Continue', value: 'continue', available: true, successLabel: 'Continue (VS Code / JetBrains / Cli)', skillsDir: '.continue' },
13
- { name: 'CoStrict', value: 'costrict', available: true, successLabel: 'CoStrict', skillsDir: '.cospec' },
40
+ {
41
+ name: 'ForgeCode',
42
+ value: 'forgecode',
43
+ available: true,
44
+ successLabel: 'ForgeCode',
45
+ skillsDir: '.forge',
46
+ },
47
+ {
48
+ name: 'CodeBuddy Code (CLI)',
49
+ value: 'codebuddy',
50
+ available: true,
51
+ successLabel: 'CodeBuddy Code',
52
+ skillsDir: '.codebuddy',
53
+ },
54
+ {
55
+ name: 'Continue',
56
+ value: 'continue',
57
+ available: true,
58
+ successLabel: 'Continue (VS Code / JetBrains / Cli)',
59
+ skillsDir: '.continue',
60
+ },
61
+ {
62
+ name: 'CoStrict',
63
+ value: 'costrict',
64
+ available: true,
65
+ successLabel: 'CoStrict',
66
+ skillsDir: '.cospec',
67
+ },
14
68
  { name: 'Crush', value: 'crush', available: true, successLabel: 'Crush', skillsDir: '.crush' },
15
- { name: 'Cursor', value: 'cursor', available: true, successLabel: 'Cursor', skillsDir: '.cursor' },
16
- { name: 'Factory Droid', value: 'factory', available: true, successLabel: 'Factory Droid', skillsDir: '.factory' },
17
- { name: 'Gemini CLI', value: 'gemini', available: true, successLabel: 'Gemini CLI', skillsDir: '.gemini' },
18
- { name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot', skillsDir: '.github', detectionPaths: ['.github/copilot-instructions.md', '.github/instructions', '.github/workflows/copilot-setup-steps.yml', '.github/prompts', '.github/agents', '.github/skills', '.github/.mcp.json'] },
69
+ {
70
+ name: 'Cursor',
71
+ value: 'cursor',
72
+ available: true,
73
+ successLabel: 'Cursor',
74
+ skillsDir: '.cursor',
75
+ },
76
+ {
77
+ name: 'Factory Droid',
78
+ value: 'factory',
79
+ available: true,
80
+ successLabel: 'Factory Droid',
81
+ skillsDir: '.factory',
82
+ },
83
+ {
84
+ name: 'Gemini CLI',
85
+ value: 'gemini',
86
+ available: true,
87
+ successLabel: 'Gemini CLI',
88
+ skillsDir: '.gemini',
89
+ },
90
+ {
91
+ name: 'GitHub Copilot',
92
+ value: 'github-copilot',
93
+ available: true,
94
+ successLabel: 'GitHub Copilot',
95
+ skillsDir: '.github',
96
+ detectionPaths: [
97
+ '.github/copilot-instructions.md',
98
+ '.github/instructions',
99
+ '.github/workflows/copilot-setup-steps.yml',
100
+ '.github/prompts',
101
+ '.github/agents',
102
+ '.github/skills',
103
+ '.github/.mcp.json',
104
+ ],
105
+ },
19
106
  { name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow', skillsDir: '.iflow' },
20
107
  { name: 'Junie', value: 'junie', available: true, successLabel: 'Junie', skillsDir: '.junie' },
21
- { name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code', skillsDir: '.kilocode' },
108
+ {
109
+ name: 'Kilo Code',
110
+ value: 'kilocode',
111
+ available: true,
112
+ successLabel: 'Kilo Code',
113
+ skillsDir: '.kilocode',
114
+ },
22
115
  { name: 'Kiro', value: 'kiro', available: true, successLabel: 'Kiro', skillsDir: '.kiro' },
23
- { name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode', skillsDir: '.opencode' },
116
+ {
117
+ name: 'OpenCode',
118
+ value: 'opencode',
119
+ available: true,
120
+ successLabel: 'OpenCode',
121
+ skillsDir: '.opencode',
122
+ },
24
123
  { name: 'Pi', value: 'pi', available: true, successLabel: 'Pi', skillsDir: '.pi' },
25
124
  { name: 'Qoder', value: 'qoder', available: true, successLabel: 'Qoder', skillsDir: '.qoder' },
26
- { name: 'Lingma', value: 'lingma', available: true, successLabel: 'Lingma', skillsDir: '.lingma' },
27
- { name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code', skillsDir: '.qwen' },
28
- { name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode', skillsDir: '.roo' },
125
+ {
126
+ name: 'Lingma',
127
+ value: 'lingma',
128
+ available: true,
129
+ successLabel: 'Lingma',
130
+ skillsDir: '.lingma',
131
+ },
132
+ {
133
+ name: 'Qwen Code',
134
+ value: 'qwen',
135
+ available: true,
136
+ successLabel: 'Qwen Code',
137
+ skillsDir: '.qwen',
138
+ },
139
+ {
140
+ name: 'RooCode',
141
+ value: 'roocode',
142
+ available: true,
143
+ successLabel: 'RooCode',
144
+ skillsDir: '.roo',
145
+ },
29
146
  { name: 'Trae', value: 'trae', available: true, successLabel: 'Trae', skillsDir: '.trae' },
30
- { name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf', skillsDir: '.windsurf' },
31
- { name: 'AGENTS.md (works with Amp, VS Code, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' },
147
+ {
148
+ name: 'Windsurf',
149
+ value: 'windsurf',
150
+ available: true,
151
+ successLabel: 'Windsurf',
152
+ skillsDir: '.windsurf',
153
+ },
154
+ {
155
+ name: 'AGENTS.md (works with Amp, VS Code, …)',
156
+ value: 'agents',
157
+ available: false,
158
+ successLabel: 'your AGENTS.md-compatible assistant',
159
+ },
32
160
  ];
33
161
  //# sourceMappingURL=config.js.map
package/dist/core/init.js CHANGED
@@ -5,8 +5,8 @@ import { createRequire } from 'module';
5
5
  import { FileSystemUtils } from '../utils/file-system.js';
6
6
  import { AI_TOOLS, LEARN_DIR } from './config.js';
7
7
  import { isInteractive } from '../utils/interactive.js';
8
- import { generateCommands, CommandAdapterRegistry, } from './command-generation/index.js';
9
- import { getSkillTemplates, getCommandContents, generateSkillContent, } from './shared/index.js';
8
+ import { generateCommands, CommandAdapterRegistry } from './command-generation/index.js';
9
+ import { getSkillTemplates, getCommandContents, generateSkillContent } from './shared/index.js';
10
10
  import { getMessages } from '../i18n/index.js';
11
11
  const require = createRequire(import.meta.url);
12
12
  const { version: VERSION } = require('../../package.json');
@@ -53,7 +53,10 @@ export class InitCommand {
53
53
  }
54
54
  if (selectedTools.length === 0) {
55
55
  console.log(chalk.yellow(m.init.noToolsSelected));
56
- console.log(chalk.dim(m.init.availableTools(availableTools.filter((t) => t.available).map((t) => t.value).join(', '))));
56
+ console.log(chalk.dim(m.init.availableTools(availableTools
57
+ .filter((t) => t.available)
58
+ .map((t) => t.value)
59
+ .join(', '))));
57
60
  return;
58
61
  }
59
62
  // Generate skill files for each tool
@@ -77,7 +80,7 @@ export class InitCommand {
77
80
  console.log(cmd(chalk.cyan('/learn:status [topic-name]'), chalk.dim(' — Visualize learning state as knowledge map heatmap')));
78
81
  console.log('');
79
82
  }
80
- async detectTools(resolvedPath) {
83
+ async detectTools(_resolvedPath) {
81
84
  return AI_TOOLS;
82
85
  }
83
86
  hasToolDir(resolvedPath, tool) {
@@ -93,7 +96,7 @@ export class InitCommand {
93
96
  }
94
97
  async interactiveSelect(tools) {
95
98
  const availableTools = tools.filter((t) => t.available && t.skillsDir);
96
- const { search, checkbox } = await import('@inquirer/prompts');
99
+ const { checkbox } = await import('@inquirer/prompts');
97
100
  // Auto-detect existing tool dirs and pre-select them
98
101
  const detected = availableTools.filter((t) => this.hasToolDir(process.cwd(), t));
99
102
  const detectedValues = new Set(detected.map((t) => t.value));
@@ -1,11 +1,31 @@
1
1
  import { getLearnTopicSkillTemplate, getLearnExplainSkillTemplate, getLearnPracticeSkillTemplate, getLearnReviewSkillTemplate, getLearnStatusSkillTemplate, getLearnTopicCommandTemplate, getLearnExplainCommandTemplate, getLearnPracticeCommandTemplate, getLearnReviewCommandTemplate, getLearnStatusCommandTemplate, } from '../templates/skill-templates.js';
2
2
  export function getSkillTemplates() {
3
3
  return [
4
- { template: getLearnTopicSkillTemplate(), dirName: 'learn-anything-topic', workflowId: 'topic' },
5
- { template: getLearnExplainSkillTemplate(), dirName: 'learn-anything-explain', workflowId: 'explain' },
6
- { template: getLearnPracticeSkillTemplate(), dirName: 'learn-anything-practice', workflowId: 'practice' },
7
- { template: getLearnReviewSkillTemplate(), dirName: 'learn-anything-review', workflowId: 'review' },
8
- { template: getLearnStatusSkillTemplate(), dirName: 'learn-anything-status', workflowId: 'status' },
4
+ {
5
+ template: getLearnTopicSkillTemplate(),
6
+ dirName: 'learn-anything-topic',
7
+ workflowId: 'topic',
8
+ },
9
+ {
10
+ template: getLearnExplainSkillTemplate(),
11
+ dirName: 'learn-anything-explain',
12
+ workflowId: 'explain',
13
+ },
14
+ {
15
+ template: getLearnPracticeSkillTemplate(),
16
+ dirName: 'learn-anything-practice',
17
+ workflowId: 'practice',
18
+ },
19
+ {
20
+ template: getLearnReviewSkillTemplate(),
21
+ dirName: 'learn-anything-review',
22
+ workflowId: 'review',
23
+ },
24
+ {
25
+ template: getLearnStatusSkillTemplate(),
26
+ dirName: 'learn-anything-status',
27
+ workflowId: 'status',
28
+ },
9
29
  ];
10
30
  }
11
31
  export function getCommandTemplates() {
@@ -1,7 +1,7 @@
1
1
  export type { SkillTemplate, CommandTemplate } from './types.js';
2
- export { getLearnTopicSkillTemplate, getLearnTopicCommandTemplate } from './workflows/learn-topic.js';
3
- export { getLearnExplainSkillTemplate, getLearnExplainCommandTemplate } from './workflows/learn-explain.js';
4
- export { getLearnPracticeSkillTemplate, getLearnPracticeCommandTemplate } from './workflows/learn-practice.js';
5
- export { getLearnReviewSkillTemplate, getLearnReviewCommandTemplate } from './workflows/learn-review.js';
6
- export { getLearnStatusSkillTemplate, getLearnStatusCommandTemplate } from './workflows/learn-status.js';
2
+ export { getLearnTopicSkillTemplate, getLearnTopicCommandTemplate, } from './workflows/learn-topic.js';
3
+ export { getLearnExplainSkillTemplate, getLearnExplainCommandTemplate, } from './workflows/learn-explain.js';
4
+ export { getLearnPracticeSkillTemplate, getLearnPracticeCommandTemplate, } from './workflows/learn-practice.js';
5
+ export { getLearnReviewSkillTemplate, getLearnReviewCommandTemplate, } from './workflows/learn-review.js';
6
+ export { getLearnStatusSkillTemplate, getLearnStatusCommandTemplate, } from './workflows/learn-status.js';
7
7
  //# sourceMappingURL=skill-templates.d.ts.map
@@ -1,6 +1,6 @@
1
- export { getLearnTopicSkillTemplate, getLearnTopicCommandTemplate } from './workflows/learn-topic.js';
2
- export { getLearnExplainSkillTemplate, getLearnExplainCommandTemplate } from './workflows/learn-explain.js';
3
- export { getLearnPracticeSkillTemplate, getLearnPracticeCommandTemplate } from './workflows/learn-practice.js';
4
- export { getLearnReviewSkillTemplate, getLearnReviewCommandTemplate } from './workflows/learn-review.js';
5
- export { getLearnStatusSkillTemplate, getLearnStatusCommandTemplate } from './workflows/learn-status.js';
1
+ export { getLearnTopicSkillTemplate, getLearnTopicCommandTemplate, } from './workflows/learn-topic.js';
2
+ export { getLearnExplainSkillTemplate, getLearnExplainCommandTemplate, } from './workflows/learn-explain.js';
3
+ export { getLearnPracticeSkillTemplate, getLearnPracticeCommandTemplate, } from './workflows/learn-practice.js';
4
+ export { getLearnReviewSkillTemplate, getLearnReviewCommandTemplate, } from './workflows/learn-review.js';
5
+ export { getLearnStatusSkillTemplate, getLearnStatusCommandTemplate, } from './workflows/learn-status.js';
6
6
  //# sourceMappingURL=skill-templates.js.map
@@ -93,7 +93,7 @@ Synthesize these signals to judge whether the user is beginner, intermediate, or
93
93
 
94
94
  ### Step 4: Record Learning Session
95
95
 
96
- ⚠️ CRITICAL — Do this in the SAME turn as your explanation, BEFORE presenting sub-topics. Do NOT skip this step.
96
+ ⚠️ CRITICAL — Write the session file FIRST, then output its content to the conversation. This ensures zero drift between what the user sees and what gets saved. Do this BEFORE presenting sub-topics (Step 5).
97
97
 
98
98
  **A) Determine the filename:**
99
99
 
@@ -108,9 +108,11 @@ Examples:
108
108
 
109
109
  Match the language the user is learning in — don't force-translate.
110
110
 
111
- **B) Save the FULL explanation — use the Write tool:**
111
+ **B) Compose then WRITE the session file FIRST — use the Write tool:**
112
112
 
113
- The session file must contain the COMPLETE explanation you just delivered not just bullet points. The user should be able to re-read this file and get the full learning experience without looking at the chat.
113
+ Compose your COMPLETE explanation (positioning, analogy, core mechanism, code example with walkthrough, misconceptions, Socratic check, and quick summary). The user should be able to re-read this file and get the full learning experience without looking at the chat.
114
+
115
+ Write this content to the session file FIRST, before outputting anything to the conversation.
114
116
 
115
117
  \`\`\`markdown
116
118
  # [Concept Name] — Learning Session
@@ -124,31 +126,31 @@ The session file must contain the COMPLETE explanation you just delivered — no
124
126
 
125
127
  ## Positioning
126
128
 
127
- [Copy the one-sentence positioning you gave — where this concept sits in the knowledge map]
129
+ [Write the one-sentence positioning — where this concept sits in the knowledge map]
128
130
 
129
131
  ## Analogy
130
132
 
131
- [Copy the real-world metaphor/analogy you used]
133
+ [Write the real-world metaphor/analogy you composed]
132
134
 
133
135
  ## Core Mechanism
134
136
 
135
- [Copy the full "what and why" explanation in clear language, with all details]
137
+ [Write the full "what and why" explanation in clear language, with all details]
136
138
 
137
139
  ## Code Example
138
140
 
139
141
  \`\`\`[language]
140
- [Copy the complete code example you showed, with all comments]
142
+ [Write the complete code example, with all comments]
141
143
  \`\`\`
142
144
 
143
145
  [Include your walkthrough of the code — what each part does]
144
146
 
145
147
  ## Common Misconceptions
146
148
 
147
- [Copy the misconceptions you discussed]
149
+ [Write the misconceptions you identified]
148
150
 
149
151
  ## Socratic Check
150
152
 
151
- [Copy the thinking questions you posed to the user]
153
+ [Write the thinking questions you composed]
152
154
 
153
155
  ---
154
156
 
@@ -163,9 +165,13 @@ The session file must contain the COMPLETE explanation you just delivered — no
163
165
  (Will be updated after the user chooses a sub-topic direction)
164
166
  \`\`\`
165
167
 
166
- **C) Update state.yaml use the Edit tool:**
168
+ **C) Output the file content to the conversation:**
169
+
170
+ After writing the session file, present the EXACT content of the file you just wrote as your conversation response. Do NOT rephrase or regenerate — copy the file content verbatim into your message. Only after echoing the file content, proceed to Step 5 (identify sub-topics).
171
+
172
+ **D) Update state.yaml — use the Edit tool:**
167
173
 
168
- Edit \`./.learn/topics/<topic-name>/state.yaml\`:
174
+ In the same turn, also use the Edit tool to update \`./.learn/topics/<topic-name>/state.yaml\`:
169
175
  - If concept status is \`unexplored\`, update to \`in_progress\`
170
176
  - Update \`last_session\` to current date
171
177
  - If the user showed good understanding, increase \`confidence\` by 0.05 to 0.1
@@ -229,9 +235,9 @@ const COMMAND_CONTENT = `Use the learn-anything-explain skill to handle the user
229
235
  Follow the workflow defined in the skill:
230
236
  1. Load context: match topic → read knowledge map → read learning state
231
237
  2. Assess user level (beginner/intermediate/advanced) and adjust teaching strategy
232
- 3. Follow the explanation structure: positioning → analogy → core mechanism → code example → common misconceptions → Socratic check
233
- 4. CRITICAL — immediately after explaining, use the Write tool to save the FULL explanation to ./.learn/topics/<topic>/sessions/<concept-name>-YYYY-MM-DD.md (match the user's language), use the Edit tool to update state.yaml
234
- 5. Identify sub-topics as recursive entry points (only AFTER saving the session)`;
238
+ 3. Compose the full explanation: positioning → analogy → core mechanism → code example → common misconceptions → Socratic check
239
+ 4. CRITICAL — Write the session file FIRST (./.learn/topics/<topic>/sessions/<concept-name>-YYYY-MM-DD.md, matching the user's language), then echo the file content verbatim to the conversation. Also update state.yaml with Edit.
240
+ 5. Identify sub-topics as recursive entry points (only AFTER saving the session and echoing to conversation)`;
235
241
  export function getLearnExplainSkillTemplate() {
236
242
  return {
237
243
  name: SKILL_NAME,
@@ -310,9 +310,11 @@ The user submits their code or answer in the chat. Review it using the framework
310
310
  - Set status to needs_practice
311
311
  - Note specific areas to focus on
312
312
 
313
- **After providing your feedback**, immediately save the session record. ⚠️ Do this in the SAME turn do NOT wait for the user's next message.
313
+ ⚠️ CRITICAL Write the session file FIRST, then echo its content to the conversation. This ensures zero drift between what the user sees and what gets saved.
314
314
 
315
- - Use the Write tool to create \`./.learn/topics/<topic-name>/sessions/<concept-name>-practice-YYYY-MM-DD.md\` match the user's language (see Step 5 for naming rules and format)
315
+ 1. **Write the session file** — Use the Write tool to create \`./.learn/topics/<topic-name>/sessions/<concept-name>-practice-YYYY-MM-DD.md\` with the full feedback content (see Step 5 for naming rules and format). Include everything: your acknowledgment, Socratic follow-up questions, edge case discussion, code quality tips, and final assessment.
316
+
317
+ 2. **Output the file content to the conversation** — After writing, present the exact content of the file you just wrote as your conversation response. Do NOT rephrase or regenerate it — copy the file content verbatim into your message.
316
318
 
317
319
  ### Step 5: Practice Session Record Format
318
320
 
@@ -375,8 +377,8 @@ Follow the workflow defined in the skill:
375
377
  2. Assess difficulty level based on state.yaml (beginner/intermediate/challenge)
376
378
  3. Project Mode: use Bash to create exercise dir → use Write to create README.md + starter file → tell user to open in IDE
377
379
  Chat Mode: generate exercise in chat (background → requirements → code template → hint)
378
- 4. Project Mode: use Read to review user's code file → optionally use Bash to run it → provide structured feedback → immediately use Write to save session record, Edit to update state.yaml
379
- Chat Mode: review code submitted in chat → provide structured feedback → immediately use Write to save session record, Edit to update state.yaml`;
380
+ 4. Project Mode: use Read to review user's code file → optionally use Bash to run it → compose feedback → Write session file FIRST → echo file content verbatim to conversation + Edit to update state.yaml
381
+ Chat Mode: review code submitted in chat → compose feedback → Write session file FIRST → echo file content verbatim to conversation + Edit to update state.yaml`;
380
382
  export function getLearnPracticeSkillTemplate() {
381
383
  return {
382
384
  name: SKILL_NAME,
@@ -8,10 +8,7 @@ export function getMessages(locale) {
8
8
  return messages[locale];
9
9
  }
10
10
  export function detectSystemLocale() {
11
- const langEnv = process.env.LANG ||
12
- process.env.LC_ALL ||
13
- process.env.LANGUAGE ||
14
- '';
11
+ const langEnv = process.env.LANG || process.env.LC_ALL || process.env.LANGUAGE || '';
15
12
  if (/^zh[_-]/i.test(langEnv)) {
16
13
  return 'zh-CN';
17
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "learn-anything-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "AI-powered recursive learning system with Socratic method and TDD practice",
5
5
  "keywords": [
6
6
  "learn-anything-cli",
@@ -46,14 +46,24 @@
46
46
  "test": "vitest run",
47
47
  "test:watch": "vitest",
48
48
  "lint": "eslint src/",
49
- "prepublishOnly": "pnpm run build"
49
+ "format": "prettier --write .",
50
+ "format:check": "prettier --check .",
51
+ "prepublishOnly": "pnpm run build",
52
+ "prepare": "husky"
50
53
  },
51
54
  "engines": {
52
- "node": ">=20.19.0"
55
+ "node": ">=20.0.0"
53
56
  },
54
57
  "devDependencies": {
58
+ "@commitlint/cli": "^21.0.2",
59
+ "@commitlint/config-conventional": "^21.0.2",
60
+ "@eslint/js": "^10.0.1",
55
61
  "@types/node": "^24.2.0",
56
62
  "eslint": "^9.39.2",
63
+ "eslint-config-prettier": "^10.1.8",
64
+ "husky": "^9.1.7",
65
+ "lint-staged": "^17.0.7",
66
+ "prettier": "^3.8.3",
57
67
  "typescript": "^5.9.3",
58
68
  "typescript-eslint": "^8.50.1",
59
69
  "vitest": "^3.2.4"
@@ -65,5 +75,14 @@
65
75
  "fast-glob": "^3.3.3",
66
76
  "yaml": "^2.8.2",
67
77
  "zod": "^4.0.17"
78
+ },
79
+ "lint-staged": {
80
+ "*.{ts,js,mjs}": [
81
+ "eslint --fix",
82
+ "prettier --write"
83
+ ],
84
+ "*.{json,md,yml,yaml}": [
85
+ "prettier --write"
86
+ ]
68
87
  }
69
88
  }