@hyuga/carrylint 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -6
- package/action.yml +1 -1
- package/package.json +1 -1
- package/src/check.mjs +43 -27
package/README.md
CHANGED
|
@@ -20,17 +20,16 @@ Other skill linters check your skill is **spec-valid**. carrylint checks it **ac
|
|
|
20
20
|
|
|
21
21
|
| kind | severity | 説明 |
|
|
22
22
|
|---|---|---|
|
|
23
|
-
| `abs-path` | **error** |
|
|
24
|
-
| `placeholder` | **error** |
|
|
25
|
-
| `undeclared-cli` |
|
|
26
|
-
| `home-path` | warn | `~/…` ホーム相対パス(ユーザー前提) |
|
|
23
|
+
| `abs-path` | **error** | 作者環境前提の絶対パス(`C:\Users\…` / `/Users/<実名>/` / `/home/<実名>/`)。`$HOME` / `~` / `%USERPROFILE%` と汎用名(`/home/user` 等)は可搬なので**対象外** |
|
|
24
|
+
| `placeholder` | **error** | 配布物に残った未完成マーカー(`<FILL_ME>` / `REPLACE_ME` / `CHANGEME` / `<INSERT …>`)。`YOUR_API_KEY` / `/path/to/` は「置き換えてね」の正当な文書慣習なので**対象外** |
|
|
25
|
+
| `undeclared-cli` | warn | 外部/プロバイダCLI(`codex` `ollama` …)を宣言なしで叩く。ホストの設定/確認(`claude mcp add` `codex --version` 等)は**除外** |
|
|
27
26
|
| `provider-env` | warn | プロバイダ固有 API キー env の生参照(`OPENAI_API_KEY` …) |
|
|
28
27
|
| `todo` | warn | 配布物に残った `TODO:` / `FIXME:` |
|
|
29
28
|
| `model-id` | opt-in | モデルID直書き(`claude-*` / `gpt-*` / `gemini-*`)※`--model-ids` で有効化 |
|
|
30
29
|
|
|
31
|
-
Low-noise by design: only
|
|
30
|
+
Low-noise by design: only unambiguous, author-only breakers are **error** (fail the PR); the rest are **warn**. Validated against a real-world audit of 230 public skills (v0.1.1): every remaining `error` was a genuine hardcoded personal path.
|
|
32
31
|
|
|
33
|
-
>
|
|
32
|
+
> 誤検知=狼少年化が唯一の死因。実データ230リポの監査で ERROR の誤検知をゼロに追い込みました(v0.1.1)。
|
|
34
33
|
|
|
35
34
|
## Use as a GitHub Action / CIで使う(定着の本体)
|
|
36
35
|
|
package/action.yml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: 'carrylint runtime-portability linter'
|
|
2
|
-
description:
|
|
2
|
+
description: "Fail CI when a skill or command isn't portable: absolute paths, undeclared CLIs, placeholders. Zero-dep, model-agnostic."
|
|
3
3
|
author: 'hyuga611'
|
|
4
4
|
branding:
|
|
5
5
|
icon: 'package'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyuga/carrylint",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Runtime-portability linter for agent skills & commands: fail CI when a SKILL.md / AGENTS.md / slash-command hardcodes machine- or model-specific assumptions (absolute paths, undeclared CLIs, unresolved placeholders). Zero-dependency, model-agnostic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/check.mjs
CHANGED
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
// SKILL.md / .claude/commands / AGENTS.md / CLAUDE.md / GEMINI.md / .cursor/rules などの本文から、
|
|
5
5
|
// 作った本人の環境・モデル前提が焼き込まれた「実行時に可搬でない」記述を検出する。
|
|
6
6
|
//
|
|
7
|
-
// ・abs-path :
|
|
8
|
-
//
|
|
9
|
-
// ・
|
|
10
|
-
//
|
|
7
|
+
// ・abs-path : 作者環境前提の絶対パス (C:\… や /Users/<実名>/ /home/<実名>/) [ERROR]
|
|
8
|
+
// ※ $HOME / ~ / %USERPROFILE% と汎用名(/home/user 等)は可搬なので対象外
|
|
9
|
+
// ・placeholder : 配布物に残った未完成マーカー (<FILL_ME> REPLACE_ME CHANGEME <INSERT…>) [ERROR]
|
|
10
|
+
// ※ YOUR_API_KEY / /path/to/ は「置き換えてね」の正当な文書慣習なので対象外
|
|
11
|
+
// ・undeclared-cli : 外部/プロバイダCLIを宣言なしで叩く。ホストの mcp add/--version 等は除外 [WARN]
|
|
11
12
|
// ・provider-env : プロバイダ固有の API キー env の生参照 [WARN]
|
|
12
13
|
// ・todo : 配布物に残った TODO:/FIXME:/XXX: [WARN]
|
|
13
14
|
// ・model-id : モデルID直書き (claude-* gpt-* gemini-*) ← 既定OFF・--model-ids で有効 [opt-in]
|
|
14
15
|
//
|
|
16
|
+
// v0.1.1: 実データ160+70リポの監査に基づき誤検知を除去($HOME/~・汎用名・API例・ホストCLI)。
|
|
17
|
+
//
|
|
15
18
|
// Agent Skills はオープン標準として "形式" は 20+ エージェントで可搬になった。carrylint は
|
|
16
19
|
// "中身が実際に動くか" を見る側。実行時に LLM も API キーも使わない純静的解析(依存ゼロ)。
|
|
17
20
|
//
|
|
@@ -65,29 +68,38 @@ export const DEFAULT_RULES = {
|
|
|
65
68
|
],
|
|
66
69
|
};
|
|
67
70
|
|
|
68
|
-
//
|
|
69
|
-
//
|
|
71
|
+
// 配布物に残っていたら「作者が埋め忘れた未完成」を示す、曖昧さゼロのマーカーのみ(ERROR)。
|
|
72
|
+
// v0.1.1: YOUR_API_KEY / /path/to/ / <your-x> は「実行時に置き換えてね」という正当な
|
|
73
|
+
// ドキュメント慣習(実データ監査で 26/26 が誤検知)だったので ERROR から除外した。
|
|
70
74
|
const PLACEHOLDER_ERROR = [
|
|
71
75
|
/<fill[_\- ]?me>?/i,
|
|
72
76
|
/\bfill[_\-]me\b/i,
|
|
73
77
|
/\breplace[_\-]?me\b/i,
|
|
74
78
|
/\bchange[_\-]?me\b/i,
|
|
75
|
-
/\byour[_\-](?:api[_\-]?key|token|secret|password)\b/i,
|
|
76
|
-
/<your-[^>]{1,40}>/i,
|
|
77
79
|
/<insert[ _\-][^>]{1,40}>/i,
|
|
78
|
-
/\bpath\/to\/your\b/i,
|
|
79
|
-
/(?:^|[\s`"'(])\/path\/to\//,
|
|
80
|
-
/\bpath\/to\/\.{3}/,
|
|
81
80
|
];
|
|
82
81
|
|
|
83
82
|
// ---------------- パターン(純粋・textスキャン) ----------------
|
|
84
83
|
|
|
85
|
-
const DRIVE_ABS = /(?<![\w.])[A-Za-z]:\\[^\s`"')<>|]+/g;
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
const TILDE_HOME = /(?<![\w.])~\/[^\s`"')<>|]*/g; // ~/foo …(WARN)
|
|
84
|
+
const DRIVE_ABS = /(?<![\w.])[A-Za-z]:\\[^\s`"')<>|]+/g; // C:\Users\atlan\…(Windows絶対=非可搬)
|
|
85
|
+
// /Users/<name>/ ・ /home/<name>/ の <name> を捕捉(汎用名/glob は後段で除外)。
|
|
86
|
+
const UNIX_USER_ABS = /(?<![\w.\-\/])\/(Users|home)\/([^\s/`"')<>|]+)([^\s`"')<>|]*)/g;
|
|
89
87
|
const TODO_MARK = /(?:\b(?:TODO|FIXME|HACK|XXX)\b\s*[::]|<!--\s*(?:TODO|FIXME)\b)/;
|
|
90
88
|
|
|
89
|
+
// v0.1.1: $HOME / ${HOME} / %USERPROFILE% / ~ は各ユーザーで解決=可搬。エラーにしない
|
|
90
|
+
// (実データ監査で「これらは可搬な書き方」だと確認)。実在の個人パスは /Users/<実名>/ のみ。
|
|
91
|
+
const GENERIC_USER = new Set([
|
|
92
|
+
'user', 'users', 'you', 'me', 'example', 'name', 'username', 'ubuntu', 'root',
|
|
93
|
+
'admin', 'foo', 'bar', 'home', 'someone', 'yourname', 'test', 'ec2-user',
|
|
94
|
+
]);
|
|
95
|
+
// ホスト/セットアップ系サブコマンドは「その場で使う道具」でなく「環境設定・確認」なので依存扱いしない
|
|
96
|
+
// (実データ監査の undeclared-cli 誤検知は大半が `claude mcp add` / `codex mcp add` 等だった)。
|
|
97
|
+
const META_SUBCMD = new Set([
|
|
98
|
+
'mcp', 'login', 'logout', 'config', 'configure', 'auth', 'add', 'install',
|
|
99
|
+
'update', 'upgrade', 'init', 'setup', 'doctor', 'version', 'help',
|
|
100
|
+
'--version', '-v', '--help', '-h',
|
|
101
|
+
]);
|
|
102
|
+
|
|
91
103
|
function push(findings, ln, kind, severity, msg) {
|
|
92
104
|
findings.push({ ln, kind, severity, msg });
|
|
93
105
|
}
|
|
@@ -156,7 +168,10 @@ function cliInvocations(line, inFence, rules) {
|
|
|
156
168
|
.replace(/^[!$#>]\s*/, '')
|
|
157
169
|
.replace(/^sudo\s+/, '')
|
|
158
170
|
.replace(/^(?:[A-Za-z_][\w]*=[^\s]*\s+)+/, '');
|
|
159
|
-
|
|
171
|
+
const mm = new RegExp('^' + w + '\\b\\s+(\\S+)', 'i').exec(rest);
|
|
172
|
+
if (!mm) continue; // 裸の言及(引数なし)は呼び出しでない
|
|
173
|
+
if (META_SUBCMD.has(mm[1].toLowerCase())) continue; // mcp / --version / login 等は設定・確認=依存でない
|
|
174
|
+
hits.add(w);
|
|
160
175
|
}
|
|
161
176
|
}
|
|
162
177
|
return hits;
|
|
@@ -197,16 +212,16 @@ export function scan(text, opts = {}) {
|
|
|
197
212
|
}
|
|
198
213
|
if (ignored.has(ln)) return;
|
|
199
214
|
|
|
200
|
-
// 1) マシン固有の絶対パス(ERROR
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
push(findings, ln, 'abs-path', 'error', `マシン固有の絶対パス \`${m[0].trim()}\` — 他人の環境で解決しません(相対パスや {baseDir} に)`);
|
|
205
|
-
}
|
|
215
|
+
// 1) マシン固有の絶対パス(ERROR)。$HOME/~ は可搬なので対象外・汎用ユーザー名の例示も除外(v0.1.1)。
|
|
216
|
+
DRIVE_ABS.lastIndex = 0;
|
|
217
|
+
for (const m of line.matchAll(DRIVE_ABS)) {
|
|
218
|
+
push(findings, ln, 'abs-path', 'error', `マシン固有の絶対パス \`${m[0].trim()}\` — 他人の環境で解決しません(相対パスや {baseDir} に)`);
|
|
206
219
|
}
|
|
207
|
-
|
|
208
|
-
for (const m of line.matchAll(
|
|
209
|
-
|
|
220
|
+
UNIX_USER_ABS.lastIndex = 0;
|
|
221
|
+
for (const m of line.matchAll(UNIX_USER_ABS)) {
|
|
222
|
+
const user = (m[2] || '').toLowerCase();
|
|
223
|
+
if (GENERIC_USER.has(user) || user.includes('*') || user.startsWith('$')) continue; // /home/user, /home/*/ 等の例示は除外
|
|
224
|
+
push(findings, ln, 'abs-path', 'error', `作者環境前提の絶対パス \`${m[0].trim()}\` — 他人の環境に \`${m[1]}/${m[2]}\` は存在しません(相対パスや {baseDir} に)`);
|
|
210
225
|
}
|
|
211
226
|
|
|
212
227
|
// 2) 未解決プレースホルダ(ERROR)
|
|
@@ -218,10 +233,11 @@ export function scan(text, opts = {}) {
|
|
|
218
233
|
}
|
|
219
234
|
}
|
|
220
235
|
|
|
221
|
-
// 3)
|
|
236
|
+
// 3) 外部/プロバイダCLIの呼び出し(宣言なし)。ホスト自身の設定/確認(mcp add 等)は除外済み。
|
|
237
|
+
// 概念的に曖昧なので v0.1.1 で ERROR→WARN に降格(PRは落とさず注意喚起のみ)。
|
|
222
238
|
for (const cli of cliInvocations(line, inFence, rules)) {
|
|
223
239
|
if (allow.has(cli) || declared.has(cli)) continue;
|
|
224
|
-
push(findings, ln, 'undeclared-cli', '
|
|
240
|
+
push(findings, ln, 'undeclared-cli', 'warn', `\`${cli}\` を呼んでいますが、インストール手順も宣言もありません — 他環境では未導入かもしれません`);
|
|
225
241
|
}
|
|
226
242
|
|
|
227
243
|
// 4) プロバイダ固有 env の生参照(WARN)
|