engly 1.0.0 β†’ 1.2.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/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.0] - 2026-09-26
9
+
10
+ ### Changed
11
+
12
+ - `engly init` for Claude Code writes the Engly block in `AGENTS.md` instead of creating a `CLAUDE.md` when there is an `AGENTS.md` and no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` in the project or any directory above it. Claude Code only reads `AGENTS.md` in that case.
13
+ - CI and release workflows run on `ubuntu-24.04` instead of `ubuntu-latest`, ahead of the label moving to Ubuntu 26 on October 19, 2026.
14
+
15
+ ## [1.1.0] - 2026-09-26
16
+
17
+ ### Changed
18
+
19
+ - README Quick start now installs with `npm install -g engly`, now that the package is published on npm.
20
+
8
21
  ## [1.0.0] - 2026-09-26
9
22
 
10
23
  First version: project foundation and `engly init` for Claude Code.
@@ -28,4 +41,6 @@ First version: project foundation and `engly init` for Claude Code.
28
41
  - Version check on pull requests to `main`: `version` in `package.json` must be greater than on `main` (semver, prereleases included), and `CHANGELOG.md` must have an entry for it. All jobs report to a single `Required checks` status for branch protection.
29
42
  - Release workflow on pushes to `main`: publishes to npm with provenance (trusted publishing, no token) and creates the `vX.Y.Z` tag and GitHub release from the CHANGELOG entry.
30
43
 
44
+ [1.2.0]: https://github.com/victorcastro/engly/releases/tag/v1.2.0
45
+ [1.1.0]: https://github.com/victorcastro/engly/releases/tag/v1.1.0
31
46
  [1.0.0]: https://github.com/victorcastro/engly/releases/tag/v1.0.0
package/README.md CHANGED
@@ -16,6 +16,11 @@ One command. Every agent. Always on. Zero cost.
16
16
 
17
17
  You write prompts to an AI all day. Why not improve your English while you do it?
18
18
 
19
+ - **Always on.** Coaching starts with your next prompt, no command to remember.
20
+ - **Never in your way.** A few lines of feedback, then the task goes on as usual.
21
+ - **Zero cost.** No API keys, no extra model. The agent you already use does the coaching.
22
+ - **For any level.** From A2 to C1, in any native language.
23
+
19
24
  Engly teaches the coding agent you already use (Claude Code, Codex, Cursor…) to coach your English. Every prompt becomes a tiny lesson: the agent shows you how to say it in clear English, points out a couple of mistakes, and then does the task as if you had written the better prompt.
20
25
 
21
26
  No extra app, no extra model, no extra cost. Just better English and clearer prompts.
@@ -52,20 +57,19 @@ Sure, here's the updated function...
52
57
 
53
58
  **Your English is already right?** Engly stays quiet and the agent just gets to work.
54
59
 
55
- ## πŸ’‘ Why Engly
56
-
57
- - **Always on.** No command to remember. Coaching starts with your next prompt.
58
- - **Never in your way.** A few lines of feedback, then the task goes on. It never asks for confirmation.
59
- - **Zero cost.** No API keys and no calls to other models. The agent you already use does the coaching.
60
- - **Safe for your files.** Engly only writes inside its own marked block and never touches anything else.
61
- - **Made for learners.** Built with Spanish speakers in mind, and it works for any native language from A2 to C1.
62
-
63
60
  ## πŸš€ Quick start
64
61
 
65
62
  > [!NOTE]
66
- > Engly is in early development. Today `engly init` works for **Claude Code**, and the package is not on npm yet. More agents and commands are coming soon.
63
+ > Engly is in early development. Today `engly init` works for **Claude Code**. More agents and commands are coming soon.
64
+
65
+ Install it with one command:
67
66
 
68
- Install it from source:
67
+ ```sh
68
+ npm install -g engly
69
+ ```
70
+
71
+ <details>
72
+ <summary>Or install from a clone</summary>
69
73
 
70
74
  ```sh
71
75
  git clone https://github.com/victorcastro/engly.git
@@ -75,22 +79,21 @@ npm run build
75
79
  npm link
76
80
  ```
77
81
 
82
+ </details>
83
+
78
84
  Then, in your project:
79
85
 
80
86
  ```sh
81
- cd your-project
82
87
  engly init
83
88
  ```
84
89
 
85
90
  Open your agent as usual. Your next prompt will be coached. πŸŽ‰
86
91
 
87
- Once Engly is on npm, installing will be a single command: `npm install -g engly`.
88
-
89
92
  ## πŸ€– Supported agents
90
93
 
91
94
  | Agent | Where Engly adds its instructions | Status |
92
95
  |---|---|---|
93
- | Claude Code | One import line in `CLAUDE.md` | βœ… Available |
96
+ | Claude Code | One import line in `CLAUDE.md`, or in `AGENTS.md` when there is no `CLAUDE.md` | βœ… Available |
94
97
  | Codex | Marked block in `AGENTS.md` | πŸ› οΈ Coming soon |
95
98
  | OpenCode | Marked block in `AGENTS.md` | πŸ› οΈ Coming soon |
96
99
  | Cursor | Its own rule file, `.cursor/rules/engly.mdc` | πŸ› οΈ Coming soon |
@@ -98,15 +101,13 @@ Once Engly is on npm, installing will be a single command: `npm install -g engly
98
101
 
99
102
  ## πŸ’¬ In-chat commands
100
103
 
101
- While you chat with your agent:
104
+ While you chat with Claude Code:
102
105
 
103
- | Skill | What it does |
104
- |---|---|
105
- | `/engly-off` | Pause the coach for this session ("I'm in a hurry"). |
106
- | `/engly-on` | Resume the coach. |
107
- | `/engly-review` | Deep review of a prompt: every mistake, why, and better alternatives. |
108
-
109
- In Codex, use `$engly-off` instead of `/engly-off`. In any agent you can also just type **"engly off"** or **"engly on"**.
106
+ ```sh
107
+ $ /engly-off # Pause the coach for this session
108
+ $ /engly-on # Resume the coach
109
+ $ /engly-review # Deep review of a prompt: every mistake, why, and better alternatives
110
+ ```
110
111
 
111
112
  ## ⌨️ CLI commands
112
113
 
package/dist/cli.js CHANGED
@@ -29,7 +29,7 @@ import * as p from "@clack/prompts";
29
29
 
30
30
  // src/adapters/detect.ts
31
31
  import { access } from "fs/promises";
32
- import { join } from "path";
32
+ import { dirname, join } from "path";
33
33
  async function anyExists(cwd, paths) {
34
34
  for (const path of paths) {
35
35
  try {
@@ -40,6 +40,15 @@ async function anyExists(cwd, paths) {
40
40
  }
41
41
  return false;
42
42
  }
43
+ async function anyExistsUpward(cwd, paths) {
44
+ let dir = cwd;
45
+ while (true) {
46
+ if (await anyExists(dir, paths)) return true;
47
+ const parent = dirname(dir);
48
+ if (parent === dir) return false;
49
+ dir = parent;
50
+ }
51
+ }
43
52
 
44
53
  // src/adapters/claude.ts
45
54
  var claude = {
@@ -47,7 +56,12 @@ var claude = {
47
56
  name: "Claude Code",
48
57
  delivery: "import",
49
58
  detect: (cwd) => anyExists(cwd, ["CLAUDE.md", ".claude"]),
50
- instructionFile: () => "CLAUDE.md",
59
+ // Claude Code reads AGENTS.md only when there is no CLAUDE.md, .claude/CLAUDE.md or
60
+ // CLAUDE.local.md in the working directory or any directory above it.
61
+ instructionFile: async (cwd) => {
62
+ if (await anyExistsUpward(cwd, ["CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md"])) return "CLAUDE.md";
63
+ return await anyExists(cwd, ["AGENTS.md"]) ? "AGENTS.md" : "CLAUDE.md";
64
+ },
51
65
  skillDirs: [".claude/skills"],
52
66
  sizeLimit: {
53
67
  maxLines: 200,
@@ -198,16 +212,16 @@ function indexesOf(text, search) {
198
212
 
199
213
  // src/core/config.ts
200
214
  import { mkdir, readFile, writeFile } from "fs/promises";
201
- import { dirname as dirname2, join as join3 } from "path";
215
+ import { dirname as dirname3, join as join3 } from "path";
202
216
 
203
217
  // src/core/paths.ts
204
218
  import { existsSync, readFileSync } from "fs";
205
- import { dirname, join as join2 } from "path";
219
+ import { dirname as dirname2, join as join2 } from "path";
206
220
  import { fileURLToPath } from "url";
207
221
  var cachedRoot;
208
222
  function packageRoot() {
209
223
  if (cachedRoot) return cachedRoot;
210
- let dir = dirname(fileURLToPath(import.meta.url));
224
+ let dir = dirname2(fileURLToPath(import.meta.url));
211
225
  while (true) {
212
226
  const pkgPath = join2(dir, "package.json");
213
227
  if (existsSync(pkgPath)) {
@@ -217,7 +231,7 @@ function packageRoot() {
217
231
  return dir;
218
232
  }
219
233
  }
220
- const parent = dirname(dir);
234
+ const parent = dirname2(dir);
221
235
  if (parent === dir) throw new Error("Could not locate the engly package root.");
222
236
  dir = parent;
223
237
  }
@@ -277,7 +291,7 @@ async function readConfig(cwd, version) {
277
291
  }
278
292
  async function writeConfig(cwd, config) {
279
293
  const path = configPath(cwd);
280
- await mkdir(dirname2(path), { recursive: true });
294
+ await mkdir(dirname3(path), { recursive: true });
281
295
  await writeFile(path, `${JSON.stringify(validateConfig(config), null, 2)}
282
296
  `, "utf8");
283
297
  }
@@ -302,7 +316,7 @@ function validateConfig(config) {
302
316
 
303
317
  // src/core/fs.ts
304
318
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
305
- import { dirname as dirname3 } from "path";
319
+ import { dirname as dirname4 } from "path";
306
320
  async function readTextIfExists(path) {
307
321
  try {
308
322
  return await readFile2(path, "utf8");
@@ -312,7 +326,7 @@ async function readTextIfExists(path) {
312
326
  }
313
327
  }
314
328
  async function writeText(path, content) {
315
- await mkdir2(dirname3(path), { recursive: true });
329
+ await mkdir2(dirname4(path), { recursive: true });
316
330
  await writeFile2(path, content, "utf8");
317
331
  }
318
332
 
@@ -398,8 +412,11 @@ async function runInit(options) {
398
412
  }
399
413
  config.style = await chooseStyle(options, config.style);
400
414
  const adapter = adapters.claude;
401
- const agentFile = adapter.instructionFile();
415
+ const agentFile = await adapter.instructionFile(cwd);
402
416
  const agentPath = join5(cwd, agentFile);
417
+ if (agentFile === "AGENTS.md") {
418
+ p.log.info("No CLAUDE.md found. Using AGENTS.md, which Claude Code reads when there is no CLAUDE.md.");
419
+ }
403
420
  const current = await readTextIfExists(agentPath);
404
421
  const blockExists = current !== null && hasBlock(current);
405
422
  let apply = options.apply;
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/commands/not-implemented.ts","../src/commands/config.ts","../src/commands/disable.ts","../src/commands/enable.ts","../src/commands/init.ts","../src/adapters/detect.ts","../src/adapters/claude.ts","../src/adapters/codex.ts","../src/adapters/copilot.ts","../src/adapters/cursor.ts","../src/adapters/opencode.ts","../src/adapters/types.ts","../src/adapters/index.ts","../src/core/block.ts","../src/core/config.ts","../src/core/paths.ts","../src/core/fs.ts","../src/core/git.ts","../src/core/instructions.ts","../src/core/size.ts","../src/commands/remove.ts","../src/commands/status.ts","../src/commands/update.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { configCommand } from './commands/config.js';\nimport { disableCommand } from './commands/disable.js';\nimport { enableCommand } from './commands/enable.js';\nimport { initCommand } from './commands/init.js';\nimport { removeCommand } from './commands/remove.js';\nimport { statusCommand } from './commands/status.js';\nimport { updateCommand } from './commands/update.js';\nimport { packageVersion } from './core/paths.js';\n\nconst program = new Command();\n\nprogram\n .name('engly')\n .description('Your English coach for AI prompts. One command. Every agent. Always on. Zero cost.')\n .version(packageVersion());\n\nprogram\n .command('init')\n .description('Install Engly in the current project')\n .option('--tools <tools>', 'comma-separated agents: claude,codex,opencode,cursor,copilot')\n .option('--style <style>', 'feedback style: light or detailed')\n .option('-y, --yes', 'accept all defaults without asking')\n .option('--no-apply', \"only install the skills, don't touch the agent files\")\n .action(initCommand);\n\nprogram.command('update').description('Update the skills and regenerate the Engly block').action(updateCommand);\nprogram.command('enable').description('Turn the coach on permanently').action(enableCommand);\nprogram.command('disable').description('Turn the coach off permanently, without uninstalling').action(disableCommand);\nprogram\n .command('config')\n .description('Change an option, e.g. \"engly config level B2\"')\n .argument('<option>', 'language, level or style')\n .argument('<value>', 'new value')\n .action(configCommand);\nprogram.command('status').description('Show where Engly is installed and whether it is active').action(statusCommand);\nprogram.command('remove').description('Remove the Engly block, skills and config').action(removeCommand);\n\ntry {\n await program.parseAsync();\n} catch (error) {\n console.error(`engly: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = 1;\n}\n","export function notImplemented(command: string): void {\n console.log(`engly ${command}: not implemented yet`);\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function configCommand(_option: string, _value: string): Promise<void> {\n notImplemented('config');\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function disableCommand(): Promise<void> {\n notImplemented('disable');\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function enableCommand(): Promise<void> {\n notImplemented('enable');\n}\n","import { join } from 'node:path';\nimport * as p from '@clack/prompts';\nimport { adapters, detectTools, IMPLEMENTED_TOOLS, skillTargets, TOOL_IDS, type ToolId } from '../adapters/index.js';\nimport { type BlockPosition, hasBlock, type UpsertAction, upsertBlock } from '../core/block.js';\nimport { defaultConfig, readConfig, STYLES, type Style, writeConfig } from '../core/config.js';\nimport { readTextIfExists, writeText } from '../core/fs.js';\nimport { gitStatus } from '../core/git.js';\nimport { readSkill, renderInstructions, SKILL_NAMES } from '../core/instructions.js';\nimport { INSTRUCTIONS_FILE, packageVersion } from '../core/paths.js';\nimport { checkSize, type SizeReport } from '../core/size.js';\n\nexport interface InitOptions {\n tools?: string;\n style?: string;\n yes?: boolean;\n apply: boolean;\n}\n\nclass CancelledError extends Error {}\n\nfunction guard<T>(value: T): Exclude<T, symbol> {\n if (p.isCancel(value)) throw new CancelledError();\n return value as Exclude<T, symbol>;\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n try {\n await runInit(options);\n } catch (error) {\n if (error instanceof CancelledError) {\n p.cancel('Cancelled. Nothing was changed.');\n process.exitCode = 1;\n return;\n }\n throw error;\n }\n}\n\nasync function runInit(options: InitOptions): Promise<void> {\n const cwd = process.cwd();\n const version = packageVersion();\n const interactive = !options.yes;\n\n if (interactive && !process.stdin.isTTY) {\n throw new Error('No interactive terminal detected. Run \"engly init --yes\" to use the defaults.');\n }\n\n p.intro(`Engly v${version} – your English coach for AI prompts`);\n\n const config = (await readConfig(cwd, version)) ?? defaultConfig(version);\n config.version = version;\n\n const tools = await chooseTools(cwd, options);\n if (tools.length === 0) {\n throw new Error(`No supported tool selected. Supported today: ${IMPLEMENTED_TOOLS.join(', ')}.`);\n }\n\n config.style = await chooseStyle(options, config.style);\n\n const adapter = adapters.claude;\n const agentFile = adapter.instructionFile();\n const agentPath = join(cwd, agentFile);\n\n const current = await readTextIfExists(agentPath);\n // Checked before any prompt or write, so broken markers fail early.\n const blockExists = current !== null && hasBlock(current);\n\n let apply = options.apply;\n let position: BlockPosition = 'end';\n if (apply && current !== null && !blockExists) {\n const placement = await choosePlacement(agentFile, checkSize(current, adapter.sizeLimit), interactive);\n if (placement === 'skip') apply = false;\n else position = placement;\n }\n const planned = upsertBlock(current ?? '', `@${INSTRUCTIONS_FILE}`, { version, position });\n\n if (apply && interactive) {\n const choice = guard(\n await p.select({\n message: `Add Engly block to:\\n${agentFile} (${adapter.name}) β†’ ${describePlan(current, planned.action)}`,\n options: [\n { value: 'yes', label: 'Yes' },\n { value: 'skills', label: 'No, only install skills' },\n ],\n }),\n );\n apply = choice === 'yes';\n }\n\n if (apply) {\n await warnIfInGit(cwd, agentFile, interactive);\n }\n\n const done: string[] = [];\n\n await writeText(join(cwd, INSTRUCTIONS_FILE), await renderInstructions(config));\n\n if (apply) {\n await writeText(agentPath, planned.text);\n done.push(`βœ“ ${agentFile} ${current === null ? 'created' : 'updated'}`);\n }\n\n for (const dir of skillTargets(tools)) {\n for (const skill of SKILL_NAMES) {\n await writeText(join(cwd, dir, skill, 'SKILL.md'), await readSkill(skill));\n }\n }\n done.push(`βœ“ Skills installed: ${SKILL_NAMES.join(', ')}`);\n\n config.tools = tools;\n config.enabled = true;\n await writeConfig(cwd, config);\n\n p.log.success(done.join('\\n'));\n p.outro(\n apply\n ? 'Done! Your next prompt will be coached.'\n : `Skills installed. Run \"engly init\" again to add the Engly block to ${agentFile}.`,\n );\n}\n\nasync function chooseTools(cwd: string, options: InitOptions): Promise<ToolId[]> {\n if (options.tools) {\n const requested = options.tools\n .split(',')\n .map((tool) => tool.trim().toLowerCase())\n .filter(Boolean);\n const unknown = requested.filter((tool) => !(TOOL_IDS as readonly string[]).includes(tool));\n if (unknown.length > 0) {\n throw new Error(`Unknown tool(s): ${unknown.join(', ')}. Use: ${TOOL_IDS.join(', ')}.`);\n }\n return keepImplemented(requested as ToolId[]);\n }\n\n const detected = await detectTools(cwd);\n const initial = detected.filter((tool) => IMPLEMENTED_TOOLS.includes(tool));\n const defaults = initial.length > 0 ? initial : [...IMPLEMENTED_TOOLS];\n\n if (options.yes) return defaults;\n\n return guard(\n await p.multiselect<ToolId>({\n message: 'Which AI tools do you use?',\n options: TOOL_IDS.map((id) => ({\n value: id,\n label: adapters[id].name,\n hint: IMPLEMENTED_TOOLS.includes(id) ? undefined : 'coming soon',\n disabled: !IMPLEMENTED_TOOLS.includes(id),\n })),\n initialValues: defaults,\n required: true,\n }),\n );\n}\n\nfunction keepImplemented(tools: ToolId[]): ToolId[] {\n const skipped = tools.filter((tool) => !IMPLEMENTED_TOOLS.includes(tool));\n if (skipped.length > 0) {\n p.log.warn(`Not supported yet, skipped: ${skipped.map((tool) => adapters[tool].name).join(', ')}`);\n }\n return tools.filter((tool) => IMPLEMENTED_TOOLS.includes(tool));\n}\n\nasync function chooseStyle(options: InitOptions, current: Style): Promise<Style> {\n if (options.style) {\n if (!(STYLES as readonly string[]).includes(options.style)) {\n throw new Error(`Invalid style \"${options.style}\". Use: ${STYLES.join(', ')}.`);\n }\n return options.style as Style;\n }\n if (options.yes) return current;\n\n return guard(\n await p.select<Style>({\n message: 'How detailed should the feedback be?',\n initialValue: current,\n options: [\n { value: 'light', label: 'Light – max 3 corrections' },\n { value: 'detailed', label: 'Detailed – explain every mistake' },\n ],\n }),\n );\n}\n\nfunction describePlan(current: string | null, action: UpsertAction): string {\n if (current === null) return 'new file';\n if (action === 'updated') return 'exists, block updated';\n return action === 'prepended' ? 'exists, block added near the top' : 'exists, block appended';\n}\n\nasync function choosePlacement(\n file: string,\n size: SizeReport | null,\n interactive: boolean,\n): Promise<BlockPosition | 'skip'> {\n if (!size) return 'end';\n\n const message = `${file} is ${size.label}\\n⚠ ${size.warning}`;\n if (!interactive) {\n p.log.warn(`${message}\\nThe Engly block goes near the top.`);\n return 'top';\n }\n\n p.log.warn(message);\n return guard(\n await p.select<BlockPosition | 'skip'>({\n message: 'Where should the block go?',\n options: [\n { value: 'top', label: 'Near the top – recommended' },\n { value: 'end', label: 'At the end' },\n { value: 'skip', label: `Skip ${file}` },\n ],\n }),\n );\n}\n\nasync function warnIfInGit(cwd: string, file: string, interactive: boolean): Promise<void> {\n const status = gitStatus(cwd, file);\n if (status !== 'tracked' && status !== 'untracked') return;\n\n const title = status === 'tracked' ? `${file} is tracked by git.` : `${file} is not ignored by git.`;\n const warning = '⚠ Your teammates will also be coached if you commit it.';\n\n if (!interactive) {\n p.log.warn(`${title}\\n${warning}`);\n return;\n }\n\n const choice = guard(\n await p.select({\n message: `${title}\\n${warning}`,\n options: [\n { value: 'continue', label: 'Continue anyway' },\n { value: 'cancel', label: 'Cancel' },\n ],\n }),\n );\n if (choice === 'cancel') throw new CancelledError();\n}\n","import { access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport async function anyExists(cwd: string, paths: string[]): Promise<boolean> {\n for (const path of paths) {\n try {\n await access(join(cwd, path));\n return true;\n } catch {}\n }\n return false;\n}\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const claude: Adapter = {\n id: 'claude',\n name: 'Claude Code',\n delivery: 'import',\n detect: (cwd) => anyExists(cwd, ['CLAUDE.md', '.claude']),\n instructionFile: () => 'CLAUDE.md',\n skillDirs: ['.claude/skills'],\n sizeLimit: {\n maxLines: 200,\n warning: 'Claude Code follows long instruction files less closely.',\n },\n};\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const codex: Adapter = {\n id: 'codex',\n name: 'Codex',\n delivery: 'embedded',\n detect: (cwd) => anyExists(cwd, ['.codex', 'AGENTS.md']),\n instructionFile: () => 'AGENTS.md',\n skillDirs: ['.agents/skills'],\n sizeLimit: {\n // Real limit is 32 KiB for all AGENTS.md files combined (spec Β§9.6).\n maxBytes: 24 * 1024,\n warning: 'Codex may stop reading long instruction files.\\nThe Engly block could be cut off.',\n },\n};\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const copilot: Adapter = {\n id: 'copilot',\n name: 'GitHub Copilot',\n delivery: 'embedded',\n detect: (cwd) => anyExists(cwd, ['.github/copilot-instructions.md', '.github/instructions']),\n instructionFile: () => '.github/copilot-instructions.md',\n skillDirs: ['.agents/skills', '.claude/skills', '.github/skills'],\n};\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const CURSOR_RULE_FRONTMATTER = '---\\ndescription: Engly English coach\\nalwaysApply: true\\n---\\n';\n\n// TODO(spec Β§15): Cursor reads .claude/skills/ only as a \"legacy\" folder and\n// was not tested. If it stops doing so, Cursor + Claude Code would get no skills.\nexport const cursor: Adapter = {\n id: 'cursor',\n name: 'Cursor',\n delivery: 'own-file',\n detect: (cwd) => anyExists(cwd, ['.cursor', '.cursorrules']),\n instructionFile: () => '.cursor/rules/engly.mdc',\n skillDirs: ['.agents/skills', '.cursor/skills', '.claude/skills'],\n};\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const opencode: Adapter = {\n id: 'opencode',\n name: 'OpenCode',\n delivery: 'embedded',\n detect: (cwd) => anyExists(cwd, ['opencode.json', 'opencode.jsonc', '.opencode']),\n instructionFile: () => 'AGENTS.md',\n skillDirs: ['.agents/skills', '.claude/skills', '.opencode/skills'],\n};\n","export const TOOL_IDS = ['claude', 'codex', 'opencode', 'cursor', 'copilot'] as const;\nexport type ToolId = (typeof TOOL_IDS)[number];\n\nexport type Delivery = 'import' | 'embedded' | 'own-file';\n\nexport interface SizeLimit {\n maxBytes?: number;\n maxLines?: number;\n warning: string;\n}\n\nexport interface Adapter {\n id: ToolId;\n name: string;\n delivery: Delivery;\n detect(cwd: string): Promise<boolean>;\n instructionFile(): string;\n // Order matters: the first folder is the one Engly installs to (see skillTargets).\n skillDirs: readonly string[];\n sizeLimit?: SizeLimit;\n}\n","import { claude } from './claude.js';\nimport { codex } from './codex.js';\nimport { copilot } from './copilot.js';\nimport { cursor } from './cursor.js';\nimport { opencode } from './opencode.js';\nimport type { Adapter, ToolId } from './types.js';\n\nexport type { Adapter, Delivery, ToolId } from './types.js';\nexport { TOOL_IDS } from './types.js';\n\n// Adding a new agent = adding its adapter here (spec Β§5).\nexport const adapters: Record<ToolId, Adapter> = { claude, codex, opencode, cursor, copilot };\n\nexport const IMPLEMENTED_TOOLS: readonly ToolId[] = ['claude'];\n\nexport function skillTargets(tools: readonly ToolId[]): string[] {\n const chosen: string[] = [];\n const byChoices = [...tools].sort((a, b) => adapters[a].skillDirs.length - adapters[b].skillDirs.length);\n for (const tool of byChoices) {\n const dirs = adapters[tool].skillDirs;\n const [preferred] = dirs;\n if (preferred && !dirs.some((dir) => chosen.includes(dir))) chosen.push(preferred);\n }\n return chosen;\n}\n\nexport async function detectTools(cwd: string): Promise<ToolId[]> {\n const found: ToolId[] = [];\n for (const adapter of Object.values(adapters)) {\n if (await adapter.detect(cwd)) found.push(adapter.id);\n }\n return found;\n}\n","export const START_MARKER_PREFIX = '<!-- engly:start';\nexport const END_MARKER = '<!-- engly:end -->';\n\nconst START_MARKER_RE = /<!-- engly:start(?: v?([^\\s>]+))? -->/g;\n\nexport type Eol = '\\n' | '\\r\\n';\nexport type BlockPosition = 'top' | 'end';\nexport type UpsertAction = 'created' | 'appended' | 'prepended' | 'updated';\n\nexport class BlockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BlockError';\n }\n}\n\nexport interface BlockRange {\n start: number;\n end: number;\n version: string | null;\n}\n\nexport function detectEol(text: string): Eol {\n const index = text.indexOf('\\n');\n if (index > 0 && text[index - 1] === '\\r') return '\\r\\n';\n return '\\n';\n}\n\nexport function startMarker(version: string): string {\n return `${START_MARKER_PREFIX} v${version.replace(/^v/, '')} -->`;\n}\n\n// Broken markers throw instead of being repaired: editing around them could destroy user content.\nexport function findBlock(text: string): BlockRange | null {\n const starts = [...text.matchAll(START_MARKER_RE)];\n const ends = indexesOf(text, END_MARKER);\n\n if (starts.length === 0 && ends.length === 0) return null;\n\n const [startMatch] = starts;\n const [endIndex] = ends;\n if (!startMatch || endIndex === undefined || starts.length > 1 || ends.length > 1) {\n throw new BlockError(\n `Expected exactly one Engly block, found ${starts.length} start and ${ends.length} end marker(s). ` +\n 'Fix the markers by hand before running Engly again.',\n );\n }\n\n const start = startMatch.index;\n if (endIndex < start) {\n throw new BlockError('The Engly end marker appears before the start marker.');\n }\n\n return { start, end: endIndex + END_MARKER.length, version: startMatch[1] ?? null };\n}\n\nexport function hasBlock(text: string): boolean {\n return findBlock(text) !== null;\n}\n\nexport function readBlock(text: string): string | null {\n const range = findBlock(text);\n if (!range) return null;\n const inner = text.slice(range.start, range.end);\n const afterStart = inner.indexOf('-->') + 3;\n return stripOneEol(inner.slice(afterStart, inner.length - END_MARKER.length), 'both');\n}\n\nexport function buildBlock(content: string, version: string, eol: Eol = '\\n'): string {\n const body = content.replace(/\\r\\n/g, '\\n').replace(/\\n+$/, '');\n const lines = [startMarker(version), ...(body === '' ? [] : body.split('\\n')), END_MARKER];\n return lines.join(eol);\n}\n\nexport interface UpsertOptions {\n version: string;\n position?: BlockPosition;\n}\n\nexport interface UpsertResult {\n text: string;\n action: UpsertAction;\n}\n\nexport function upsertBlock(text: string, content: string, options: UpsertOptions): UpsertResult {\n const eol = detectEol(text);\n const block = buildBlock(content, options.version, eol);\n const range = findBlock(text);\n\n if (range) {\n return { text: text.slice(0, range.start) + block + text.slice(range.end), action: 'updated' };\n }\n\n if (text === '') {\n return { text: block + eol, action: 'created' };\n }\n\n if (options.position === 'top') {\n return { text: block + eol + eol + text, action: 'prepended' };\n }\n\n const separator = endsWithEol(text) ? eol : eol + eol;\n return { text: text + separator + block + eol, action: 'appended' };\n}\n\nexport interface RemoveResult {\n text: string;\n removed: boolean;\n}\n\nexport function removeBlock(text: string): RemoveResult {\n const range = findBlock(text);\n if (!range) return { text, removed: false };\n\n let before = text.slice(0, range.start);\n let after = stripOneEol(text.slice(range.end), 'start');\n\n if (before === '') {\n after = stripOneEol(after, 'start');\n } else if (/(\\r?\\n)\\r?\\n$/.test(before)) {\n before = stripOneEol(before, 'end');\n }\n\n return { text: before + after, removed: true };\n}\n\nfunction endsWithEol(text: string): boolean {\n return text.endsWith('\\n');\n}\n\nfunction stripOneEol(text: string, side: 'start' | 'end' | 'both'): string {\n let result = text;\n if (side === 'start' || side === 'both') {\n if (result.startsWith('\\r\\n')) result = result.slice(2);\n else if (result.startsWith('\\n')) result = result.slice(1);\n }\n if (side === 'end' || side === 'both') {\n if (result.endsWith('\\r\\n')) result = result.slice(0, -2);\n else if (result.endsWith('\\n')) result = result.slice(0, -1);\n }\n return result;\n}\n\nfunction indexesOf(text: string, search: string): number[] {\n const result: number[] = [];\n let index = text.indexOf(search);\n while (index !== -1) {\n result.push(index);\n index = text.indexOf(search, index + search.length);\n }\n return result;\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport type { ToolId } from '../adapters/types.js';\nimport { CONFIG_FILE } from './paths.js';\n\nexport const LEVELS = ['auto', 'A2', 'B1', 'B2', 'C1'] as const;\nexport const STYLES = ['light', 'detailed'] as const;\n\nexport type Level = (typeof LEVELS)[number];\nexport type Style = (typeof STYLES)[number];\n\nexport interface EnglyConfig {\n version: string;\n language: string;\n level: Level;\n style: Style;\n reviewEnglish: boolean;\n enabled: boolean;\n tools: ToolId[];\n}\n\nexport function defaultConfig(version: string): EnglyConfig {\n return {\n version,\n language: 'auto',\n level: 'auto',\n style: 'light',\n reviewEnglish: true,\n enabled: true,\n tools: [],\n };\n}\n\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nexport function configPath(cwd: string): string {\n return join(cwd, CONFIG_FILE);\n}\n\nexport async function readConfig(cwd: string, version: string): Promise<EnglyConfig | null> {\n let raw: string;\n try {\n raw = await readFile(configPath(cwd), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw error;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new ConfigError(`${CONFIG_FILE} is not valid JSON.`);\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new ConfigError(`${CONFIG_FILE} must contain a JSON object.`);\n }\n\n return validateConfig({ ...defaultConfig(version), ...(parsed as Partial<EnglyConfig>) });\n}\n\nexport async function writeConfig(cwd: string, config: EnglyConfig): Promise<void> {\n const path = configPath(cwd);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(validateConfig(config), null, 2)}\\n`, 'utf8');\n}\n\nexport function validateConfig(config: EnglyConfig): EnglyConfig {\n if (!(LEVELS as readonly string[]).includes(config.level)) {\n throw new ConfigError(`Invalid level \"${config.level}\". Use one of: ${LEVELS.join(', ')}.`);\n }\n if (!(STYLES as readonly string[]).includes(config.style)) {\n throw new ConfigError(`Invalid style \"${config.style}\". Use one of: ${STYLES.join(', ')}.`);\n }\n if (config.language !== 'auto' && !/^[a-z]{2,3}$/.test(config.language)) {\n throw new ConfigError(`Invalid language \"${config.language}\". Use \"auto\" or a language code like \"es\".`);\n }\n if (typeof config.enabled !== 'boolean' || typeof config.reviewEnglish !== 'boolean') {\n throw new ConfigError('\"enabled\" and \"reviewEnglish\" must be true or false.');\n }\n if (!Array.isArray(config.tools)) {\n throw new ConfigError('\"tools\" must be a list.');\n }\n return config;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nlet cachedRoot: string | undefined;\n\nexport function packageRoot(): string {\n if (cachedRoot) return cachedRoot;\n let dir = dirname(fileURLToPath(import.meta.url));\n while (true) {\n const pkgPath = join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string };\n if (pkg.name === 'engly') {\n cachedRoot = dir;\n return dir;\n }\n }\n const parent = dirname(dir);\n if (parent === dir) throw new Error('Could not locate the engly package root.');\n dir = parent;\n }\n}\n\nexport function templatesDir(): string {\n return join(packageRoot(), 'templates');\n}\n\nexport function packageVersion(): string {\n const pkg = JSON.parse(readFileSync(join(packageRoot(), 'package.json'), 'utf8')) as { version: string };\n return pkg.version;\n}\n\nexport const ENGLY_DIR = '.engly';\nexport const CONFIG_FILE = `${ENGLY_DIR}/config.json`;\nexport const INSTRUCTIONS_FILE = `${ENGLY_DIR}/engly.md`;\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nexport async function readTextIfExists(path: string): Promise<string | null> {\n try {\n return await readFile(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw error;\n }\n}\n\nexport async function writeText(path: string, content: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, 'utf8');\n}\n","import { spawnSync } from 'node:child_process';\n\nexport type GitStatus = 'tracked' | 'untracked' | 'ignored' | 'no-git';\n\nfunction git(cwd: string, args: string[]): number | null {\n const result = spawnSync('git', args, { cwd, stdio: 'ignore' });\n if (result.error) return null;\n return result.status;\n}\n\nexport function gitStatus(cwd: string, relativePath: string): GitStatus {\n if (git(cwd, ['rev-parse', '--is-inside-work-tree']) !== 0) return 'no-git';\n if (git(cwd, ['ls-files', '--error-unmatch', '--', relativePath]) === 0) return 'tracked';\n if (git(cwd, ['check-ignore', '-q', '--', relativePath]) === 0) return 'ignored';\n return 'untracked';\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EnglyConfig } from './config.js';\nimport { templatesDir } from './paths.js';\n\nexport const SKILL_NAMES = ['engly-on', 'engly-off', 'engly-review'] as const;\nexport type SkillName = (typeof SKILL_NAMES)[number];\n\nexport async function renderInstructions(config: EnglyConfig): Promise<string> {\n const template = await readFile(join(templatesDir(), 'engly.md'), 'utf8');\n\n const values: Record<string, string> = {\n language: config.language === 'auto' ? 'auto' : config.language,\n level: config.level,\n styleRule:\n config.style === 'light'\n ? 'show up to 3 corrections (\"wrong\" β†’ \"right\" with a short reason) and the improved prompt.'\n : 'explain every mistake (\"wrong\" β†’ \"right\" with a short reason) and show the improved prompt.',\n englishRule: config.reviewEnglish\n ? 'Prompt in correct English: show nothing.'\n : 'Prompt in English: show nothing, even if it has mistakes. Only coach prompts in other languages.',\n lengthRule: config.style === 'light' ? 'Keep the note to 6 lines at most.' : 'Keep the note as short as possible.',\n };\n\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key: string) => values[key] ?? match);\n}\n\nexport async function readSkill(name: SkillName): Promise<string> {\n return readFile(join(templatesDir(), 'skills', name, 'SKILL.md'), 'utf8');\n}\n","import type { SizeLimit } from '../adapters/types.js';\n\nexport interface SizeReport {\n label: string;\n warning: string;\n}\n\nexport function checkSize(text: string, limit: SizeLimit | undefined): SizeReport | null {\n if (!limit) return null;\n\n const bytes = Buffer.byteLength(text, 'utf8');\n if (limit.maxBytes !== undefined && bytes > limit.maxBytes) {\n return { label: `${(bytes / 1024).toFixed(1)} KB`, warning: limit.warning };\n }\n\n const lines = text === '' ? 0 : text.split('\\n').length - (text.endsWith('\\n') ? 1 : 0);\n if (limit.maxLines !== undefined && lines > limit.maxLines) {\n return { label: `${lines} lines`, warning: limit.warning };\n }\n\n return null;\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function removeCommand(): Promise<void> {\n notImplemented('remove');\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function statusCommand(): Promise<void> {\n notImplemented('status');\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function updateCommand(): Promise<void> {\n notImplemented('update');\n}\n"],"mappings":"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","names":["join","dirname","join","join","join","dirname","mkdir","readFile","writeFile","dirname","readFile","join","readFile","join","join"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/not-implemented.ts","../src/commands/config.ts","../src/commands/disable.ts","../src/commands/enable.ts","../src/commands/init.ts","../src/adapters/detect.ts","../src/adapters/claude.ts","../src/adapters/codex.ts","../src/adapters/copilot.ts","../src/adapters/cursor.ts","../src/adapters/opencode.ts","../src/adapters/types.ts","../src/adapters/index.ts","../src/core/block.ts","../src/core/config.ts","../src/core/paths.ts","../src/core/fs.ts","../src/core/git.ts","../src/core/instructions.ts","../src/core/size.ts","../src/commands/remove.ts","../src/commands/status.ts","../src/commands/update.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { configCommand } from './commands/config.js';\nimport { disableCommand } from './commands/disable.js';\nimport { enableCommand } from './commands/enable.js';\nimport { initCommand } from './commands/init.js';\nimport { removeCommand } from './commands/remove.js';\nimport { statusCommand } from './commands/status.js';\nimport { updateCommand } from './commands/update.js';\nimport { packageVersion } from './core/paths.js';\n\nconst program = new Command();\n\nprogram\n .name('engly')\n .description('Your English coach for AI prompts. One command. Every agent. Always on. Zero cost.')\n .version(packageVersion());\n\nprogram\n .command('init')\n .description('Install Engly in the current project')\n .option('--tools <tools>', 'comma-separated agents: claude,codex,opencode,cursor,copilot')\n .option('--style <style>', 'feedback style: light or detailed')\n .option('-y, --yes', 'accept all defaults without asking')\n .option('--no-apply', \"only install the skills, don't touch the agent files\")\n .action(initCommand);\n\nprogram.command('update').description('Update the skills and regenerate the Engly block').action(updateCommand);\nprogram.command('enable').description('Turn the coach on permanently').action(enableCommand);\nprogram.command('disable').description('Turn the coach off permanently, without uninstalling').action(disableCommand);\nprogram\n .command('config')\n .description('Change an option, e.g. \"engly config level B2\"')\n .argument('<option>', 'language, level or style')\n .argument('<value>', 'new value')\n .action(configCommand);\nprogram.command('status').description('Show where Engly is installed and whether it is active').action(statusCommand);\nprogram.command('remove').description('Remove the Engly block, skills and config').action(removeCommand);\n\ntry {\n await program.parseAsync();\n} catch (error) {\n console.error(`engly: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = 1;\n}\n","export function notImplemented(command: string): void {\n console.log(`engly ${command}: not implemented yet`);\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function configCommand(_option: string, _value: string): Promise<void> {\n notImplemented('config');\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function disableCommand(): Promise<void> {\n notImplemented('disable');\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function enableCommand(): Promise<void> {\n notImplemented('enable');\n}\n","import { join } from 'node:path';\nimport * as p from '@clack/prompts';\nimport { adapters, detectTools, IMPLEMENTED_TOOLS, skillTargets, TOOL_IDS, type ToolId } from '../adapters/index.js';\nimport { type BlockPosition, hasBlock, type UpsertAction, upsertBlock } from '../core/block.js';\nimport { defaultConfig, readConfig, STYLES, type Style, writeConfig } from '../core/config.js';\nimport { readTextIfExists, writeText } from '../core/fs.js';\nimport { gitStatus } from '../core/git.js';\nimport { readSkill, renderInstructions, SKILL_NAMES } from '../core/instructions.js';\nimport { INSTRUCTIONS_FILE, packageVersion } from '../core/paths.js';\nimport { checkSize, type SizeReport } from '../core/size.js';\n\nexport interface InitOptions {\n tools?: string;\n style?: string;\n yes?: boolean;\n apply: boolean;\n}\n\nclass CancelledError extends Error {}\n\nfunction guard<T>(value: T): Exclude<T, symbol> {\n if (p.isCancel(value)) throw new CancelledError();\n return value as Exclude<T, symbol>;\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n try {\n await runInit(options);\n } catch (error) {\n if (error instanceof CancelledError) {\n p.cancel('Cancelled. Nothing was changed.');\n process.exitCode = 1;\n return;\n }\n throw error;\n }\n}\n\nasync function runInit(options: InitOptions): Promise<void> {\n const cwd = process.cwd();\n const version = packageVersion();\n const interactive = !options.yes;\n\n if (interactive && !process.stdin.isTTY) {\n throw new Error('No interactive terminal detected. Run \"engly init --yes\" to use the defaults.');\n }\n\n p.intro(`Engly v${version} – your English coach for AI prompts`);\n\n const config = (await readConfig(cwd, version)) ?? defaultConfig(version);\n config.version = version;\n\n const tools = await chooseTools(cwd, options);\n if (tools.length === 0) {\n throw new Error(`No supported tool selected. Supported today: ${IMPLEMENTED_TOOLS.join(', ')}.`);\n }\n\n config.style = await chooseStyle(options, config.style);\n\n const adapter = adapters.claude;\n const agentFile = await adapter.instructionFile(cwd);\n const agentPath = join(cwd, agentFile);\n if (agentFile === 'AGENTS.md') {\n p.log.info('No CLAUDE.md found. Using AGENTS.md, which Claude Code reads when there is no CLAUDE.md.');\n }\n\n const current = await readTextIfExists(agentPath);\n // Checked before any prompt or write, so broken markers fail early.\n const blockExists = current !== null && hasBlock(current);\n\n let apply = options.apply;\n let position: BlockPosition = 'end';\n if (apply && current !== null && !blockExists) {\n const placement = await choosePlacement(agentFile, checkSize(current, adapter.sizeLimit), interactive);\n if (placement === 'skip') apply = false;\n else position = placement;\n }\n const planned = upsertBlock(current ?? '', `@${INSTRUCTIONS_FILE}`, { version, position });\n\n if (apply && interactive) {\n const choice = guard(\n await p.select({\n message: `Add Engly block to:\\n${agentFile} (${adapter.name}) β†’ ${describePlan(current, planned.action)}`,\n options: [\n { value: 'yes', label: 'Yes' },\n { value: 'skills', label: 'No, only install skills' },\n ],\n }),\n );\n apply = choice === 'yes';\n }\n\n if (apply) {\n await warnIfInGit(cwd, agentFile, interactive);\n }\n\n const done: string[] = [];\n\n await writeText(join(cwd, INSTRUCTIONS_FILE), await renderInstructions(config));\n\n if (apply) {\n await writeText(agentPath, planned.text);\n done.push(`βœ“ ${agentFile} ${current === null ? 'created' : 'updated'}`);\n }\n\n for (const dir of skillTargets(tools)) {\n for (const skill of SKILL_NAMES) {\n await writeText(join(cwd, dir, skill, 'SKILL.md'), await readSkill(skill));\n }\n }\n done.push(`βœ“ Skills installed: ${SKILL_NAMES.join(', ')}`);\n\n config.tools = tools;\n config.enabled = true;\n await writeConfig(cwd, config);\n\n p.log.success(done.join('\\n'));\n p.outro(\n apply\n ? 'Done! Your next prompt will be coached.'\n : `Skills installed. Run \"engly init\" again to add the Engly block to ${agentFile}.`,\n );\n}\n\nasync function chooseTools(cwd: string, options: InitOptions): Promise<ToolId[]> {\n if (options.tools) {\n const requested = options.tools\n .split(',')\n .map((tool) => tool.trim().toLowerCase())\n .filter(Boolean);\n const unknown = requested.filter((tool) => !(TOOL_IDS as readonly string[]).includes(tool));\n if (unknown.length > 0) {\n throw new Error(`Unknown tool(s): ${unknown.join(', ')}. Use: ${TOOL_IDS.join(', ')}.`);\n }\n return keepImplemented(requested as ToolId[]);\n }\n\n const detected = await detectTools(cwd);\n const initial = detected.filter((tool) => IMPLEMENTED_TOOLS.includes(tool));\n const defaults = initial.length > 0 ? initial : [...IMPLEMENTED_TOOLS];\n\n if (options.yes) return defaults;\n\n return guard(\n await p.multiselect<ToolId>({\n message: 'Which AI tools do you use?',\n options: TOOL_IDS.map((id) => ({\n value: id,\n label: adapters[id].name,\n hint: IMPLEMENTED_TOOLS.includes(id) ? undefined : 'coming soon',\n disabled: !IMPLEMENTED_TOOLS.includes(id),\n })),\n initialValues: defaults,\n required: true,\n }),\n );\n}\n\nfunction keepImplemented(tools: ToolId[]): ToolId[] {\n const skipped = tools.filter((tool) => !IMPLEMENTED_TOOLS.includes(tool));\n if (skipped.length > 0) {\n p.log.warn(`Not supported yet, skipped: ${skipped.map((tool) => adapters[tool].name).join(', ')}`);\n }\n return tools.filter((tool) => IMPLEMENTED_TOOLS.includes(tool));\n}\n\nasync function chooseStyle(options: InitOptions, current: Style): Promise<Style> {\n if (options.style) {\n if (!(STYLES as readonly string[]).includes(options.style)) {\n throw new Error(`Invalid style \"${options.style}\". Use: ${STYLES.join(', ')}.`);\n }\n return options.style as Style;\n }\n if (options.yes) return current;\n\n return guard(\n await p.select<Style>({\n message: 'How detailed should the feedback be?',\n initialValue: current,\n options: [\n { value: 'light', label: 'Light – max 3 corrections' },\n { value: 'detailed', label: 'Detailed – explain every mistake' },\n ],\n }),\n );\n}\n\nfunction describePlan(current: string | null, action: UpsertAction): string {\n if (current === null) return 'new file';\n if (action === 'updated') return 'exists, block updated';\n return action === 'prepended' ? 'exists, block added near the top' : 'exists, block appended';\n}\n\nasync function choosePlacement(\n file: string,\n size: SizeReport | null,\n interactive: boolean,\n): Promise<BlockPosition | 'skip'> {\n if (!size) return 'end';\n\n const message = `${file} is ${size.label}\\n⚠ ${size.warning}`;\n if (!interactive) {\n p.log.warn(`${message}\\nThe Engly block goes near the top.`);\n return 'top';\n }\n\n p.log.warn(message);\n return guard(\n await p.select<BlockPosition | 'skip'>({\n message: 'Where should the block go?',\n options: [\n { value: 'top', label: 'Near the top – recommended' },\n { value: 'end', label: 'At the end' },\n { value: 'skip', label: `Skip ${file}` },\n ],\n }),\n );\n}\n\nasync function warnIfInGit(cwd: string, file: string, interactive: boolean): Promise<void> {\n const status = gitStatus(cwd, file);\n if (status !== 'tracked' && status !== 'untracked') return;\n\n const title = status === 'tracked' ? `${file} is tracked by git.` : `${file} is not ignored by git.`;\n const warning = '⚠ Your teammates will also be coached if you commit it.';\n\n if (!interactive) {\n p.log.warn(`${title}\\n${warning}`);\n return;\n }\n\n const choice = guard(\n await p.select({\n message: `${title}\\n${warning}`,\n options: [\n { value: 'continue', label: 'Continue anyway' },\n { value: 'cancel', label: 'Cancel' },\n ],\n }),\n );\n if (choice === 'cancel') throw new CancelledError();\n}\n","import { access } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\nexport async function anyExists(cwd: string, paths: string[]): Promise<boolean> {\n for (const path of paths) {\n try {\n await access(join(cwd, path));\n return true;\n } catch {}\n }\n return false;\n}\n\n// Same as anyExists, but also looks in every directory above `cwd`.\nexport async function anyExistsUpward(cwd: string, paths: string[]): Promise<boolean> {\n let dir = cwd;\n while (true) {\n if (await anyExists(dir, paths)) return true;\n const parent = dirname(dir);\n if (parent === dir) return false;\n dir = parent;\n }\n}\n","import { anyExists, anyExistsUpward } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const claude: Adapter = {\n id: 'claude',\n name: 'Claude Code',\n delivery: 'import',\n detect: (cwd) => anyExists(cwd, ['CLAUDE.md', '.claude']),\n // Claude Code reads AGENTS.md only when there is no CLAUDE.md, .claude/CLAUDE.md or\n // CLAUDE.local.md in the working directory or any directory above it.\n instructionFile: async (cwd) => {\n if (await anyExistsUpward(cwd, ['CLAUDE.md', '.claude/CLAUDE.md', 'CLAUDE.local.md'])) return 'CLAUDE.md';\n return (await anyExists(cwd, ['AGENTS.md'])) ? 'AGENTS.md' : 'CLAUDE.md';\n },\n skillDirs: ['.claude/skills'],\n sizeLimit: {\n maxLines: 200,\n warning: 'Claude Code follows long instruction files less closely.',\n },\n};\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const codex: Adapter = {\n id: 'codex',\n name: 'Codex',\n delivery: 'embedded',\n detect: (cwd) => anyExists(cwd, ['.codex', 'AGENTS.md']),\n instructionFile: () => 'AGENTS.md',\n skillDirs: ['.agents/skills'],\n sizeLimit: {\n // Real limit is 32 KiB for all AGENTS.md files combined (spec Β§9.6).\n maxBytes: 24 * 1024,\n warning: 'Codex may stop reading long instruction files.\\nThe Engly block could be cut off.',\n },\n};\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const copilot: Adapter = {\n id: 'copilot',\n name: 'GitHub Copilot',\n delivery: 'embedded',\n detect: (cwd) => anyExists(cwd, ['.github/copilot-instructions.md', '.github/instructions']),\n instructionFile: () => '.github/copilot-instructions.md',\n skillDirs: ['.agents/skills', '.claude/skills', '.github/skills'],\n};\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const CURSOR_RULE_FRONTMATTER = '---\\ndescription: Engly English coach\\nalwaysApply: true\\n---\\n';\n\n// TODO(spec Β§15): Cursor reads .claude/skills/ only as a \"legacy\" folder and\n// was not tested. If it stops doing so, Cursor + Claude Code would get no skills.\nexport const cursor: Adapter = {\n id: 'cursor',\n name: 'Cursor',\n delivery: 'own-file',\n detect: (cwd) => anyExists(cwd, ['.cursor', '.cursorrules']),\n instructionFile: () => '.cursor/rules/engly.mdc',\n skillDirs: ['.agents/skills', '.cursor/skills', '.claude/skills'],\n};\n","import { anyExists } from './detect.js';\nimport type { Adapter } from './types.js';\n\nexport const opencode: Adapter = {\n id: 'opencode',\n name: 'OpenCode',\n delivery: 'embedded',\n detect: (cwd) => anyExists(cwd, ['opencode.json', 'opencode.jsonc', '.opencode']),\n instructionFile: () => 'AGENTS.md',\n skillDirs: ['.agents/skills', '.claude/skills', '.opencode/skills'],\n};\n","export const TOOL_IDS = ['claude', 'codex', 'opencode', 'cursor', 'copilot'] as const;\nexport type ToolId = (typeof TOOL_IDS)[number];\n\nexport type Delivery = 'import' | 'embedded' | 'own-file';\n\nexport interface SizeLimit {\n maxBytes?: number;\n maxLines?: number;\n warning: string;\n}\n\nexport interface Adapter {\n id: ToolId;\n name: string;\n delivery: Delivery;\n detect(cwd: string): Promise<boolean>;\n instructionFile(cwd: string): string | Promise<string>;\n // Order matters: the first folder is the one Engly installs to (see skillTargets).\n skillDirs: readonly string[];\n sizeLimit?: SizeLimit;\n}\n","import { claude } from './claude.js';\nimport { codex } from './codex.js';\nimport { copilot } from './copilot.js';\nimport { cursor } from './cursor.js';\nimport { opencode } from './opencode.js';\nimport type { Adapter, ToolId } from './types.js';\n\nexport type { Adapter, Delivery, ToolId } from './types.js';\nexport { TOOL_IDS } from './types.js';\n\n// Adding a new agent = adding its adapter here (spec Β§5).\nexport const adapters: Record<ToolId, Adapter> = { claude, codex, opencode, cursor, copilot };\n\nexport const IMPLEMENTED_TOOLS: readonly ToolId[] = ['claude'];\n\nexport function skillTargets(tools: readonly ToolId[]): string[] {\n const chosen: string[] = [];\n const byChoices = [...tools].sort((a, b) => adapters[a].skillDirs.length - adapters[b].skillDirs.length);\n for (const tool of byChoices) {\n const dirs = adapters[tool].skillDirs;\n const [preferred] = dirs;\n if (preferred && !dirs.some((dir) => chosen.includes(dir))) chosen.push(preferred);\n }\n return chosen;\n}\n\nexport async function detectTools(cwd: string): Promise<ToolId[]> {\n const found: ToolId[] = [];\n for (const adapter of Object.values(adapters)) {\n if (await adapter.detect(cwd)) found.push(adapter.id);\n }\n return found;\n}\n","export const START_MARKER_PREFIX = '<!-- engly:start';\nexport const END_MARKER = '<!-- engly:end -->';\n\nconst START_MARKER_RE = /<!-- engly:start(?: v?([^\\s>]+))? -->/g;\n\nexport type Eol = '\\n' | '\\r\\n';\nexport type BlockPosition = 'top' | 'end';\nexport type UpsertAction = 'created' | 'appended' | 'prepended' | 'updated';\n\nexport class BlockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BlockError';\n }\n}\n\nexport interface BlockRange {\n start: number;\n end: number;\n version: string | null;\n}\n\nexport function detectEol(text: string): Eol {\n const index = text.indexOf('\\n');\n if (index > 0 && text[index - 1] === '\\r') return '\\r\\n';\n return '\\n';\n}\n\nexport function startMarker(version: string): string {\n return `${START_MARKER_PREFIX} v${version.replace(/^v/, '')} -->`;\n}\n\n// Broken markers throw instead of being repaired: editing around them could destroy user content.\nexport function findBlock(text: string): BlockRange | null {\n const starts = [...text.matchAll(START_MARKER_RE)];\n const ends = indexesOf(text, END_MARKER);\n\n if (starts.length === 0 && ends.length === 0) return null;\n\n const [startMatch] = starts;\n const [endIndex] = ends;\n if (!startMatch || endIndex === undefined || starts.length > 1 || ends.length > 1) {\n throw new BlockError(\n `Expected exactly one Engly block, found ${starts.length} start and ${ends.length} end marker(s). ` +\n 'Fix the markers by hand before running Engly again.',\n );\n }\n\n const start = startMatch.index;\n if (endIndex < start) {\n throw new BlockError('The Engly end marker appears before the start marker.');\n }\n\n return { start, end: endIndex + END_MARKER.length, version: startMatch[1] ?? null };\n}\n\nexport function hasBlock(text: string): boolean {\n return findBlock(text) !== null;\n}\n\nexport function readBlock(text: string): string | null {\n const range = findBlock(text);\n if (!range) return null;\n const inner = text.slice(range.start, range.end);\n const afterStart = inner.indexOf('-->') + 3;\n return stripOneEol(inner.slice(afterStart, inner.length - END_MARKER.length), 'both');\n}\n\nexport function buildBlock(content: string, version: string, eol: Eol = '\\n'): string {\n const body = content.replace(/\\r\\n/g, '\\n').replace(/\\n+$/, '');\n const lines = [startMarker(version), ...(body === '' ? [] : body.split('\\n')), END_MARKER];\n return lines.join(eol);\n}\n\nexport interface UpsertOptions {\n version: string;\n position?: BlockPosition;\n}\n\nexport interface UpsertResult {\n text: string;\n action: UpsertAction;\n}\n\nexport function upsertBlock(text: string, content: string, options: UpsertOptions): UpsertResult {\n const eol = detectEol(text);\n const block = buildBlock(content, options.version, eol);\n const range = findBlock(text);\n\n if (range) {\n return { text: text.slice(0, range.start) + block + text.slice(range.end), action: 'updated' };\n }\n\n if (text === '') {\n return { text: block + eol, action: 'created' };\n }\n\n if (options.position === 'top') {\n return { text: block + eol + eol + text, action: 'prepended' };\n }\n\n const separator = endsWithEol(text) ? eol : eol + eol;\n return { text: text + separator + block + eol, action: 'appended' };\n}\n\nexport interface RemoveResult {\n text: string;\n removed: boolean;\n}\n\nexport function removeBlock(text: string): RemoveResult {\n const range = findBlock(text);\n if (!range) return { text, removed: false };\n\n let before = text.slice(0, range.start);\n let after = stripOneEol(text.slice(range.end), 'start');\n\n if (before === '') {\n after = stripOneEol(after, 'start');\n } else if (/(\\r?\\n)\\r?\\n$/.test(before)) {\n before = stripOneEol(before, 'end');\n }\n\n return { text: before + after, removed: true };\n}\n\nfunction endsWithEol(text: string): boolean {\n return text.endsWith('\\n');\n}\n\nfunction stripOneEol(text: string, side: 'start' | 'end' | 'both'): string {\n let result = text;\n if (side === 'start' || side === 'both') {\n if (result.startsWith('\\r\\n')) result = result.slice(2);\n else if (result.startsWith('\\n')) result = result.slice(1);\n }\n if (side === 'end' || side === 'both') {\n if (result.endsWith('\\r\\n')) result = result.slice(0, -2);\n else if (result.endsWith('\\n')) result = result.slice(0, -1);\n }\n return result;\n}\n\nfunction indexesOf(text: string, search: string): number[] {\n const result: number[] = [];\n let index = text.indexOf(search);\n while (index !== -1) {\n result.push(index);\n index = text.indexOf(search, index + search.length);\n }\n return result;\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport type { ToolId } from '../adapters/types.js';\nimport { CONFIG_FILE } from './paths.js';\n\nexport const LEVELS = ['auto', 'A2', 'B1', 'B2', 'C1'] as const;\nexport const STYLES = ['light', 'detailed'] as const;\n\nexport type Level = (typeof LEVELS)[number];\nexport type Style = (typeof STYLES)[number];\n\nexport interface EnglyConfig {\n version: string;\n language: string;\n level: Level;\n style: Style;\n reviewEnglish: boolean;\n enabled: boolean;\n tools: ToolId[];\n}\n\nexport function defaultConfig(version: string): EnglyConfig {\n return {\n version,\n language: 'auto',\n level: 'auto',\n style: 'light',\n reviewEnglish: true,\n enabled: true,\n tools: [],\n };\n}\n\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nexport function configPath(cwd: string): string {\n return join(cwd, CONFIG_FILE);\n}\n\nexport async function readConfig(cwd: string, version: string): Promise<EnglyConfig | null> {\n let raw: string;\n try {\n raw = await readFile(configPath(cwd), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw error;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new ConfigError(`${CONFIG_FILE} is not valid JSON.`);\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new ConfigError(`${CONFIG_FILE} must contain a JSON object.`);\n }\n\n return validateConfig({ ...defaultConfig(version), ...(parsed as Partial<EnglyConfig>) });\n}\n\nexport async function writeConfig(cwd: string, config: EnglyConfig): Promise<void> {\n const path = configPath(cwd);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(validateConfig(config), null, 2)}\\n`, 'utf8');\n}\n\nexport function validateConfig(config: EnglyConfig): EnglyConfig {\n if (!(LEVELS as readonly string[]).includes(config.level)) {\n throw new ConfigError(`Invalid level \"${config.level}\". Use one of: ${LEVELS.join(', ')}.`);\n }\n if (!(STYLES as readonly string[]).includes(config.style)) {\n throw new ConfigError(`Invalid style \"${config.style}\". Use one of: ${STYLES.join(', ')}.`);\n }\n if (config.language !== 'auto' && !/^[a-z]{2,3}$/.test(config.language)) {\n throw new ConfigError(`Invalid language \"${config.language}\". Use \"auto\" or a language code like \"es\".`);\n }\n if (typeof config.enabled !== 'boolean' || typeof config.reviewEnglish !== 'boolean') {\n throw new ConfigError('\"enabled\" and \"reviewEnglish\" must be true or false.');\n }\n if (!Array.isArray(config.tools)) {\n throw new ConfigError('\"tools\" must be a list.');\n }\n return config;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nlet cachedRoot: string | undefined;\n\nexport function packageRoot(): string {\n if (cachedRoot) return cachedRoot;\n let dir = dirname(fileURLToPath(import.meta.url));\n while (true) {\n const pkgPath = join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string };\n if (pkg.name === 'engly') {\n cachedRoot = dir;\n return dir;\n }\n }\n const parent = dirname(dir);\n if (parent === dir) throw new Error('Could not locate the engly package root.');\n dir = parent;\n }\n}\n\nexport function templatesDir(): string {\n return join(packageRoot(), 'templates');\n}\n\nexport function packageVersion(): string {\n const pkg = JSON.parse(readFileSync(join(packageRoot(), 'package.json'), 'utf8')) as { version: string };\n return pkg.version;\n}\n\nexport const ENGLY_DIR = '.engly';\nexport const CONFIG_FILE = `${ENGLY_DIR}/config.json`;\nexport const INSTRUCTIONS_FILE = `${ENGLY_DIR}/engly.md`;\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nexport async function readTextIfExists(path: string): Promise<string | null> {\n try {\n return await readFile(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw error;\n }\n}\n\nexport async function writeText(path: string, content: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, 'utf8');\n}\n","import { spawnSync } from 'node:child_process';\n\nexport type GitStatus = 'tracked' | 'untracked' | 'ignored' | 'no-git';\n\nfunction git(cwd: string, args: string[]): number | null {\n const result = spawnSync('git', args, { cwd, stdio: 'ignore' });\n if (result.error) return null;\n return result.status;\n}\n\nexport function gitStatus(cwd: string, relativePath: string): GitStatus {\n if (git(cwd, ['rev-parse', '--is-inside-work-tree']) !== 0) return 'no-git';\n if (git(cwd, ['ls-files', '--error-unmatch', '--', relativePath]) === 0) return 'tracked';\n if (git(cwd, ['check-ignore', '-q', '--', relativePath]) === 0) return 'ignored';\n return 'untracked';\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EnglyConfig } from './config.js';\nimport { templatesDir } from './paths.js';\n\nexport const SKILL_NAMES = ['engly-on', 'engly-off', 'engly-review'] as const;\nexport type SkillName = (typeof SKILL_NAMES)[number];\n\nexport async function renderInstructions(config: EnglyConfig): Promise<string> {\n const template = await readFile(join(templatesDir(), 'engly.md'), 'utf8');\n\n const values: Record<string, string> = {\n language: config.language === 'auto' ? 'auto' : config.language,\n level: config.level,\n styleRule:\n config.style === 'light'\n ? 'show up to 3 corrections (\"wrong\" β†’ \"right\" with a short reason) and the improved prompt.'\n : 'explain every mistake (\"wrong\" β†’ \"right\" with a short reason) and show the improved prompt.',\n englishRule: config.reviewEnglish\n ? 'Prompt in correct English: show nothing.'\n : 'Prompt in English: show nothing, even if it has mistakes. Only coach prompts in other languages.',\n lengthRule: config.style === 'light' ? 'Keep the note to 6 lines at most.' : 'Keep the note as short as possible.',\n };\n\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key: string) => values[key] ?? match);\n}\n\nexport async function readSkill(name: SkillName): Promise<string> {\n return readFile(join(templatesDir(), 'skills', name, 'SKILL.md'), 'utf8');\n}\n","import type { SizeLimit } from '../adapters/types.js';\n\nexport interface SizeReport {\n label: string;\n warning: string;\n}\n\nexport function checkSize(text: string, limit: SizeLimit | undefined): SizeReport | null {\n if (!limit) return null;\n\n const bytes = Buffer.byteLength(text, 'utf8');\n if (limit.maxBytes !== undefined && bytes > limit.maxBytes) {\n return { label: `${(bytes / 1024).toFixed(1)} KB`, warning: limit.warning };\n }\n\n const lines = text === '' ? 0 : text.split('\\n').length - (text.endsWith('\\n') ? 1 : 0);\n if (limit.maxLines !== undefined && lines > limit.maxLines) {\n return { label: `${lines} lines`, warning: limit.warning };\n }\n\n return null;\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function removeCommand(): Promise<void> {\n notImplemented('remove');\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function statusCommand(): Promise<void> {\n notImplemented('status');\n}\n","import { notImplemented } from './not-implemented.js';\n\nexport async function updateCommand(): Promise<void> {\n notImplemented('update');\n}\n"],"mappings":"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","names":["join","dirname","join","dirname","join","join","dirname","mkdir","readFile","writeFile","dirname","readFile","join","readFile","join","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engly",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Your English coach for AI prompts. One command. Every agent. Always on. Zero cost.",
5
5
  "type": "module",
6
6
  "bin": {