left-skills 0.2.0 → 0.3.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 CHANGED
@@ -56,6 +56,8 @@ npm uninstall -g left-skills # remove binary
56
56
  left-skills usage # human-readable report
57
57
  left-skills usage --json # for AI (JSON)
58
58
  left-skills usage --since 7 # last 7 days
59
+ left-skills doctor # diagnose install (✓/✗ + fix)
60
+ left-skills report --markdown > report.md # export report
59
61
  ```
60
62
 
61
63
  Example:
package/README.zh.md CHANGED
@@ -56,6 +56,8 @@ npm uninstall -g left-skills # 卸 binary
56
56
  left-skills usage # 人看报告
57
57
  left-skills usage --json # AI 用(JSON)
58
58
  left-skills usage --since 7 # 近 7 天
59
+ left-skills doctor # 诊断安装(✓/✗ + 建议)
60
+ left-skills report --markdown > report.md # 导出报告
59
61
  ```
60
62
 
61
63
  报告示例:
package/dist/cli.js CHANGED
@@ -3201,6 +3201,22 @@ function relativeTime(iso) {
3201
3201
  if (days === 1) return "1 \u5929\u524D";
3202
3202
  return `${days} \u5929\u524D`;
3203
3203
  }
3204
+ function formatMarkdown(report) {
3205
+ const lines = [];
3206
+ lines.push(`# left-skills usage report`);
3207
+ lines.push("");
3208
+ lines.push(`Generated: ${report.generated_at} | Skills: ${report.skills.length}`);
3209
+ lines.push("");
3210
+ lines.push("| skill | manual | AI | mention | total | last |");
3211
+ lines.push("|---|---|---|---|---|---|");
3212
+ for (const s of report.skills) {
3213
+ const total = s.manual + s.ai + s.mention;
3214
+ const last = s.last_used ? relativeTime(s.last_used) : "-";
3215
+ const mark = total === 0 ? " \u26A0" : "";
3216
+ lines.push(`| ${s.name}${mark} | ${s.manual} | ${s.ai} | ${s.mention} | ${total} | ${last} |`);
3217
+ }
3218
+ return lines.join("\n");
3219
+ }
3204
3220
 
3205
3221
  // src/install.ts
3206
3222
  var import_node_fs3 = require("fs");
@@ -3272,10 +3288,64 @@ function globalSettingsPath() {
3272
3288
  return (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".claude", "settings.json");
3273
3289
  }
3274
3290
 
3291
+ // src/doctor.ts
3292
+ var import_node_fs4 = require("fs");
3293
+ var import_node_os4 = require("os");
3294
+ var import_node_path4 = require("path");
3295
+ var import_node_child_process = require("child_process");
3296
+ function checkBinary() {
3297
+ try {
3298
+ const path = (0, import_node_child_process.execSync)("which left-skills", { encoding: "utf-8" }).trim();
3299
+ return { name: "binary in PATH", ok: true, detail: path };
3300
+ } catch {
3301
+ return { name: "binary in PATH", ok: false, detail: "left-skills \u547D\u4EE4\u627E\u4E0D\u5230", fix: "npm i -g left-skills" };
3302
+ }
3303
+ }
3304
+ function checkHook() {
3305
+ const settingsPath = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".claude", "settings.json");
3306
+ if (!(0, import_node_fs4.existsSync)(settingsPath)) {
3307
+ return { name: "hook in settings", ok: false, detail: "~/.claude/settings.json \u4E0D\u5B58\u5728", fix: "left-skills install --write" };
3308
+ }
3309
+ try {
3310
+ const settings = JSON.parse((0, import_node_fs4.readFileSync)(settingsPath, "utf-8"));
3311
+ const hooks = settings.hooks || {};
3312
+ const hasLeft = Object.values(hooks).some(
3313
+ (entries) => entries.some((e) => (e.hooks || []).some((h) => h.command && h.command.includes("left-skills")))
3314
+ );
3315
+ return hasLeft ? { name: "hook in settings", ok: true, detail: "~/.claude/settings.json \u6709 left-skills hook" } : { name: "hook in settings", ok: false, detail: "settings.json \u65E0 left-skills hook", fix: "left-skills install --write" };
3316
+ } catch {
3317
+ return { name: "hook in settings", ok: false, detail: "settings.json \u89E3\u6790\u5931\u8D25", fix: "\u68C0\u67E5 ~/.claude/settings.json JSON" };
3318
+ }
3319
+ }
3320
+ function checkUsageFile() {
3321
+ const usagePath = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".left-skills", "usage.json");
3322
+ return (0, import_node_fs4.existsSync)(usagePath) ? { name: "usage.json", ok: true, detail: "~/.left-skills/usage.json \u5B58\u5728" } : { name: "usage.json", ok: false, detail: "~/.left-skills/usage.json \u4E0D\u5B58\u5728(\u9996\u6B21\u7528\u540E\u521B\u5EFA)", fix: "\u6253 /skill \u6216 AI \u8C03 skill \u89E6\u53D1 hook \u8BB0\u5F55" };
3323
+ }
3324
+ function checkHookCanRun() {
3325
+ try {
3326
+ (0, import_node_child_process.execSync)('echo "{}" | left-skills hook UserPromptExpansion', { encoding: "utf-8", stdio: "pipe" });
3327
+ return { name: "hook command runs", ok: true, detail: "left-skills hook \u80FD\u6267\u884C" };
3328
+ } catch {
3329
+ return { name: "hook command runs", ok: false, detail: "left-skills hook \u547D\u4EE4\u6267\u884C\u5931\u8D25", fix: "\u68C0\u67E5 left-skills binary / node" };
3330
+ }
3331
+ }
3332
+ function checkNodeVersion() {
3333
+ try {
3334
+ const ver = (0, import_node_child_process.execSync)("node --version", { encoding: "utf-8" }).trim();
3335
+ const major = parseInt(ver.slice(1).split(".")[0], 10);
3336
+ return major >= 18 ? { name: "node version", ok: true, detail: `${ver} (\u226518)` } : { name: "node version", ok: false, detail: `${ver} (<18)`, fix: "\u5347\u7EA7 node \u5230 18+" };
3337
+ } catch {
3338
+ return { name: "node version", ok: false, detail: "node \u547D\u4EE4\u627E\u4E0D\u5230", fix: "\u88C5 node 18+" };
3339
+ }
3340
+ }
3341
+ function runDoctor() {
3342
+ return [checkBinary(), checkHook(), checkUsageFile(), checkHookCanRun(), checkNodeVersion()];
3343
+ }
3344
+
3275
3345
  // package.json
3276
3346
  var package_default = {
3277
3347
  name: "left-skills",
3278
- version: "0.2.0",
3348
+ version: "0.3.0",
3279
3349
  description: "\u7ED9 AI \u7528\u7684 skill \u751F\u547D\u5468\u671F\u7BA1\u7406\u5DE5\u5177 \u2014 MVP: skill \u8C03\u7528\u4F7F\u7528\u7EDF\u8BA1",
3280
3350
  bin: {
3281
3351
  "left-skills": "./dist/cli.js"
@@ -3329,6 +3399,19 @@ program2.command("usage").description("skill \u8C03\u7528\u4F7F\u7528\u62A5\u544
3329
3399
  console.log(formatHuman(report));
3330
3400
  }
3331
3401
  });
3402
+ program2.command("report").description("\u5BFC\u51FA usage \u62A5\u544A markdown(\u53EF > report.md \u5206\u4EAB)").option("--markdown", "\u8F93\u51FA markdown(\u9ED8\u8BA4\u5373 markdown)").option("--since <days>", "\u65F6\u95F4\u7A97\u53E3(\u5929,\u9ED8\u8BA4 30)", "30").action((opts) => {
3403
+ const since = parseInt(opts.since, 10) || 30;
3404
+ const report = buildReport(since);
3405
+ console.log(formatMarkdown(report));
3406
+ });
3407
+ program2.command("doctor").description("\u8BCA\u65AD left-skills \u5B89\u88C5/hook \u914D\u7F6E(\u2713/\u2717 + \u4FEE\u590D\u5EFA\u8BAE)").action(() => {
3408
+ const results = runDoctor();
3409
+ for (const r of results) {
3410
+ const mark = r.ok ? "\u2713" : "\u2717";
3411
+ console.log(`${mark} ${r.name}: ${r.detail}`);
3412
+ if (r.fix) console.log(` \u2192 \u4FEE\u590D: ${r.fix}`);
3413
+ }
3414
+ });
3332
3415
  program2.command("install").description("\u8F93\u51FA hook \u914D\u7F6E\u7247\u6BB5(\u9ED8\u8BA4)\u6216 --write \u81EA\u52A8\u5199\u8FDB ~/.claude/settings.json(\u5408\u5E76+\u5907\u4EFD .bak)").option("--write", "\u81EA\u52A8\u5199 hook \u5230 settings.json(\u5408\u5E76\u53BB\u91CD + \u5907\u4EFD .bak)", false).action((opts) => {
3333
3416
  if (opts.write) {
3334
3417
  const path = globalSettingsPath();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "left-skills",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "给 AI 用的 skill 生命周期管理工具 — MVP: skill 调用使用统计",
5
5
  "bin": {
6
6
  "left-skills": "./dist/cli.js"