prompt-translator 0.0.1

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 ADDED
@@ -0,0 +1,206 @@
1
+ # prompt-translator
2
+
3
+ CLI tool to generate customized prompt-translator skills for AI agents.
4
+
5
+ ## Installation
6
+
7
+ ### Global installation
8
+ ```bash
9
+ bun install -g prompt-translator
10
+ ```
11
+
12
+ ### Using bunx (no install)
13
+ ```bash
14
+ bunx prompt-translator
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Run the CLI without any arguments to start the interactive installer:
20
+
21
+ ```bash
22
+ prompt-translator
23
+ ```
24
+
25
+ ### Interactive Flow
26
+
27
+ 1. **Select AI Tool**
28
+ - `OpenCode` — for OpenCode CLI
29
+ - `Claude` — for claude.ai / Claude Code
30
+ - `Other` — for Codex, Gemini, and other agents
31
+
32
+ 2. **Select Language** (BCP-47 tag)
33
+ - Choose from supported languages: zh, ja, ko, es, ru, fr, de, pt, it
34
+
35
+ 3. **Select Region** (if multiple options)
36
+ - For languages with multiple regions, select the appropriate one
37
+ - Single-region languages (ja, ko, ru, pt) are auto-selected
38
+
39
+ 4. **Select CEFR Level**
40
+ - A1–C2 (default: B2)
41
+
42
+ 5. **Confirm** — Review and generate
43
+
44
+ ### Example Session
45
+
46
+ ```
47
+ $ prompt-translator
48
+
49
+ Prompt Translator Skill Generator
50
+
51
+ ? Which AI tool are you using? OpenCode
52
+ → config: ~/.config/opencode/AGENTS.md skills: ~/.config/opencode/skills/prompt-translator
53
+
54
+ ? Language tag (type to search): zh
55
+ ? Region for zh? TW
56
+
57
+ ✓ BCP-47 tag: zh-TW
58
+
59
+ ? Target English level (CEFR): B2 — Upper Intermediate (recommended)
60
+
61
+ Tool: OpenCode
62
+ Language: zh-TW
63
+ CEFR: B2
64
+ Skills: ~/.config/opencode/skills/prompt-translator
65
+
66
+ ? Generate skill with these settings? Yes
67
+ ```
68
+
69
+ ## Installation Paths
70
+
71
+ ### OpenCode
72
+ - Skills directory: `~/.config/opencode/skills/prompt-translator/`
73
+ - Config file: `~/.config/opencode/AGENTS.md`
74
+
75
+ ### Claude
76
+ - Skills directory: `~/.claude/skills/prompt-translator/`
77
+ - Config file: `~/.claude/CLAUDE.md`
78
+
79
+ ### Other (Codex, Gemini, etc.)
80
+ - Skills directory: `~/.agents/skills/prompt-translator/`
81
+ - Config file: `~/.agents/AGENTS.md`
82
+
83
+ ## Configuration File Update
84
+
85
+ The CLI will:
86
+
87
+ 1. **Scan** for existing `CLAUDE.md` or `AGENTS.md`
88
+ 2. **If found**: Ask whether to add the skill to `## Required Skills`
89
+ 3. **If not found**: Print a snippet for you to add manually
90
+
91
+ ### Manual Snippet
92
+
93
+ If no config file exists, add this to your `AGENTS.md` or `CLAUDE.md`:
94
+
95
+ ```markdown
96
+ ## Required Skills
97
+
98
+ - **prompt-translator**: Use when the user's prompt contains [Language] text that needs translation to English before processing. Translates [Language] to CEFR [Level] English while preserving existing English, technical terms, and proper nouns.
99
+ ```
100
+
101
+ ## CEFR Levels
102
+
103
+ | Level | Name | Description |
104
+ |-------|------|-------------|
105
+ | A1 | Beginner | Basic phrases and familiar everyday expressions |
106
+ | A2 | Elementary | Simple sentences and frequently used expressions |
107
+ | B1 | Intermediate | Clear standard communication on familiar matters |
108
+ | **B2** | **Upper Intermediate** | **Complex texts and technical discussion** *(default)* |
109
+ | C1 | Advanced | Fluent expression without much searching |
110
+ | C2 | Proficient | Near-native precision and nuance |
111
+
112
+ ## Supported Languages
113
+
114
+ The following BCP-47 language tags are supported:
115
+
116
+ ### Chinese
117
+ - `zh-TW` — Traditional Chinese (Taiwan)
118
+ - `zh-HK` — Traditional Chinese (Hong Kong)
119
+ - `zh-CN` — Simplified Chinese (China)
120
+
121
+ ### Japanese
122
+ - `ja-JP` — Japanese (Japan)
123
+
124
+ ### Korean
125
+ - `ko-KR` — Korean (South Korea)
126
+
127
+ ### Spanish
128
+ - `es-ES` — Spanish (Spain)
129
+ - `es-MX` — Spanish (Mexico)
130
+
131
+ ### Russian
132
+ - `ru-RU` — Russian (Russia)
133
+
134
+ ### French
135
+ - `fr-FR` — French (France)
136
+ - `fr-CA` — French (Canada)
137
+
138
+ ### German
139
+ - `de-DE` — German (Germany)
140
+ - `de-CH` — German (Switzerland)
141
+
142
+ ### Portuguese
143
+ - `pt-BR` — Portuguese (Brazil)
144
+
145
+ ### Italian
146
+ - `it-IT` — Italian (Italy)
147
+ - `it-CH` — Italian (Switzerland)
148
+
149
+ ## Development
150
+
151
+ ### Setup
152
+ ```bash
153
+ bun install
154
+ ```
155
+
156
+ ### Build
157
+ ```bash
158
+ bun run build
159
+ ```
160
+
161
+ ### Test
162
+ ```bash
163
+ bun run test
164
+ ```
165
+
166
+ ## Project Structure
167
+
168
+ ```
169
+ prompt-translator/
170
+ ├── src/
171
+ │ ├── cli.ts # CLI entry point
172
+ │ ├── interactive.ts # Interactive prompts
173
+ │ ├── generate.ts # Skill generation logic
174
+ │ ├── languages.ts # Language name utilities
175
+ │ ├── cefr.ts # CEFR level definitions
176
+ │ ├── paths.ts # File path resolution
177
+ │ └── install.ts # Config file installation
178
+ ├── templates/
179
+ │ ├── SKILL.md.hbs # Skill template (Handlebars)
180
+ │ └── languages.json # Example translations per language
181
+ └── README.md
182
+ ```
183
+
184
+ ### Adding/Modifying Examples
185
+
186
+ Edit `templates/languages.json` to add or modify translation examples. Each language key contains an array of `{input, output}` pairs:
187
+
188
+ ```json
189
+ {
190
+ "zh": [
191
+ { "input": "請幫我寫一個 Python script", "output": "Please help me write a Python script" },
192
+ ...
193
+ ],
194
+ "ja": [...],
195
+ ...
196
+ }
197
+ ```
198
+
199
+ ### Template Customization
200
+
201
+ Edit `templates/SKILL.md.hbs` to modify the skill structure:
202
+ - `{{skillName}}` — Name of the skill
203
+ - `{{languageName}}` — Human-readable language name
204
+ - `{{cefrLevel}}` — CEFR level (A1–C2)
205
+ - `{{cefrDescription}}` — Description of the level
206
+ - `{{#each examples}}` — Loop through examples
package/dist/cefr.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ export declare const CEFR_LEVELS: {
2
+ readonly A1: {
3
+ readonly name: "Beginner";
4
+ readonly description: "basic phrases and familiar everyday expressions";
5
+ };
6
+ readonly A2: {
7
+ readonly name: "Elementary";
8
+ readonly description: "simple sentences and frequently used expressions";
9
+ };
10
+ readonly B1: {
11
+ readonly name: "Intermediate";
12
+ readonly description: "clear standard communication on familiar matters";
13
+ };
14
+ readonly B2: {
15
+ readonly name: "Upper Intermediate";
16
+ readonly description: "complex texts and technical discussion in specialization";
17
+ };
18
+ readonly C1: {
19
+ readonly name: "Advanced";
20
+ readonly description: "fluent expression without much searching for expressions";
21
+ };
22
+ readonly C2: {
23
+ readonly name: "Proficient";
24
+ readonly description: "near-native precision and nuance";
25
+ };
26
+ };
27
+ export type CEFRLevel = keyof typeof CEFR_LEVELS;
28
+ export declare function isValidCEFR(level: string): level is CEFRLevel;
29
+ export declare function getCEFRInfo(level: CEFRLevel): {
30
+ readonly name: "Beginner";
31
+ readonly description: "basic phrases and familiar everyday expressions";
32
+ } | {
33
+ readonly name: "Elementary";
34
+ readonly description: "simple sentences and frequently used expressions";
35
+ } | {
36
+ readonly name: "Intermediate";
37
+ readonly description: "clear standard communication on familiar matters";
38
+ } | {
39
+ readonly name: "Upper Intermediate";
40
+ readonly description: "complex texts and technical discussion in specialization";
41
+ } | {
42
+ readonly name: "Advanced";
43
+ readonly description: "fluent expression without much searching for expressions";
44
+ } | {
45
+ readonly name: "Proficient";
46
+ readonly description: "near-native precision and nuance";
47
+ };
48
+ export declare function getDefaultCEFR(): CEFRLevel;
49
+ //# sourceMappingURL=cefr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cefr.d.ts","sourceRoot":"","sources":["../src/cefr.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;CAyBd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,WAAW,CAAC;AAEjD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,SAAS,CAE7D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS;;;;;;;;;;;;;;;;;;EAE3C;AAED,wBAAgB,cAAc,IAAI,SAAS,CAE1C"}
package/dist/cefr.js ADDED
@@ -0,0 +1,36 @@
1
+ export const CEFR_LEVELS = {
2
+ A1: {
3
+ name: 'Beginner',
4
+ description: 'basic phrases and familiar everyday expressions',
5
+ },
6
+ A2: {
7
+ name: 'Elementary',
8
+ description: 'simple sentences and frequently used expressions',
9
+ },
10
+ B1: {
11
+ name: 'Intermediate',
12
+ description: 'clear standard communication on familiar matters',
13
+ },
14
+ B2: {
15
+ name: 'Upper Intermediate',
16
+ description: 'complex texts and technical discussion in specialization',
17
+ },
18
+ C1: {
19
+ name: 'Advanced',
20
+ description: 'fluent expression without much searching for expressions',
21
+ },
22
+ C2: {
23
+ name: 'Proficient',
24
+ description: 'near-native precision and nuance',
25
+ },
26
+ };
27
+ export function isValidCEFR(level) {
28
+ return level.toUpperCase() in CEFR_LEVELS;
29
+ }
30
+ export function getCEFRInfo(level) {
31
+ return CEFR_LEVELS[level];
32
+ }
33
+ export function getDefaultCEFR() {
34
+ return 'B2';
35
+ }
36
+ //# sourceMappingURL=cefr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cefr.js","sourceRoot":"","sources":["../src/cefr.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,EAAE,EAAE;QACH,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,iDAAiD;KAC9D;IACD,EAAE,EAAE;QACH,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,kDAAkD;KAC/D;IACD,EAAE,EAAE;QACH,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,kDAAkD;KAC/D;IACD,EAAE,EAAE;QACH,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,0DAA0D;KACvE;IACD,EAAE,EAAE;QACH,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,0DAA0D;KACvE;IACD,EAAE,EAAE;QACH,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,kCAAkC;KAC/C;CACQ,CAAC;AAIX,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,OAAO,KAAK,CAAC,WAAW,EAAE,IAAI,WAAW,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAgB;IAC3C,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc;IAC7B,OAAO,IAAI,CAAC;AACb,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { runInteractive } from './interactive.js';
4
+ const program = new Command();
5
+ program
6
+ .name('prompt-translator')
7
+ .description('Generate customized prompt-translator skills for AI agents')
8
+ .version('1.0.0')
9
+ .action(async () => {
10
+ await runInteractive();
11
+ });
12
+ program.parse();
13
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACL,IAAI,CAAC,mBAAmB,CAAC;KACzB,WAAW,CAAC,4DAA4D,CAAC;KACzE,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,KAAK,IAAI,EAAE;IAClB,MAAM,cAAc,EAAE,CAAC;AACxB,CAAC,CAAC,CAAC;AAEJ,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type CEFRLevel } from './cefr.js';
2
+ import type { ToolTarget } from './paths.js';
3
+ export declare const SKILL_NAME = "prompt-translator";
4
+ export interface GenerateOptions {
5
+ lang: string;
6
+ cefr: CEFRLevel;
7
+ target: ToolTarget;
8
+ cwd: string;
9
+ }
10
+ export interface GenerateResult {
11
+ skillPath: string;
12
+ skillName: string;
13
+ skillDescription: string;
14
+ }
15
+ export declare function generateSkill(options: GenerateOptions): Promise<GenerateResult>;
16
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,WAAW,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,UAAU,sBAAsB,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;CACzB;AAED,wBAAsB,aAAa,CAClC,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,cAAc,CAAC,CAuCzB"}
@@ -0,0 +1,45 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import Handlebars from 'handlebars';
5
+ import chalk from 'chalk';
6
+ import { getCEFRInfo } from './cefr.js';
7
+ import { getLanguageName } from './languages.js';
8
+ export const SKILL_NAME = 'prompt-translator';
9
+ export async function generateSkill(options) {
10
+ const { lang, cefr, target } = options;
11
+ const languageName = getLanguageName(lang);
12
+ const cefrInfo = getCEFRInfo(cefr);
13
+ const examplesData = await loadExamples();
14
+ const examples = getExamples(lang, examplesData);
15
+ const templateData = {
16
+ skillName: SKILL_NAME,
17
+ languageName,
18
+ cefrLevel: cefr,
19
+ cefrDescription: cefrInfo.description,
20
+ ...examples,
21
+ };
22
+ // skill dir is fixed: target.skillsDir already includes "prompt-translator"
23
+ await mkdir(target.skillsDir, { recursive: true });
24
+ const templatePath = join(dirname(fileURLToPath(import.meta.url)), '..', 'templates', 'SKILL.md.hbs');
25
+ const templateContent = await readFile(templatePath, 'utf-8');
26
+ const skillContent = Handlebars.compile(templateContent)(templateData);
27
+ const skillPath = join(target.skillsDir, 'SKILL.md');
28
+ await writeFile(skillPath, skillContent);
29
+ console.log(chalk.green(` ✓ Skill written to ${skillPath}`));
30
+ const skillDescription = `Use when the user's prompt contains ${languageName} text that needs translation to English before processing. ` +
31
+ `Translates ${languageName} to CEFR ${cefr} English while preserving existing English, technical terms, and proper nouns.`;
32
+ return { skillPath, skillName: SKILL_NAME, skillDescription };
33
+ }
34
+ // ── examples ──────────────────────────────────────────────────────────────────
35
+ async function loadExamples() {
36
+ const filePath = join(dirname(fileURLToPath(import.meta.url)), '..', 'templates', 'languages.json');
37
+ const content = await readFile(filePath, 'utf-8');
38
+ return JSON.parse(content);
39
+ }
40
+ function getExamples(lang, data) {
41
+ const base = lang.split('-')[0].toLowerCase();
42
+ const examples = data[base] ?? data.zh;
43
+ return { examples };
44
+ }
45
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAkB,WAAW,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,MAAM,CAAC,MAAM,UAAU,GAAG,mBAAmB,CAAC;AAe9C,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,OAAwB;IAExB,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAEvC,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,YAAY,GAAG,MAAM,YAAY,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAEjD,MAAM,YAAY,GAAG;QACpB,SAAS,EAAE,UAAU;QACrB,YAAY;QACZ,SAAS,EAAE,IAAI;QACf,eAAe,EAAE,QAAQ,CAAC,WAAW;QACrC,GAAG,QAAQ;KACX,CAAC;IAEF,4EAA4E;IAC5E,MAAM,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEnD,MAAM,YAAY,GAAG,IAAI,CACxB,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EACvC,IAAI,EACJ,WAAW,EACX,cAAc,CACd,CAAC;IAEF,MAAM,eAAe,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC9D,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,YAAY,CAAC,CAAC;IAEvE,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACrD,MAAM,SAAS,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAEzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,SAAS,EAAE,CAAC,CAAC,CAAC;IAE9D,MAAM,gBAAgB,GACrB,uCAAuC,YAAY,6DAA6D;QAChH,cAAc,YAAY,YAAY,IAAI,gFAAgF,CAAC;IAE5H,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC;AAC/D,CAAC;AAED,iFAAiF;AAEjF,KAAK,UAAU,YAAY;IAG1B,MAAM,QAAQ,GAAG,IAAI,CACpB,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EACvC,IAAI,EACJ,WAAW,EACX,gBAAgB,CAChB,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,WAAW,CACnB,IAAY,EACZ,IAA8D;IAE9D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;IACvC,OAAO,EAAE,QAAQ,EAAE,CAAC;AACrB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { ToolTarget } from './paths.js';
2
+ export interface InstallOptions {
3
+ target: ToolTarget;
4
+ skillName: string;
5
+ skillDescription: string;
6
+ cwd: string;
7
+ }
8
+ export declare function installConfig(options: InstallOptions): Promise<void>;
9
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGxC,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;CACZ;AAID,wBAAsB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAuC1E"}
@@ -0,0 +1,69 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readFile, writeFile } from 'node:fs/promises';
3
+ import prompts from 'prompts';
4
+ import chalk from 'chalk';
5
+ import { SKILL_NAME } from './generate.js';
6
+ const SECTION_HEADER = '## Required Skills';
7
+ export async function installConfig(options) {
8
+ const { target, skillName, skillDescription } = options;
9
+ const { configFile, configFileName } = target;
10
+ const entry = `- **${skillName}**: ${skillDescription}`;
11
+ // Config file does not exist — print suggestion only, do not create
12
+ if (!existsSync(configFile)) {
13
+ console.log('');
14
+ console.log(chalk.yellow(`${configFileName} not found at ${configFile}`));
15
+ console.log(chalk.gray('Add the following snippet to your config file manually:\n'));
16
+ printSuggestion(entry);
17
+ return;
18
+ }
19
+ // Config file exists — ask whether to update
20
+ const { shouldUpdate } = await prompts({
21
+ type: 'confirm',
22
+ name: 'shouldUpdate',
23
+ message: `Found ${configFileName}. Add skill to "${SECTION_HEADER}"?`,
24
+ initial: true,
25
+ });
26
+ if (!shouldUpdate) {
27
+ console.log('');
28
+ console.log(chalk.gray('Skipped. Add the following snippet manually:\n'));
29
+ printSuggestion(entry);
30
+ return;
31
+ }
32
+ const original = await readFile(configFile, 'utf-8');
33
+ // Backup before modifying
34
+ await writeFile(`${configFile}.bak`, original);
35
+ const updated = upsertSkillEntry(original, entry);
36
+ await writeFile(configFile, updated);
37
+ console.log(chalk.green(` ✓ Updated ${configFileName}`));
38
+ }
39
+ // ── helpers ───────────────────────────────────────────────────────────────────
40
+ function printSuggestion(entry) {
41
+ console.log(chalk.cyan(`${SECTION_HEADER}\n\n${entry}`));
42
+ console.log('');
43
+ }
44
+ function upsertSkillEntry(content, entry) {
45
+ const sectionIdx = content.indexOf(SECTION_HEADER);
46
+ if (sectionIdx === -1) {
47
+ // Append section at end
48
+ const suffix = content.endsWith('\n') ? '' : '\n';
49
+ return `${content}${suffix}\n${SECTION_HEADER}\n\n${entry}\n`;
50
+ }
51
+ // Find section boundaries
52
+ const afterHeader = sectionIdx + SECTION_HEADER.length;
53
+ const nextSectionMatch = content.slice(afterHeader).match(/\n## /);
54
+ const sectionEnd = nextSectionMatch
55
+ ? afterHeader + nextSectionMatch.index
56
+ : content.length;
57
+ const sectionBody = content.slice(afterHeader, sectionEnd);
58
+ // Already listed — update in place
59
+ if (sectionBody.includes(SKILL_NAME)) {
60
+ const pattern = new RegExp(`^- \\*\\*${SKILL_NAME}\\*\\*:.*$`, 'm');
61
+ const newBody = sectionBody.replace(pattern, entry);
62
+ return content.slice(0, afterHeader) + newBody + content.slice(sectionEnd);
63
+ }
64
+ // Append to section
65
+ const trimmed = sectionBody.trimEnd();
66
+ const newBody = trimmed.length ? `${trimmed}\n${entry}\n` : `\n\n${entry}\n`;
67
+ return content.slice(0, afterHeader) + newBody + content.slice(sectionEnd);
68
+ }
69
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type ToolTarget } from './paths.js';
2
+ import type { CEFRLevel } from './cefr.js';
3
+ export interface InteractiveResult {
4
+ tool: 'claude' | 'other';
5
+ lang: string;
6
+ cefr: CEFRLevel;
7
+ target: ToolTarget;
8
+ }
9
+ export declare function runInteractive(): Promise<void>;
10
+ //# sourceMappingURL=interactive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,UAAU,EAGf,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAI3C,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;CACnB;AAiHD,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAqIpD"}