ts-abbr-skill 1.0.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 +102 -0
- package/README.zh-CN.md +102 -0
- package/dist/cli.js +80 -0
- package/package.json +38 -0
- package/skill/SKILL.md +63 -0
- package/skill/config/default.config.json +4 -0
- package/skill/config/schema.json +21 -0
- package/skill/dictionary/default.json +100 -0
- package/skill/scripts/rename-with-ts-morph.ts +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ts-abbr-skill
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="assets/logo.png" alt="ts-abbr-skill" width="480">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="README.zh-CN.md">đ¨đŗ įŽäŊ䏿</a> |
|
|
9
|
+
<a href="README.md">đēđ¸ English</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://github.com/aShu-guo/ts-abbreviation-skill/releases"><img src="https://img.shields.io/github/v/release/aShu-guo/ts-abbreviation-skill?style=for-the-badge&color=blue" alt="GitHub Release"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/ts-abbr-skill"><img src="https://img.shields.io/npm/v/ts-abbr-skill?style=for-the-badge&logo=npm&color=red" alt="npm version"></a>
|
|
15
|
+
<a href="https://github.com/aShu-guo/ts-abbreviation-skill/blob/main/LICENSE"><img src="https://img.shields.io/github/license/aShu-guo/ts-abbreviation-skill?style=for-the-badge&color=green" alt="License"></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="https://www.npmjs.com/package/ts-abbr-skill"><img src="https://img.shields.io/npm/dm/ts-abbr-skill?style=flat-square&label=downloads" alt="npm downloads"></a>
|
|
20
|
+
<a href="https://github.com/aShu-guo/ts-abbreviation-skill/stargazers"><img src="https://img.shields.io/github/stars/aShu-guo/ts-abbreviation-skill?style=flat-square&logo=github" alt="GitHub stars"></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
A Claude Code skill that makes generated/refactored TypeScript naming follow a shared abbreviation dictionary (`business` â `biz`, `context` â `ctx`, `configuration` â `cfg`, ...) instead of verbose full words.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx ts-abbr-skill
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
You'll be asked where to install:
|
|
32
|
+
|
|
33
|
+
- **Project** â `./.claude/skills/ts-abbr-skill/` (recommended for team-shared conventions, commit this to your repo)
|
|
34
|
+
- **Global** â `~/.claude/skills/ts-abbr-skill/` (applies to all your projects)
|
|
35
|
+
|
|
36
|
+
Both can be selected at once. Re-running the installer is safe: your customized `config/default.config.json` and `dictionary/default.json` are never overwritten.
|
|
37
|
+
|
|
38
|
+
## What it does
|
|
39
|
+
|
|
40
|
+
- **When generating new code**: Claude checks the abbreviation dictionary before naming local variables/parameters and uses the abbreviated form when a match exists.
|
|
41
|
+
- **When refactoring existing code**: batch renames go through an AST-aware rename script (`ts-morph`-based), never plain-text find/replace, so cross-file references stay correct. A `tsc --noEmit` check is required afterward before the rename is considered done.
|
|
42
|
+
|
|
43
|
+
See [`skill/SKILL.md`](skill/SKILL.md) for the full rules Claude follows.
|
|
44
|
+
|
|
45
|
+
## Built-in Dictionary (excerpt)
|
|
46
|
+
|
|
47
|
+
| Full word | Abbreviation |
|
|
48
|
+
|-----------|--------------|
|
|
49
|
+
| business | biz |
|
|
50
|
+
| context | ctx |
|
|
51
|
+
| configuration / config | cfg |
|
|
52
|
+
| parameter / parameters | param / params |
|
|
53
|
+
| response | res |
|
|
54
|
+
| request | req |
|
|
55
|
+
| temporary | tmp |
|
|
56
|
+
| reference | ref |
|
|
57
|
+
| element | el |
|
|
58
|
+
| repository | repo |
|
|
59
|
+
| service | svc |
|
|
60
|
+
| database | db |
|
|
61
|
+
| application | app |
|
|
62
|
+
| ... | ... |
|
|
63
|
+
|
|
64
|
+
Full dictionary: [`skill/dictionary/default.json`](skill/dictionary/default.json)
|
|
65
|
+
|
|
66
|
+
## Customize
|
|
67
|
+
|
|
68
|
+
**Scope** â edit `<install target>/config/default.config.json`:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{ "scope": ["variable", "parameter"], "exported": false }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`scope` accepts `variable`, `parameter`, `localFunction`, `class`. `exported` controls whether public/exported symbols are eligible (default `false`).
|
|
75
|
+
|
|
76
|
+
**Project-specific additions** â add `.claude/ts-abbr.local.json` at your repo root; entries are merged on top of the default dictionary:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"warehouse": "wh",
|
|
81
|
+
"department": "dept"
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Development
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm install
|
|
89
|
+
npm run build # bundles src/cli.ts -> dist/cli.js
|
|
90
|
+
node dist/cli.js # try the installer locally
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For iterating on skill content, use a symlink instead of rebuilding:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
ln -sfn /path/to/ts-abbreviation-skill/skill \
|
|
97
|
+
your-test-project/.claude/skills/ts-abbr-skill
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ts-abbr-skill
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="assets/logo.png" alt="ts-abbr-skill" width="480">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="README.zh-CN.md">đ¨đŗ įŽäŊ䏿</a> |
|
|
9
|
+
<a href="README.md">đēđ¸ English</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://github.com/aShu-guo/ts-abbreviation-skill/releases"><img src="https://img.shields.io/github/v/release/aShu-guo/ts-abbreviation-skill?style=for-the-badge&color=blue" alt="GitHub Release"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/ts-abbr-skill"><img src="https://img.shields.io/npm/v/ts-abbr-skill?style=for-the-badge&logo=npm&color=red" alt="npm version"></a>
|
|
15
|
+
<a href="https://github.com/aShu-guo/ts-abbreviation-skill/blob/main/LICENSE"><img src="https://img.shields.io/github/license/aShu-guo/ts-abbreviation-skill?style=for-the-badge&color=green" alt="License"></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="https://www.npmjs.com/package/ts-abbr-skill"><img src="https://img.shields.io/npm/dm/ts-abbr-skill?style=flat-square&label=downloads" alt="npm downloads"></a>
|
|
20
|
+
<a href="https://github.com/aShu-guo/ts-abbreviation-skill/stargazers"><img src="https://img.shields.io/github/stars/aShu-guo/ts-abbreviation-skill?style=flat-square&logo=github" alt="GitHub stars"></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
ä¸ä¸Ē Claude Code Skillīŧ莊įæ/éæį TypeScript äģŖį åŊåéĩåžĒįģä¸įįŧŠåč¯å
¸īŧ`business` â `biz`ã`context` â `ctx`ã`configuration` â `cfg` įīŧīŧč䏿¯åéŋįå
¨į§°ã
|
|
24
|
+
|
|
25
|
+
## åŽčŖ
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx ts-abbr-skill
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
åŽčŖ
æļäŧč¯ĸéŽčŖ
å°åĒéīŧ
|
|
32
|
+
|
|
33
|
+
- **饚įŽįē§** â `./.claude/skills/ts-abbr-skill/`īŧæ¨čåĸéå
ąäēĢīŧæäē¤å°äģåēīŧ
|
|
34
|
+
- **å
¨åą** â `~/.claude/skills/ts-abbr-skill/`īŧéį¨äēäŊ ææį饚įŽīŧ
|
|
35
|
+
|
|
36
|
+
两ä¸Ēé饚å¯äģĨåæļåžéãéå¤čŋčĄåŽčŖ
卿¯åŽå
¨įīŧįŽæ 莝åžä¸åˇ˛åå¨į `config/default.config.json` å `dictionary/default.json` ä¸äŧčĸĢčĻįã
|
|
37
|
+
|
|
38
|
+
## ææ
|
|
39
|
+
|
|
40
|
+
- **įææ°äģŖį æļ**īŧClaude å¨åŊååąé¨åé/åæ°äšåæĨįŧŠåč¯å
¸īŧæåšé
åäŊŋį¨įŧŠååŊĸåŧã
|
|
41
|
+
- **éæåˇ˛æäģŖį æļ**īŧæšéæšååŧēåļčĩ°åēäē AST įéåŊåčæŦīŧ`ts-morph`īŧīŧä¸å
莸į睿æŦæŋæĸīŧéŋå
蝝æšåįŦĻ串åéĸéå表äŊį¨ååååéãæšåååŋ
éĄģčˇ `tsc --noEmit` æ ĄéĒīŧä¸éčŋä¸įŽåŽæã
|
|
42
|
+
|
|
43
|
+
åŽæ´č§åč§ [`skill/SKILL.md`](skill/SKILL.md)ã
|
|
44
|
+
|
|
45
|
+
## å
įŊŽč¯å
¸æåŊ
|
|
46
|
+
|
|
47
|
+
| å
¨į§° | įŧŠå |
|
|
48
|
+
|------|------|
|
|
49
|
+
| business | biz |
|
|
50
|
+
| context | ctx |
|
|
51
|
+
| configuration / config | cfg |
|
|
52
|
+
| parameter / parameters | param / params |
|
|
53
|
+
| response | res |
|
|
54
|
+
| request | req |
|
|
55
|
+
| temporary | tmp |
|
|
56
|
+
| reference | ref |
|
|
57
|
+
| element | el |
|
|
58
|
+
| repository | repo |
|
|
59
|
+
| service | svc |
|
|
60
|
+
| database | db |
|
|
61
|
+
| application | app |
|
|
62
|
+
| ... | ... |
|
|
63
|
+
|
|
64
|
+
åŽæ´č¯å
¸č§ [`skill/dictionary/default.json`](skill/dictionary/default.json)ã
|
|
65
|
+
|
|
66
|
+
## čĒåŽäš
|
|
67
|
+
|
|
68
|
+
**č°æ´įæčå´** â äŋŽæšåŽčŖ
įŽåŊéį `config/default.config.json`īŧ
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{ "scope": ["variable", "parameter"], "exported": false }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`scope` å¯éåŧīŧ`variable`ã`parameter`ã`localFunction`ã`class`ã`exported` æ§å￝åĻ寚å¯ŧåē/å
Ŧå
ąįŦĻåˇįæīŧéģ莤 `false`īŧã
|
|
75
|
+
|
|
76
|
+
**æˇģå 饚įŽä¸åąįŧŠå** â å¨äģåēæ šįŽåŊæž `.claude/ts-abbr.local.json`īŧ蝿ĄäŧååšļčĻįéģ莤č¯å
¸īŧ
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"warehouse": "wh",
|
|
81
|
+
"department": "dept"
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## æŦå°åŧå
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm install
|
|
89
|
+
npm run build # æåģē src/cli.ts -> dist/cli.js
|
|
90
|
+
node dist/cli.js # æŦå°æĩč¯åŽčŖ
å¨
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
čŋäģŖč°č¯ Skill å
厚æļīŧ፠symlink äģŖæŋæ¯æŦĄéæ°æåģēīŧ
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
ln -sfn /path/to/ts-abbreviation-skill/skill \
|
|
97
|
+
your-test-project/.claude/skills/ts-abbr-skill
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import prompts from "prompts";
|
|
5
|
+
|
|
6
|
+
// src/install.ts
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import os from "os";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
var SKILL_NAME = "ts-abbreviation-skill";
|
|
13
|
+
var PROTECTED_RELATIVE_PATHS = ["config/default.config.json", "dictionary/default.json"];
|
|
14
|
+
function getSkillSourceDir() {
|
|
15
|
+
return path.join(__dirname, "..", "skill");
|
|
16
|
+
}
|
|
17
|
+
function getTargetDir(scope, cwd) {
|
|
18
|
+
const base = scope === "project" ? path.join(cwd, ".claude", "skills") : path.join(os.homedir(), ".claude", "skills");
|
|
19
|
+
return path.join(base, SKILL_NAME);
|
|
20
|
+
}
|
|
21
|
+
function copyRecursive(srcDir, destDir, protectedRelPaths, relPrefix = "") {
|
|
22
|
+
const skipped = [];
|
|
23
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
24
|
+
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
25
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
26
|
+
const destPath = path.join(destDir, entry.name);
|
|
27
|
+
const relPath = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
28
|
+
if (entry.isDirectory()) {
|
|
29
|
+
skipped.push(...copyRecursive(srcPath, destPath, protectedRelPaths, relPath));
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (protectedRelPaths.has(relPath) && fs.existsSync(destPath)) {
|
|
33
|
+
skipped.push(relPath);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
fs.copyFileSync(srcPath, destPath);
|
|
37
|
+
}
|
|
38
|
+
return skipped;
|
|
39
|
+
}
|
|
40
|
+
function installSkill(scope, cwd = process.cwd()) {
|
|
41
|
+
const srcDir = getSkillSourceDir();
|
|
42
|
+
const targetDir = getTargetDir(scope, cwd);
|
|
43
|
+
const skippedExisting = copyRecursive(srcDir, targetDir, new Set(PROTECTED_RELATIVE_PATHS));
|
|
44
|
+
return { targetDir, skippedExisting };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/cli.ts
|
|
48
|
+
async function main() {
|
|
49
|
+
console.log("ts-abbreviation-skill installer\n");
|
|
50
|
+
const { scopes } = await prompts({
|
|
51
|
+
type: "multiselect",
|
|
52
|
+
name: "scopes",
|
|
53
|
+
message: "Where should this skill be installed?",
|
|
54
|
+
choices: [
|
|
55
|
+
{ title: "This project (./.claude/skills/)", value: "project", selected: true },
|
|
56
|
+
{ title: "Global (~/.claude/skills/)", value: "global" }
|
|
57
|
+
],
|
|
58
|
+
min: 1
|
|
59
|
+
});
|
|
60
|
+
if (!scopes || scopes.length === 0) {
|
|
61
|
+
console.log("No install scope selected, aborting.");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
for (const scope of scopes) {
|
|
65
|
+
const { targetDir, skippedExisting } = installSkill(scope);
|
|
66
|
+
console.log(`
|
|
67
|
+
\u2714 Installed to ${targetDir}`);
|
|
68
|
+
if (skippedExisting.length > 0) {
|
|
69
|
+
console.log(` Kept existing customizations (not overwritten): ${skippedExisting.join(", ")}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
console.log("\nNext steps:");
|
|
73
|
+
console.log(" - Restart/reopen Claude Code so it picks up the new skill.");
|
|
74
|
+
console.log(" - Customize scope/exported in <target>/config/default.config.json if needed.");
|
|
75
|
+
console.log(" - Add project-specific abbreviations via .claude/ts-abbreviation.local.json in your repo.");
|
|
76
|
+
}
|
|
77
|
+
main().catch((err) => {
|
|
78
|
+
console.error(err);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ts-abbr-skill",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code skill that applies a configurable abbreviation dictionary (businessâbiz, contextâctx, ...) to TypeScript variable/parameter naming.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ts-abbr-skill": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"skill"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup",
|
|
15
|
+
"dev": "tsup --watch"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"claude-code",
|
|
19
|
+
"skill",
|
|
20
|
+
"typescript",
|
|
21
|
+
"naming",
|
|
22
|
+
"abbreviation"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"prompts": "^2.4.2"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.14.0",
|
|
30
|
+
"@types/prompts": "^2.4.9",
|
|
31
|
+
"ts-morph": "^23.0.0",
|
|
32
|
+
"tsup": "^8.2.4",
|
|
33
|
+
"typescript": "^5.5.4"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ts-abbreviation-skill
|
|
3
|
+
description: Use when writing or refactoring TypeScript variable, parameter, function, or class names â applies a configurable abbreviation dictionary (businessâbiz, contextâctx, configurationâcfg, etc.) so generated and refactored code matches the team's naming conventions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TS Abbreviation Skill
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Generated TypeScript code tends to use full, verbose English words for local names (`business`, `context`, `configuration`). This skill applies a shared abbreviation dictionary so naming stays short and consistent, in both newly generated code and refactors of existing code.
|
|
11
|
+
|
|
12
|
+
## Config and Dictionary Lookup Order
|
|
13
|
+
|
|
14
|
+
Before naming anything, resolve config and dictionary by merging, most specific first:
|
|
15
|
+
|
|
16
|
+
1. Project override: `.claude/ts-abbreviation.local.json` in the current repo root (if present) â entries here are merged on top of the default dictionary/config, not a full replacement.
|
|
17
|
+
2. Default config/dictionary shipped with this skill: `config/default.config.json`, `dictionary/default.json`.
|
|
18
|
+
|
|
19
|
+
Default config (`config/default.config.json`):
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"scope": ["variable", "parameter"],
|
|
23
|
+
"exported": false
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `scope`: which kinds of identifiers this applies to. Possible values: `variable`, `parameter`, `localFunction`, `class`. Only apply abbreviations to kinds listed here.
|
|
28
|
+
- `exported`: whether exported/public symbols (exported consts, public class members, public function names) are in scope. Default `false` â never abbreviate a symbol that's part of a public API unless the project config explicitly sets `exported: true`.
|
|
29
|
+
|
|
30
|
+
If the project has no `.claude/ts-abbreviation.local.json`, use the defaults as-is.
|
|
31
|
+
|
|
32
|
+
## Mode 1: Generating New Code
|
|
33
|
+
|
|
34
|
+
When about to name a new local variable, parameter, (optionally) local function, or class â per the resolved `scope` â look it up in the merged dictionary. If a whole-word match exists (case-sensitively adapting to camelCase/PascalCase as needed), use the abbreviation instead of the full word. Compound identifiers should abbreviate the matched segment only, keeping the rest of the identifier as-is (e.g. `businessContext` â `bizCtx`, `userConfiguration` â `userCfg`).
|
|
35
|
+
|
|
36
|
+
Do not abbreviate:
|
|
37
|
+
- Symbols outside the configured `scope`
|
|
38
|
+
- Exported/public symbols unless `exported: true` is configured
|
|
39
|
+
- Identifiers where abbreviating would collide with an existing name in the same scope
|
|
40
|
+
|
|
41
|
+
**This is a two-pass task, not a one-pass one.** First draft the code naturally. Then, before finishing, do a second pass: enumerate *every* declared identifier in what you just wrote â every parameter, and every local variable (`const`/`let` inside the function body), regardless of whether that word appeared in the user's request â and check each one against the dictionary independently. It's easy to abbreviate the parameters (because they're named right there in the request) and forget local variables the code introduces on its own (intermediate results, loop variables, destructured values). Both are in the default `scope` and both need the same check.
|
|
42
|
+
|
|
43
|
+
For example, in a function that takes `configuration` and an `identifier` and returns a `message`, the parameters *and* any local variable holding an intermediate value (e.g. a fetched record, a formatted string) all need the same dictionary lookup â not just the ones named in the prompt. Type/class names and exported symbol names are the only things that stay full by default (see `scope`/`exported` above).
|
|
44
|
+
|
|
45
|
+
## Mode 2: Refactoring Existing Code (batch rename)
|
|
46
|
+
|
|
47
|
+
Renaming an existing identifier across a codebase is only safe if every reference updates together. **Never use plain-text `grep`/`sed`/find-and-replace to rename identifiers** â it will corrupt string literals, comments, unrelated identically-named locals in other scopes, and any partial-word matches.
|
|
48
|
+
|
|
49
|
+
Instead:
|
|
50
|
+
|
|
51
|
+
1. Identify the exact declaration site (file, line, column) of the symbol to rename.
|
|
52
|
+
2. Use `scripts/rename-with-ts-morph.ts` to perform an AST-aware rename â it resolves the symbol via the TypeScript compiler API and updates every reference consistently.
|
|
53
|
+
```bash
|
|
54
|
+
npx tsx scripts/rename-with-ts-morph.ts --file src/foo.ts --line 12 --col 7 --newName bizCtx
|
|
55
|
+
```
|
|
56
|
+
(Requires `ts-morph` as a dependency in the target project â install with `npm i -D ts-morph` if not already present.)
|
|
57
|
+
3. After renaming, run `tsc --noEmit` in the target project and confirm it exits with no errors before considering the rename complete. Do not report a rename as done without this check passing â if it fails, fix the fallout (e.g. missed re-export) before finishing.
|
|
58
|
+
|
|
59
|
+
## Common Mistakes
|
|
60
|
+
|
|
61
|
+
- Abbreviating an exported API name because it "looked local" â check `scope`/`exported` config first.
|
|
62
|
+
- Using sed/grep-based renames for "just one variable" â even single renames can hit false positives (e.g. a `context` string literal or an unrelated `context` param in another function).
|
|
63
|
+
- Skipping the `tsc --noEmit` check after a rename because "it looked like a simple change."
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "ts-abbreviation-skill config",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"properties": {
|
|
7
|
+
"scope": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"description": "Identifier kinds this skill is allowed to abbreviate.",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"enum": ["variable", "parameter", "localFunction", "class"]
|
|
13
|
+
},
|
|
14
|
+
"minItems": 1
|
|
15
|
+
},
|
|
16
|
+
"exported": {
|
|
17
|
+
"type": "boolean",
|
|
18
|
+
"description": "Whether exported/public symbols may be abbreviated. Defaults to false."
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"business": "biz",
|
|
3
|
+
"context": "ctx",
|
|
4
|
+
"configuration": "cfg",
|
|
5
|
+
"config": "cfg",
|
|
6
|
+
"parameter": "param",
|
|
7
|
+
"parameters": "params",
|
|
8
|
+
"argument": "arg",
|
|
9
|
+
"arguments": "args",
|
|
10
|
+
"temporary": "tmp",
|
|
11
|
+
"reference": "ref",
|
|
12
|
+
"references": "refs",
|
|
13
|
+
"element": "el",
|
|
14
|
+
"elements": "els",
|
|
15
|
+
"index": "idx",
|
|
16
|
+
"indexes": "idxs",
|
|
17
|
+
"indices": "idxs",
|
|
18
|
+
"value": "val",
|
|
19
|
+
"values": "vals",
|
|
20
|
+
"message": "msg",
|
|
21
|
+
"messages": "msgs",
|
|
22
|
+
"response": "res",
|
|
23
|
+
"request": "req",
|
|
24
|
+
"callback": "cb",
|
|
25
|
+
"attribute": "attr",
|
|
26
|
+
"attributes": "attrs",
|
|
27
|
+
"property": "prop",
|
|
28
|
+
"properties": "props",
|
|
29
|
+
"environment": "env",
|
|
30
|
+
"directory": "dir",
|
|
31
|
+
"directories": "dirs",
|
|
32
|
+
"document": "doc",
|
|
33
|
+
"documents": "docs",
|
|
34
|
+
"component": "comp",
|
|
35
|
+
"utility": "util",
|
|
36
|
+
"utilities": "utils",
|
|
37
|
+
"controller": "ctrl",
|
|
38
|
+
"repository": "repo",
|
|
39
|
+
"database": "db",
|
|
40
|
+
"identifier": "id",
|
|
41
|
+
"identifiers": "ids",
|
|
42
|
+
"number": "num",
|
|
43
|
+
"numbers": "nums",
|
|
44
|
+
"string": "str",
|
|
45
|
+
"strings": "strs",
|
|
46
|
+
"object": "obj",
|
|
47
|
+
"objects": "objs",
|
|
48
|
+
"array": "arr",
|
|
49
|
+
"arrays": "arrs",
|
|
50
|
+
"boolean": "bool",
|
|
51
|
+
"booleans": "bools",
|
|
52
|
+
"function": "fn",
|
|
53
|
+
"functions": "fns",
|
|
54
|
+
"variable": "var",
|
|
55
|
+
"variables": "vars",
|
|
56
|
+
"current": "cur",
|
|
57
|
+
"previous": "prev",
|
|
58
|
+
"initialize": "init",
|
|
59
|
+
"initialization": "init",
|
|
60
|
+
"authentication": "auth",
|
|
61
|
+
"authorization": "authz",
|
|
62
|
+
"administrator": "admin",
|
|
63
|
+
"application": "app",
|
|
64
|
+
"package": "pkg",
|
|
65
|
+
"source": "src",
|
|
66
|
+
"destination": "dest",
|
|
67
|
+
"description": "desc",
|
|
68
|
+
"information": "info",
|
|
69
|
+
"language": "lang",
|
|
70
|
+
"library": "lib",
|
|
71
|
+
"libraries": "libs",
|
|
72
|
+
"specification": "spec",
|
|
73
|
+
"specifications": "specs",
|
|
74
|
+
"structure": "struct",
|
|
75
|
+
"synchronize": "sync",
|
|
76
|
+
"synchronization": "sync",
|
|
77
|
+
"transaction": "txn",
|
|
78
|
+
"transactions": "txns",
|
|
79
|
+
"coordinate": "coord",
|
|
80
|
+
"coordinates": "coords",
|
|
81
|
+
"column": "col",
|
|
82
|
+
"columns": "cols",
|
|
83
|
+
"position": "pos",
|
|
84
|
+
"dimension": "dim",
|
|
85
|
+
"dimensions": "dims",
|
|
86
|
+
"maximum": "max",
|
|
87
|
+
"minimum": "min",
|
|
88
|
+
"average": "avg",
|
|
89
|
+
"total": "sum",
|
|
90
|
+
"increment": "inc",
|
|
91
|
+
"decrement": "dec",
|
|
92
|
+
"expression": "expr",
|
|
93
|
+
"statement": "stmt",
|
|
94
|
+
"declaration": "decl",
|
|
95
|
+
"definition": "def",
|
|
96
|
+
"implementation": "impl",
|
|
97
|
+
"instance": "inst",
|
|
98
|
+
"namespace": "ns",
|
|
99
|
+
"seconds": "secs"
|
|
100
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { Project, SyntaxKind } from 'ts-morph'
|
|
3
|
+
|
|
4
|
+
interface RenameArgs {
|
|
5
|
+
file: string
|
|
6
|
+
line: number
|
|
7
|
+
col: number
|
|
8
|
+
newName: string
|
|
9
|
+
tsconfig?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function parseArgs(): RenameArgs {
|
|
13
|
+
const argv = process.argv.slice(2)
|
|
14
|
+
const raw: Record<string, string> = {}
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < argv.length; i++) {
|
|
17
|
+
const arg = argv[i]
|
|
18
|
+
if (arg.startsWith('--')) {
|
|
19
|
+
raw[arg.slice(2)] = argv[i + 1]
|
|
20
|
+
i++
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { file, line, col, newName, tsconfig } = raw
|
|
25
|
+
if (!file || !line || !col || !newName) {
|
|
26
|
+
console.error(
|
|
27
|
+
'Usage: rename-with-ts-morph --file <path> --line <n> --col <n> --newName <name> [--tsconfig <path>]',
|
|
28
|
+
)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
file,
|
|
34
|
+
line: Number(line),
|
|
35
|
+
col: Number(col),
|
|
36
|
+
newName,
|
|
37
|
+
tsconfig,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function main() {
|
|
42
|
+
const { file, line, col, newName, tsconfig } = parseArgs()
|
|
43
|
+
|
|
44
|
+
const project = new Project({
|
|
45
|
+
tsConfigFilePath: path.resolve(tsconfig ?? 'tsconfig.json'),
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const filePath = path.resolve(file)
|
|
49
|
+
const sourceFile = project.getSourceFileOrThrow(filePath)
|
|
50
|
+
|
|
51
|
+
const pos = sourceFile.compilerNode.getPositionOfLineAndCharacter(line - 1, col - 1)
|
|
52
|
+
const node = sourceFile.getDescendantAtPos(pos)
|
|
53
|
+
if (!node) {
|
|
54
|
+
console.error(`No node found at ${file}:${line}:${col}`)
|
|
55
|
+
process.exit(1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const identifierNode =
|
|
59
|
+
node.getKind() === SyntaxKind.Identifier ? node : node.getFirstAncestorByKind(SyntaxKind.Identifier)
|
|
60
|
+
if (!identifierNode) {
|
|
61
|
+
console.error(`No identifier found at ${file}:${line}:${col}`)
|
|
62
|
+
process.exit(1)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const identifier = identifierNode.asKindOrThrow(SyntaxKind.Identifier)
|
|
66
|
+
const oldName = identifier.getText()
|
|
67
|
+
identifier.rename(newName)
|
|
68
|
+
project.saveSync()
|
|
69
|
+
|
|
70
|
+
console.log(`Renamed "${oldName}" -> "${newName}" at ${file}:${line}:${col} (all references updated).`)
|
|
71
|
+
console.log('Next: run `tsc --noEmit` to confirm the rename did not break anything.')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
main()
|