@gangvy/claude-code-measurement-marker-check 0.1.0 → 0.1.2

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "measurement-marker-check",
3
3
  "description": "P0 renovation measurement drawing marker check skill for Claude Code. Upload a measurement image, identify missing annotations, and produce a review checklist.",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "author": {
6
6
  "name": "nkkj-BrainHack"
7
7
  }
package/README.md CHANGED
@@ -25,6 +25,34 @@ npm --prefix claude-code-measurement-marker-check run smoke
25
25
 
26
26
  ## Claude Code 加载
27
27
 
28
+ 推荐给 VSCode Claude Code 使用工作区安装:
29
+
30
+ ```bash
31
+ npx @gangvy/claude-code-measurement-marker-check workspace --smoke
32
+ ```
33
+
34
+ 安装后会生成:
35
+
36
+ ```text
37
+ .\.claude\plugins\measurement-marker-check
38
+ .\.claude\skills\measurement-marker-check\SKILL.md
39
+ ```
40
+
41
+ 如果 Claude Code 会话已经打开,安装后重启当前 VSCode Claude Code 会话。
42
+
43
+ 用户级安装:
44
+
45
+ ```bash
46
+ npm install -g @gangvy/claude-code-measurement-marker-check
47
+ claude-measure-marker install
48
+ ```
49
+
50
+ 免全局安装到用户级插件目录:
51
+
52
+ ```bash
53
+ npx @gangvy/claude-code-measurement-marker-check install
54
+ ```
55
+
28
56
  ```bash
29
57
  claude --plugin-dir ./claude-code-measurement-marker-check
30
58
  ```
@@ -4,63 +4,136 @@ const os = require('os');
4
4
  const path = require('path');
5
5
  const { spawnSync } = require('child_process');
6
6
 
7
- const ROOT = path.resolve(__dirname, '..');
7
+ const SOURCE_ROOT = path.resolve(__dirname, '..');
8
+ const WORKSPACE_ROOT = process.cwd();
8
9
  const DEFAULT_TARGET = path.join(os.homedir(), '.claude', 'plugins', 'measurement-marker-check');
10
+ const WORKSPACE_TARGET = path.join(WORKSPACE_ROOT, '.claude', 'plugins', 'measurement-marker-check');
11
+ const WORKSPACE_PLUGINS_ROOT = path.join(WORKSPACE_ROOT, '.claude', 'plugins');
12
+
9
13
  const COPY_ENTRIES = [
10
14
  '.claude-plugin',
11
15
  'README.md',
12
16
  'scripts',
13
17
  'skill-package-manifest.json',
14
18
  'skills',
19
+ 'test-fixtures',
15
20
  'tools',
16
21
  'package.json'
17
22
  ];
18
23
 
24
+ function expandHome(value) {
25
+ return String(value || '').replace(/^~(?=$|[\\/])/, os.homedir());
26
+ }
27
+
19
28
  function parseArgs(argv) {
20
- const args = { command: argv[0] || 'help', target: DEFAULT_TARGET, smoke: false };
21
- for (let i = 1; i < argv.length; i++) {
29
+ const first = argv[0] && !argv[0].startsWith('--') ? argv[0] : 'install';
30
+ const args = {
31
+ command: first,
32
+ target: DEFAULT_TARGET,
33
+ smoke: false,
34
+ force: false,
35
+ help: false
36
+ };
37
+
38
+ if (first === 'workspace' || first === 'install-workspace') {
39
+ args.command = 'install';
40
+ args.target = WORKSPACE_TARGET;
41
+ }
42
+
43
+ for (let i = first === argv[0] ? 1 : 0; i < argv.length; i++) {
22
44
  const token = argv[i];
23
45
  if (token === '--target' && argv[i + 1]) {
24
- args.target = path.resolve(argv[++i]);
46
+ args.target = argv[++i];
47
+ } else if (token.startsWith('--target=')) {
48
+ args.target = token.slice('--target='.length);
49
+ } else if (token === '--workspace') {
50
+ args.target = WORKSPACE_TARGET;
25
51
  } else if (token === '--smoke') {
26
52
  args.smoke = true;
53
+ } else if (token === '--force') {
54
+ args.force = true;
55
+ } else if (token === '--help' || token === '-h') {
56
+ args.help = true;
27
57
  }
28
58
  }
59
+
60
+ args.target = path.resolve(expandHome(args.target));
29
61
  return args;
30
62
  }
31
63
 
32
64
  function printHelp() {
33
65
  console.log([
34
- 'Claude 量尺图缺标记技能包安装工具',
66
+ 'Claude measurement marker skill package installer',
35
67
  '',
36
68
  'Usage:',
37
69
  ' claude-measure-marker install [--target <dir>] [--smoke]',
70
+ ' claude-measure-marker workspace [--smoke]',
71
+ ' claude-measure-marker install --workspace [--smoke]',
38
72
  ' claude-measure-marker check',
39
73
  ' claude-measure-marker smoke',
40
74
  ' claude-measure-marker path',
41
75
  '',
42
- 'Examples:',
76
+ 'npx:',
43
77
  ' npx @gangvy/claude-code-measurement-marker-check install',
44
- ' claude --plugin-dir "%USERPROFILE%\\.claude\\plugins\\measurement-marker-check"'
78
+ ' npx @gangvy/claude-code-measurement-marker-check workspace --smoke',
79
+ '',
80
+ 'Options:',
81
+ ' --workspace Install into ./.claude/plugins/measurement-marker-check',
82
+ ' --target <dir> Install into a custom directory',
83
+ ' --force Allow overwriting an existing custom target',
84
+ ' --smoke Run sample smoke checks after install',
85
+ ' --help, -h Show help'
45
86
  ].join('\n'));
46
87
  }
47
88
 
48
- function removeTarget(target) {
49
- if (!target.includes(path.join(os.homedir(), '.claude', 'plugins')) && fs.existsSync(target)) {
50
- throw new Error(`Refusing to overwrite non-Claude plugin directory: ${target}`);
89
+ function ensureDir(dirPath) {
90
+ fs.mkdirSync(dirPath, { recursive: true });
91
+ }
92
+
93
+ function isInside(parentDir, childDir) {
94
+ const relative = path.relative(path.resolve(parentDir), path.resolve(childDir));
95
+ return relative === '' || (!!relative && !relative.startsWith('..') && !path.isAbsolute(relative));
96
+ }
97
+
98
+ function isSafeDefaultTarget(targetDir) {
99
+ return path.resolve(targetDir) === path.resolve(DEFAULT_TARGET);
100
+ }
101
+
102
+ function isSafeWorkspaceTarget(targetDir) {
103
+ return isInside(WORKSPACE_PLUGINS_ROOT, targetDir);
104
+ }
105
+
106
+ function canOverwriteTarget(targetDir, force) {
107
+ return force || isSafeDefaultTarget(targetDir) || isSafeWorkspaceTarget(targetDir);
108
+ }
109
+
110
+ function copyDirRecursive(source, destination) {
111
+ const stat = fs.statSync(source);
112
+ if (stat.isDirectory()) {
113
+ ensureDir(destination);
114
+ for (const child of fs.readdirSync(source)) {
115
+ if (child === 'node_modules' || child === 'outputs' || child === '.git') continue;
116
+ copyDirRecursive(path.join(source, child), path.join(destination, child));
117
+ }
118
+ return;
51
119
  }
52
- fs.rmSync(target, { recursive: true, force: true });
120
+ ensureDir(path.dirname(destination));
121
+ fs.copyFileSync(source, destination);
53
122
  }
54
123
 
55
- function copyPlugin(target) {
56
- removeTarget(target);
57
- fs.mkdirSync(target, { recursive: true });
124
+ function copyPlugin(target, force) {
125
+ if (fs.existsSync(target)) {
126
+ if (!canOverwriteTarget(target, force)) {
127
+ throw new Error(`Refusing to overwrite custom target without --force: ${target}`);
128
+ }
129
+ fs.rmSync(target, { recursive: true, force: true });
130
+ }
131
+ ensureDir(target);
58
132
 
59
133
  for (const entry of COPY_ENTRIES) {
60
- const source = path.join(ROOT, entry);
134
+ const source = path.join(SOURCE_ROOT, entry);
61
135
  if (!fs.existsSync(source)) continue;
62
- const destination = path.join(target, entry);
63
- fs.cpSync(source, destination, { recursive: true });
136
+ copyDirRecursive(source, path.join(target, entry));
64
137
  }
65
138
  }
66
139
 
@@ -69,14 +142,13 @@ function checkPlugin(target) {
69
142
  '.claude-plugin/plugin.json',
70
143
  'skills/measurement-marker-check/SKILL.md',
71
144
  'tools/measurement-marker-check-run.js',
145
+ 'scripts/smoke.js',
72
146
  'package.json'
73
147
  ];
74
-
75
148
  const missing = required.filter(entry => !fs.existsSync(path.join(target, entry)));
76
149
  if (missing.length) {
77
- throw new Error(`安装目录缺少文件:${missing.join(', ')}`);
150
+ throw new Error(`Install target is missing required files: ${missing.join(', ')}`);
78
151
  }
79
-
80
152
  return {
81
153
  status: 'ok',
82
154
  target,
@@ -84,6 +156,102 @@ function checkPlugin(target) {
84
156
  };
85
157
  }
86
158
 
159
+ function asForwardSlash(filePath) {
160
+ return path.resolve(filePath).replace(/\\/g, '/');
161
+ }
162
+
163
+ function quotedPath(filePath) {
164
+ return `"${asForwardSlash(filePath)}"`;
165
+ }
166
+
167
+ function workspaceSkillText(target) {
168
+ const runner = path.join(target, 'tools', 'measurement-marker-check-run.js');
169
+ return [
170
+ '---',
171
+ 'name: measurement-marker-check',
172
+ 'description: Check 装修量尺图、手绘量尺单、户型草图或现场测量图片中是否存在疑似未标记位置,输出缺标记清单、待复核区域和下一步补标建议。Use when the user asks for 实战二、装修量尺、量尺图识别、尺寸图缺标记、哪里没标、未标注检查 or measurement drawing review.',
173
+ 'allowed-tools: Read Write Bash(node *)',
174
+ '---',
175
+ '',
176
+ '# 装修量尺图缺标记检查官',
177
+ '',
178
+ '你是面向设计/量尺团队的量尺图缺标记检查官。用户要的是业务复核清单,不是技术日志。',
179
+ '',
180
+ '## 工作流',
181
+ '',
182
+ '1. 如果用户没有上传图片或给出图片路径,先请用户上传量尺图、手绘量尺单、户型草图或现场测量截图。',
183
+ '2. 如果用户上传了图片,先直接观察图片内容,整理已标记内容、疑似缺标位置、待人工复核区域。',
184
+ '3. 把观察结果写成结构化 JSON,保存到 `outputs/measurement-marker-check/input.json`。',
185
+ '4. 调用本地工具生成稳定报告。',
186
+ '5. 优先把工具返回的 `assistantMessage` 正文发给用户,文件路径只放末尾作为归档信息。',
187
+ '',
188
+ '## 检查口径',
189
+ '',
190
+ '- 空间名称:客厅、卧室、厨房、卫生间、阳台等。',
191
+ '- 关键尺寸:长、宽、高、门洞、窗洞、墙面、柜体位置等。',
192
+ '- 门窗洞口:宽高、离地高度、开启方向、门套或窗台限制。',
193
+ '- 梁柱/墙体限制:梁高、柱位、墙厚、承重/非承重提示。',
194
+ '- 水电/管道/烟道:下水、地漏、插座、开关、燃气、烟道、排水管。',
195
+ '- 安装避让备注:需避让、不可拆、需复核、现场限制、手写备注。',
196
+ '',
197
+ '## 工具输入 JSON 结构',
198
+ '',
199
+ '```json',
200
+ '{',
201
+ ' "project": "项目或样本名称",',
202
+ ' "image": "图片文件名或路径",',
203
+ ' "detectedMarkers": [',
204
+ ' { "area": "厨房右侧墙面", "markerType": "关键尺寸", "value": "3200mm", "evidence": "墙面横向尺寸已标注", "confidence": 0.9 }',
205
+ ' ],',
206
+ ' "missingMarkers": [',
207
+ ' { "area": "窗户下沿", "missingType": "窗台高度", "risk": "可能影响台面、吊柜或窗帘盒设计", "evidence": "窗户位置已画出,但没有离地高度标注", "confidence": 0.78 }',
208
+ ' ],',
209
+ ' "uncertainAreas": [',
210
+ ' { "area": "厨房右上角", "reason": "疑似烟道或立管,但文字不清晰", "action": "请现场照片或原始量尺单复核", "confidence": 0.58 }',
211
+ ' ],',
212
+ ' "overallJudgement": "存在关键缺标记,建议人工复核后再进入标准尺寸表。"',
213
+ '}',
214
+ '```',
215
+ '',
216
+ '## 生成报告命令',
217
+ '',
218
+ '```bash',
219
+ `node ${quotedPath(runner)} --input "outputs/measurement-marker-check/input.json" --output "outputs/measurement-marker-check" --result-prefix MEASURE_MARKER_RESULT`,
220
+ '```',
221
+ '',
222
+ '## 演示样例命令',
223
+ '',
224
+ '```bash',
225
+ `node ${quotedPath(runner)} --sample --output "outputs/measurement-marker-check-sample" --result-prefix MEASURE_MARKER_RESULT`,
226
+ '```',
227
+ '',
228
+ '## 输出规则',
229
+ '',
230
+ '- 先给结论:是否存在疑似缺标记。',
231
+ '- 必须明确“区域 + 缺失类型 + 风险 + 图像依据 + 置信度”。',
232
+ '- 看不清的地方归入“待人工复核区域”,不要强行判断。',
233
+ '- 不要把 P0 结果说成最终工程结论;它是辅助复核清单。',
234
+ '- 不要只返回文件路径。',
235
+ '',
236
+ '## 推荐启动话术',
237
+ '',
238
+ '```text',
239
+ '帮我检查这张装修量尺图有没有哪里未标记。',
240
+ '重点看空间名称、门窗洞口、长宽高、管道、插座/烟道和现场限制备注。',
241
+ '先给我缺标记清单和人工复核建议。',
242
+ '```',
243
+ ''
244
+ ].join('\n');
245
+ }
246
+
247
+ function writeWorkspaceSkillEntry(target) {
248
+ if (!isSafeWorkspaceTarget(target)) return;
249
+ const skillDir = path.join(WORKSPACE_ROOT, '.claude', 'skills', 'measurement-marker-check');
250
+ ensureDir(skillDir);
251
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), workspaceSkillText(target), 'utf8');
252
+ console.log('Wrote workspace skill entry: .\\.claude\\skills\\measurement-marker-check\\SKILL.md');
253
+ }
254
+
87
255
  function runSmoke(target) {
88
256
  const result = spawnSync(process.execPath, ['scripts/smoke.js'], {
89
257
  cwd: target,
@@ -96,18 +264,35 @@ function runSmoke(target) {
96
264
  }
97
265
 
98
266
  function printNextSteps(target) {
267
+ const workspaceMode = isSafeWorkspaceTarget(target);
99
268
  console.log('');
100
- console.log('安装完成。下一步:');
101
- console.log(` claude --plugin-dir "${target}"`);
269
+ console.log('Install complete.');
102
270
  console.log('');
103
- console.log('进入 Claude Code 后可说:');
271
+ if (workspaceMode) {
272
+ console.log('VSCode workspace mode is ready.');
273
+ console.log('');
274
+ console.log('Generated project files:');
275
+ console.log(' .\\.claude\\skills\\measurement-marker-check\\SKILL.md');
276
+ console.log(' .\\.claude\\plugins\\measurement-marker-check');
277
+ console.log('');
278
+ console.log('Restart the VSCode Claude Code session if it was already open.');
279
+ } else {
280
+ console.log('Claude Code CLI launch command:');
281
+ console.log(` claude --plugin-dir "${target}"`);
282
+ }
283
+ console.log('');
284
+ console.log('Try this prompt in Claude Code:');
104
285
  console.log(' 帮我检查这张装修量尺图有没有哪里未标记。');
286
+ console.log(' 重点看空间名称、门窗洞口、长宽高、管道、插座/烟道和现场限制备注。');
287
+ console.log(' 先给我缺标记清单和人工复核建议。');
288
+ console.log('');
289
+ console.log(`Install target: ${target}`);
105
290
  }
106
291
 
107
292
  function main() {
108
293
  const args = parseArgs(process.argv.slice(2));
109
294
 
110
- if (args.command === 'help' || args.command === '--help' || args.command === '-h') {
295
+ if (args.help || args.command === 'help') {
111
296
  printHelp();
112
297
  return;
113
298
  }
@@ -118,9 +303,10 @@ function main() {
118
303
  }
119
304
 
120
305
  if (args.command === 'install') {
121
- copyPlugin(args.target);
306
+ copyPlugin(args.target, args.force);
122
307
  const check = checkPlugin(args.target);
123
308
  console.log(JSON.stringify(check, null, 2));
309
+ writeWorkspaceSkillEntry(args.target);
124
310
  if (args.smoke) runSmoke(args.target);
125
311
  printNextSteps(args.target);
126
312
  return;
@@ -132,7 +318,7 @@ function main() {
132
318
  }
133
319
 
134
320
  if (args.command === 'smoke') {
135
- runSmoke(ROOT);
321
+ runSmoke(SOURCE_ROOT);
136
322
  return;
137
323
  }
138
324
 
@@ -143,6 +329,6 @@ function main() {
143
329
  try {
144
330
  main();
145
331
  } catch (error) {
146
- console.error(`claude-measure-marker 执行失败:${error.message}`);
332
+ console.error(`claude-measure-marker failed: ${error.message}`);
147
333
  process.exit(1);
148
334
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gangvy/claude-code-measurement-marker-check",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Claude Code skill for P0 renovation measurement drawing missing-marker checks.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-measurement-marker-check",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Claude Code 独立技能包:装修量尺图缺标记检查。P0 支持上传量尺图后识别疑似未标记位置、待复核区域和补标建议。",
5
5
  "plugin": "measurement-marker-check",
6
6
  "skills": [